Files
PaperClipAI/packages/adapter-utils
Priya RamanandPaperclip 8036019807 fix(sandbox): bound the duplex request-id bytes in the codec and the broker
The duplex frame codec accepted a request or a response `id` of any length up
to the whole-frame cap. The host broker keeps one id per distinct dispatched
request for the no-replay guarantee. A provider that controls the transport
could send distinct near-frame-limit ids and force the host to retain many
gigabytes before the lifetime count ceiling, so the host adapter process could
exhaust memory.

Add a finite protocol bound for the id field:
- Export `DEFAULT_MAX_DUPLEX_REQUEST_ID_BYTES` (256 bytes). The generated
  gateway builds each id with `randomUUID()` (36 bytes), so this bound gives
  large headroom and caps the retained bytes per id.
- Reject an over-limit id as an `id_too_large` protocol error in both codec
  copies (the host codec and the embedded gateway codec). The read path returns
  the error; it never throws. The whole-frame cap stays unchanged.
- Enforce the id bound in the broker before the seen set, the pending record,
  and the forward, as defense in depth. The refusal allocates no controller,
  no timer, and no forward, and records no telemetry.
- State the worst-case retained id bytes at the lifetime-limit definition
  (50,000 * 256 = 12,800,000 bytes).

Add shared fixture vectors for a maximal-allowed id and an over-limit id, so
both codec copies prove the same bound. Add regression tests for the flood,
the no-replay path, and the byte-not-character measure.

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-20 05:40:14 +00:00
..

@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) to remoteDir before the run starts. Runs with no git remote configured.
  • restoreWorkspaceFromSshExecution({ spec, localDir, remoteDir, ... }) — syncs the remote cwd back into localDir after the run, including any new commits the agent created. Also runs with no git remote configured.

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.