Adds the harness plug-in kind (interface 1). packages/plugins/harness/pi and claude-code hold what core branched on: the adapter, thinking levels, preflight, version probe, sandbox mounts, builtins line, typed tool names, gate timeout, bundle files, adapter env, turn end and session-files path. Core reaches a module only through the loader. A session's route is the instance's harness, model and thinking vars, or the three fallback. vars; a missing one refuses no-route, and the system file's execution keys are never read for a session. The launcher checks the primary route, then the fallback: harness-off, harness-refused, thinking-unsupported, then preflight. A failed preflight falls back when a fallback is set, and any refusal of the fallback names the primary's failure too. Account checks run only when the route's module counts an account. The manifest records route, routeReason and harnessVersion, replacing piVersion and claudeVersion. pi gets --thinking, Claude Code --effort, both from MOSAIC_THINKING. The business file refuses a partial fallback. routes and routes.set are human-only bus verbs outside ORGANIZATION_KEYS; checkRoutes is shared by routes.set, instances.set and limits.set and checks only changed routes. The chief of staff changes an instance's model, and the assistant's whole vars (Q169), through limits.set, so its route write runs the same checks. A model outside launch.models refuses unknown-model, primary or fallback, and routes.set passes that code through. A new run binds only on a switched-on harness module name and records its transcript path, which the usage reader uses. Settings gains a Routing table with six selects per instance and loses the Role models fieldset; it posts changed rows to /api/routes. webui/src/bus.mjs is unchanged. Until Jason's `mosaic plugins set` adds "harness": ["claude-code"], a new launch refuses harness-off.
123 lines
4.8 KiB
Bash
123 lines
4.8 KiB
Bash
#!/bin/sh
|
|
# Pi adapter: implements the Mosaic adapter contract for the pinned
|
|
# @earendil-works/pi-coding-agent CLI.
|
|
#
|
|
# Contract: see /opt/mosaic/adapters/README.md.
|
|
# Headless (default): stdout = response only; stderr = diagnostics; exit 0.
|
|
# Interactive (MOSAIC_INTERACTIVE=1): full pi TUI on the attached terminal.
|
|
set -eu
|
|
|
|
[ -n "${MOSAIC_SYSTEM_PROMPT_FILE:-}" ] || { echo "pi adapter: MOSAIC_SYSTEM_PROMPT_FILE is required" >&2; exit 2; }
|
|
[ -r "$MOSAIC_SYSTEM_PROMPT_FILE" ] || { echo "pi adapter: system prompt not readable: $MOSAIC_SYSTEM_PROMPT_FILE" >&2; exit 2; }
|
|
# MOSAIC_AGENT_NAME is optional in headless mode (identity section is then
|
|
# omitted); interactive launches always set it via scripts/agent.sh.
|
|
|
|
: "${PI_PROVIDER:?pi adapter: PI_PROVIDER is required}"
|
|
: "${PI_MODEL:?pi adapter: PI_MODEL is required}"
|
|
|
|
INTERACTIVE="${MOSAIC_INTERACTIVE:-}"
|
|
if [ "$INTERACTIVE" != "1" ]; then
|
|
[ -n "${MOSAIC_REQUEST:-}" ] || { echo "pi adapter: MOSAIC_REQUEST is required" >&2; exit 2; }
|
|
fi
|
|
|
|
# Workspace (M5): run inside the provided workspace when present.
|
|
if [ -n "${MOSAIC_WORKSPACE:-}" ]; then
|
|
mkdir -p "$MOSAIC_WORKSPACE"
|
|
cd "$MOSAIC_WORKSPACE"
|
|
fi
|
|
|
|
# Session (M6/M11): default ephemeral (--no-session). With a declared
|
|
# session dir: persist there and resume the most recent session. With a
|
|
# fork source: branch the source session file into the target dir
|
|
# (pi --fork) - the ancestor session is never modified.
|
|
SESSION_FLAGS="--no-session"
|
|
if [ -n "${MOSAIC_SESSION_FORK:-}" ]; then
|
|
[ -n "${MOSAIC_SESSION_DIR:-}" ] || { echo "pi adapter: session fork requires MOSAIC_SESSION_DIR" >&2; exit 2; }
|
|
mkdir -p "$MOSAIC_SESSION_DIR"
|
|
SESSION_FLAGS="--fork $MOSAIC_SESSION_FORK --session-dir $MOSAIC_SESSION_DIR"
|
|
elif [ -n "${MOSAIC_SESSION_DIR:-}" ]; then
|
|
mkdir -p "$MOSAIC_SESSION_DIR"
|
|
SESSION_FLAGS="--session-dir $MOSAIC_SESSION_DIR"
|
|
if [ -n "$(ls -A "$MOSAIC_SESSION_DIR" 2>/dev/null)" ]; then
|
|
SESSION_FLAGS="$SESSION_FLAGS -c"
|
|
fi
|
|
fi
|
|
|
|
# Capabilities (M5): explicit allowlist or no tools.
|
|
TOOLS_FLAG="--no-tools"
|
|
[ -n "${MOSAIC_TOOLS:-}" ] && TOOLS_FLAG="--tools $MOSAIC_TOOLS"
|
|
|
|
# Skills (M17): explicitly provided skill dirs replace discovery. When none
|
|
# are provided the agent runs with --no-skills (nothing ambient to find).
|
|
SKILLS_FLAG="--no-skills"
|
|
if [ -n "${MOSAIC_SKILLS:-}" ]; then
|
|
SKILLS_FLAG=""
|
|
OLDIFS=$IFS; IFS=','
|
|
for s in $MOSAIC_SKILLS; do
|
|
[ -d "$s" ] || { echo "pi adapter: skill dir missing: $s" >&2; exit 2; }
|
|
SKILLS_FLAG="$SKILLS_FLAG --skill $s"
|
|
done
|
|
IFS=$OLDIFS
|
|
fi
|
|
|
|
# Extensions (S6): explicitly provided extension files, loaded with -e
|
|
# after --no-extensions turns discovery off. A missing file refuses here;
|
|
# pi itself also refuses to start on a missing or broken -e (row S0 line 3).
|
|
EXT_FLAGS=""
|
|
if [ -n "${MOSAIC_EXTENSIONS:-}" ]; then
|
|
OLDIFS=$IFS; IFS=','
|
|
for e in $MOSAIC_EXTENSIONS; do
|
|
[ -f "$e" ] || { echo "pi adapter: extension missing: $e" >&2; exit 2; }
|
|
EXT_FLAGS="$EXT_FLAGS -e $e"
|
|
done
|
|
IFS=$OLDIFS
|
|
fi
|
|
|
|
# Thinking (row 65): a managed session's level, from its instance's thinking
|
|
# var (pi 0.85.1: off, minimal, low, medium, high, xhigh, max). Workers from
|
|
# run-task.sh set none and keep pi's default.
|
|
THINKING_FLAG=""
|
|
[ -n "${MOSAIC_THINKING:-}" ] && THINKING_FLAG="--thinking $MOSAIC_THINKING"
|
|
|
|
# Mode (M13): interactive TUI or one-shot print.
|
|
PRINT_MODE="-p"
|
|
REQUEST_ARG=""
|
|
if [ "$INTERACTIVE" = "1" ]; then
|
|
PRINT_MODE=""
|
|
else
|
|
REQUEST_ARG="$MOSAIC_REQUEST"
|
|
fi
|
|
|
|
# All flags documented in the pi package README (CLI Reference):
|
|
# -p/--print one-shot mode: print the response and exit (omitted in
|
|
# interactive TUI mode)
|
|
# --system-prompt replace the default prompt with the generated one
|
|
# --no-* no ambient context/skills/extensions/templates/themes
|
|
# EXT_FLAGS the explicitly provided extensions only (-e)
|
|
# SESSION_FLAGS ephemeral | persistent | forked (per env)
|
|
# TOOLS_FLAG per capabilities
|
|
# THINKING_FLAG --thinking <level> for a managed session, else nothing
|
|
# --offline no startup network operations (update checks/telemetry)
|
|
# --no-approve ignore project-local .pi/ files (settings, SYSTEM.md)
|
|
# whatever trust is saved for the workspace
|
|
PROMPT_CONTENT="$(cat "$MOSAIC_SYSTEM_PROMPT_FILE")"
|
|
set -- \
|
|
--offline \
|
|
--no-approve \
|
|
--no-extensions \
|
|
$EXT_FLAGS \
|
|
$SKILLS_FLAG \
|
|
--no-prompt-templates \
|
|
--no-themes \
|
|
--no-context-files \
|
|
$TOOLS_FLAG \
|
|
$SESSION_FLAGS \
|
|
--provider "$PI_PROVIDER" \
|
|
--model "$PI_MODEL" \
|
|
$THINKING_FLAG \
|
|
--system-prompt "$PROMPT_CONTENT"
|
|
# One-shot mode appends -p and the request (both safely quoted);
|
|
# interactive mode appends nothing - clean TUI.
|
|
[ "$INTERACTIVE" = "1" ] || set -- "$@" -p "$MOSAIC_REQUEST"
|
|
exec pi "$@"
|