mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-31 06:04:11 +01:00
## Thinking Path > - Paperclip manages agents and their work. > - Native Runner must bind each provider turn to the selected task, account, model, and instructions. > - Antigravity needs native ACP admission and physical process ownership across continuation. > - A saved conversation must not reuse an old process owner or silently switch its effective model. > - This pull request adds the native candidate behind exact operator admission. > - The default release gate remains closed while qualification is pending. ## Linked Issues or Issue Description Builds on merged #18187. Product account setup and the candidate producer are in dependent #18189. The repeatable browser journey is in #18190. Refs: #12503, #14938; those proposals use legacy CLI adapters. **Agent or provider** Google Antigravity ACP 1.4.3. **Why this adapter is useful** Users need to set up an Antigravity agent and run real work through their managed Google subscription. **How the agent is invoked** Native Paperclip Runner uses the pinned Antigravity ACP runtime. Exact operator admission is required while qualification remains pending. ## What Changed - Add native Antigravity ACP model and instruction admission, selected-account homes, and fenced refresh ownership. - Carry exact candidate authority from the control plane through Runner, Rust, the sidecar, and each current run. Clamp daemon lifetime to the absolute permission expiry, including replacement owners. - Preserve a rotated OAuth refresh token before later identity verification can fail, including failed or cancelled startup admission. Make private lease-close retries safe. - Bound native probe retirement and retry the exact original owner; do not delete credential homes before physical proof. - Preserve Gemini API-key recovery when shared Google subscription capability is added. - Resume the same tagged native conversation with fresh per-turn process ownership and physical native retirement. - Include native manifests in isolated Rust image stages, release cache keys, and dev rebuild detection. The workflow changes only extend existing cache input lists. - Keep current master provider behavior and append Grok 13, Pi 35, and Copilot 84 input identities. Preserve prior identities, including saved Copilot 82 and 83 runs. ## Verification - Runner TypeScript main checks, profile generators, Rust formatting, and offline workspace checks pass. The native backend integration target passes 28 tests. - The complete transport and cleanup group passes 387 tests without unhandled rejections. These checks use local scripted children and actual process and port ownership. - Saved-profile decoding, native-execution parsing, installation, and selected-credential controls pass 208 focused tests. All 109 pre-existing profile identity files remain byte-for-byte unchanged. Copilot, Grok, and shared TypeScript/Rust generation checks pass. - The final recovery fixture passes all seven cases. After the master rebase, the combined recovery and owned-Node-runtime group passes all 10 cases. Each expiry case sets one 45-second absolute grant before startup. It includes the deliberate six-second cold delay, four existing eight-second command budgets, and active-turn headroom. The test proves an active turn before expiry and then checks the daemon, owned process group, child processes, and all three native port fences. The 60-second test deadline does not renew provider authority. - The consolidated model-discovery and pending-release group passes 22 tests. Google credential compatibility and auth recovery pass 25 tests, including existing Gemini API-key recovery. The full product settings test group remains intact in #18189. - Earlier failures remain in their original CI runs and local records. They exposed the Copilot 82 decoder gap, an expired five-second fixture bootstrap ticket, and a fixture grant consumed before activation on loaded CI. Each repair has a separate result. Production ticket limits and admission behavior are unchanged by the final test commits. - An unrelated decision-expiry check failed in [the earlier CI run](https://github.com/paperclipai/paperclip/actions/runs/40941986698/job/123688668997). Its owning code is unchanged from the base. The unchanged suite passes 20 tests on isolated PostgreSQL. A controlled database rollback reproduces the earlier wake: clean startup schedules the 120-second decision expiry, while rollback reconciliation leaves a 30-second continuation-coalescing wake. The decision stays open, with no wake delivery or error. The fresh final-head server shard passes; the failed result is preserved. - Final native commit `ec6bc7e4d77f5ae0cc4946dbba6c662e95a87f5b` passed `ci / verify` and `ci / e2e`. Fresh Greptile and Paperclip reviews both score 5/5, with no open findings. Antigravity retains the recorded native `.5` closure and exact low-model profile. This PR does not claim global or cross-platform qualification. ## Risks - The new transport carries private managed credentials. Exact ownership, account generation, model, instructions, and native retirement are fail-closed. - Only local macOS ARM64 has a verified complete Antigravity runtime. Default Antigravity task execution remains disabled without current trusted operator admission. The Google server sign-in lane and production candidate producer ship in #18189; this native slice does not expose the subscription catalog choice. - Shared launch inputs change provider identities. New identities are append-only; existing recorded identities remain available. ## Model Used OpenAI Codex (GPT-6). The host did not expose the exact model revision or context limit. The model used tools for code edits, tests, and review. ## 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>
161 lines
9.5 KiB
Docker
161 lines
9.5 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
|
|
COPY packages/paperclip-runner/acpx-profiles.json packages/paperclip-runner/antigravity-distributions.json ../
|
|
RUN cargo build --locked --release -j 2 -p paperclip-runner-core --bin paperclip-runnerd \
|
|
&& strip /workspace/packages/paperclip-runner/runner/target/release/paperclip-runnerd
|
|
|
|
FROM node:24.21.0-bookworm@sha256:5a750d3be5e5c80275f8c9a5367c3aed99c2875656590c8d0701c7ee687f5f0a AS provider-pack-build
|
|
RUN corepack enable && corepack prepare pnpm@9.15.4 --activate
|
|
# Bound build-time memory on hosts that expose many CPUs but little RAM.
|
|
# These settings belong only to this build stage, never the runtime image.
|
|
ENV NODE_OPTIONS=--max-old-space-size=1024 \
|
|
npm_config_child_concurrency=1 \
|
|
npm_config_network_concurrency=4
|
|
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
|
|
# Include the complete workspace manifest graph so source-owned patches apply.
|
|
# Verify the reviewed lockfile as supplied; never resolve dependencies again
|
|
# against the registry during an immutable image build.
|
|
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=342a01e487a68d0ad9cb8d8852a0ffed4dd7b0fa58e2e047d1064e9340fe6f57
|
|
# pnpm 9 subtracts PNPM_WORKERS from available CPUs; it is not a worker count.
|
|
# Subtract all available CPUs to select its minimum (one tarball worker).
|
|
RUN export PNPM_WORKERS="$(node -p 'require("node:os").availableParallelism()')" \
|
|
&& 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
|
|
# Optional qualification assets; this never promotes candidate profiles.
|
|
ARG PAPERCLIP_RUNNER_CANDIDATE_PROVIDERS=
|
|
RUN test -n "${PAPERCLIP_RUNNER_SOURCE_REVISION}"
|
|
RUN export PNPM_WORKERS="$(node -p 'require("node:os").availableParallelism()')" \
|
|
&& 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 \
|
|
"--candidate-providers=${PAPERCLIP_RUNNER_CANDIDATE_PROVIDERS}" \
|
|
&& 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.70 \
|
|
@google/gemini-cli@0.65.0 \
|
|
@moonshot-ai/kimi-code@2.3.3 \
|
|
&& 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.10.22-27dcf5f
|
|
ARG CURSOR_SHA256_AMD64=85296944e4600a83c4b764f578d1502bf9c416db200d33a593e8f3ecabfdc294
|
|
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.103.3
|
|
ARG GH_SHA256_AMD64=4c806978ffce1cec70853302a56d70ac6da4f19bae7adca4e1a8ab00633dd91d
|
|
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 packages/paperclip-runner/scripts/provision-grok.mjs /tmp/grok-provision/scripts/provision-grok.mjs
|
|
COPY packages/paperclip-runner/src/providers/grok/platforms.json /tmp/grok-provision/src/providers/grok/platforms.json
|
|
RUN node /tmp/grok-provision/scripts/provision-grok.mjs /opt/paperclip/providers/grok/1.0.13/grok && rm -rf /tmp/grok-provision
|
|
|
|
COPY --from=runnerd-build /workspace/packages/paperclip-runner/runner/target/release/paperclip-runnerd /usr/local/bin/paperclip-runnerd
|
|
COPY --chown=0:0 --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 "$(codex --version)" = "codex-cli 0.160.0"; \
|
|
test "$(claude --version)" = "2.1.286 (Claude Code)"; \
|
|
test "$(opencode --version)" = "1.18.34"; \
|
|
test "$(acpx --version)" = "0.13.1"; \
|
|
test "$(claude-agent-acp --version)" = "0.73.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}"
|