Cleanup backend and agent provider abstractions #216

Merged
didericis merged 21 commits from issue-215-dockerfile-colocation into main 2026-06-08 23:01:36 -04:00
7 changed files with 11 additions and 17 deletions
Showing only changes of commit 007133bfac - Show all commits
@@ -126,11 +126,10 @@ def apply_capability_change(slug: str, new_dockerfile: str) -> tuple[str, str]:
def _repo_dockerfile_path() -> Path: def _repo_dockerfile_path() -> Path:
"""Path to the repo's Claude Dockerfile (one dir above this module's """Path to the Claude provider Dockerfile. Resolved at call time so
package root). Resolved at call time so the path is correct the path is correct regardless of where this module is imported from."""
regardless of where this module is imported from.""" # bot_bottle/backend/docker/ -> bot_bottle/ -> contrib/claude/Dockerfile
# bot_bottle/backend/docker/capability_apply.py -> repo root return Path(__file__).resolve().parent.parent.parent / "contrib" / "claude" / "Dockerfile"
Outdated
Review

Let's have the path be declared in the agent provider, and have it be a public attribute. Probably want to set it relative to file specifically for these inbuilt agent providers, but other agent providers could declare it elsewhere.

The other option would be to hardcode the agent provider to look for a dockerfile that sits next to the agent provider file in an agent provider contrib folder. What seems like the better option? Am thinking defaulting to a dockerfile that sits next to the agent provider file if no public attribute is set when declaring an agent provider probably makes sesne.

Let's have the path be declared in the agent provider, and have it be a public attribute. Probably want to set it relative to __file__ specifically for these inbuilt agent providers, but other agent providers could declare it elsewhere. The other option would be to hardcode the agent provider to look for a dockerfile that sits next to the agent provider file in an agent provider contrib folder. What seems like the better option? Am thinking defaulting to a dockerfile that sits next to the agent provider file if no public attribute is set when declaring an agent provider probably makes sesne.
return Path(__file__).resolve().parent.parent.parent.parent / "Dockerfile.claude"
def snapshot_transcript(slug: str) -> None: def snapshot_transcript(slug: str) -> None:
+4 -5
View File
4
@@ -209,17 +209,16 @@ def resolve_plan(
supervise_plan = None supervise_plan = None
if bottle.supervise: if bottle.supervise:
# Current Dockerfile for the agent image. Read from the repo # Current Dockerfile for the agent image. For `--cwd` derived
# root; for `--cwd` derived images the base Dockerfile is what # images the base Dockerfile is what the agent should propose
# the agent should propose changes against (the derived layer # changes against (the derived layer is just a workspace copy).
# is just a workspace copy).
# (routes.yaml used to land here too but PRD 0017 chunk 3 # (routes.yaml used to land here too but PRD 0017 chunk 3
# moved it behind the `list-egress-routes` MCP tool so the # moved it behind the `list-egress-routes` MCP tool so the
# agent gets live state rather than a launch-time snapshot.) # agent gets live state rather than a launch-time snapshot.)
supervise_dockerfile_path = ( supervise_dockerfile_path = (
Path(dockerfile_path) Path(dockerfile_path)
if dockerfile_path if dockerfile_path
else Path(__file__).resolve().parent.parent.parent.parent / "Dockerfile.claude" else Path(__file__).resolve().parent.parent.parent / "contrib" / "claude" / "Dockerfile"
) )
dockerfile_content = ( dockerfile_content = (
supervise_dockerfile_path.read_text(encoding="utf-8") supervise_dockerfile_path.read_text(encoding="utf-8")
+1 -3
View File
@@ -28,8 +28,6 @@ if TYPE_CHECKING:
from ...backend import Bottle, BottlePlan from ...backend import Bottle, BottlePlan
_REPO_ROOT = Path(__file__).resolve().parents[3]
_SUPERVISE_MCP_NAME = "supervise" _SUPERVISE_MCP_NAME = "supervise"
@@ -44,7 +42,7 @@ _RUNTIME = AgentProviderRuntime(
template="claude", template="claude",
command="claude", command="claude",
image="bot-bottle-claude:latest", image="bot-bottle-claude:latest",
dockerfile=str(_REPO_ROOT / "Dockerfile.claude"), dockerfile=str(Path(__file__).resolve().parent / "Dockerfile"),
prompt_mode="append_file", prompt_mode="append_file",
bypass_args=("--dangerously-skip-permissions",), bypass_args=("--dangerously-skip-permissions",),
resume_args=("--continue",), resume_args=("--continue",),
+1 -3
View File
@@ -32,8 +32,6 @@ if TYPE_CHECKING:
from ...backend import Bottle, BottlePlan from ...backend import Bottle, BottlePlan
_REPO_ROOT = Path(__file__).resolve().parents[3]
_SUPERVISE_MCP_NAME = "supervise" _SUPERVISE_MCP_NAME = "supervise"
@@ -52,7 +50,7 @@ _RUNTIME = AgentProviderRuntime(
template="codex", template="codex",
command="codex", command="codex",
image="bot-bottle-codex:latest", image="bot-bottle-codex:latest",
dockerfile=str(_REPO_ROOT / "Dockerfile.codex"), dockerfile=str(Path(__file__).resolve().parent / "Dockerfile"),
prompt_mode="read_prompt_file", prompt_mode="read_prompt_file",
bypass_args=("--dangerously-bypass-approvals-and-sandbox",), bypass_args=("--dangerously-bypass-approvals-and-sandbox",),
resume_args=("resume", "--last"), resume_args=("resume", "--last"),
+1 -1
View File
@@ -35,5 +35,5 @@ chmod 600 "$fake_key_dir/fake-key"
# Build the image graph quietly so the recorded run shows only the # Build the image graph quietly so the recorded run shows only the
# bottle launch and the four `!` probes, not BuildKit progress. # bottle launch and the four `!` probes, not BuildKit progress.
docker build -q -f Dockerfile.claude -t bot-bottle-claude:latest . >/dev/null 2>&1 || true docker build -q -f bot_bottle/contrib/claude/Dockerfile -t bot-bottle-claude:latest . >/dev/null 2>&1 || true
docker build -q -f Dockerfile.git-gate -t bot-bottle-git-gate:latest . >/dev/null 2>&1 || true docker build -q -f Dockerfile.git-gate -t bot-bottle-git-gate:latest . >/dev/null 2>&1 || true