refactor(ai): strip Goose harness (Phase 1) — native/simple host-side only
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Remove the Goose agentic harness across the codebase per
docs/spec-native-harness.md §3. Goose made N sequential model calls inside the
sandbox (slow on CPU-only hardware) and forced an in-container→host Ollama
gateway that tripped the rootless-Podman slirp4netns loopback bug.
- bridge.py: delete _run_goose/_goose_argv/_goose_present + GOOSE_* consts and
the present-cache; __init__ now takes harness="simple"/max_turns=5; granted
!task runs _run_simple until the native loop lands (Phase 2).
- __main__.py: --harness {native,simple} (was {goose,simple}); drop
--goose-max-turns, add --max-turns; default harness simple.
- app.rs: /ai start accepts native|simple (plain aliases simple) instead of a
bare plain flag; refresh harness comments.
- sbx.rs: remove the in-container Ollama gateway (Docker host-gateway / Podman
slirp4netns host-loopback) and the dk_bootstrap OLLAMA_HOST env — kills the
slirp4netns loopback bug; drop Goose comments.
- bootstrap.sh: drop goose from the prereq probe.
- bootstrap-ai.sh: remove the entire Goose install block, --no-goose flag,
GOOSE_INSTALLER_URL, host config writer, and goose_bin helper.
- sandbox-bootstrap.sh: remove the in-sandbox Goose binary install + config.
- spec-goose-harness.md: banner — harness portion superseded; Podman stays.
cargo check + py_compile clean. No Goose refs remain (headroom/ untouched).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+18
-11
@@ -2624,11 +2624,12 @@ fn handle_command(
|
||||
app.sys("an AI agent is already running from this client — /ai stop first");
|
||||
} else {
|
||||
// Trailing flag words (any order): `allow` auto-grants the agent
|
||||
// sandbox drive on launch; `plain` selects the legacy one-shot
|
||||
// injector instead of the default Goose harness.
|
||||
// sandbox drive on launch; the harness word `native` (default) or
|
||||
// `simple` picks the granted-`!task` harness. `plain` is a back-compat
|
||||
// alias for `simple`.
|
||||
let mut raw = rest.trim();
|
||||
let mut grant_sbx = false;
|
||||
let mut plain = false;
|
||||
let mut harness: Option<&str> = None;
|
||||
loop {
|
||||
if let Some(head) = raw
|
||||
.strip_suffix("allow")
|
||||
@@ -2638,17 +2639,20 @@ fn handle_command(
|
||||
raw = head.trim();
|
||||
continue;
|
||||
}
|
||||
if let Some(head) = raw
|
||||
.strip_suffix("plain")
|
||||
.filter(|h| h.is_empty() || h.ends_with(' '))
|
||||
if let Some((word, head)) = ["native", "simple", "plain"]
|
||||
.iter()
|
||||
.find_map(|w| {
|
||||
raw.strip_suffix(w)
|
||||
.filter(|h| h.is_empty() || h.ends_with(' '))
|
||||
.map(|h| (*w, h))
|
||||
})
|
||||
{
|
||||
plain = true;
|
||||
harness = Some(if word == "native" { "native" } else { "simple" });
|
||||
raw = head.trim();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
let harness = if plain { Some("simple") } else { None };
|
||||
// A bare name (no ':' tag, no '/' path) is a models.toml profile;
|
||||
// anything else is treated as a literal Ollama model tag.
|
||||
let (profile, model): (Option<&str>, &str) = if raw.is_empty() {
|
||||
@@ -2671,7 +2675,10 @@ fn handle_command(
|
||||
Some(p) => format!("profile {p}"),
|
||||
None => format!("ollama/{model}"),
|
||||
};
|
||||
let hdesc = if plain { ", simple harness" } else { "" };
|
||||
let hdesc = match harness {
|
||||
Some(h) => format!(", {h} harness"),
|
||||
None => String::new(),
|
||||
};
|
||||
app.sys(format!(
|
||||
"⛧ summoning {name} ({desc}{hdesc})… it will announce when online"
|
||||
));
|
||||
@@ -3245,8 +3252,8 @@ fn spawn_agent(
|
||||
.arg(model);
|
||||
}
|
||||
}
|
||||
// Override the agent's default `!task` harness (goose) when the launcher
|
||||
// asked for the legacy one-shot injector via `/ai start … plain`.
|
||||
// Override the agent's default `!task` harness when the launcher asked for a
|
||||
// specific one via `/ai start … native|simple` (plain aliases simple).
|
||||
if let Some(h) = harness {
|
||||
cmd.arg("--harness").arg(h);
|
||||
}
|
||||
|
||||
+7
-27
@@ -685,7 +685,7 @@ impl Backend {
|
||||
/// sandbox — `docker`/`podman exec`, `multipass exec`, or host `local`.
|
||||
/// Advertised in the `_sbx:status` frame (distinct from `label`, whose Local
|
||||
/// value is the cosmetic "local-shell") so the bridge can build the right
|
||||
/// `goose run` invocation for the backend that holds the sandbox.
|
||||
/// `<engine> exec` invocation for the backend that holds the sandbox.
|
||||
pub fn engine(self) -> &'static str {
|
||||
match self {
|
||||
Backend::Local => "local",
|
||||
@@ -777,19 +777,11 @@ pub fn prepare(
|
||||
// surfaced through the returned error (shown in the error popup).
|
||||
let mut run = Command::new(engine);
|
||||
run.args(["run", "-d", "--name", name, "--hostname", name, "-w", "/root"]);
|
||||
// Goose (and any in-container tool) reaches the host Ollama via a
|
||||
// gateway. Docker maps `host.docker.internal` to the host gateway IP.
|
||||
// For rootless Podman the native `host.containers.internal` resolves to
|
||||
// the host's *LAN* interface, which can't reach an Ollama bound to
|
||||
// 127.0.0.1 (the safe default) — so request slirp4netns host-loopback
|
||||
// forwarding and point the in-container OLLAMA_HOST at the slirp gateway
|
||||
// 10.0.2.2 (see dk_bootstrap). Without this the granted `!task` path dies
|
||||
// with "Could not connect to host.containers.internal:11434".
|
||||
if backend == Backend::Docker {
|
||||
run.arg("--add-host=host.docker.internal:host-gateway");
|
||||
} else if backend == Backend::Podman {
|
||||
run.arg("--network=slirp4netns:allow_host_loopback=true");
|
||||
}
|
||||
// The native harness runs the model host-side and only execs commands
|
||||
// into the container, so the sandbox no longer needs to reach host
|
||||
// Ollama. The old in-container Ollama gateway (Docker host-gateway /
|
||||
// Podman slirp4netns host-loopback) is therefore gone — and with it the
|
||||
// rootless-Podman loopback bug it used to work around.
|
||||
run.args([image, "sleep", "infinity"]);
|
||||
let out = run
|
||||
.output()
|
||||
@@ -1246,21 +1238,9 @@ fn dk_bootstrap(engine: &str, name: &str) {
|
||||
"apt-get update -qq && apt-get install -y --no-install-recommends $HH_SBX_PKGS".into()
|
||||
});
|
||||
let pkgs_env = format!("HH_SBX_PKGS={}", sandbox_pkgs());
|
||||
// The in-container Goose config points OLLAMA_HOST at the host gateway, whose
|
||||
// address depends on the engine. Docker resolves `host.docker.internal` via the
|
||||
// `--add-host` we add at run time. Rootless Podman's `host.containers.internal`
|
||||
// resolves to the host LAN IP and can't reach a loopback-bound Ollama, so we
|
||||
// launch the container with slirp4netns:allow_host_loopback=true (see launch())
|
||||
// and target the slirp host-loopback gateway 10.0.2.2 instead. Pass it through
|
||||
// so sandbox-bootstrap.sh can bake the right URL.
|
||||
let gateway = if engine == "podman" {
|
||||
"HH_OLLAMA_HOST=http://10.0.2.2:11434"
|
||||
} else {
|
||||
"HH_OLLAMA_HOST=http://host.docker.internal:11434"
|
||||
};
|
||||
let child = Command::new(engine)
|
||||
.args([
|
||||
"exec", "-i", "-e", &pkgs_env, "-e", gateway, name, "bash", "-s",
|
||||
"exec", "-i", "-e", &pkgs_env, name, "bash", "-s",
|
||||
])
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::null())
|
||||
|
||||
Reference in New Issue
Block a user