mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-07 07:23:08 +02:00
## Thinking Path - Paperclip Runner needs its runtime preinstalled for fast sandbox startup. - Native and local adapters should launch one current CLI installation per provider. - An older global copy can shadow that installation, and exact native compatibility pins must match it. - Update the qualified releases and binary digests, expose shared CLI entrypoints from the provider pack, and prefer the image-owned bin directory. - Keep dependency installation in the image build; task startup only discovers, links, and verifies artifacts. ## Linked Issues or Issue Description **What happened?** Remote native startup rejected a stale global Codex, while CLI-only images lacked runnerd entirely. **Expected behavior** An image-baked runtime starts without uploading binaries or installing packages. All adapters share the same current provider CLI. **Steps to reproduce** Start a native remote task with the old global Codex and the updated runtime available only under `/opt/paperclip-runner/bin`. **Paperclip version or commit** Discovery behavior at `54a99d884`. **Deployment mode** Docker with a remote sandbox. ## What Changed - Prefer `/opt/paperclip-runner/bin`, then the user's local bin directory, then PATH. Existing metadata and version validation remains in force. - Qualify Codex 0.153.4, OpenCode 1.18.29, and Claude SDK 0.3.263 / CLI 2.1.263. Update binary digests, TypeScript/Rust checks, registry defaults, and the displayed OpenCode version together. - Share Codex and Claude's native executable with the ACP bridges through exact dependency overrides. Preserve the separately qualified ACP bridge implementations and their security patches. - Expose shared provider-pack CLI launchers; fail the pack build if Codex ACP resolves a separate Codex installation. Update the eval image's other agent CLIs to current stable releases and remove duplicate global provider installs. - Document the single-current-CLI policy in source comments and development guidance. Latest stable releases are resolved at review/build preparation and pinned; task startup never auto-updates. ## Verification - Native-session and adapter-registry suites: 158 tests passed. - Provider suites: 88 tests passed, 7 Linux-only checks skipped on macOS. One existing macOS temporary-path alias assertion passed when rerun with canonical `TMPDIR=/private/tmp`. - Package-contract and OpenCode materialization tests: 11 passed. - Full typecheck, build, and token gates passed. Rust native-provider/recovery tests: 19 passed. - Broad local suite: 5,974 passed, 23 failed, 41 skipped. Failures are in unchanged macOS workspace/path/port and connection suites; focused runtime tests pass. All latest-head Linux PR checks passed, including the full test shards, typecheck, build, runner verification, browser suites, and canary dry run. - The standalone fleet image built with one current provider CLI each and passed native Codex/Claude binary-integrity checks. A disposable Daytona sandbox reported ready in 798 ms; its baked runner completed an API-key `gpt-5.6-luna` turn in 2,430 ms and returned the expected marker with a usage receipt. No runtime artifacts were uploaded or installed. - The normal shared `codex exec` entrypoint also completed an API-key `gpt-5.6-luna` turn in 2,321 ms. - Both image builds verify the complete generated lockfile against a reviewed SHA-256 before package installation or lifecycle execution. Root lockfile changes remain CI-owned. Merge and rollout remain on hold for operator review. ## Risks - Updating provider CLIs changes their behavior for all adapters; version probes and live native smoke testing are required before image promotion. - The image-owned directory takes precedence. Its entries must launch the same shared CLI as the global PATH, not a private older/newer copy. - Application qualification pins and the deployed image must move together. No startup fallback installation is added. - No schema or authentication-policy changes. ## Model Used OpenAI GPT-6 (Codex). The session does not expose a more specific model ID or context-window size. Used reasoning, repository inspection, code execution, and browser verification. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
142 lines
8.2 KiB
Docker
142 lines
8.2 KiB
Docker
# syntax=docker/dockerfile:1.7@sha256:a57df69d0ea827fb7266491f2813635de6f17269be881f696fbfdf2d83dda33e
|
|
|
|
# Keep the Rust toolchain explicit so the runner artifact is reproducible and
|
|
# is always built for the same platform as the final Daytona image. Base-image
|
|
# digests are the reviewed linux/amd64 manifests; the tags are annotations.
|
|
FROM rust:1.97-bookworm@sha256:408fe88047cef61a2087653b0c5255fa51c0f2d6d94ddedd7a2562a9b91a46f6 AS runnerd-build
|
|
|
|
WORKDIR /workspace/packages/paperclip-runner/runner
|
|
COPY packages/paperclip-runner/runner/Cargo.toml packages/paperclip-runner/runner/Cargo.lock ./
|
|
COPY packages/paperclip-runner/runner/crates ./crates
|
|
COPY packages/paperclip-runner/protocol ../protocol
|
|
RUN cargo build --locked --release -p paperclip-runner-core --bin paperclip-runnerd \
|
|
&& strip /workspace/packages/paperclip-runner/runner/target/release/paperclip-runnerd
|
|
|
|
FROM node:24-bookworm@sha256:9137a20e25879e0b557227b57e3ee4e9af4bde29eb3db66134cd1723e84f830b AS provider-pack-build
|
|
RUN corepack enable && corepack prepare pnpm@9.15.4 --activate
|
|
WORKDIR /workspace
|
|
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc tsconfig.base.json ./
|
|
COPY patches ./patches
|
|
COPY scripts/link-plugin-dev-sdk.mjs ./scripts/link-plugin-dev-sdk.mjs
|
|
# CI owns pnpm-lock.yaml and regenerates it for manifest-only PRs. Include the
|
|
# complete workspace manifest graph so all source-owned patches apply, then
|
|
# resolve before the frozen install. This happens only during image creation.
|
|
COPY packages ./packages
|
|
COPY server/package.json ./server/package.json
|
|
COPY ui/package.json ./ui/package.json
|
|
COPY cli/package.json ./cli/package.json
|
|
# The complete resolved lock (including transitive integrity hashes) is reviewed.
|
|
# Reject registry-time drift BEFORE installing packages or running lifecycle code.
|
|
# Refresh this digest together with source/provider dependency changes.
|
|
ARG PAPERCLIP_RUNNER_LOCK_SHA256=47a7c09302d47843054d0301f8f52f3da935b9c6ac771bace0409da752b6af7f
|
|
RUN pnpm install --resolution-only --ignore-scripts --no-frozen-lockfile \
|
|
&& printf '%s pnpm-lock.yaml\n' "${PAPERCLIP_RUNNER_LOCK_SHA256}" > /tmp/provider-lock.sha256 \
|
|
&& sha256sum -c /tmp/provider-lock.sha256 \
|
|
&& pnpm install --frozen-lockfile --filter '@paperclipai/paperclip-runner...'
|
|
ARG PAPERCLIP_RUNNER_SOURCE_REVISION
|
|
RUN test -n "${PAPERCLIP_RUNNER_SOURCE_REVISION}"
|
|
RUN pnpm --filter @paperclipai/paperclip-runner build:typescript \
|
|
&& PAPERCLIP_RUNNER_SOURCE_REVISION="${PAPERCLIP_RUNNER_SOURCE_REVISION}" \
|
|
node packages/paperclip-runner/scripts/build-provider-pack.mjs /provider-pack \
|
|
&& chmod -R a+rX /provider-pack
|
|
|
|
# Fleet sandbox base image. Keep this section aligned with
|
|
# paperclipai/paperclip-cloud/fleet-sandbox-image/Dockerfile. The only Paperclip
|
|
# runner-specific addition is /usr/local/bin/paperclip-runnerd below.
|
|
FROM daytonaio/sandbox:0.8.0@sha256:eadf88e4391072b7ad4bed27d9cadfc9fe9d8ed375d9219d34c2ccb518f213e3
|
|
|
|
USER root
|
|
|
|
ENV PAPERCLIP_RUNNER_PROVIDER_PACK_ROOT=/opt/paperclip-runner/provider-pack
|
|
ENV PATH=${PAPERCLIP_RUNNER_PROVIDER_PACK_ROOT}/node_modules/.bin:/usr/local/share/nvm/current/bin:/usr/local/python/current/bin:/usr/local/py-utils/bin:${PATH}
|
|
|
|
# Share the pack's latest stable Codex, Claude and OpenCode with every adapter.
|
|
# Never add a second CLI version to work around a runner compatibility pin.
|
|
RUN npm uninstall -g @anthropic-ai/claude-code @openai/codex opencode-ai \
|
|
&& npm install -g \
|
|
@xai-official/grok@1.0.13 \
|
|
@google/gemini-cli@0.58.0 \
|
|
@moonshot-ai/kimi-code@0.41.0 \
|
|
&& npm cache clean --force
|
|
|
|
# Hermes requires Python >=3.11,<3.14; the image's default Python is newer.
|
|
# Use the distro interpreter in a dedicated environment without changing PATH
|
|
# or keeping an older Hermes CLI. Everything is installed before sandbox boot.
|
|
# Debian 13 in the sandbox base already includes Python 3.13 and python3-venv.
|
|
RUN /usr/bin/python3 -m venv /opt/hermes \
|
|
&& /opt/hermes/bin/pip install --no-cache-dir hermes-agent==0.19.0 \
|
|
&& ln -sf /opt/hermes/bin/hermes /usr/local/bin/hermes
|
|
|
|
ARG CURSOR_VERSION=2026.09.02-c22c1a3
|
|
ARG CURSOR_SHA256_AMD64=b73b59854762535c0fc20d7ccc51c3b5a356a851491088d60a362be48750f53c
|
|
RUN set -eu; \
|
|
arch="$(dpkg --print-architecture)"; \
|
|
[ "$arch" = "amd64" ] || { echo "FATAL: cursor pin only covers amd64, not $arch" >&2; exit 1; }; \
|
|
dir="/opt/cursor/versions/${CURSOR_VERSION}"; mkdir -p "$dir"; \
|
|
curl -fsSL "https://downloads.cursor.com/lab/${CURSOR_VERSION}/linux/x64/agent-cli-package.tar.gz" -o /tmp/cursor.tgz; \
|
|
printf '%s /tmp/cursor.tgz\n' "${CURSOR_SHA256_AMD64}" > /tmp/cursor.sha256; \
|
|
sha256sum -c /tmp/cursor.sha256; \
|
|
tar --strip-components=1 -xzf /tmp/cursor.tgz -C "$dir"; \
|
|
rm -f /tmp/cursor.tgz /tmp/cursor.sha256; \
|
|
ln -sf "$dir/cursor-agent" /usr/local/bin/cursor-agent; \
|
|
ln -sf /usr/local/bin/cursor-agent /usr/local/bin/agent; \
|
|
chmod -R a+rX /opt/cursor
|
|
|
|
ARG GH_VERSION=2.100.0
|
|
ARG GH_SHA256_AMD64=e4d4bb4498e8d007abe545b6568926793ace1b6447da598294a610018cb164be
|
|
RUN set -eu; \
|
|
arch="$(dpkg --print-architecture)"; \
|
|
[ "$arch" = "amd64" ] || { echo "FATAL: gh pin only covers amd64, not $arch" >&2; exit 1; }; \
|
|
curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz" -o /tmp/gh.tgz; \
|
|
printf '%s /tmp/gh.tgz\n' "${GH_SHA256_AMD64}" > /tmp/gh.sha256; \
|
|
sha256sum -c /tmp/gh.sha256; \
|
|
tar -xzf /tmp/gh.tgz -C /tmp; \
|
|
install "/tmp/gh_${GH_VERSION}_linux_amd64/bin/gh" /usr/local/bin/gh; \
|
|
rm -rf /tmp/gh.tgz /tmp/gh.sha256 "/tmp/gh_${GH_VERSION}_linux_amd64"
|
|
|
|
COPY --from=runnerd-build /workspace/packages/paperclip-runner/runner/target/release/paperclip-runnerd /usr/local/bin/paperclip-runnerd
|
|
COPY --from=provider-pack-build /provider-pack /opt/paperclip-runner/provider-pack
|
|
RUN set -eu; for cli in codex claude opencode; do \
|
|
printf '#!/bin/sh\nexec /opt/paperclip-runner/provider-pack/node_modules/.bin/%s "$@"\n' "$cli" > "/usr/local/bin/$cli"; \
|
|
chmod 755 "/usr/local/bin/$cli"; \
|
|
done
|
|
|
|
# Keep revision-dependent metadata below the stable agent CLI installation
|
|
# layers. A source-only image miss can then reuse those expensive layers from
|
|
# the trusted registry cache.
|
|
ARG PAPERCLIP_RUNNER_CONTENT_ID
|
|
ARG PAPERCLIP_RUNNER_SOURCE_REVISION
|
|
RUN test -n "${PAPERCLIP_RUNNER_CONTENT_ID}" \
|
|
&& test -n "${PAPERCLIP_RUNNER_SOURCE_REVISION}"
|
|
|
|
RUN set -eu; \
|
|
printf '%s\n' 'export PATH=/opt/paperclip-runner/provider-pack/node_modules/.bin:$PATH' \
|
|
> /etc/profile.d/01-paperclip-runner-provider-pack.sh; \
|
|
chmod 0644 /etc/profile.d/01-paperclip-runner-provider-pack.sh; \
|
|
for command_name in acpx claude-agent-acp codex-acp claude codex grok gemini kimi opencode cursor-agent agent hermes gh paperclip-runnerd; do \
|
|
command -v "$command_name" >/dev/null || { echo "FATAL: $command_name not on PATH after build" >&2; exit 1; }; \
|
|
done; \
|
|
metadata="$(paperclip-runnerd --build-metadata)"; \
|
|
/opt/paperclip-runner/provider-pack/node_modules/node/bin/node -e "const [major,minor,patch]=process.versions.node.split('.').map(Number); if (major<24 || (major===24 && (minor<11 || (minor===11 && patch<0)))) process.exit(1)"; \
|
|
test -f /opt/paperclip-runner/provider-pack/provider-pack.json; \
|
|
printf '%s' "$metadata" | grep -q '"dial_ws_loopback"'; \
|
|
printf '%s' "$metadata" | grep -q '"dial_wss"'; \
|
|
printf '%s' "$metadata" | grep -q '"listen_ws"'; \
|
|
echo "all agent CLIs and runnerd transport modes are available"
|
|
|
|
USER daytona
|
|
|
|
RUN /bin/sh -lc 'set -eu; \
|
|
test -r /opt/paperclip-runner/provider-pack/provider-pack.json; \
|
|
/opt/paperclip-runner/provider-pack/node_modules/node/bin/node -e \
|
|
"JSON.parse(require(\"node:fs\").readFileSync(\"/opt/paperclip-runner/provider-pack/provider-pack.json\", \"utf8\"))"; \
|
|
for command_name in acpx claude-agent-acp codex-acp; do \
|
|
command -v "$command_name" >/dev/null || { echo "FATAL: $command_name not on PATH for daytona user" >&2; exit 1; }; \
|
|
done; \
|
|
test "$(acpx --version)" = "0.13.1"; \
|
|
test "$(claude-agent-acp --version)" = "0.70.0"; \
|
|
test "$(codex-acp --version)" = "@agentclientprotocol/codex-acp 1.6.2"'
|
|
|
|
LABEL io.paperclip.runner.content-id="${PAPERCLIP_RUNNER_CONTENT_ID}" \
|
|
org.opencontainers.image.revision="${PAPERCLIP_RUNNER_SOURCE_REVISION}"
|