{"version":3,"file":"confine.cjs","names":["resolve"],"sources":["../src/confine.ts"],"sourcesContent":["/**\n * Confinement policy generation: rules that keep an agent's file tools inside a set of directories.\n *\n * Deny-first evaluation means \"deny everything except these directories\" cannot be written as a blanket deny plus allows, because the deny would beat the allows. No pattern dialect can negate either: patterns are exact, `prefix:*` or `*` wildcards. The generated policy therefore does three things the evaluator can express:\n *\n * 1. Allow the file tools on each root and on everything below it.\n * 2. Deny any path containing a `..` segment, because a wildcard allow such as `/work/app/*` matches the text `/work/app/../secret` even though that path leaves the root.\n * 3. Set `defaultMode` so that a file tool call matching no allow rule asks (or is denied). That mode is global: it applies to every tool without a rule of its own, not only the file tools.\n *\n * This is a convenience, not a sandbox. Only the file tools are constrained; a `Bash` command can still read or write anywhere. Use the `sandbox` field for enforcement.\n */\n\nimport { resolve } from \"node:path\";\n\nimport type { AgentPermissionPolicy, Rule } from \"./schema.ts\";\n\n/** The tools whose path argument is confined. */\nconst FILE_TOOLS = [\"Read\", \"Edit\", \"Write\"] as const;\n\n/** What happens to a file tool call outside the roots: `ask` prompts (default mode `restricted`), `deny` refuses (default mode `readonly`). */\nexport type OutsideBehaviour = \"ask\" | \"deny\";\n\n/** Inputs to {@link confine}. */\nexport interface ConfineOptions {\n  /** Directories the file tools may reach. Relative entries resolve against `cwd`. */\n  roots: readonly string[];\n  /** The agent's working directory, which needs no `additionalDirectories` entry. */\n  cwd: string;\n  /** Behaviour outside the roots. Defaults to `ask`. */\n  outside?: OutsideBehaviour;\n}\n\n/** Escape a literal path for use inside a wildcard pattern, where `*` matches any run of characters and a backslash escapes. */\nfunction escapePath(path: string): string {\n  return path.replace(/[\\\\*]/g, \"\\\\$&\");\n}\n\n/**\n * Generate a canonical policy that confines `Read`, `Edit` and `Write` to `options.roots`.\n *\n * Paths are normalised with `path.resolve` against `cwd`: made absolute, `.` and `..` segments collapsed, trailing separators removed, duplicates dropped. Tool inputs are matched as literal text, so a call whose path is relative or contains `..` is never allowed by the root rules.\n *\n * @throws Error if `roots` is empty.\n */\nexport function confine(options: ConfineOptions): AgentPermissionPolicy {\n  const { cwd, outside = \"ask\" } = options;\n  const roots = [...new Set(options.roots.map((root) => resolve(cwd, root)))];\n  if (roots.length === 0) throw new Error(\"at least one root is required\");\n\n  const rules: Rule[] = [];\n\n  // The root itself is matched exactly; the wildcard requires the separator, so a sibling that shares the root as a string prefix (`/work/app-secrets`) does not match.\n  for (const root of roots) {\n    const below = root.endsWith(\"/\") ? root : `${root}/`;\n    for (const tool of FILE_TOOLS) {\n      rules.push({ tool, pattern: escapePath(root), tier: \"allow\" });\n      rules.push({ tool, pattern: `${escapePath(below)}*`, tier: \"allow\" });\n    }\n  }\n\n  for (const tool of FILE_TOOLS) {\n    for (const pattern of [\"..\", \"../*\", \"*/..\", \"*/../*\"]) {\n      rules.push({ tool, pattern, tier: \"deny\" });\n    }\n  }\n\n  const policy: AgentPermissionPolicy = {\n    defaultMode: outside === \"deny\" ? \"readonly\" : \"restricted\",\n    rules,\n  };\n  const additionalDirectories = roots.filter((root) => root !== resolve(cwd));\n  if (additionalDirectories.length > 0) {\n    policy.permissions = { additionalDirectories };\n  }\n  return policy;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiBA,MAAM,aAAa;CAAC;CAAQ;CAAQ;AAAO;;AAgB3C,SAAS,WAAW,MAAsB;CACxC,OAAO,KAAK,QAAQ,UAAU,MAAM;AACtC;;;;;;;;AASA,SAAgB,QAAQ,SAAgD;CACtE,MAAM,EAAE,KAAK,UAAU,UAAU;CACjC,MAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,QAAQ,MAAM,KAAK,aAASA,mBAAQ,KAAK,IAAI,CAAC,CAAC,CAAC;CAC1E,IAAI,MAAM,WAAW,GAAG,MAAM,IAAI,MAAM,+BAA+B;CAEvE,MAAM,QAAgB,CAAC;CAGvB,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,KAAK,SAAS,GAAG,IAAI,OAAO,GAAG,KAAK;EAClD,KAAK,MAAM,QAAQ,YAAY;GAC7B,MAAM,KAAK;IAAE;IAAM,SAAS,WAAW,IAAI;IAAG,MAAM;GAAQ,CAAC;GAC7D,MAAM,KAAK;IAAE;IAAM,SAAS,GAAG,WAAW,KAAK,EAAE;IAAI,MAAM;GAAQ,CAAC;EACtE;CACF;CAEA,KAAK,MAAM,QAAQ,YACjB,KAAK,MAAM,WAAW;EAAC;EAAM;EAAQ;EAAQ;CAAQ,GACnD,MAAM,KAAK;EAAE;EAAM;EAAS,MAAM;CAAO,CAAC;CAI9C,MAAM,SAAgC;EACpC,aAAa,YAAY,SAAS,aAAa;EAC/C;CACF;CACA,MAAM,wBAAwB,MAAM,QAAQ,SAAS,aAASA,mBAAQ,GAAG,CAAC;CAC1E,IAAI,sBAAsB,SAAS,GACjC,OAAO,cAAc,EAAE,sBAAsB;CAE/C,OAAO;AACT"}