Files
PaperClipAI/packages/adapter-utils
DottaandClaude Opus 5.5 46f32ed33f fix(sentry): stop treating short host settings as credentials in run-failure redaction (#20013)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - When an agent run fails, Paperclip sends a redacted failure report
to Sentry so the team can fix the cause
> - Redaction treats the server's own environment values as possible
secrets and removes them wherever they appear in the report
> - Since #19461, every value under an environment name the allowlist
does not know counts as a secret, even very short ones such as `vi`,
`RUN`, `off` or `1`
> - On a host with such settings, the report text is damaged: `service`
becomes `ser***REDACTED***ce`, and a `1` anywhere can vanish. One server
test failed on every such host, while CI (which has no such values)
stayed green
> - This pull request keeps the fail-closed rule for an agent's
configured environment, and requires a plausible credential in the
server's own environment
> - The benefit is that Sentry reports stay readable on every host, and
real credentials stay private

## Linked Issues or Issue Description

Refs #19461 (introduced the catch-all), #14585 (added the failing test).

**What happened?**
`heartbeat-process-recovery.test.ts` › "redacts opaque environment-bound
credentials from Sentry diagnostics: result" failed locally on `master`:
`expected 'ser***REDACTED***ce' to be 'service'`. Instrumentation showed
that the server's own `process.env` contributed these redaction values:
`["false","67591","true","0x0","RUN","off","vi","64","1","0","8"]`. They
came from a desktop agent session's environment.

**Expected behavior**
Short host settings are not credentials. Declared, secret-named and
credential-shaped values stay private. An agent's configured environment
keeps its fail-closed rule.

**Steps to reproduce**
Run that test on a machine whose environment contains
`SOME_UNKNOWN_NAME=vi`.

## What Changed

- `packages/adapter-utils/src/command-redaction.ts`:
`collectDiagnosticEnvironmentSecrets` gets an option, `{ hostProcess:
true }`. With it, an undeclared value under an unrecognized name is
collected only in these cases:
- it has at least 8 characters and is not a bare number, hex number or
boolean-like word; or
- its name has a credential segment: `TOKEN`, `SECRET`, `PASSWORD`,
`BEARER` or `PRIVATE`, or a final `KEY` or `KEY_ID`.

  Without the option, behavior is unchanged.
- `server/src/services/run-failure-report.ts`: the report collects the
server's own `process.env` with `hostProcess: true`. The agent's
configured environment (`heartbeat.ts`) and the provider environment
(`terminal-session-failure.ts`) keep the default.

## Verification

- `command-redaction.test.ts` (42 pass). The new test fails on `master`
and passes here. It checks three things: the host's short values are not
collected; a short value under `AWS_ACCESS_KEY_ID` and a long opaque
value stay private; and an agent binding `CUSTOM_BINDING: "1"` stays
private.
- `run-failure-diagnostics.test.ts` (135 pass, unchanged). These tests
pin the agent-environment fail-closed rule.
- `heartbeat-process-recovery.test.ts` › "redacts opaque
environment-bound credentials…": both modes pass here. The `result` mode
failed on unchanged `master` on this host.
- The adapter-utils and server typechecks pass.

## Risks

- The server's own environment now leaves out a non-credential-named
value that is shorter than 8 characters or is a bare number or boolean,
for example `MY_PIN=1234`. Values under credential names, declared
secrets and credential-shaped values are still redacted.
- No Telemetry path changes; this is the Sentry run-failure report.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, with tool use and code
execution in Claude Code.

## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 05:50:30 -06: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.

Sandbox bridge deadlines

Command-managed bridge control operations (queue reads, input delivery, setup, and cleanup) have a host-enforced deadline of at most 30 seconds per shell command. A shorter configured timeout still applies. These operations cannot inherit the agent run's hours-long lifetime or wait forever on a provider that ignores its timeout. Long-lived agent session commands keep their own limits.

A control timeout reports a transport failure. It does not prove that the remote command stopped and does not replay an uncertain write. The process session closes failed input delivery and uses its existing shutdown path; normal execution settlement must still verify termination.

Native archive transfer deadlines

Native syncIn and syncOut calls accept an optional minimumTimeoutMs budget. The server validates this budget and extends the worker RPC deadline. Providers can honor this field for file transfers without changing their configuration. Daytona uses the larger of this minimum and its configured timeout. Other providers keep their existing behavior. The minimum is limited to 30 minutes and does not change sandbox lifetimes, stored settings or the timeout for other commands.

The remote-workspace archive path uses the verified capture size for downloads and the local archive size for uploads. Large archives get one second per MiB plus 60 seconds, capped at 30 minutes. Small archives keep the existing provider timeout when that calculation is no greater than five minutes. Archive size, checksum, confinement and host space checks still apply.

An exact retained-sandbox resume keeps its recorded provider timeout and RPC overhead. It uses the normal one-minute fallback only when no timeout was recorded. A five-minute cold restore must not be cut off by that fallback. Resume still targets the recorded allocation and does not acquire a replacement.

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.