Files
synapbus/image-build
Algis DumbrisandClaude Opus 4.6 bd54aac82f fix(synapbus-agent): early-exit on __coalesced__ synthetic triggers
When the reactor sets pending_work while an agent run is in progress,
checkPendingWork fires a synthetic follow-up event with FromAgent=
__coalesced__ and body="Coalesced trigger: process all pending
messages." That event gets delivered to the container as a
message.json with that placeholder body, and the wrapper happily
feeds it to gemini — which then produces a spurious "What does this
demo do?" reply because the only thing the model sees is a generic
filler body.

The doc-gardener coordinator kept emitting stray replies to algis
between real DELEGATED: messages because of this. The reactor would
fail the coalesced run ("Received system trigger..."), the critic
would get confused by intermediate traffic, and the /goals panel
would accumulate garbage.

Wrapper now checks $FROM at the top of main. If it's __coalesced__
we log it and exit 0 without invoking the CLI. The reactor marks
the run succeeded, no tokens burned, no spurious DMs produced. Any
real pending work re-triggers naturally when the next actual
message arrives.

Smoke-verified:
  docker run --rm -v /tmp/test:/workspace synapbus-agent:latest \
    /usr/local/bin/synapbus-agent-wrapper.sh
  [wrapper test] cli=gemini from=__coalesced__ body_bytes=9
  [wrapper test] synthetic coalesced trigger — skipping CLI invocation
  EXIT=0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:19:46 +03:00
..

SynapBus container images

The docker harness backend (internal/harness/docker/) runs each agent inside an ephemeral container. This directory holds the canonical agent image SynapBus's bundled examples reference.

synapbus-agent

The default image. Debian bookworm-slim base with:

  • gemini CLI (@google/gemini-cli)
  • claude CLI (@anthropic-ai/claude-code)
  • tini as PID 1 (signal forwarding + zombie reaping)
  • Standard tooling the example wrappers use: jq, sqlite3, curl, git, python3
  • Non-root agent user (uid 1000, gid 1000) matching the typical host user

No SynapBus binary lives in the image. Agents reach the SynapBus MCP server on the host at host.docker.internal:<port> — the harness rewrites .gemini/settings.json URLs from 127.0.0.1 to the gateway hostname automatically.

Build

Local single-arch:

docker build -t synapbus-agent:latest image-build/synapbus-agent

Multi-arch via buildx (recommended for sharing the image):

docker buildx build \
    --platform linux/amd64,linux/arm64 \
    -t synapbus-agent:latest \
    --load \
    image-build/synapbus-agent

Pin specific CLI versions with build args:

docker build \
    --build-arg GEMINI_CLI_VERSION=0.37.1 \
    --build-arg CLAUDE_CODE_VERSION=1.0.0 \
    -t synapbus-agent:0.37.1 \
    image-build/synapbus-agent

Wire an agent to use it

In harness_config_json add a docker block:

{
  "gemini_md": "...",
  "mcp_servers": [...],
  "env": {...},
  "docker": {
    "image": "synapbus-agent:latest",
    "memory": "1g",
    "cpus": "1.0",
    "network": "bridge"
  }
}

The reactor will pick the docker backend automatically when it sees the docker.image field. Default security posture: --cap-drop=ALL, --security-opt=no-new-privileges, --read-only root with tmpfs /tmp, --pids-limit=512, --user=<host uid:gid>. Override via the typed fields in the docker block (memory, cpus, cap_add, extra_mounts, read_only_root, user).