Files
PaperClipAI/docs/adapters/overview.md
Devin FoleyandPaperclip 35a4448c02 fix(cursor): select failure diagnostics after trace notices (#14636)
## Thinking Path

> - Paperclip manages work performed by AI agents.
> - The Cursor CLI adapter turns process output into run results.
> - Cursor can print a trace-file notice before a real error.
> - The adapter used the first stderr line as the failure summary.
> - This could hide the error behind an informational file path.
> - This change selects the first diagnostic after that known notice and
preserves the full logs.

## Linked Issues or Issue Description

**What happened?**

When Cursor exits with a nonzero code, a leading `cursor-retrieval:
tracing to ...` notice can become the error summary. A later error
remains in stderr but is absent from the summary. If the notice is the
only output, the summary does not explain that the process exited
unsuccessfully.

**Expected behavior**

Prefer the structured error, then a stderr diagnostic, then the exit
code. Keep the run failed and preserve the original logs.

**Steps to reproduce**

1. Use a fixture Cursor executable that writes the trace-file notice to
stderr.
2. Write `Authentication failed` on the next line, then exit with code
7.
3. The old adapter reports the trace-file notice. This change reports
the authentication error.
4. Repeat with only the notice. This change reports `Cursor exited with
code 7`.

**Paperclip version or commit**

Reproduced against master commit
`17780751551b3bc1c2521f7694026c34534c46c9` with local process fixtures.

**Deployment mode**

Local CLI adapter. The diagnostic helper is also used by environment
probes.

Searched open Cursor PRs and issues. PRs #14631 and #14435 concern
native ACP support; #11106 concerns MCP configuration. None changes this
legacy CLI diagnostic selection.

## What Changed

- Skip only the exact trace-location notice when choosing a diagnostic
line.
- Remove terminal control codes from summary candidates.
- Use the same selection for execution and environment probes.
- Preserve structured-error priority, exit status, retry decisions, and
raw stdout/stderr.
- Add child-process regression tests and narrow parsing cases. Document
the behavior.

## Verification

- Two execution regression cases failed on the previous implementation;
structured-error priority already passed.
- `pnpm exec vitest run packages/adapters/cursor-local`: all 16 tests
passed across five files.
- `pnpm --filter @paperclipai/adapter-cursor-local typecheck` passed.
- `pnpm -r typecheck` passed.
- All GitHub CI checks passed on the PR head. One preview-runtime
readiness test failed on the first attempt; its full local suite passed
(28 tests, three skips) and the failed CI shard passed on retry. No
unrelated source change was needed.
- The broad local `pnpm test:run` command did not complete in the
available verification window and was stopped; no full local-suite pass
is claimed. The full sharded GitHub CI suite passed. `pnpm build`
passed.
- Tests use local fixture processes. They make no Cursor provider
requests.

## Risks

A future Cursor notice format may no longer match and will remain
visible. Retrieval error lines and unknown diagnostics remain visible.
This improves diagnosis; it does not claim to fix an unknown provider or
machine failure. There are no schema, authentication, cancellation, or
retry-policy changes.

## Model Used

OpenAI Codex (GPT-6), with reasoning, repository inspection, and command
execution. The session does not expose a more specific model revision or
context-window size.

## 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 described the issue in-PR following the relevant issue
template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run the targeted tests locally and they pass; full checks
are in progress
- [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>
2026-09-29 16:36:36 -07:00

149 lines
9.4 KiB
Markdown

---
title: Adapters Overview
summary: What adapters are and how they connect agents to Paperclip
---
Adapters are the bridge between Paperclip's orchestration layer and agent runtimes. Each adapter knows how to invoke a specific type of AI agent and capture its results.
## How Adapters Work
When a heartbeat fires, Paperclip:
1. Looks up the agent's `adapterType` and `adapterConfig`
2. Calls the adapter's `execute()` function with the execution context
3. The adapter spawns or calls the agent runtime
4. The adapter captures stdout, parses usage/cost data, and returns a structured result
## Built-in Adapters
| Adapter | Type Key | Description |
|---------|----------|-------------|
| [Claude Code](/adapters/claude-local) | `claude_local` | Runs Claude Code CLI locally, with a native ACP engine when available |
| [Codex](/adapters/codex-local) | `codex_local` | Runs OpenAI Codex CLI locally, with a native ACP engine when available |
| [Gemini CLI](/adapters/gemini-local) | `gemini_local` | Runs Gemini CLI locally (experimental — adapter package exists, not yet in stable type enum) |
| [Kimi Code CLI](/adapters/kimi-local) | `kimi_local` | Runs Kimi Code CLI locally through ACP, with explicitly selectable headless `-p` mode |
| OpenCode | `opencode_local` | Runs OpenCode CLI locally (multi-provider `provider/model`) |
| Cursor | `cursor` | Runs Cursor in background mode |
| Pi | `pi_local` | Runs an embedded Pi agent locally |
| Hermes | `hermes_local` | Runs the local Hermes CLI through `@paperclipai/hermes-paperclip-adapter` |
| Hermes Gateway | `hermes_gateway` | Calls an already-running Hermes API server through `@paperclipai/hermes-paperclip-adapter/gateway` |
| OpenClaw Gateway | `openclaw_gateway` | Connects to an OpenClaw gateway endpoint |
| [Process](/adapters/process) | `process` | Executes arbitrary shell commands |
| [HTTP](/adapters/http) | `http` | Sends webhooks to external agents |
## Credential ownership for sandbox targets
Local CLI adapters can run on the Paperclip host, SSH targets, or managed
sandbox targets. The adapter decides which credential home is authoritative
before the CLI starts:
| Adapter | Credential topology | Which credential file wins on managed sandbox targets |
|---------|---------------------|-------------------------------------------------------|
| [`codex_local`](/adapters/codex-local) | Host-owns-auth for Paperclip-managed `CODEX_HOME` | A host-owned `auth.json` is symlinked into the managed `CODEX_HOME` and uploaded to the sandbox. If a per-agent `OPENAI_API_KEY` is configured, Paperclip writes an API-key `auth.json` instead and that file wins. A login baked into the sandbox image is shadowed because Codex runs with Paperclip's uploaded `CODEX_HOME`. |
| [`claude_local`](/adapters/claude-local) | Snapshot-owns-auth for managed remote Claude config | A configured `ANTHROPIC_API_KEY` or `CLAUDE_CODE_OAUTH_TOKEN` (agent or environment env) wins over any stored login. Otherwise Paperclip uploads only sanitized settings and skill/runtime assets, and when the remote managed config has no Claude credential files it copies `.credentials.json` or `credentials.json` from the sandbox image's own `$HOME/.claude`, so the image's login wins. |
Worked examples:
- **Codex sandbox with host ChatGPT login:** the host `~/.codex/auth.json`
is symlinked into the managed home, then uploaded as the sandbox
`CODEX_HOME`. Codex reads that uploaded file and does not use any
`auth.json` already present inside the sandbox image.
- **Claude sandbox with image login:** Paperclip materializes a remote
`CLAUDE_CONFIG_DIR`, then fills missing `.credentials.json` /
`credentials.json` from the sandbox image's own `$HOME/.claude`. The
snapshot's Claude login is the credential source for the run.
### Hermes local vs gateway
Use `hermes_local` when Paperclip should start the local `hermes` CLI on the
same host for each heartbeat. Use `hermes_gateway` when Hermes is already
running as an HTTP/SSE API server and Paperclip should call that server instead
of spawning a process. Both type keys are stable built-ins.
The unified Hermes package owns both built-in adapters. The older
`@paperclipai/adapter-hermes-gateway` package remains only as a deprecated
compatibility shim that re-exports the gateway entrypoints for one release.
New plugin overrides should target `@paperclipai/hermes-paperclip-adapter` and
set the desired type key (`hermes_local` or `hermes_gateway`).
### External (plugin) adapters
These adapters ship as standalone npm packages and are installed via the plugin system:
| Adapter | Package | Type Key | Description |
|---------|---------|----------|-------------|
| Droid | `@henkey/droid-paperclip-adapter` | `droid_local` | Runs Factory Droid locally |
## External Adapters
You can build and distribute adapters as standalone packages — no changes to Paperclip's source code required. External adapters are loaded at startup via the plugin system.
```sh
# Install from npm via API
curl -X POST http://localhost:3102/api/adapters \
-d '{"packageName": "my-paperclip-adapter"}'
# Or link from a local directory
curl -X POST http://localhost:3102/api/adapters \
-d '{"localPath": "/home/user/my-adapter"}'
```
See [External Adapters](/adapters/external-adapters) for the full guide.
## Adapter Architecture
Each adapter is a package with modules consumed by three registries:
```
my-adapter/
src/
index.ts # Shared metadata (type, label, models)
server/
execute.ts # Core execution logic
parse.ts # Output parsing
test.ts # Environment diagnostics
ui-parser.ts # Self-contained UI transcript parser (for external adapters)
cli/
format-event.ts # Terminal output for `paperclipai run --watch`
```
| Registry | What it does | Source |
|----------|-------------|--------|
| **Server** | Executes agents, captures results | `createServerAdapter()` from package root |
| **UI** | Renders run transcripts, provides config forms | `ui-parser.js` (dynamic) or static import (built-in) |
| **CLI** | Formats terminal output for live watching | Static import |
## Choosing an Adapter
- **Need a coding agent?** Use `claude_local`, `codex_local`, `opencode_local`, `hermes_local`, or install `droid_local` as an external plugin
- **Need the richest live run feedback?** Use `claude_local`, `codex_local`, or `gemini_local` with `adapterConfig.engine` set to `acp` when the execution environment satisfies the ACP prerequisites — see [Feedback granularity](#feedback-granularity)
- **Need Hermes on another host or already running as a service?** Use `hermes_gateway`
- **Need to run a script or command?** Use `process`
- **Need to call a custom external service?** Use `http`
- **Need something custom?** [Create your own adapter](/adapters/creating-an-adapter) or [build an external adapter plugin](/adapters/external-adapters)
## Feedback Granularity
Adapter choice determines how much structured, live detail a run's transcript can show while the agent is still working. Every adapter's stdout is streamed to the run log and rendered live in the UI — including runs on sandbox execution targets, whose logs are tailed and delivered incrementally — but the *granularity* of what you see depends on the event stream the adapter emits.
Rough tiers, richest first:
1. **Native ACP engine (`claude_local`, `codex_local`, or `gemini_local` with `engine: "acp"`) — full structured event stream.** ACP emits a JSONL event per meaningful runtime moment: `acpx.session` (agent, mode, session identity), `acpx.status` (progress text plus context-window usage), `acpx.text_delta` (assistant/thinking token deltas), `acpx.tool_call` (tool title, call id, and status updates as the call progresses), `acpx.result` (stop reason summary), and `acpx.error` (code, message, retryability). The transcript renders these as live-updating message, thinking, tool, and status blocks, and repeated `acpx.tool_call` status updates fold into a single tool card instead of stacking duplicates.
2. **CLI wrappers (`claude_local`, `codex_local`, `cursor`, `opencode_local`, …).** These parse each CLI's own streaming JSON output. You get assistant text, tool calls/results, and a final usage/cost summary, but granularity is limited to what the CLI prints — some emit tool progress, others only call/finish pairs.
3. **Generic adapters (`process`, `http`).** Plain stdout/stderr lines with no structured transcript — you see raw output only.
**Recommendation:** use the native ACP engine on `claude_local`, `codex_local`, or `gemini_local` when the selected execution environment supports it. Rich ACP status events (including context usage) and incremental tool-call updates give the closest thing to watching the agent work locally.
## UI Parser Contract
External adapters can ship a self-contained UI parser that tells the Paperclip web UI how to render their stdout. Without it, the UI uses a generic shell parser. See the [UI Parser Contract](/adapters/adapter-ui-parser) for details.
### Cursor failure details
The Cursor CLI adapter uses structured error output first, then the first
nonempty diagnostic line. It skips the informational `cursor-retrieval: tracing
to ...` file-location notice. If the CLI exits unsuccessfully with only that
notice, the run shows the exit code. The original stdout and stderr remain in
the local run result and log for troubleshooting. Environment probes use the
same diagnostic selection. This does not change retry or success decisions.