From bab78044a136e2619ca9e7ab380cfe4461016866 Mon Sep 17 00:00:00 2001 From: Joakim Persson Date: Wed, 8 Jul 2026 00:16:38 +0200 Subject: [PATCH] feat: bake OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true (base ENV) opencode gates native background subagents behind this experimental flag; oh-my-opencode-slim V2+ makes background orchestration its default workflow, so the omos variant was effectively degraded without it. Set as a base-level ENV (both variants, harmless for plain opencode) and overridable at runtime. Documented in lockstep: README env table, .env.example, CHANGELOG Unreleased, and AGENTS.md (with a removal trigger for when opencode promotes it out of experimental). smoke-test asserts the var is baked into the image env. Also folds the prior CLI-tools additions into the CHANGELOG Unreleased block. --- .env.example | 6 ++++++ AGENTS.md | 1 + CHANGELOG.md | 29 +++++++++++++++++++++++++++++ Dockerfile.base | 11 +++++++++++ README.md | 1 + scripts/smoke-test.sh | 5 +++++ 6 files changed, 53 insertions(+) diff --git a/.env.example b/.env.example index 1c67202..a88bbe0 100644 --- a/.env.example +++ b/.env.example @@ -117,3 +117,9 @@ SSH_KEY_PATH=~/.ssh # # on image pull. Independent of ENABLE_OMOS. # # See docs/omos-skills.md # OMOS_RESET=false # Force regenerate oh-my-opencode-slim config on next start (does not affect skills) + +# ── Background subagents (opencode experimental flag) ──────────────── +# Baked ON in the image (ENV in Dockerfile.base) because OMOS V2+ default +# orchestration depends on opencode's native background subagents. Uncomment +# to opt out (e.g. to force blocking/foreground orchestration). +# OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=false diff --git a/AGENTS.md b/AGENTS.md index 7c6a738..c9889e5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -108,6 +108,7 @@ curl -s https://api.github.com/repos/anomalyco/opencode/releases/tags/v1.15.10 | - **Registry buildkit cache-export is currently disabled** — do NOT re-add `cache-from`/`cache-to` to the `build-base` step in `.gitea/workflows/docker-publish-split.yml` without first verifying that buildkit's `mode=max` cache-export to `registry-1.docker.io` no longer returns HTTP 400 from the Hub CDN edge. The regression surfaced ~2026-05-23 and broke five consecutive opencode-devbox publish attempts (runs #332/333/334/336 + a rerun); root-caused on 2026-05-28 by a manual host-side publish that reproduced the same 400 only on `--cache-to` while image push worked fine. Failure shape is stable (`Offset:0` in the `_state` token, HTML response body = CDN-tier rejection, not registry backend), repo-specific (we're the only repo writing `:base-buildcache` mode=max), and explains why pinning `setup-buildx-action@v4.0.0` didn't help (action pin doesn't change the bundled buildkit version on the catthehacker runner image). Trade-off: dockerfile.base changes pay a full ~3 min rebuild instead of pulling cached layers; unchanged bases short-circuit at the Hub-probe step in `base-decide` and never re-build anyway. Variants don't use registry cache so they're unaffected. Re-enable condition: upstream moby/buildkit fix lands AND a low-risk test run succeeds without 400s. See CHANGELOG v1.15.12 `Unreleased` block for the full diagnostic chain. Manual escape-hatch publish procedure: `docs/manual-host-publish.md`. - **Push steps wrap `docker buildx build --push` in a 3-attempt retry loop** (15s, 30s backoff) for transient `registry-1.docker.io` blips — rate limits, brief 5xx, CDN flap. Implemented as inline `shell: bash` steps with `docker buildx build` raw rather than `docker/build-push-action@v7` so the loop is visible and tweakable. Affects the 1 base + 5 variant push steps in `.gitea/workflows/docker-publish-split.yml`; smoke-test builds (`load: true`, no push) are untouched. **This does NOT mask deterministic failures** — a true regression (like the cache-export 400 of 2026-05-23..28) fails all 3 attempts identically and the job still fails. Orthogonal to the cache-export disablement above: cache-export was about a deterministic protocol mismatch, retry is about absorbing genuine transients. Both are belt-and-braces with the `ci-release-watcher` skill's transient-rerun heuristic. If you change the matrix of push steps, keep the retry wrapper consistent across them — the pattern is duplicated rather than factored out because Gitea Actions doesn't support reusable composite shell steps cleanly. - **Shell scripts use `set -euo pipefail`** — both entrypoints are strict. Errors in volume chown or SSH permission operations are intentionally suppressed with `|| true`. +- **Background subagents flag baked ON — experimental, watch for promotion** — `Dockerfile.base` sets `ENV OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true`. opencode gates native background subagents behind this flag (`packages/opencode/src/tool/task.ts` fails with `Background subagents require OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true` when unset); `oh-my-opencode-slim` V2+ makes background orchestration its **default** workflow, so the omos variant is effectively degraded without it. It's a base ENV (applies to both variants; harmless on plain opencode — only *enables* a capability) and stays runtime-overridable (`-e …=false`). It's counted in the base hash, so editing that line advances `base-latest`. **REMOVAL TRIGGER:** when opencode promotes background subagents out of `EXPERIMENTAL_` (flag renamed or made default), drop the ENV. No upstream roadmap date as of opencode 1.17.15 / omos 2.1.0 (2026-07). Documented in lockstep in README env table, `.env.example`, and asserted by `scripts/smoke-test.sh` (`bg-subagents env baked`). - **MemPalace `diary_write` anyOf workaround — upstream watch target** — `Dockerfile.base` carries a perl RUN block that strips a root-level `anyOf` from `mempalace_diary_write`'s advertised `inputSchema`. Mempalace 3.3.x/3.4.0 advertise `anyOf: [{required:[entry]},{required:[content]}]`, which Anthropic's tools API (and Codex) reject at session start (`input_schema does not support oneOf, allOf, or anyOf at the top level`), making the whole MCP server fail to load. The workaround is idempotent and self-deactivating: when upstream ships the real fix the regex stops matching and the build prints `WARN: ... upstream may have changed shape` — **that WARN is the signal to delete the RUN block.** Upstream status (last checked **2026-06-14**): issue **#1728 is still OPEN**; PR **#1735 is CLOSED UNMERGED (2026-06-11) — do NOT watch it, it is dead**; PR **#1717 is the current live fix candidate**; mempalace PyPI latest is **3.4.0 (== our pin)**, so **no release contains the fix yet** and the workaround must stay. **Removal trigger:** a mempalace release **> 3.4.0** that actually strips the root-level `anyOf` lands on PyPI — then bump `MEMPALACE_VERSION` (in lockstep with pi-devbox) and drop the RUN block. NOTE: `MEMPALACE_VERSION` (the pip pin) and `MEMPALACE_TOOLKIT_REF` (the git ref for the `mempalace-toolkit` clone) are unrelated despite the shared prefix; do not conflate them. - **MemPalace install path** — installed via `uv tool install` into `/opt/uv-tools/mempalace/`. Both the `mempalace` CLI and the `mempalace-mcp` MCP server binary are shipped as entry points by the mempalace package itself and placed on PATH by uv as shims whose shebangs point at the venv's Python. No hand-rolled wrapper is needed. Do not use `pip install --break-system-packages` — that was the previous approach and has been removed. Do not use `["python3", "-m", "mempalace.mcp_server"]` in `opencode.jsonc` — system Python can't import from the uv venv. - **generate-config.py idempotency** — the script MUST never overwrite an existing `opencode.jsonc` or legacy `opencode.json`. Config persists in the `devbox-opencode-config` named volume; accidentally clobbering that file would destroy hand-edits. The smoke test asserts this. diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fca3f9..365b289 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,35 @@ Tags follow **independent semver** (since `v2.0.0`) — they version *this image --- +## Unreleased + +CLI-toolset parity with the sibling `pi-devbox` image, plus background-subagent +enablement. Touches `Dockerfile.base` (apt list, new binary, new ENV) so the +base image rebuilds (`base-` advances). + +### Added + +- **CLI tooling parity with pi-devbox.** Added `yq` (YAML query/transform, + pairs with `jq`), `pandoc` (Markdown↔HTML/etc. document converter), and + `graphviz` (`dot` diagram rendering) via apt; `tealdeer` (the `tldr` command — + quick command examples) as a ~5 MB static musl binary via the floated + `TEALDEER_VERSION` arg; and the `dot-watch` wrapper (`rootfs/usr/local/bin/dot-watch`, + auto-renders a `.dot` file to PNG on save, graphviz-only). Deliberately did + **not** port `socat`, `imagemagick`, or the pi-studio-specific `studio-expose` + bridge. smoke-test asserts presence of all five. +- **Background subagents enabled by default (`OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true`).** + Baked as a base-level `ENV` in `Dockerfile.base`. opencode gates native + background subagents behind this experimental flag; OMOS V2+ makes background + orchestration its default workflow, so the omos variant was effectively + degraded without it. Overridable at runtime (`-e OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=false`). + Documented in README env table and `.env.example`; smoke-test asserts the var + is present in the image environment. Marked experimental upstream — carries a + removal-trigger note in `Dockerfile.base` + AGENTS.md for when opencode + promotes it out of experimental (no upstream roadmap date as of opencode + 1.17.15 / omos 2.1.0). + +--- + ## v2.5.0 — 2026-07-02 Minor release. Headline: **optional shared/external MemPalace** — diff --git a/Dockerfile.base b/Dockerfile.base index 183bad7..b3cfa07 100644 --- a/Dockerfile.base +++ b/Dockerfile.base @@ -403,6 +403,17 @@ ENV LANGUAGE=en_US:en ENV LC_ALL=en_US.UTF-8 ENV EDITOR=nvim ENV PATH="/home/developer/.local/bin:/home/developer/.cargo/bin:${PATH}" +# Enable opencode's native background subagents. opencode gates this behind an +# experimental flag (packages/opencode/src/tool/task.ts errors with +# "Background subagents require OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true" +# when unset). oh-my-opencode-slim V2+ makes background orchestration its DEFAULT +# workflow, so the omos variant is effectively broken without this. Baked here as +# a base ENV (applies to both variants; harmless for plain opencode — it only +# *enables* a capability). Overridable at runtime: -e OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=false. +# REMOVAL TRIGGER: when opencode promotes background subagents out of experimental +# (flag becomes default / renamed), drop this ENV. No upstream roadmap date as of +# opencode 1.17.15 / omos 2.1.0 (2026-07). +ENV OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true # ── Node.js (required for opencode/pi/omos at variant build + MCP servers) ── ARG NODE_VERSION=22 diff --git a/README.md b/README.md index 4f94a3e..4553a6d 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,7 @@ docker compose exec -u developer devbox aws --version | `OMOS_TMUX` | Enable tmux pane integration for OMOS | `false` | | `OMOS_SKILLS` | Symlink bundled OMOS skills from the image into `~/.agents/skills/` each start | `true` | | `OMOS_RESET` | Force regenerate OMOS config on next start (does not affect skills) | `false` | +| `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS` | Enable opencode's native background subagents. Baked on in the image because OMOS V2+ default orchestration depends on it. Set `false` to opt out. opencode marks this **experimental** — see [AGENTS.md](AGENTS.md) removal trigger | `true` | | `SKILLSET_CONTAINER_PATH` | Path to skillset repo inside container (for auto-deploy when not at /workspace/skillset) | Auto-detect | ### Reaching your LAN from the container diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index 9c9bf96..4f7d3d4 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -143,6 +143,11 @@ run "graphviz (dot)" "dot -V" run "tldr (tealdeer)" "tldr --version" run "dot-watch" "test -x /usr/local/bin/dot-watch && bash -n /usr/local/bin/dot-watch && echo ok" +# Background subagents: opencode gates them behind this experimental env var, +# and OMOS V2+ default orchestration depends on it. Baked ON as an ENV in +# Dockerfile.base — assert it's present in the image environment (both variants). +run_expect "bg-subagents env baked" "printenv OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS" "true" + # SSH ControlMaster baked defaults: the config file must exist (image-level) # and ssh -G must report ControlPath rooted at /tmp/sshcm/ for an arbitrary # host. Catches both regressions: someone removing the conf file, OR something