Files
turnstone/Dockerfile
T
Patrick Buckley 460308241d fix(tools): lower working directory and workspace into fs tool descriptions
The process cwd was nowhere in the model's context: shells start in the
inherited process cwd (spawn_group_leader passes no cwd), relative file
paths resolve against it, but nothing told the model where it was
standing — in stock Docker every shell ran in /data while user files sat
in the /workspace mount, and the model's only recourse was to probe with
pwd (#857, #833).

Lower both facts into the tool schemas, where they gate intrinsically on
tool availability (a persona without fs tools carries no note, and
coordinator envelopes are untouched):

- tools/*.json: cwd_note/workspace_note metadata templates on bash,
  read_file, write_file, edit_file, search, diff_file; bash also states
  the fresh-shell-per-call semantics (cd does not persist) and drops a
  stale reference to the removed man tool.
- tools.apply_cwd_context(): renders the notes into descriptions;
  deep-copies noted tools (the fs dicts are shared across
  TOOLS/INTERACTIVE_TOOLS/TASK_AGENT_TOOLS and aliased through
  merge_mcp_tools), passes note-less tools through by reference.
- ChatSession._apply_cwd_notes(): wraps every fresh interactive build of
  _tools AND _task_tools (construction, MCP catalog change, MCP
  disconnect) — assignment-time, so the wire tools block stays
  byte-stable for provider prompt caches. os.getcwd() is OSError-guarded
  (MCP rebuilds run on a background thread; eval tears down its
  workdir); the workspace hint drops when the dir is missing or equals
  the cwd. Task-agent sub-agents carry their own notes via _task_tools,
  independent of parent persona visibility.
- config.get_workspace_dir(): [tools] workspace_dir with
  TURNSTONE_WORKSPACE env fallback (searxng pattern), informational
  only — no chdir, no path confinement (per-workstream working-dir
  grants are a separate planned feature).
- Dockerfile: ENV TURNSTONE_WORKSPACE=/workspace so stock deployments
  surface the mount with zero operator config.
- docs/docker.md: document the /data working directory, the
  working_dir: /workspace compose override as the operator-level fix,
  and the SQLite-fallback-DB-in-cwd caveat.

Closes #857
2026-07-19 19:09:58 -07:00

76 lines
3.0 KiB
Docker

# =============================================================================
# Turnstone — Docker build with uv for reproducible, locked installs
# Single image for all services: server, console, channel, eval
# =============================================================================
FROM python:3.14-slim
LABEL org.opencontainers.image.title="turnstone" \
org.opencontainers.image.description="Multi-node AI orchestration platform"
COPY --from=ghcr.io/astral-sh/uv:0.11.29 /uv /usr/local/bin/uv
# Remove the slim image's man page exclusion so man-db has actual content
RUN rm -f /etc/dpkg/dpkg.cfg.d/docker
# System dependencies: psycopg (libpq5), developer tooling for agent workflows.
# ripgrep is the preferred backend for the search tool — natively bounds
# per-line, per-file, and per-filesize so pathological inputs (minified
# bundles, training-data JSONL with multi-MB single records) can't OOM us.
# ffmpeg transcodes omni STT uploads (browser webm/opus) to the 16 kHz mono
# WAV the omni chat-audio lane decodes.
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \
libpq5 git curl jq man-db manpages procps file ripgrep ffmpeg \
&& rm -rf /var/lib/apt/lists/*
# Node.js LTS (for npx-based MCP servers like @modelcontextprotocol/server-github)
COPY --from=node:24-slim /usr/local/bin/node /usr/local/bin/node
COPY --from=node:24-slim /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
# Non-root user
RUN useradd --create-home --shell /bin/bash turnstone
WORKDIR /app
# Install dependencies first (cached layer — only re-runs when deps change)
COPY pyproject.toml uv.lock README.md LICENSE NOTICE THIRD-PARTY-NOTICES ./
RUN uv sync --frozen --no-install-project --no-dev \
--no-compile --extra all
# Install the project itself
COPY turnstone/ turnstone/
RUN uv sync --frozen --no-dev \
--no-compile --extra all
# Compile bytecode in a separate step (avoids fd exhaustion during install)
RUN python -m compileall -q .venv turnstone/
# Add venv to PATH so entry points are found
ENV PATH="/app/.venv/bin:$PATH"
# Health check script (stdlib only, no pip deps needed)
COPY docker/healthcheck.py /usr/local/bin/healthcheck.py
# Entrypoint script — runs migrations before starting
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
# Data directory — SQLite DB is created in CWD
WORKDIR /data
RUN chown turnstone:turnstone /data
# Workspace mount point — bind-mount a host directory here. The env var
# surfaces the path in the model's shell/file tool descriptions
# (config.get_workspace_dir); without it the mount is invisible to the
# model, whose cwd is /data below.
RUN mkdir -p /workspace && chown turnstone:turnstone /workspace
ENV TURNSTONE_WORKSPACE=/workspace
USER turnstone
ENTRYPOINT ["entrypoint.sh"]
# Default command (overridden per service in compose.yaml)
CMD ["turnstone-server", "--host", "0.0.0.0", "--port", "8080"]