## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agent runs execute through adapters (e.g. `acpx_local`), which can run locally, over SSH, or inside sandbox execution targets, each with a wall-clock execution timeout > - Sandbox-backed runs defaulted to a 30-minute wall-clock backstop (`DEFAULT_REMOTE_SANDBOX_ADAPTER_TIMEOUT_SEC = 1800`), which kills healthy long agent runs that are still making progress — long before the recovery watchdog's 4h critical threshold would even consider them stuck > - On top of that, `acpx_local` resolved its timeout directly from `adapterConfig.timeoutSec` instead of the shared execution-target resolver, and its timeout failures surfaced as a bare `Timed out after Ns` — giving operators no clue which timer fired or which knob raises it > - This pull request raises the sandbox backstop to 4h (aligned with the recovery watchdog), routes `acpx_local` through the shared timeout resolver, logs the effective timeout and its source at run start, and makes every timeout error message self-describing > - The benefit is that long-running sandbox agent runs no longer die at 30 minutes, and when a wall-clock timeout does fire, the run log states exactly which timer fired and how to configure it ## Linked Issues or Issue Description Refs #4535 (related: wall-clock execution timeouts killing agent runs that are still making progress — that issue covers a different hardcoded 600s timer, but the operator pain is the same). No exact public issue exists for this one, so describing it in-PR: **Bug:** A long sandbox-backed `acpx_local` agent run was killed with a bare `Timed out after 1800s` even though the agent was actively working. - **What happened:** The run hit the 30-minute `DEFAULT_REMOTE_SANDBOX_ADAPTER_TIMEOUT_SEC` backstop. `acpx_local` never consulted the shared execution-target timeout resolution (it read `adapterConfig.timeoutSec` directly, default 0), so on sandbox targets the sandbox-provider default applied with no adapter-level say. The resulting error named neither the timer that fired nor the knob that controls it. - **Expected:** Healthy long runs should not be killed by a 30-minute wall-clock backstop when the recovery watchdog only treats runs as critically stuck after 4h of output silence; and any timeout error should say which timeout fired and how to raise it. - **Impact:** Long, legitimate agent runs in sandboxes fail mid-work; operators waste time reverse-engineering which of several timers produced "Timed out after Ns". ## What Changed - `packages/adapter-utils/src/execution-target.ts` - `DEFAULT_REMOTE_SANDBOX_ADAPTER_TIMEOUT_SEC` raised from `1_800` to `14_400` (4h), with a comment explaining it intentionally matches the recovery watchdog's `ACTIVE_RUN_OUTPUT_CRITICAL_THRESHOLD_MS` (4h) so the adapter backstop never fires before the watchdog path. Output-inactivity monitors remain the primary hang detectors. - New `resolveAdapterExecutionTargetTimeout(target, configuredTimeoutSec)` returns `{ timeoutSec, source }` where `source` is `configured` / `sandbox_default` / `unlimited`. The existing `resolveAdapterExecutionTargetTimeoutSec` is preserved as a thin wrapper, so current callers are unaffected. - New `formatAdapterExecutionTimeoutErrorMessage(resolution)` and `formatAdapterExecutionTimeoutStartLogLine(resolution)` produce self-describing messages that name the timer that fired and the `adapterConfig.timeoutSec` knob that controls it. - `packages/adapters/acpx-local/src/server/execute.ts` - `buildRuntime` now resolves the wall-clock timeout through the shared resolver: sandbox targets default to the 4h backstop, local/SSH keep the historical "0 = no adapter timeout", and a configured `adapterConfig.timeoutSec` always wins. - The executor logs the effective timeout and its source at run start (`[paperclip] Adapter execution timeout: …`), so a later timeout is diagnosable from the run log alone. - All three bare timeout messages (timer cancel reason, turn result `errorMessage`, catch-path `messageOverride`) now use the self-describing format. - `packages/adapters/acpx-local/src/index.ts` — the adapter configuration doc for `timeoutSec` states the sandbox default and that the output-inactivity monitor remains the primary hang detector. - Tests: `packages/adapter-utils/src/execution-target-sandbox.test.ts` and `packages/adapters/acpx-local/src/server/execute.test.ts` (see Verification). ## Verification - `pnpm --filter @paperclipai/adapter-utils typecheck` — passes - `pnpm --filter @paperclipai/adapter-acpx-local typecheck` — passes - `npx vitest run packages/adapter-utils/src/execution-target-sandbox.test.ts packages/adapters/acpx-local/src/server/execute.test.ts` — 2 files, 40 tests, all pass - New/updated test coverage: - sandbox default resolves to 4h (and the constant is asserted to be `4 * 60 * 60`) - `resolveAdapterExecutionTargetTimeout` reports `configured` / `sandbox_default` / `unlimited` sources with the correct precedence (configured > sandbox default; local/SSH stay unlimited) - exact wording of the self-describing error message and the start-of-run log line - `acpx_local` runtime picks up the sandbox default into `timeoutMs`, keeps the unlimited local default, honors configured-over-default precedence, emits the start-of-run log line, and surfaces the self-describing `errorMessage`/cancel reason when the wall-clock timer kills a turn ## Risks - **Behavioral shift:** sandbox-backed adapter runs that previously hit the 30-minute backstop now run up to 4h before the adapter kills them. Genuinely hung runs are still caught much earlier by the adapters' output-inactivity monitors and by the recovery watchdog; the wall-clock timer is a last-resort kill switch. Operators who relied on the 30-minute default can restore it explicitly via `adapterConfig.timeoutSec`. - **Error-message consumers:** any tooling that pattern-matched the exact `Timed out after Ns` string from `acpx_local` will see the new self-describing message instead. - No API or schema changes; `resolveAdapterExecutionTargetTimeoutSec` keeps its exact signature and behavior (modulo the raised sandbox default). > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - Claude (Anthropic) via Claude Code CLI — model ID `claude-fable-5`, extended thinking enabled, agentic tool use (file edits, shell, test execution) ## 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 --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
7.0 KiB
Agent Runtime Guide
Status: User-facing guide Last updated: 2026-03-26 Audience: Operators setting up and running agents in Paperclip
1. What this system does
Agents in Paperclip do not run continuously.
They run in heartbeats: short execution windows triggered by a wakeup.
Each heartbeat:
- Starts the configured agent adapter (for example, Claude CLI or Codex CLI)
- Gives it the current prompt/context
- Lets it work until it exits, times out, or is cancelled
- Stores results (status, token usage, errors, logs)
- Updates the UI live
2. When an agent wakes up
An agent can be woken up in four ways:
timer: scheduled interval (for example every 5 minutes)assignment: when work is assigned/checked out to that agenton_demand: manual wakeup (button/API)automation: system-triggered wakeup for future automations
If an agent is already running, new wakeups are merged (coalesced) instead of launching duplicate runs.
3. What to configure per agent
3.1 Adapter choice
Built-in adapters:
claude_local: runs your localclaudeCLIcodex_local: runs your localcodexCLIopencode_local: runs your localopencodeCLIcursor: runs Cursor in background modepi_local: runs an embedded Pi agent locallyhermes_local: starts your localhermesCLI through@paperclipai/hermes-paperclip-adapterhermes_gateway: calls an already-running Hermes API server through@paperclipai/hermes-paperclip-adapter/gatewayopenclaw_gateway: connects to an OpenClaw gateway endpointprocess: generic shell command adapterhttp: calls an external HTTP endpoint
External plugin adapters (install via the adapter manager or API):
droid_local: runs your local Factory Droid CLI (@henkey/droid-paperclip-adapter)
For local CLI adapters (claude_local, codex_local, opencode_local, hermes_local, droid_local), Paperclip assumes the CLI is already installed and authenticated on the host machine. For hermes_gateway, Paperclip assumes the Hermes API server is already running, reachable from the Paperclip server, and configured with an API key. The older @paperclipai/adapter-hermes-gateway npm package is only a deprecated compatibility shim; the adapter type remains hermes_gateway.
3.2 Runtime behavior
In agent runtime settings, configure heartbeat policy:
enabled: allow scheduled heartbeatsintervalSec: timer interval (0 = disabled)wakeOnAssignment: wake when assigned workwakeOnOnDemand: allow ping-style on-demand wakeupswakeOnAutomation: allow system automation wakeups
3.3 Working directory and execution limits
For local adapters, set:
cwd(working directory)timeoutSec(max runtime per heartbeat;0uses the target default — no adapter timeout on local/SSH, a 4-hour backstop on sandbox targets — and a negative value disables the adapter timeout everywhere, including sandboxes)graceSec(time before force-kill after timeout/cancel)- optional env vars and extra CLI args
- use Test environment in agent configuration to run adapter-specific diagnostics before saving
3.4 Prompt templates
You can set:
promptTemplate: used for every run (first run and resumed sessions)
Templates support variables like {{agent.id}}, {{agent.name}}, and run context values.
Note:
bootstrapPromptTemplateis deprecated and should not be used for new agents. Existing configs that use it will continue to work but should be migrated to the managed instructions bundle system.
4. Session resume behavior
Paperclip stores session IDs for resumable adapters.
- Next heartbeat reuses the saved session automatically.
- This gives continuity across heartbeats.
- You can reset a session if context gets stale or confused.
Use session reset when:
- you significantly changed prompt strategy
- the agent is stuck in a bad loop
- you want a clean restart
5. Logs, status, and run history
For each heartbeat run you get:
- run status (
queued,running,succeeded,failed,timed_out,cancelled) - error text and stderr/stdout excerpts
- token usage/cost when available from the adapter
- full logs (stored outside core run rows, optimized for large output)
In local/dev setups, full logs are stored on disk under the configured run-log path.
6. Live updates in the UI
Paperclip pushes runtime/activity updates to the browser in real time.
You should see live changes for:
- agent status
- heartbeat run status
- task/activity updates caused by agent work
- dashboard/cost/activity panels as relevant
If the connection drops, the UI reconnects automatically.
7. Common operating patterns
7.1 Simple autonomous loop
- Enable timer wakeups (for example every 300s)
- Keep assignment wakeups on
- Use a focused prompt template that tells agents to act in the same heartbeat, leave durable progress, and mark blocked work with an owner/action
- Watch run logs and adjust prompt/config over time
7.2 Event-driven loop (less constant polling)
- Disable timer or set a long interval
- Keep wake-on-assignment enabled
- Use child issues, comments, and on-demand wakeups for handoffs instead of loops that poll agents, sessions, or processes
7.3 Safety-first loop
- Short timeout
- Conservative prompt
- Monitor errors + cancel quickly when needed
- Reset sessions when drift appears
8. Troubleshooting
If runs fail repeatedly:
- Check adapter command availability (e.g.
claude/codex/opencode/hermesinstalled and logged in). - Verify
cwdexists and is accessible. - Inspect run error + stderr excerpt, then full log.
- Confirm timeout is not too low.
- Reset session and retry.
- Pause agent if it is causing repeated bad updates.
Typical failure causes:
- CLI not installed/authenticated
- bad working directory
- malformed adapter args/env
- prompt too broad or missing constraints
- process timeout
Claude-specific note:
- If
ANTHROPIC_API_KEYis set in adapter env or host environment, Claude uses API-key auth instead of subscription login. Paperclip surfaces this as a warning in environment tests, not a hard error.
9. Security and risk notes
Local CLI adapters run unsandboxed on the host machine.
That means:
- prompt instructions matter
- configured credentials/env vars are sensitive
- working directory permissions matter
Start with least privilege where possible, and avoid exposing secrets in broad reusable prompts unless intentionally required.
10. Minimal setup checklist
- Choose adapter (e.g.
claude_local,codex_local,opencode_local,hermes_local,hermes_gateway,cursor, oropenclaw_gateway). External plugins likedroid_localare also available via the adapter manager. - Set
cwdto the target workspace (for local adapters). - Optionally add a prompt template (
promptTemplate) or use the managed instructions bundle. - Configure heartbeat policy (timer and/or assignment wakeups).
- Trigger a manual wakeup.
- Confirm run succeeds and session/token usage is recorded.
- Watch live updates and iterate prompt/config.