# Agent configuration and policy files

> For the end-to-end setup flow, see [Brass Agent install and configure](./agent-install-and-configure.md).

P7 adds a small JSON configuration file for the experimental `brass-agent` CLI.
The goal is to keep policy out of hardcoded TypeScript while preserving the
runtime boundary:

```txt
src/core
  ↑
src/agent policy/config
  ↑
src/agent/cli loads .brass-agent.json
```

`src/core` still does not know that the agent exists. The CLI loads config as a
Node setup step, then passes the resolved policy into `AgentEnv`.

## Discovery

By default, the CLI searches upward from `--cwd` for either file:

```txt
.brass-agent.json
brass-agent.config.json
```

You can force a specific file:

```bash
brass-agent --config ./agent.policy.json "fix the failing tests"
```

Or disable config loading entirely:

```bash
brass-agent --no-config "fix the failing tests"
```

## Precedence

Configuration is intentionally lower precedence than explicit execution knobs:

```txt
CLI flags > environment variables > .brass-agent.json > built-in defaults
```

Examples:

```txt
--mode write beats config.mode
--approval deny beats config.approval
BRASS_LLM_PROVIDER beats config.llm.provider
BRASS_LLM_MODEL beats config.llm.model
```

## Minimal config

```json
{
  "mode": "propose",
  "approval": "auto",
  "llm": {
    "provider": "google",
    "model": "gemini-2.5-flash",
    "apiKeyEnv": "GEMINI_API_KEY"
  }
}
```

Do not store API keys in the config file. Use `apiKeyEnv` and keep the secret in
the environment.

## LLM config

```json
{
  "llm": {
    "provider": "google",
    "model": "gemini-2.5-flash",
    "apiKeyEnv": "GEMINI_API_KEY",
    "temperature": 0.2,
    "maxOutputTokens": 4096
  }
}
```

OpenAI-compatible example:

```json
{
  "llm": {
    "provider": "openai-compatible",
    "endpoint": "https://api.openai.com/v1/chat/completions",
    "model": "gpt-4.1",
    "apiKeyEnv": "BRASS_LLM_API_KEY"
  }
}
```

Offline/fake example:

```json
{
  "llm": {
    "provider": "fake",
    "fakeResponse": "Fake plan from config"
  }
}
```

## Project command discovery

P8 adds project-aware command discovery. The agent reads `package.json`, checks
common lockfiles, infers npm/pnpm/yarn/bun, and selects validation commands from
scripts instead of assuming `npm test`.

Basic example:

```json
{
  "project": {
    "packageManager": "auto",
    "testScriptNames": ["test", "test:ci", "test:unit"],
    "includeTypecheck": true,
    "maxValidationCommands": 2
  }
}
```

Exact validation commands override discovery:

```json
{
  "project": {
    "validationCommands": [
      "pnpm run test:unit",
      "pnpm run typecheck"
    ]
  }
}
```

Use an empty array to disable shell validation:

```json
{
  "project": {
    "validationCommands": []
  }
}
```

Supported `project.packageManager` values are `auto`, `npm`, `pnpm`,
`yarn`, and `bun`.

See [Agent project command discovery](./agent-project-commands.md).

## Context discovery

P12 adds a bounded context discovery pass before planning. It reads direct files
mentioned by validation output, searches likely identifiers, and reads a small
number of matched files before calling the LLM.

```json
{
  "context": {
    "enabled": true,
    "maxSearchQueries": 3,
    "maxFiles": 4,
    "maxSearchResults": 40,
    "globs": ["*.ts", "*.tsx", "*.json"]
  }
}
```

Disable it when you want the old minimal context behavior:

```json
{
  "context": {
    "enabled": false
  }
}
```

See [Agent context discovery](./agent-context-discovery.md).

## Patch quality loop

P13 adds a bounded repair loop for patches generated by the agent. If a generated patch fails to apply, or if validation still fails after `patch.applied`, the agent can ask the LLM for an incremental repair patch.

```json
{
  "patchQuality": {
    "enabled": true,
    "maxRepairAttempts": 1
  }
}
```

Set `maxRepairAttempts` to `0` or `enabled` to `false` to keep one-shot apply behavior. Repairs are disabled for exact patch-file runs such as `--apply-patch-file` so the VS Code preview flow still applies exactly the patch the user approved.

See [Agent patch quality loop](./agent-patch-quality-loop.md).


## Automatic rollback safety

P14 adds automatic rollback safety for generated patches. After validation fails
and the patch quality repair budget is exhausted, the agent can reverse-apply
generated patches through `PatchService.rollback`.

```json
{
  "rollback": {
    "enabled": true,
    "onFinalValidationFailure": true,
    "strategy": "all",
    "maxRollbackDepth": 8,
    "runValidationAfterRollback": true,
    "allowForSuppliedPatches": false
  }
}
```

`strategy` can be `last` or `all`. Exact patch-file flows such as
`--apply-patch-file` are protected by default, so VS Code preview still applies
exactly the patch the user approved.

See [Agent automatic rollback safety](./agent-rollback-safety.md).

## Batch runs

P21 adds optional default batch goals. The CLI uses `config.batch.goals` only when no explicit goal, `--preset`, `--patch-file`, or `--batch-file` is provided.

```json
{
  "batch": {
    "stopOnFailure": true,
    "goals": [
      { "preset": "inspect" },
      { "preset": "typecheck" },
      { "preset": "lint" }
    ]
  }
}
```

See [Agent batch runs](./agent-batch.md).

## Permission config

Shell commands are matched against the command array joined with spaces. Patterns
support `*` as a simple wildcard.

Built-in safe validation commands remain allowed by default for npm, pnpm, yarn,
and bun, along with read-only git commands. Examples include:

```txt
npm test
npm run test*
npm run typecheck
pnpm test
pnpm run test*
yarn run lint*
bun run check
git status
git diff
git log
```

You can extend them:

```json
{
  "permissions": {
    "shell": {
      "allow": [
        "npm run typecheck",
        "npm run lint",
        "pnpm test *"
      ],
      "ask": [
        {
          "pattern": "npm run build",
          "reason": "Build can be slow; confirm before running it.",
          "risk": "medium",
          "defaultAnswer": "approve"
        }
      ],
      "deny": [
        "rm *",
        "git push *"
      ]
    }
  }
}
```

For a strict allowlist, disable inherited defaults:

```json
{
  "permissions": {
    "shell": {
      "inheritDefaults": false,
      "allow": ["npm test"]
    }
  }
}
```

Deny rules win over ask rules, and ask rules win over allow rules.

## Patch apply policy

`patch.apply` is still denied in `read-only` and `propose` modes. In `write` and
`autonomous` modes, you can configure the decision:

```json
{
  "permissions": {
    "patchApply": {
      "decision": "ask",
      "reason": "Apply the generated diff to the workspace.",
      "risk": "high",
      "defaultAnswer": "reject"
    }
  }
}
```

Short form:

```json
{
  "permissions": {
    "patchApply": "ask"
  }
}
```

Supported values are `allow`, `ask`, and `deny`.

## Tool policy overrides

Tool policies let a project tune timeouts and retries without changing code.
Keys are `AgentAction.type` strings:

```json
{
  "tools": {
    "fs.readFile": {
      "timeoutMs": 10000,
      "retries": 1
    },
    "fs.exists": {
      "timeoutMs": 5000,
      "retries": 1
    },
    "llm.complete": {
      "timeoutMs": 90000,
      "retries": 3
    },
    "shell.exec": {
      "timeoutMs": 180000,
      "retries": 0
    },
    "patch.apply": {
      "timeoutMs": 30000,
      "retries": 0
    }
  }
}
```

Retryability is still controlled in code by error class. Config can tune counts
and timeouts, but it cannot make unsafe errors retryable.

## Full example

```json
{
  "mode": "propose",
  "approval": "auto",
  "llm": {
    "provider": "google",
    "model": "gemini-2.5-flash",
    "apiKeyEnv": "GEMINI_API_KEY",
    "temperature": 0.2,
    "maxOutputTokens": 4096
  },
  "project": {
    "packageManager": "auto",
    "testScriptNames": ["test", "test:ci", "test:unit"],
    "includeTypecheck": true,
    "maxValidationCommands": 2
  },
  "patchQuality": {
    "enabled": true,
    "maxRepairAttempts": 1
  },
  "rollback": {
    "enabled": true,
    "strategy": "all",
    "maxRollbackDepth": 8
  },
  "permissions": {
    "shell": {
      "inheritDefaults": true,
      "allow": [
        "npm run typecheck",
        "npm run lint"
      ],
      "ask": [
        {
          "pattern": "npm run build",
          "reason": "Build may take a while.",
          "risk": "medium",
          "defaultAnswer": "approve"
        }
      ],
      "deny": [
        "git push *",
        "rm *"
      ]
    },
    "patchApply": {
      "decision": "ask",
      "risk": "high",
      "defaultAnswer": "reject"
    }
  },
  "tools": {
    "llm.complete": {
      "timeoutMs": 90000,
      "retries": 2
    },
    "shell.exec": {
      "timeoutMs": 180000,
      "retries": 0
    }
  }
}
```


## Context exclude globs

```json
{
  "context": {
    "excludeGlobs": [".env*", "secrets/**", "*.pem", "*.key"]
  }
}
```

These globs reduce what context discovery reads or searches before prompting the LLM.

## Language config

Natural-language responses can match the user's prompt automatically, or be fixed per workspace:

```json
{
  "language": {
    "response": "es"
  }
}
```

Supported values are `auto`, `match-user`, `en`, `es`, `pt`, `fr`, `de`, `it`, and `custom`.
Use `custom` with `language.custom` for a human-readable language name.

The language policy affects explanations and summaries only. Code, identifiers,
file paths, shell commands, logs, and unified diffs remain unchanged.

The VS Code extension can create or update this section through **Brass Agent:
Configure Workspace** or `/workspace` in the Chat view.
