Files
stack/adapters
jason.woltje 9f00397110 roles, harness, cli: a managed session gets its role's skills as prompt text on both harnesses (row 64, #1557)
Author and gate owner: Sage (mos-0855).

Role files take an optional skills list. The bundle reads each named skill
from <dataRoot>/skills-enabled, copies it into the bundle and renders every
file into prompt.md between skill-file markers, so the prompt text matches
the bundle copy byte for byte. The role's skills together are capped at
SKILL_BYTES (64 KiB): the claude adapter passes prompt.md as one argument,
and Linux caps one argument at 128 KiB. Symlinks are refused, not followed.
The launcher checks the list before a run exists: skill-not-enabled names
the activate command, anything else refuses skill-refused.

Pi keeps --no-skills; Claude Code keeps --disable-slash-commands and
--restricted. The two harness modules and MOSAIC_SKILLS are unchanged.
First lists: coder, reviewer, cto and pm, ms-unslop on each.
2026-10-19 16:11:36 -05:00
..

Mosaic runtime adapters

An adapter is the entire harness-specific surface of the system. Everything upstream of an adapter — configuration, contracts, missions, tasks, run records — is harness-agnostic; everything inside an adapter may assume one specific agent runtime.

Contract

An adapter lives at:

/opt/mosaic/adapters/<name>/adapter.sh

and must be executable. The dispatcher (/opt/mosaic/src/run-agent.sh) selects it via MOSAIC_ADAPTER (default: pi) and execs it after the system prompt has been generated.

Inputs (environment):

Variable Meaning
MOSAIC_SYSTEM_PROMPT_FILE Absolute path to the generated system prompt (contracts + optional mission section). Read it; do not modify it.
MOSAIC_REQUEST The exact user request text (may contain newlines).
MOSAIC_PROVIDER Configured provider name.
MOSAIC_MODEL Configured model id.

Optional, adapter-specific (documented per adapter):

Variable Meaning
MOSAIC_MOCK_RESPONSE mock only: the verbatim response to emit

Outputs:

  • stdout: the model response text — the only channel the orchestrator captures
  • stderr: diagnostics (never credentials)
  • exit 0: success; nonzero: failure

Rules

  1. Adapters print ONLY the response on stdout. Status lines go to stderr.
  2. Adapters never read configuration files; the resolved settings arrive via environment.
  3. Adapters never write outside /var/lib/mosaic.
  4. Adding a worker adapter requires: a new directory, the contract implementation, and adding the name to the allowlist in scripts/mosaic-config.mjs. A managed-session adapter (below) is selected by the launch bundle, not by execution.adapter, and is not on that allowlist unless it also works as a worker adapter.

Included adapters

  • pi — the pinned @earendil-works/pi-coding-agent CLI in noninteractive print mode (-p), ambient discovery disabled, stdin detached.
  • mock — deterministic echo of MOSAIC_MOCK_RESPONSE. Test-only: never use it where a real model response is required.
  • claude — the host's claude CLI, for managed sessions only (below): MOSAIC_CLAUDE_BIN when set, else claude on PATH. Not a worker adapter: the image has no claude, and the adapter refuses without the bundle's hook and MCP files.

Managed sessions (slice 1 S6)

The session runner (packages/harness/src/runner.mjs) runs one adapter call per turn on the host, as /bin/sh <adapters/<name>/adapter.sh> (the repository keeps adapters 0644; only the image sets the mode), in the session's workspace and its own process group. Same contract, plus:

Variable Meaning
MOSAIC_WORKSPACE The session's workspace; the adapter runs there.
MOSAIC_SESSION_DIR The session's persistent directory; later turns resume the session kept there.
MOSAIC_TOOLS The built-in tool limit (pi names for pi, Claude Code names for claude), comma-separated.
MOSAIC_POLICY_FILE, MOSAIC_TOOLS_FILE The bundle's gate policy and typed tools.
MOSAIC_TOOL_SOCKET The runner's tool socket, which the typed tools call.
MOSAIC_TURN_MARKER pi: the extension writes it at agent_end; a pi turn that exits 0 without it failed.
MOSAIC_EXTENSIONS pi only: extension files loaded with -e; a missing one exits 2.
MOSAIC_CLAUDE_SETTINGS, MOSAIC_CLAUDE_MCP_CONFIG claude only: the bundle's gate hook and MCP server; required.
MOSAIC_THINKING The route's thinking level (row 65), which the harness module's levels() already checked. pi passes --thinking (off, minimal, low, medium, high, xhigh, max) and omits it when unset, as for run-task.sh workers. claude passes --effort (low, medium, high, xhigh, max) and requires it; unset exits 2.
MOSAIC_CLAUDE_BIN claude only: absolute path of the CLI to run, from the host's environment (the bus unit pins it). Unset or empty runs claude from PATH; a relative path or a non-executable file exits 2.

The layers each adapter relies on, and what they don't cover, are in packages/harness/README.md.

A role's skills (row 64)

A managed session's skills reach both adapters as text in the bundle's prompt.md, which they pass with --system-prompt. Neither adapter loads a skill of its own: pi keeps --no-skills, since role sessions never get MOSAIC_SKILLS, and claude keeps --disable-slash-commands and --restricted. Sage chose this on 2026-10-19 from a probe, recorded in agents/sage/work/queue-64/probe.md:

  • Pi's --skill puts only a skill's name and description in the prompt, and the model has to read the SKILL.md itself. The gate refuses every file-tool path outside the workspace, so the session couldn't open its own skills. Opening the gate to the bundle would change a security boundary.
  • Claude Code loads a bundle-local --plugin-dir skill under --restricted only with its Skill tool on, and then it also offers its own bundled skills. Which ones it offers changes with the Claude Code version, and the Skill tool would pass through the gate hook.

The cost is prompt size: a role's skill text is in every turn, where the other routes load a skill when it's used. The bundle caps a role's skills at 64 KiB together, since claude passes prompt.md as one argument and Linux caps one argument at 128 KiB.