Enforce the O(1) readiness-buffer cap on every pre-READY path. Before this change the gate checked the cap only on the no-newline branch. An over-cap blank prefix followed by a valid nonce-bound READY frame in one chunk reached READY acceptance without a cap check. The gate now rejects when the cursor passes the cap after it skips a line, and before it decodes a completed candidate line. The cap check has priority over READY acceptance on every path. A cap breach settles protocol contamination and falls back to the file bridge with bounded cleanup. Make the readiness gate tolerant of pre-READY noise. A PTY channel echoes the launch wrapper line before it sets raw mode, so the first line is often not the READY frame. The gate now skips each pre-READY line that does not decode as a READY frame, then accepts the first line that decodes as a READY frame with the matching nonce. A READY frame with a wrong nonce still fails the handshake. The skipped bytes stay in the capped buffer, so the cap and the timeout still bound the wait, and the newline scan stays linear in the bytes received. Fix the gateway launch command. A POSIX shell rejects an environment-assignment prefix on exec, so exec NAME=value command exits 127 and the gateway never starts. Use exec env NAME=value command, which keeps the exec-replaces-shell property and passes the assigned environment. Add fallback_reason to the channel-open span on the fallback path only. fallback_reason is an existing closed dimension key, so no new key reaches a sink. Co-authored-by: Paperclip <noreply@paperclip.ing>
@paperclipai/adapter-utils
Shared utilities for Paperclip adapters: process spawning, environment injection, sandbox/SSH transport, workspace sync, and the round-trip helpers that move code between the local execution-workspace cwd and wherever the agent actually runs.
For the adapter-author guide see
docs/adapters/creating-an-adapter.md
and the in-repo notes at packages/adapters/AUTHORING.md.
No-remote-git contract
The local execution-workspace cwd is the only persistence boundary across runs. No adapter may depend on a git remote for cross-run state.
Adapters that run the agent on a different host should use the SSH round-trip
helpers in src/ssh.ts:
prepareWorkspaceForSshExecution({ spec, localDir, remoteDir })— bundles the local cwd (tracked files, dirty edits, untracked additions, and the git history needed to reconstruct it) toremoteDirbefore the run starts. Runs with nogit remoteconfigured.restoreWorkspaceFromSshExecution({ spec, localDir, remoteDir, ... })— syncs the remote cwd back intolocalDirafter the run, including any new commits the agent created. Also runs with nogit remoteconfigured.
prepareRemoteManagedRuntime in
src/remote-managed-runtime.ts wraps both
calls for adapters that want a per-run remote workspace and an automatic
restoreWorkspace() finally hook.
The invariant is pinned by the no-remote-git contract case in
src/ssh-fixture.test.ts, which asserts that a
remote-only commit propagates to the local worktree through the
prepare → restore round-trip with no git remote configured at any point. Do
not regress that test.