feat(supervise): Docker lifecycle + bottle integration (PRD 0013)
Phase 3 of PRD 0013. Wires the supervise sidecar into bottle launch: - Manifest: bottle.supervise (bool, default False). Opt-in for v1 so existing bottles are unchanged. - supervise.py: adds SupervisePlan + abstract Supervise(ABC) with a prepare template that stages the per-bottle queue dir on the host and the current-config dir under stage_dir (routes.json + allowlist + Dockerfile). Stdlib-only so it still runs as the in-container shared helper. - backend/docker/supervise.py: DockerSupervise concrete start/stop. No egress network (the sidecar doesn't make outbound calls); just the bottle's internal network with network-alias "supervise" and a bind-mount of the host queue dir at /run/supervise/queue. - Prepare wires supervise.prepare into the DockerBottlePlan, derives routes_content from cred_proxy_plan, allowlist_content from pipelock_effective_allowlist, and dockerfile_content from the repo's Dockerfile. supervise sidecar added to the orphan probe. - Launch starts the supervise sidecar after pipelock + cred-proxy but before the agent (so DNS resolution for `supervise` is up on the agent's first tool call). - Agent container gets a read-only bind-mount of the current-config dir at /etc/claude-bottle/current-config when supervise is enabled. - bottle_plan print + to_dict surface the supervise state. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ from ...git_gate import GitGatePlan
|
||||
from ...log import info
|
||||
from ...manifest import Agent, Bottle
|
||||
from ...pipelock import PipelockProxyPlan, pipelock_effective_allowlist
|
||||
from ...supervise import SupervisePlan
|
||||
from .. import BottlePlan
|
||||
|
||||
|
||||
@@ -53,6 +54,9 @@ class DockerBottlePlan(BottlePlan):
|
||||
proxy_plan: PipelockProxyPlan
|
||||
git_gate_plan: GitGatePlan
|
||||
cred_proxy_plan: CredProxyPlan
|
||||
# None when bottle.supervise is False. PRD 0013 supervise sidecar
|
||||
# is opt-in via the manifest's bottle.supervise field.
|
||||
supervise_plan: SupervisePlan | None
|
||||
allowlist_summary: str
|
||||
use_runsc: bool
|
||||
|
||||
@@ -116,6 +120,12 @@ class DockerBottlePlan(BottlePlan):
|
||||
info(" cred-proxy : (none)")
|
||||
info(f" egress : {self.allowlist_summary}")
|
||||
info(" tls intercept : pipelock (per-bottle ephemeral CA, generated at launch)")
|
||||
if self.supervise_plan is not None:
|
||||
info(
|
||||
f" supervise : enabled; queue at {self.supervise_plan.queue_dir}"
|
||||
)
|
||||
else:
|
||||
info(" supervise : disabled (set bottle.supervise=true to enable)")
|
||||
info(
|
||||
f"prompt : {len(v.agent.prompt)} chars; "
|
||||
f"first line: {v.prompt_first_line or '(empty)'}"
|
||||
@@ -169,6 +179,14 @@ class DockerBottlePlan(BottlePlan):
|
||||
"ca_fingerprint": None,
|
||||
},
|
||||
},
|
||||
"supervise": {
|
||||
"enabled": self.supervise_plan is not None,
|
||||
"queue_dir": (
|
||||
str(self.supervise_plan.queue_dir)
|
||||
if self.supervise_plan is not None
|
||||
else None
|
||||
),
|
||||
},
|
||||
"prompt": {
|
||||
"length": len(v.agent.prompt),
|
||||
"first_line": v.prompt_first_line,
|
||||
|
||||
Reference in New Issue
Block a user