Files
roboco/docker/scripts/sdk-startup-hook.sh
T
Renn F 3742483e1c fix(agent): pin uv to baked /app/.venv so MCP/SDK servers start instantly (#179)
Every agent MCP server is launched as `uv run python -m roboco.mcp.<server>`
(via the orchestrator-generated mcp-config.json) and the SDK server via
`uv run python -m roboco.agent_sdk.server` (sdk-startup-hook.sh) — both with
cwd = the agent's WORKSPACE, not /app. `uv run` then resolves a cwd-relative
`.venv` (≠ the image's baked /app/.venv), ignores VIRTUAL_ENV with a warning,
and RE-SYNCS the full dependency set (torch/lancedb/pyarrow/scipy, ~350MB)
into a fresh venv on every spawn.

A warm host uv wheel cache masks this (fast re-resolve from cached wheels —
earlier runs this session opened PR #26/#28/#29 fine). On a COLD cache (first
spawn after an image rebuild — exactly when deploying new fixes) the download
takes minutes, the MCP servers never register, and the agent burns its whole
budget with "No such tool available: mcp__roboco-*" before reaping. Observed
this session: be-dev-1 never claimed; /tmp/sdk-server.log showed the live
torch/lancedb download + the `VIRTUAL_ENV ... will be ignored` warning.

Fix: set UV_PROJECT_ENVIRONMENT=/app/.venv in (1) every MCP server's env in
the generated mcp-config.json (one place — shared mcp_env dict) and (2) the
SDK startup hook. uv then reuses the pre-baked image venv instantly,
regardless of cwd or cache state. Not a regression from this session's code
(none of #172b/#175/#176/#177/#178 touched the launch/venv path — verified);
a pre-existing launch-cwd fragility that rebuilding to deploy exposed.

Test: _generate_mcp_config asserts every server env pins
UV_PROJECT_ENVIRONMENT=/app/.venv. make quality green.
2026-05-23 01:23:18 +02:00

55 lines
2.2 KiB
Bash

#!/bin/bash
# SessionStart hook — starts the SDK server and prints the pre-rendered
# task briefing into the session so Claude doesn't burn its first turns
# on tool discovery (roboco_task_scan → roboco_task_get → read files).
#
# The briefing is written by the orchestrator before the container spawns
# (_write_agent_briefing) and mounted read-only at /app/briefing.md.
SDK_PORT="${ROBOCO_SDK_PORT:-9000}"
AGENT_ID="${ROBOCO_AGENT_ID:-unknown}"
LOG_FILE="/tmp/sdk-server.log"
BRIEFING_FILE="/app/briefing.md"
PRECOMPACT_FILE="/tmp/roboco-precompact-${AGENT_ID}.md"
# #179: this hook runs with cwd = the agent's workspace, so a bare
# `uv run` resolves a cwd-relative `.venv` (≠ the baked /app/.venv),
# ignores VIRTUAL_ENV with a warning, and RE-SYNCS the full dependency
# set (torch/lancedb/pyarrow/scipy, ~350MB) into a fresh venv. On a cold
# uv wheel cache (first spawn after an image rebuild) that download takes
# minutes and the SDK/MCP layer never comes up before the agent reaps.
# Pin uv to the pre-baked image venv so it starts instantly regardless
# of cwd. (The orchestrator sets the same var in every MCP server's env
# in the generated mcp-config.json — keep both in sync.)
export UV_PROJECT_ENVIRONMENT=/app/.venv
# --- SDK bring-up ---------------------------------------------------------
if ! curl -sf "http://localhost:${SDK_PORT}/health" >/dev/null 2>&1; then
echo "[SDK] Starting for agent ${AGENT_ID} on port ${SDK_PORT}..."
nohup uv run python -m roboco.agent_sdk.server > "$LOG_FILE" 2>&1 &
SDK_PID=$!
sleep 2
if curl -sf "http://localhost:${SDK_PORT}/health" >/dev/null 2>&1; then
echo "[SDK] Ready (PID: ${SDK_PID})"
else
echo "[SDK] Starting in background (PID: ${SDK_PID}, check ${LOG_FILE})"
fi
fi
# Reset budget/terminal counters at the start of every session.
curl -sf -m 2 -X POST "http://localhost:${SDK_PORT}/budget/reset" >/dev/null 2>&1 || true
# --- Briefing + PreCompact recovery --------------------------------------
# Compact restore comes FIRST so it's clear what this session is resuming.
if [[ -s "$PRECOMPACT_FILE" ]]; then
echo "### Resumed from compact"
cat "$PRECOMPACT_FILE"
echo
fi
if [[ -s "$BRIEFING_FILE" ]]; then
cat "$BRIEFING_FILE"
fi
exit 0