A new package, nisse-ui: a read-only page for studying how the agents work. For each work item it shows the steps (what people and the orchestrator said, and each run), each run's turns, and each turn's request read against the log it was made from: - each message beside the events it came from, tagged unchanged, labelled, narrated, shortened (by how much) or a compaction, with what fitting dropped; - what is new since the turn before, and where the request first differs (where prompt reuse had to stop), next to the server's count; - the reply with its reasoning, and the raw JSON. Runs going in circles are flagged by nisse_core.loop.circles (repeated near-identical replies, one correction given again and again, repeated apologies), a pure function M7.8 can reuse in Driver.run. In nisse-core: - render.py: message_indexes(), from the same code render() uses, so the page's matching can't drift from what was sent. - calls.py: the record notes when recording began; the reader gains calls_for(item) and call(id). - state/readonly.py: opens an agent's SQLite file without creating its -wal/-shm files (mode=ro alone does, once the writer has closed). The page reads only through read-only connections, escapes everything, and sends a Content Security Policy that allows no script. It listens on 127.0.0.1; until M6.6d puts it behind Authelia, reach it over an SSH tunnel. Jinja2 is its one new dependency, for autoescaping. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
112 lines
4.6 KiB
Docker
112 lines
4.6 KiB
Docker
# The agents as container images, one each (Development Plan §3; M1.5
|
|
# nisse-dev, M7.6 nisse-review, X4).
|
|
#
|
|
# Each is stateless, runs as its own user, and keeps its state in a volume
|
|
# (/var/lib/<agent>); its configuration is mounted read-only
|
|
# (/etc/<agent>/<agent>.yaml) and its secrets come as environment variables
|
|
# (docker --env-file). nisse-dev's SSH and deploy keys are mounted read-only
|
|
# wherever its configuration says; it runs no model-chosen command here
|
|
# (L13): those go to build VMs over SSH. nisse-review needs only git, for
|
|
# its read-only clones, kept in its volume (scratch/) between restarts.
|
|
#
|
|
# docker build -t nisse-dev . nisse-dev
|
|
# docker build --target review -t nisse-review . nisse-review
|
|
# docker build --target test -t nisse-dev:test . plus the tests, for
|
|
# scripts/e2e.sh --image
|
|
# docker build --target test --build-arg TEST_BASE=<image> -t <tag> .
|
|
# the tests over another image: nisse-review's (TEST_BASE=review), or
|
|
# a release as published (M1.5, M7.6 acceptance)
|
|
|
|
# What the test stage stacks on: this build's agent, or a published image.
|
|
ARG TEST_BASE=runtime
|
|
|
|
# The workspace's source, from which each agent's environment is installed.
|
|
FROM python:3.12-slim AS source
|
|
COPY --from=ghcr.io/astral-sh/uv:0.12.10 /uv /usr/local/bin/uv
|
|
ENV UV_PROJECT_ENVIRONMENT=/opt/nisse UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
|
|
WORKDIR /src
|
|
COPY pyproject.toml uv.lock ./
|
|
COPY nisse-core nisse-core
|
|
COPY nisse-dev nisse-dev
|
|
COPY nisse-ops nisse-ops
|
|
COPY nisse-review nisse-review
|
|
COPY nisse-ui nisse-ui
|
|
|
|
# nisse-dev and what it needs, installed (not editable): the source tree
|
|
# isn't part of the image.
|
|
FROM source AS build
|
|
RUN uv sync --frozen --no-dev --package nisse-dev --no-editable
|
|
|
|
# nisse-review and what it needs, in an environment of its own.
|
|
FROM source AS build-review
|
|
RUN uv sync --frozen --no-dev --package nisse-review --no-editable
|
|
|
|
FROM python:3.12-slim AS review
|
|
ARG VERSION=dev
|
|
LABEL org.opencontainers.image.title=nisse-review org.opencontainers.image.version=$VERSION
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends git \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& useradd --create-home --uid 10002 nisse-review \
|
|
&& mkdir -p /var/lib/nisse-review /etc/nisse-review \
|
|
&& chown nisse-review /var/lib/nisse-review
|
|
COPY --from=build-review /opt/nisse /opt/nisse
|
|
ENV PATH=/opt/nisse/bin:$PATH \
|
|
NISSE_CONFIG=/etc/nisse-review/nisse-review.yaml \
|
|
NISSE_ENV=/run/nisse-review/.env
|
|
VOLUME /var/lib/nisse-review
|
|
USER nisse-review
|
|
ENTRYPOINT ["nisse-review"]
|
|
|
|
FROM python:3.12-slim AS runtime
|
|
ARG VERSION=dev
|
|
LABEL org.opencontainers.image.title=nisse-dev org.opencontainers.image.version=$VERSION
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends openssh-client \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& useradd --create-home --uid 10001 nisse \
|
|
&& mkdir -p /var/lib/nisse-dev /etc/nisse-dev \
|
|
&& chown nisse /var/lib/nisse-dev
|
|
COPY --from=build /opt/nisse /opt/nisse
|
|
ENV PATH=/opt/nisse/bin:$PATH \
|
|
NISSE_CONFIG=/etc/nisse-dev/nisse-dev.yaml \
|
|
NISSE_ENV=/run/nisse-dev/.env
|
|
VOLUME /var/lib/nisse-dev
|
|
USER nisse
|
|
ENTRYPOINT ["nisse-dev"]
|
|
|
|
# The tests over an image's own package (this build's, or a published one,
|
|
# TEST_BASE): only the test tools are added, never the agent reinstalled, so
|
|
# the end-to-end suite runs against what was built, not the source beside it.
|
|
# The suite's own code imports every agent's package (nisse-dev's config,
|
|
# for one), so a package the base lacks is added without its dependencies:
|
|
# what the image installed stays as it was.
|
|
FROM ${TEST_BASE} AS test
|
|
USER root
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends git openssh-server \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& mkdir -p /run/sshd
|
|
COPY --from=build /usr/local/bin/uv /usr/local/bin/uv
|
|
WORKDIR /src
|
|
COPY pyproject.toml uv.lock ./
|
|
COPY nisse-core nisse-core
|
|
COPY nisse-dev nisse-dev
|
|
COPY nisse-ops nisse-ops
|
|
COPY nisse-review nisse-review
|
|
COPY tests tests
|
|
COPY scripts scripts
|
|
COPY conftest.py ./
|
|
ENV UV_PROJECT_ENVIRONMENT=/opt/nisse UV_NO_SYNC=1
|
|
RUN uv export --frozen --only-group dev --no-hashes --no-emit-project > /tmp/dev.txt \
|
|
&& uv pip install --quiet --python /opt/nisse/bin/python -r /tmp/dev.txt \
|
|
&& for package in nisse_dev nisse_review; do \
|
|
/opt/nisse/bin/python -c "import $package" 2>/dev/null \
|
|
|| uv pip install --quiet --no-deps --python /opt/nisse/bin/python \
|
|
"./$(echo "$package" | tr _ -)"; \
|
|
done
|
|
ENTRYPOINT []
|
|
|
|
# The default target: the agent itself.
|
|
FROM runtime AS agent
|