brindle 0.3.10__tar.gz → 0.3.12__tar.gz
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.
- {brindle-0.3.10 → brindle-0.3.12}/PKG-INFO +56 -4
- {brindle-0.3.10 → brindle-0.3.12}/README.md +55 -3
- {brindle-0.3.10 → brindle-0.3.12}/pyproject.toml +1 -1
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/agents.py +19 -2
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/cli.py +4 -1
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/cloud_credential.py +24 -4
- brindle-0.3.12/src/brindle/codemap.py +422 -0
- brindle-0.3.12/src/brindle/codemap_docs.py +65 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/company_login.py +38 -6
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/cost.py +17 -2
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/cull.py +6 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/events.py +29 -6
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/history.py +1 -1
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/learning.py +14 -10
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pipeline.py +14 -6
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/policy.py +4 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/account.py +1 -2
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/auth.py +2 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/learning.py +11 -9
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/license.py +63 -10
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/managed_models.py +22 -4
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/org_profiles.py +2 -1
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/route_health.py +5 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/status.py +20 -1
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/team_policy.py +47 -7
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/providers.py +5 -2
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/usage.py +44 -7
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/watch.py +11 -3
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/workspaces.py +16 -4
- brindle-0.3.10/src/brindle/codemap.py +0 -73
- {brindle-0.3.10 → brindle-0.3.12}/.gitignore +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/LICENSE +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/SCHEDULE-A +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/__init__.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/__main__.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/account.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/airgap.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/antigravity.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/autopilot.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/budget.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/__init__.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/backend-auditor.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/developer-codex.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/developer-fable.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/developer-gemini.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/developer-heavy.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/developer-light.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/developer-local.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/developer.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/qa.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/reviewer-codex.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/reviewer-gemini.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/reviewer-local.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/reviewer.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/subagent.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_agents/supervisor.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_rules/__init__.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_rules/security/backend.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_rules/style/minimal-diff.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/builtin_rules/tests/only.md +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/ci_adapters.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/ci_client.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/ci_federation.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/ci_hosts.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/codex_hook.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/company_identity.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/config.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/conflicts.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/cost_estimate.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/db.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/demo.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/detect.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/doctor.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/escapes.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/gates.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/git.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/guardrails.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/identity_lock.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/inbox.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/keys_cmds.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/keystore.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/launch_health.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/learned_rules.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/legacy.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/mcp_server.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/model_access.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/model_availability.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/native/__init__.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/native/client.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/native/loop.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/native/permissions.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/native/runner.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/native/serve.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/native/tools.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pane_auth.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/permissions.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/plugins.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pool.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pricing.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/__init__.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/_files.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/audit_chain.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/audit_export.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/brief_learning.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/cost_centers.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/credentials.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/features.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/keys.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/loopback.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/orgkey.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/rollout.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/settings_sync.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/pro/team_events.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/procs.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/profiles.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/quota.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/repo_cmds.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/repos.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/rewind.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/rule_checks.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/savings.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/scratch.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/secrets.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/self_serve.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/services.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/sessions.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/sidebar_follow.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/signin_pause.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/status_cache.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/tasks.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/tmux.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/updates.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/view.py +0 -0
- {brindle-0.3.10 → brindle-0.3.12}/src/brindle/watch_costs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brindle
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.12
|
|
4
4
|
Summary: Run coding agents in parallel, each on its own git branch, with a supervisor that delegates, reviews, and merges
|
|
5
5
|
Project-URL: Homepage, https://pawdelta.com/brindle/
|
|
6
6
|
Project-URL: Documentation, https://pawdelta.com/brindle/docs/recommended-use
|
|
@@ -94,7 +94,7 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
|
|
|
94
94
|
brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
|
|
95
95
|
|
|
96
96
|
`brindle --version` prints the installed version; the tmux status bar of every brindle
|
|
97
|
-
session shows it too (`brindle 0.3.
|
|
97
|
+
session shows it too (`brindle 0.3.12`). A session started before an upgrade keeps
|
|
98
98
|
running the old code, and shows the old number, until you restart it.
|
|
99
99
|
|
|
100
100
|
## Quick start
|
|
@@ -1865,12 +1865,64 @@ documents its own quirks (no prompt caching on most, smaller context windows),
|
|
|
1865
1865
|
so prefer the native provider for open-weight models and keep this route for
|
|
1866
1866
|
Claude itself behind a gateway.
|
|
1867
1867
|
|
|
1868
|
+
To run workers on your own Bedrock, Vertex, Codex or Antigravity account, see
|
|
1869
|
+
[Running workers on your own cloud](#running-workers-on-your-own-cloud) below.
|
|
1870
|
+
|
|
1868
1871
|
Codex agents report status through Codex's `notify` hook (brindle passes
|
|
1869
1872
|
`-c notify=[...]` at launch, leaving your own Codex config alone): a completed turn
|
|
1870
1873
|
marks the agent idle and delivers any queued message. Codex has no Stop hook, so for
|
|
1871
1874
|
an autopilot supervisor on Codex that turn end is also where brindle tells it to keep
|
|
1872
1875
|
going, with the same limit on reminders as Claude Code.
|
|
1873
1876
|
|
|
1877
|
+
### Running workers on your own cloud
|
|
1878
|
+
|
|
1879
|
+
Any brindle user, free tier included, can run a worker's model on their own cloud
|
|
1880
|
+
account. A profile's `env.NAME: value` lines are set in the agent's environment
|
|
1881
|
+
before brindle's own variables, so the profile decides which backend the worker
|
|
1882
|
+
uses. Profiles live in `.brindle/agents/` in the repo, or in your user profiles
|
|
1883
|
+
folder; `brindle profile new` writes one.
|
|
1884
|
+
|
|
1885
|
+
```markdown
|
|
1886
|
+
---
|
|
1887
|
+
name: developer-bedrock
|
|
1888
|
+
provider: claude
|
|
1889
|
+
model: us.anthropic.claude-haiku-4-5-20251001-v1:0 # a Bedrock model id your account can use
|
|
1890
|
+
env.CLAUDE_CODE_USE_BEDROCK: 1
|
|
1891
|
+
env.AWS_REGION: us-east-1
|
|
1892
|
+
---
|
|
1893
|
+
```
|
|
1894
|
+
|
|
1895
|
+
Pass `developer-bedrock` as the worker profile (`agent_profile` in `assign`), or
|
|
1896
|
+
list it under `routing` in `.brindle/config.json`.
|
|
1897
|
+
|
|
1898
|
+
- **Claude Code on Bedrock.** Set `env.CLAUDE_CODE_USE_BEDROCK: 1` and
|
|
1899
|
+
`env.AWS_REGION`. The worker uses the AWS credentials on this machine (an
|
|
1900
|
+
`AWS_PROFILE` from `~/.aws/config`, SSO or environment keys), so sign in to AWS
|
|
1901
|
+
first. See [Claude Code on Amazon Bedrock](https://code.claude.com/docs/en/amazon-bedrock).
|
|
1902
|
+
- **Claude Code on Vertex AI.** Set `env.CLAUDE_CODE_USE_VERTEX: 1`,
|
|
1903
|
+
`env.CLOUD_ML_REGION` and `env.ANTHROPIC_VERTEX_PROJECT_ID`. The worker
|
|
1904
|
+
authenticates with Application Default Credentials, so run
|
|
1905
|
+
`gcloud auth application-default login` once. See
|
|
1906
|
+
[Claude Code on Google Vertex AI](https://code.claude.com/docs/en/google-vertex-ai).
|
|
1907
|
+
- **Codex on Bedrock.** Codex picks its provider in `config.toml` in its home
|
|
1908
|
+
folder (`$CODEX_HOME`, default `~/.codex`). Point that file at the
|
|
1909
|
+
`amazon-bedrock` provider, as described in
|
|
1910
|
+
[Codex advanced configuration](https://learn.chatgpt.com/docs/config-file/config-advanced).
|
|
1911
|
+
To give one profile its own Codex home, add `env.CODEX_HOME: /path/to/home`
|
|
1912
|
+
to it. Each Codex home keeps its own sign-in (`auth.json`).
|
|
1913
|
+
- **Google Antigravity.** `agy` can run on a Gemini API key: set
|
|
1914
|
+
`"modelProvider": "gemini"` in `~/.gemini/antigravity-cli/settings.json`, and
|
|
1915
|
+
export `GEMINI_API_KEY` in your shell, or put it in a profile in your user
|
|
1916
|
+
profiles folder; never in a profile under `.brindle/agents/`, which is committed. For a
|
|
1917
|
+
Gemini-compatible gateway, set `env.GOOGLE_GEMINI_BASE_URL` to its URL. As of
|
|
1918
|
+
1.3.4, `agy` ignores Vertex AI credentials, so use a Gemini API key or gateway.
|
|
1919
|
+
The full setup is in [Google Antigravity](#google-antigravity) below.
|
|
1920
|
+
|
|
1921
|
+
If your org runs brindle for a team, its admins can set a route for everyone in
|
|
1922
|
+
the org's Routing settings on the brindle dashboard; an enforced org route replaces
|
|
1923
|
+
a profile's `env.` lines, so a profile can't move a worker off the company's route. See
|
|
1924
|
+
[Using your company's access](#using-your-companys-access).
|
|
1925
|
+
|
|
1874
1926
|
### Using your company's access
|
|
1875
1927
|
|
|
1876
1928
|
Two ways to run brindle's agents on your company's Claude (Bedrock, Vertex,
|
|
@@ -1908,8 +1960,8 @@ agy # sign in with your Google account, then quit
|
|
|
1908
1960
|
To run unattended workers without a browser login, `agy` (1.1.13 or newer) can use
|
|
1909
1961
|
a Gemini API key instead: put `"modelProvider": "gemini"` in
|
|
1910
1962
|
`~/.gemini/antigravity-cli/settings.json` and set `GEMINI_API_KEY`, in your
|
|
1911
|
-
environment or in a profile's `env.GEMINI_API_KEY: ...` line
|
|
1912
|
-
|
|
1963
|
+
environment or in a profile's `env.GEMINI_API_KEY: ...` line in your user
|
|
1964
|
+
profiles folder (never in `.brindle/agents/`, which is committed). The key alone does nothing: without `modelProvider`, `agy` still asks
|
|
1913
1965
|
you to sign in. `agy` takes no other key or token from the environment; its
|
|
1914
1966
|
Google Cloud, Workforce Identity and Application Default Credentials sign-ins are
|
|
1915
1967
|
chosen on its sign-in screen.
|
|
@@ -63,7 +63,7 @@ uv tool install --editable ~/Projects/brindle # or from a local checkout
|
|
|
63
63
|
brindle drives Claude Code, so you need that too (`npm install -g @anthropic-ai/claude-code`).
|
|
64
64
|
|
|
65
65
|
`brindle --version` prints the installed version; the tmux status bar of every brindle
|
|
66
|
-
session shows it too (`brindle 0.3.
|
|
66
|
+
session shows it too (`brindle 0.3.12`). A session started before an upgrade keeps
|
|
67
67
|
running the old code, and shows the old number, until you restart it.
|
|
68
68
|
|
|
69
69
|
## Quick start
|
|
@@ -1834,12 +1834,64 @@ documents its own quirks (no prompt caching on most, smaller context windows),
|
|
|
1834
1834
|
so prefer the native provider for open-weight models and keep this route for
|
|
1835
1835
|
Claude itself behind a gateway.
|
|
1836
1836
|
|
|
1837
|
+
To run workers on your own Bedrock, Vertex, Codex or Antigravity account, see
|
|
1838
|
+
[Running workers on your own cloud](#running-workers-on-your-own-cloud) below.
|
|
1839
|
+
|
|
1837
1840
|
Codex agents report status through Codex's `notify` hook (brindle passes
|
|
1838
1841
|
`-c notify=[...]` at launch, leaving your own Codex config alone): a completed turn
|
|
1839
1842
|
marks the agent idle and delivers any queued message. Codex has no Stop hook, so for
|
|
1840
1843
|
an autopilot supervisor on Codex that turn end is also where brindle tells it to keep
|
|
1841
1844
|
going, with the same limit on reminders as Claude Code.
|
|
1842
1845
|
|
|
1846
|
+
### Running workers on your own cloud
|
|
1847
|
+
|
|
1848
|
+
Any brindle user, free tier included, can run a worker's model on their own cloud
|
|
1849
|
+
account. A profile's `env.NAME: value` lines are set in the agent's environment
|
|
1850
|
+
before brindle's own variables, so the profile decides which backend the worker
|
|
1851
|
+
uses. Profiles live in `.brindle/agents/` in the repo, or in your user profiles
|
|
1852
|
+
folder; `brindle profile new` writes one.
|
|
1853
|
+
|
|
1854
|
+
```markdown
|
|
1855
|
+
---
|
|
1856
|
+
name: developer-bedrock
|
|
1857
|
+
provider: claude
|
|
1858
|
+
model: us.anthropic.claude-haiku-4-5-20251001-v1:0 # a Bedrock model id your account can use
|
|
1859
|
+
env.CLAUDE_CODE_USE_BEDROCK: 1
|
|
1860
|
+
env.AWS_REGION: us-east-1
|
|
1861
|
+
---
|
|
1862
|
+
```
|
|
1863
|
+
|
|
1864
|
+
Pass `developer-bedrock` as the worker profile (`agent_profile` in `assign`), or
|
|
1865
|
+
list it under `routing` in `.brindle/config.json`.
|
|
1866
|
+
|
|
1867
|
+
- **Claude Code on Bedrock.** Set `env.CLAUDE_CODE_USE_BEDROCK: 1` and
|
|
1868
|
+
`env.AWS_REGION`. The worker uses the AWS credentials on this machine (an
|
|
1869
|
+
`AWS_PROFILE` from `~/.aws/config`, SSO or environment keys), so sign in to AWS
|
|
1870
|
+
first. See [Claude Code on Amazon Bedrock](https://code.claude.com/docs/en/amazon-bedrock).
|
|
1871
|
+
- **Claude Code on Vertex AI.** Set `env.CLAUDE_CODE_USE_VERTEX: 1`,
|
|
1872
|
+
`env.CLOUD_ML_REGION` and `env.ANTHROPIC_VERTEX_PROJECT_ID`. The worker
|
|
1873
|
+
authenticates with Application Default Credentials, so run
|
|
1874
|
+
`gcloud auth application-default login` once. See
|
|
1875
|
+
[Claude Code on Google Vertex AI](https://code.claude.com/docs/en/google-vertex-ai).
|
|
1876
|
+
- **Codex on Bedrock.** Codex picks its provider in `config.toml` in its home
|
|
1877
|
+
folder (`$CODEX_HOME`, default `~/.codex`). Point that file at the
|
|
1878
|
+
`amazon-bedrock` provider, as described in
|
|
1879
|
+
[Codex advanced configuration](https://learn.chatgpt.com/docs/config-file/config-advanced).
|
|
1880
|
+
To give one profile its own Codex home, add `env.CODEX_HOME: /path/to/home`
|
|
1881
|
+
to it. Each Codex home keeps its own sign-in (`auth.json`).
|
|
1882
|
+
- **Google Antigravity.** `agy` can run on a Gemini API key: set
|
|
1883
|
+
`"modelProvider": "gemini"` in `~/.gemini/antigravity-cli/settings.json`, and
|
|
1884
|
+
export `GEMINI_API_KEY` in your shell, or put it in a profile in your user
|
|
1885
|
+
profiles folder; never in a profile under `.brindle/agents/`, which is committed. For a
|
|
1886
|
+
Gemini-compatible gateway, set `env.GOOGLE_GEMINI_BASE_URL` to its URL. As of
|
|
1887
|
+
1.3.4, `agy` ignores Vertex AI credentials, so use a Gemini API key or gateway.
|
|
1888
|
+
The full setup is in [Google Antigravity](#google-antigravity) below.
|
|
1889
|
+
|
|
1890
|
+
If your org runs brindle for a team, its admins can set a route for everyone in
|
|
1891
|
+
the org's Routing settings on the brindle dashboard; an enforced org route replaces
|
|
1892
|
+
a profile's `env.` lines, so a profile can't move a worker off the company's route. See
|
|
1893
|
+
[Using your company's access](#using-your-companys-access).
|
|
1894
|
+
|
|
1843
1895
|
### Using your company's access
|
|
1844
1896
|
|
|
1845
1897
|
Two ways to run brindle's agents on your company's Claude (Bedrock, Vertex,
|
|
@@ -1877,8 +1929,8 @@ agy # sign in with your Google account, then quit
|
|
|
1877
1929
|
To run unattended workers without a browser login, `agy` (1.1.13 or newer) can use
|
|
1878
1930
|
a Gemini API key instead: put `"modelProvider": "gemini"` in
|
|
1879
1931
|
`~/.gemini/antigravity-cli/settings.json` and set `GEMINI_API_KEY`, in your
|
|
1880
|
-
environment or in a profile's `env.GEMINI_API_KEY: ...` line
|
|
1881
|
-
|
|
1932
|
+
environment or in a profile's `env.GEMINI_API_KEY: ...` line in your user
|
|
1933
|
+
profiles folder (never in `.brindle/agents/`, which is committed). The key alone does nothing: without `modelProvider`, `agy` still asks
|
|
1882
1934
|
you to sign in. `agy` takes no other key or token from the environment; its
|
|
1883
1935
|
Google Cloud, Workforce Identity and Application Default Credentials sign-ins are
|
|
1884
1936
|
chosen on its sign-in screen.
|
|
@@ -148,7 +148,7 @@ def worker_guidance(ws: Workspace) -> str:
|
|
|
148
148
|
checks = load_repo_config(ws.repo_root).checks
|
|
149
149
|
except ValueError:
|
|
150
150
|
checks = []
|
|
151
|
-
parts = (codemap.guidance(ws
|
|
151
|
+
parts = (codemap.guidance(ws), test_guidance(checks),
|
|
152
152
|
PERMISSION_GUIDANCE.format(root=ws.repo_root))
|
|
153
153
|
return "\n".join(p for p in parts if p)
|
|
154
154
|
|
|
@@ -379,6 +379,9 @@ def spawn(
|
|
|
379
379
|
if plan and not prompt:
|
|
380
380
|
prompt = agent.task = pilot.kickoff(plan)
|
|
381
381
|
db.update_agent(agent_id, task=prompt)
|
|
382
|
+
if mode == "interactive":
|
|
383
|
+
from brindle import codemap
|
|
384
|
+
codemap.seed_session(ws.path, ws.repo_root)
|
|
382
385
|
try:
|
|
383
386
|
_launch(db, agent, ws, prompt=prompt, resume=None, watch_pane=watch_pane,
|
|
384
387
|
background_setup=background_setup, model_checked=True)
|
|
@@ -449,9 +452,12 @@ def _profile_for(db: DB, agent: Agent, ws: Workspace, m=_UNREAD):
|
|
|
449
452
|
if agent.mode == "interactive":
|
|
450
453
|
from brindle import codemap, repos
|
|
451
454
|
|
|
452
|
-
note = codemap.guidance(ws
|
|
455
|
+
note = codemap.guidance(ws)
|
|
453
456
|
if note:
|
|
454
457
|
profile = replace(profile, prompt=f"{profile.prompt}\n\n{note}".strip())
|
|
458
|
+
repo_map = codemap.supervisor_map(ws) if autopilot_on or agent.profile == "supervisor" else None
|
|
459
|
+
if repo_map:
|
|
460
|
+
profile = replace(profile, prompt=f"{profile.prompt}\n\n{repo_map}".strip())
|
|
455
461
|
attached = repos.guidance(db, agent.id) # a session root's attached repos
|
|
456
462
|
if attached:
|
|
457
463
|
profile = replace(profile, prompt=f"{profile.prompt}\n\n{attached}".strip())
|
|
@@ -831,6 +837,11 @@ def _launch(db: DB, agent: Agent, ws: Workspace, *, prompt: str | None,
|
|
|
831
837
|
from brindle.providers import trust_folder
|
|
832
838
|
|
|
833
839
|
trust_folder(ws.path) # no trust dialog for nobody to answer
|
|
840
|
+
if provider.name == "antigravity" and m is not None and m.setup is not None and m.setup.antigravity is not None:
|
|
841
|
+
try:
|
|
842
|
+
providers.set_agy_gemini()
|
|
843
|
+
except (OSError, ValueError) as e:
|
|
844
|
+
raise AgentError(f"couldn't set Antigravity to the company Gemini route in its settings.json: {e}") from e
|
|
834
845
|
argv = provider.command(LaunchContext(agent.id, profile, prompt, resume=resume, cwd=ws.path,
|
|
835
846
|
mode=agent.mode, plan_first=bool(agent.plan_first)))
|
|
836
847
|
if prompt or db.pending_count(agent.id):
|
|
@@ -1675,6 +1686,7 @@ def report_result(db: DB, agent_id: str, result: str, forward: bool = True,
|
|
|
1675
1686
|
else:
|
|
1676
1687
|
agent = get(db, agent_id)
|
|
1677
1688
|
ws = db.get_workspace(agent.workspace_id)
|
|
1689
|
+
first = agent.result is None
|
|
1678
1690
|
db.set_result(agent.id, result)
|
|
1679
1691
|
# Usage and history are extras: never let them stop the result arriving.
|
|
1680
1692
|
try:
|
|
@@ -1701,6 +1713,8 @@ def report_result(db: DB, agent_id: str, result: str, forward: bool = True,
|
|
|
1701
1713
|
else:
|
|
1702
1714
|
forwarded = result
|
|
1703
1715
|
if ws and agent.mode in ("handoff", "handoff_detached", "assign"):
|
|
1716
|
+
if first:
|
|
1717
|
+
pipeline.note_reported(db, ws, agent)
|
|
1704
1718
|
warm_checks(ws)
|
|
1705
1719
|
if forward and pipeline.on_report(db, agent, ws, forwarded):
|
|
1706
1720
|
return ("result recorded. brindle is having your branch reviewed; it will merge it, "
|
|
@@ -2397,6 +2411,9 @@ def pre_tool_decision(db: DB, agent: Agent, payload: dict) -> dict | None:
|
|
|
2397
2411
|
return None
|
|
2398
2412
|
command = str((payload.get("tool_input") or {}).get("command", ""))
|
|
2399
2413
|
ws = db.get_workspace(agent.workspace_id)
|
|
2414
|
+
from brindle import codemap
|
|
2415
|
+
|
|
2416
|
+
codemap.record_query(db, agent, ws, command)
|
|
2400
2417
|
try:
|
|
2401
2418
|
profile = load_profile(agent.profile, ws.repo_root if ws else None)
|
|
2402
2419
|
except Exception:
|
|
@@ -458,8 +458,11 @@ def _session_worktree(db: DB, ws: Workspace) -> Workspace:
|
|
|
458
458
|
n = 2
|
|
459
459
|
while git.ok(["rev-parse", "--verify", "--quiet", f"refs/heads/brindle/session-{n}"], ws.repo_root):
|
|
460
460
|
n += 1
|
|
461
|
-
|
|
461
|
+
created = workspaces.create(db, ws.repo_root, f"brindle/session-{n}", base=ws.branch or None,
|
|
462
462
|
start=git.out(["rev-parse", "HEAD"], ws.path), apply_prefix=False).workspace
|
|
463
|
+
from brindle import codemap
|
|
464
|
+
codemap.seed_session(created.path, created.repo_root)
|
|
465
|
+
return created
|
|
463
466
|
|
|
464
467
|
|
|
465
468
|
def _pause_running(db: DB, ws: Workspace, stop_procs: bool = True) -> None:
|
|
@@ -346,7 +346,7 @@ def assume_role(sts: Sts, region: str, role_arn: str, id_token: str, now: float)
|
|
|
346
346
|
c = claims(id_token)
|
|
347
347
|
status, raw = sts(f"https://sts.{region}.amazonaws.com/", {
|
|
348
348
|
"Action": "AssumeRoleWithWebIdentity", "Version": "2011-06-15", "RoleArn": role_arn,
|
|
349
|
-
"RoleSessionName": session_name(str(c.get("member") or ""), str(c.get("fid") or "")),
|
|
349
|
+
"RoleSessionName": session_name(str(c.get("member") or c.get("sub") or ""), str(c.get("fid") or "")),
|
|
350
350
|
"WebIdentityToken": id_token, "DurationSeconds": str(AWS_SESSION_SECONDS)})
|
|
351
351
|
leaves = _xml(raw)
|
|
352
352
|
if status != 200:
|
|
@@ -432,13 +432,28 @@ def device_headers() -> dict[str, str]:
|
|
|
432
432
|
return {"X-Brindle-Version": brindle.__version__}
|
|
433
433
|
|
|
434
434
|
|
|
435
|
+
def _personal(store, client, org_id: str) -> bool:
|
|
436
|
+
"""Whether ``org_id`` is the signed-in user's own org (no team org is
|
|
437
|
+
selected and the stored entitlement is for it)."""
|
|
438
|
+
from brindle.pro import license
|
|
439
|
+
|
|
440
|
+
try:
|
|
441
|
+
if (store.load() or {}).get("org_id"):
|
|
442
|
+
return False
|
|
443
|
+
ent = license.current(store=store, client=client)
|
|
444
|
+
except Exception: # noqa: BLE001 - no usable entitlement: treat it as a team org
|
|
445
|
+
return False
|
|
446
|
+
return ent.org_id == org_id and "team" not in ent.features and ent.plan not in ("team", "enterprise")
|
|
447
|
+
|
|
448
|
+
|
|
435
449
|
def _mint(client, store, org_id: str, cloud: str) -> str:
|
|
436
450
|
"""An ID token for ``cloud`` from PawDelta. A revoked or too-old sign-in
|
|
437
451
|
signs the family out on the server, then deletes the stored credentials."""
|
|
438
452
|
from brindle.pro import auth
|
|
439
453
|
|
|
454
|
+
path = "/me/federation/token" if _personal(store, client, org_id) else f"/orgs/{org_id}/federation/token"
|
|
440
455
|
try:
|
|
441
|
-
status, body = auth.authed(client, store, "POST",
|
|
456
|
+
status, body = auth.authed(client, store, "POST", path,
|
|
442
457
|
auth.JSONBody({"cloud": cloud}), headers=device_headers())
|
|
443
458
|
except auth.AuthError as e:
|
|
444
459
|
if isinstance(e, auth.TransportError):
|
|
@@ -515,7 +530,7 @@ def credential(cloud: str, org_id: str | None = None, *, client=None, store=None
|
|
|
515
530
|
sts: Sts | None = None, now: float | None = None) -> str:
|
|
516
531
|
"""What ``cloud``'s hook prints (empty for Azure, which reads a file).
|
|
517
532
|
Raises :class:`CredentialError` with the line to show."""
|
|
518
|
-
from brindle.pro import auth, credentials
|
|
533
|
+
from brindle.pro import auth, credentials, license
|
|
519
534
|
|
|
520
535
|
if cloud not in CLOUDS:
|
|
521
536
|
raise CredentialError(f"unknown cloud {cloud}")
|
|
@@ -526,11 +541,16 @@ def credential(cloud: str, org_id: str | None = None, *, client=None, store=None
|
|
|
526
541
|
cur = {}
|
|
527
542
|
if not cur.get("refresh_token"):
|
|
528
543
|
raise CredentialError(LOGIN_LINE)
|
|
544
|
+
client = client or auth.Client(cur.get("base_url"))
|
|
529
545
|
org_id = org_id or cur.get("org_id")
|
|
546
|
+
if not org_id:
|
|
547
|
+
try: # a personal sign-in stores no org: use the entitlement's own
|
|
548
|
+
org_id = license.current(store=store, client=client).org_id
|
|
549
|
+
except Exception: # noqa: BLE001 - no usable entitlement
|
|
550
|
+
pass
|
|
530
551
|
if not org_id:
|
|
531
552
|
raise CredentialError("no organization given; pass --org ID")
|
|
532
553
|
d = org_dir(org_id, create=True)
|
|
533
|
-
client = client or auth.Client(cur.get("base_url"))
|
|
534
554
|
now = time.time() if now is None else now
|
|
535
555
|
with mint_lock(d):
|
|
536
556
|
# Under the lock: the process that held it may have just minted.
|