brindle 0.3.11__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.
Files changed (134) hide show
  1. {brindle-0.3.11 → brindle-0.3.12}/PKG-INFO +56 -4
  2. {brindle-0.3.11 → brindle-0.3.12}/README.md +55 -3
  3. {brindle-0.3.11 → brindle-0.3.12}/pyproject.toml +1 -1
  4. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/agents.py +19 -2
  5. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/cli.py +4 -1
  6. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/cloud_credential.py +24 -4
  7. brindle-0.3.12/src/brindle/codemap.py +422 -0
  8. brindle-0.3.12/src/brindle/codemap_docs.py +65 -0
  9. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/company_login.py +16 -6
  10. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/cost.py +17 -2
  11. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/cull.py +6 -0
  12. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/events.py +29 -6
  13. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/history.py +1 -1
  14. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/learning.py +14 -10
  15. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pipeline.py +14 -6
  16. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/learning.py +11 -9
  17. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/managed_models.py +22 -4
  18. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/route_health.py +5 -0
  19. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/team_policy.py +26 -3
  20. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/usage.py +44 -7
  21. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/workspaces.py +16 -4
  22. brindle-0.3.11/src/brindle/codemap.py +0 -73
  23. {brindle-0.3.11 → brindle-0.3.12}/.gitignore +0 -0
  24. {brindle-0.3.11 → brindle-0.3.12}/LICENSE +0 -0
  25. {brindle-0.3.11 → brindle-0.3.12}/SCHEDULE-A +0 -0
  26. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/__init__.py +0 -0
  27. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/__main__.py +0 -0
  28. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/account.py +0 -0
  29. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/airgap.py +0 -0
  30. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/antigravity.py +0 -0
  31. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/autopilot.py +0 -0
  32. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/budget.py +0 -0
  33. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/__init__.py +0 -0
  34. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/backend-auditor.md +0 -0
  35. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/developer-codex.md +0 -0
  36. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/developer-fable.md +0 -0
  37. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/developer-gemini.md +0 -0
  38. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/developer-heavy.md +0 -0
  39. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/developer-light.md +0 -0
  40. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/developer-local.md +0 -0
  41. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/developer.md +0 -0
  42. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/qa.md +0 -0
  43. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/reviewer-codex.md +0 -0
  44. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/reviewer-gemini.md +0 -0
  45. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/reviewer-local.md +0 -0
  46. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/reviewer.md +0 -0
  47. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/subagent.md +0 -0
  48. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_agents/supervisor.md +0 -0
  49. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_rules/__init__.py +0 -0
  50. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_rules/security/backend.md +0 -0
  51. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_rules/style/minimal-diff.md +0 -0
  52. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/builtin_rules/tests/only.md +0 -0
  53. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/ci_adapters.py +0 -0
  54. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/ci_client.py +0 -0
  55. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/ci_federation.py +0 -0
  56. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/ci_hosts.py +0 -0
  57. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/codex_hook.py +0 -0
  58. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/company_identity.py +0 -0
  59. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/config.py +0 -0
  60. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/conflicts.py +0 -0
  61. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/cost_estimate.py +0 -0
  62. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/db.py +0 -0
  63. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/demo.py +0 -0
  64. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/detect.py +0 -0
  65. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/doctor.py +0 -0
  66. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/escapes.py +0 -0
  67. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/gates.py +0 -0
  68. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/git.py +0 -0
  69. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/guardrails.py +0 -0
  70. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/identity_lock.py +0 -0
  71. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/inbox.py +0 -0
  72. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/keys_cmds.py +0 -0
  73. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/keystore.py +0 -0
  74. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/launch_health.py +0 -0
  75. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/learned_rules.py +0 -0
  76. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/legacy.py +0 -0
  77. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/mcp_server.py +0 -0
  78. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/model_access.py +0 -0
  79. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/model_availability.py +0 -0
  80. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/native/__init__.py +0 -0
  81. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/native/client.py +0 -0
  82. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/native/loop.py +0 -0
  83. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/native/permissions.py +0 -0
  84. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/native/runner.py +0 -0
  85. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/native/serve.py +0 -0
  86. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/native/tools.py +0 -0
  87. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pane_auth.py +0 -0
  88. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/permissions.py +0 -0
  89. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/plugins.py +0 -0
  90. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/policy.py +0 -0
  91. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pool.py +0 -0
  92. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pricing.py +0 -0
  93. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/__init__.py +0 -0
  94. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/_files.py +0 -0
  95. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/account.py +0 -0
  96. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/audit_chain.py +0 -0
  97. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/audit_export.py +0 -0
  98. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/auth.py +0 -0
  99. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/brief_learning.py +0 -0
  100. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/cost_centers.py +0 -0
  101. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/credentials.py +0 -0
  102. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/features.py +0 -0
  103. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/keys.py +0 -0
  104. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/license.py +0 -0
  105. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/loopback.py +0 -0
  106. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/org_profiles.py +0 -0
  107. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/orgkey.py +0 -0
  108. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/rollout.py +0 -0
  109. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/settings_sync.py +0 -0
  110. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/status.py +0 -0
  111. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/pro/team_events.py +0 -0
  112. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/procs.py +0 -0
  113. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/profiles.py +0 -0
  114. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/providers.py +0 -0
  115. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/quota.py +0 -0
  116. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/repo_cmds.py +0 -0
  117. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/repos.py +0 -0
  118. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/rewind.py +0 -0
  119. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/rule_checks.py +0 -0
  120. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/savings.py +0 -0
  121. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/scratch.py +0 -0
  122. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/secrets.py +0 -0
  123. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/self_serve.py +0 -0
  124. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/services.py +0 -0
  125. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/sessions.py +0 -0
  126. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/sidebar_follow.py +0 -0
  127. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/signin_pause.py +0 -0
  128. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/status_cache.py +0 -0
  129. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/tasks.py +0 -0
  130. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/tmux.py +0 -0
  131. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/updates.py +0 -0
  132. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/view.py +0 -0
  133. {brindle-0.3.11 → brindle-0.3.12}/src/brindle/watch.py +0 -0
  134. {brindle-0.3.11 → 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.11
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.11`). A session started before an upgrade keeps
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 (keep the key out of
1912
- the repo). The key alone does nothing: without `modelProvider`, `agy` still asks
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.11`). A session started before an upgrade keeps
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 (keep the key out of
1881
- the repo). The key alone does nothing: without `modelProvider`, `agy` still asks
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.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "brindle"
3
- version = "0.3.11"
3
+ version = "0.3.12"
4
4
  description = "Run coding agents in parallel, each on its own git branch, with a supervisor that delegates, reviews, and merges"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -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.repo_root), test_guidance(checks),
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.repo_root)
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
- return workspaces.create(db, ws.repo_root, f"brindle/session-{n}", base=ws.branch or None,
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", f"/orgs/{org_id}/federation/token",
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.