MOSAIC_CLAUDE_BIN names the claude binary by absolute path instead of finding it on PATH. scripts/bus-service.sh render|install --claude PATH writes it into the unit (default: command -v claude in the installing shell); the claude adapter, the launcher's version read and the seat session environment all use it. Mos's Q18 ask: the unit's PATH found /usr/bin/claude 2.1.259, which refuses claude-opus-5-5. Candidate: round 2 manifest 21c1ab05, round 1 (a2008d8b) rebased ontof2f2861bafter row 55 changed docs/TOOLS.md and packages/cli/README.md; the changed lines are byte-identical. Approved by Filbert (27349, 27374) and Dewey (27350, 27375). Sage's gate onf2f2861bplus the patch: cli 112, seat 27, harness 57, nine scripts/test-*.sh 0 failed, Docker on. Co-Authored-By: Claude Opus 5.5 <[email protected]>
90 lines
3.9 KiB
Bash
90 lines
3.9 KiB
Bash
#!/bin/sh
|
|
# Claude Code adapter: implements the Mosaic adapter contract for the host's
|
|
# `claude` CLI, for managed sessions (slice 1 S6). Headless only. The CLI is
|
|
# MOSAIC_CLAUDE_BIN when set (the bus unit pins it), else `claude` on PATH.
|
|
#
|
|
# Contract: see adapters/README.md.
|
|
# stdout = the turn's answer only; stderr = diagnostics; exit 0 on success.
|
|
#
|
|
# The layers this relies on are row S0 lines 1-5 (docs/plans/2026-10-04_slice-1.md,
|
|
# "What S6 can rely on"): the PreToolUse command hook in MOSAIC_CLAUDE_SETTINGS,
|
|
# wrapped as `timeout -k 2 10 <gate> || exit 2`, and the tool limit (--tools).
|
|
# --bare is never passed: it turns settings hooks off.
|
|
set -eu
|
|
|
|
need() {
|
|
eval "v=\${$1:-}"
|
|
[ -n "$v" ] || { echo "claude adapter: $1 is required" >&2; exit 2; }
|
|
}
|
|
need MOSAIC_SYSTEM_PROMPT_FILE
|
|
need MOSAIC_REQUEST
|
|
need MOSAIC_WORKSPACE
|
|
need MOSAIC_SESSION_DIR
|
|
need MOSAIC_MODEL
|
|
need MOSAIC_CLAUDE_SETTINGS
|
|
need MOSAIC_CLAUDE_MCP_CONFIG
|
|
for f in "$MOSAIC_SYSTEM_PROMPT_FILE" "$MOSAIC_CLAUDE_SETTINGS" "$MOSAIC_CLAUDE_MCP_CONFIG"; do
|
|
[ -r "$f" ] || { echo "claude adapter: not readable: $f" >&2; exit 2; }
|
|
done
|
|
[ "${MOSAIC_INTERACTIVE:-}" != "1" ] || { echo "claude adapter: interactive mode isn't supported" >&2; exit 2; }
|
|
CLAUDE=claude
|
|
if [ -n "${MOSAIC_CLAUDE_BIN:-}" ]; then
|
|
case "$MOSAIC_CLAUDE_BIN" in
|
|
/*) ;;
|
|
*) echo "claude adapter: MOSAIC_CLAUDE_BIN must be an absolute path: $MOSAIC_CLAUDE_BIN" >&2; exit 2 ;;
|
|
esac
|
|
[ -f "$MOSAIC_CLAUDE_BIN" ] && [ -x "$MOSAIC_CLAUDE_BIN" ] || { echo "claude adapter: MOSAIC_CLAUDE_BIN is not an executable file: $MOSAIC_CLAUDE_BIN" >&2; exit 2; }
|
|
CLAUDE=$MOSAIC_CLAUDE_BIN
|
|
fi
|
|
|
|
mkdir -p "$MOSAIC_WORKSPACE" "$MOSAIC_SESSION_DIR"
|
|
cd "$MOSAIC_WORKSPACE"
|
|
|
|
# Session: the first turn names a new session id, later turns resume it.
|
|
# The id is kept only after a turn succeeds, so a failed first turn never
|
|
# leaves a --resume of a session that doesn't exist.
|
|
ID_FILE="$MOSAIC_SESSION_DIR/claude-session-id"
|
|
if [ -s "$ID_FILE" ]; then
|
|
SESSION_FLAG=--resume
|
|
SESSION_ID=$(cat "$ID_FILE")
|
|
else
|
|
SESSION_FLAG=--session-id
|
|
SESSION_ID=$(cat /proc/sys/kernel/random/uuid)
|
|
fi
|
|
|
|
# Flags:
|
|
# -p one-shot: print the answer and exit
|
|
# --output-format text stdout is the answer only
|
|
# --system-prompt the generated prompt replaces the default
|
|
# --restricted no user/project/local settings files; --settings
|
|
# (the gate hook) still applies; no code-running
|
|
# tool unless --tools names it; file tools confined
|
|
# to the working directory; no CLAUDE.md file or
|
|
# auto-memory in the prompt. The S0 lines above
|
|
# don't depend on it, but it is what keeps founder
|
|
# and repository memory out; a test fails without it.
|
|
# --tools the built-in tool limit (S0 line 5); empty = none
|
|
# --allowedTools the same tools plus the mosaic MCP server, so
|
|
# --permission-mode dontAsk nothing waits on a prompt and anything else is denied
|
|
# --settings the gate hook (S0 lines 1-4)
|
|
# --strict-mcp-config only the bundle's MCP server, which serves the
|
|
# --mcp-config typed bus tools
|
|
# --disable-slash-commands no skills (the bundle lists none)
|
|
ALLOWED=mcp__mosaic
|
|
[ -z "${MOSAIC_TOOLS:-}" ] || ALLOWED="$MOSAIC_TOOLS,mcp__mosaic"
|
|
PROMPT_CONTENT="$(cat "$MOSAIC_SYSTEM_PROMPT_FILE")"
|
|
"$CLAUDE" -p "$MOSAIC_REQUEST" \
|
|
--output-format text \
|
|
--system-prompt "$PROMPT_CONTENT" \
|
|
--model "$MOSAIC_MODEL" \
|
|
--restricted \
|
|
--tools "${MOSAIC_TOOLS:-}" \
|
|
--allowedTools "$ALLOWED" \
|
|
--permission-mode dontAsk \
|
|
--settings "$MOSAIC_CLAUDE_SETTINGS" \
|
|
--strict-mcp-config \
|
|
--mcp-config "$MOSAIC_CLAUDE_MCP_CONFIG" \
|
|
--disable-slash-commands \
|
|
"$SESSION_FLAG" "$SESSION_ID" || exit $?
|
|
[ "$SESSION_FLAG" = --resume ] || printf '%s\n' "$SESSION_ID" > "$ID_FILE"
|