Select the duplex transport only when the experimental setting enableSandboxDuplexBridge is exactly true and the resolved capability duplexCommandStream is exactly true. Any other value of either gate selects the file bridge. The host assigns the loopback port and one random per-open nonce, and passes both to the gateway only through the launch environment. The host builds the sandbox-facing origin http://127.0.0.1:<assigned port> from its own stored port. No field of any channel frame contributes to the endpoint. Change the READY frame from an address string to a strict version-plus-nonce schema in the host codec, the embedded gateway codec, and the shared fixture; the gateway echoes the nonce and sends READY only after it binds the assigned port. The gateway binds exactly that port or writes a diagnostic and exits nonzero. Readiness fails closed. On a mismatched nonce, an address-bearing or incomplete READY frame, protocol contamination before READY, a bind failure, or a timeout, the host closes the partial channel inside a bounded budget and selects the file bridge, and leaves no live provider session. The broker starts only after readiness passes, so no request that carries the bridge token reaches a rejected endpoint. Teardown closes the channel before lease release. 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.