Commit Graph
23 Commits
Author SHA1 Message Date
Nicky LeachandPaperclip 6a69a3d6b0 refactor(observability): stop writing detailed per-step timing to the run log (#10776)
## Thinking Path

> - Paperclip tracks agent work for the team.
> - The startup timing path already emits detailed spans.
> - The run log repeats the same detailed timing.
> - That makes the event larger than it needs to be.
> - This pull request removes the redundant per-step timing fields from
the run-log payload.
> - The benefit is a smaller log with the same detail still available in
traces.

## Linked Issues or Issue Description

No public GitHub issue or open pull request matches this change. I
described the enhancement below.

**What existing behavior does this improve?**
`run.startup.step` event output.

**Subsystem affected**
Cross-cutting (multiple of the above)

**Current behavior**
`run.startup.step` writes `roundTrips`, `providerExecMs`,
`providerGetMs`, `createRuntimeMs`, and `ensureSessionMs`. The same
detail already exists in the spans.

**Proposed behavior**
`run.startup.step` keeps only `step`, `durationMs`, and `outcome`. The
heartbeat lifecycle timestamps stay unchanged.

**Reason and benefit**
This change removes redundant data from the run log. It keeps the useful
detail in trace spans. It also makes the payload smaller and easier to
read. The revert path stays clear because the removed data has one
producer chain.

**Breaking changes**
Yes. Consumers that read the removed fields must switch to span data or
the remaining event fields. The heartbeat lifecycle timestamps do not
change.

**Additional context**
I searched GitHub for related open PRs and issues. I found no match.

## What Changed

- Removed the redundant per-step timing fields from the run-log payload.
- Removed the now-dead producer chain that fed those fields.
- Kept the span-level timing data and the heartbeat lifecycle
timestamps.

## Verification

- `adapter-utils` typecheck clean
- `server` typecheck clean
- `startup-timing` suite: 30 passed
- `adapter-utils` `execute` suite: 99 passed
- `server` `environment-execution-target` suite: 21 passed

## Risks

- Consumers that still read the removed fields will need a code change.
- This is a one-way-door data removal for the run log.

## Model Used

OpenAI Codex, GPT-5, tool use enabled.

## 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 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
- [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-08-03 15:58:21 -07:00
Nicky LeachandPaperclip c09ea7112b feat(observability): add granular OpenTelemetry spans for sandbox startup and execution (#10758)
## Thinking Path

> - Paperclip coordinates AI agent work and records execution data.
> - The sandbox startup path and the host-to-sandbox execution path need
clearer OpenTelemetry spans.
> - The trace data now shows wait time, critical path data, and bounded
labels.
> - This pull request adds bounded spans and closed attribute helpers
for sandbox startup and execution.
> - The result is better trace data with low cardinality and no secret
leakage.

## Linked Issues or Issue Description

- No public GitHub issue exists for this branch.
- Related PR: #10536
- This PR extends the sandbox OpenTelemetry work with exec spans, root
timing, and bounded labels.

## What Changed

- Added a closed span-attribute contract for sandbox startup data.
- Added a bounded label helper for command and region values.
- Threaded the active step context into the execution path.
- Added a `sandbox.exec` span with true timestamps and bounded
attributes.
- Added skipped-step and root-span timing data with low-cardinality
context.
- Moved handshake sub-times and bridge batch data onto spans.

## Verification

- `pnpm --filter @paperclipai/adapter-utils run typecheck`
- `pnpm --filter @paperclipai/server run typecheck`
- `pnpm --filter @paperclipai/adapter-utils exec vitest run`
- `pnpm --filter @paperclipai/server exec vitest run
environment-execution-target`
- `git log --oneline
origin/master..origin/feat/sandbox-startup-otel-spans`
- `git diff origin/master...origin/feat/sandbox-startup-otel-spans
--stat`

## Risks

- Span attribute rules could still miss a future field if new code skips
the shared helper.
- Low-cardinality labels may hide some detail, but that is the intended
tradeoff.
- Tracing stays fail-open, so a missing tracer still hides data rather
than stopping work.

## Model Used

- OpenAI Codex, GPT-5, tool use enabled. The runtime did not expose the
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 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 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
- [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

The current head still has one failing required check: `e2e`. The PR
stays unready for board handoff until that check passes.

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-03 10:13:19 -07:00
scotttongandClaude Fable 5 c185e64b77 feat(ui): chat-style task view behind an experimental flag (#10606)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators spend most of their time on the issue detail page. They
talk to the assigned agent there through comments.
> - The current page reads as a ticket form. The thread sits below
properties, the composer sits mid-page, and live agent activity renders
as dense transcript logs.
> - Talking to an agent is a conversation. A chat-first layout matches
that mental model better than a ticket form.
> - A layout change this large must not disrupt current users. It needs
a safe opt-in path and full parity with the existing thread features.
> - This pull request adds a chat-style task view behind a new
"Chat-Style Tasks" experiment toggle. The flag is off by default and the
existing page is unchanged when it is off.
> - The benefit is a focused, readable conversation with the agent: live
tool activity folds into compact summaries, the composer stays at the
bottom, and properties, plan, and artifacts move into header tabs.

## Linked Issues or Issue Description

Refs #49 (chat with agents is a much-wanted feature).

Related PRs found in the dedup search:
- #4489 — an earlier, closed attempt to promote the conversation to the
primary surface on issue detail. This PR is a fresh, flag-gated take on
the same goal.
- #8837 — an open PR that proposes a two-column task layout. It
restructures the same page but keeps the ticket paradigm; this PR is
orthogonal because it is opt-in and chat-first.

**Subsystem affected**

UI (issue detail page).

**Problem or motivation**

The issue detail page presents agent conversations as a ticket:
properties first, thread below, composer in the middle of the page, and
raw transcript noise during live runs. Users who mainly converse with
their agents must scroll past chrome to follow the conversation, and
live activity is hard to read.

**Proposed solution**

An opt-in chat-style view of the issue detail page, gated by a new
"Chat-Style Tasks" experiment toggle in Settings → Experimental. With
the flag on, the thread fills the center pane, the composer docks to the
bottom of the viewport, Properties / Plan / Artifacts become header
tabs, live turns show a status pill with the current tool action and
elapsed time, and settled turns collapse to a "Worked · N tools" summary
that expands into per-tool rows. With the flag off, nothing changes.

**Alternatives considered**

Restyling the existing layout in place (rejected: too disruptive without
an opt-out), and a separate chat page beside the issue page (rejected:
splits the task's single source of truth). A per-request lab page
(`/task-chat-lab`, dev-only) was kept for design iteration instead.

**Roadmap alignment**

ROADMAP.md "CEO Chat" wants lighter conversations that still resolve to
real work objects. This PR keeps the core task-and-comments model — it
only changes presentation, opt-in — so it does not duplicate that
planned work.

## What Changed

- New `enableTaskChatRedesign` instance setting, exposed as a
"Chat-Style Tasks" experiment card in Settings → Experimental (shared
feature catalog, validators, server instance-settings service, and UI
settings page).
- New `ui/src/components/task-chat/` component family: chat thread with
turn grouping, agent reply bubbles, live status pill, collapsible turn
summaries with per-tool rows, plan tab with a sticky CTA action bar,
inline interaction cards, per-request mode chips, and a bottom-docked
composer.
- A shared tool taxonomy (`tool-taxonomy.ts`) maps tool names to verbs
and icons; the status pill, tool rows, and the classic transcript view
all use it.
- A transcript adapter converts stored run logs into chat turns; it
dedupes tool-call updates by `toolUseId` so tool counts match the
expanded rows, and it keeps a tool row's first real name when later
generic updates arrive.
- Composer: posts on Cmd/Ctrl+Enter, supports image paste with
object-URL thumbnail previews (revoked on clear/unmount), and uploads
through the issue attachments route.
- `IssueDetail.tsx`: with the flag on, pane tabs move to the header bar,
the header is not sticky, and the chat fills the center; with the flag
off, the previous layout renders unchanged.
- Motion tokens for the new animations live in `ui/src/index.css` with a
`motion-tokens.ts` catalog and a test that keeps the two in sync (the
catalog now also covers the shared enter/exit/swap tokens that the
decision/quicklook block declares).
- A dev-only `/task-chat-lab` page with fixtures and a tweak panel for
motion tuning.

## Verification

- `pnpm typecheck` — clean across the workspace.
- `pnpm check:token-gates` — 3/3 CLEAN.
- `cd ui && pnpm vitest run` — 3,344 of 3,345 tests pass locally. The
one failure is `IssueProperties.test.tsx` monitor-row time formatting,
which is timezone-sensitive: it also fails on unmodified `origin/master`
in a non-UTC timezone and passes with `TZ=UTC`. It is not related to
this change.
- `cd server && pnpm vitest run
src/__tests__/instance-settings-service.test.ts` — 21/21 pass (covers
the new setting).
- Manual: start the dev server, open Settings → Experimental, enable
"Chat-Style Tasks", and open any issue. The thread fills the page, the
composer docks to the bottom, and Properties / Plan / Artifacts appear
as header tabs. Assign an agent and comment to watch a live run: the
status pill shows the current tool action with elapsed time, and the
finished turn folds into a "Worked · N tools" summary. Disable the
toggle and confirm the classic page is unchanged.
- Visual snapshot baselines are intentionally not updated: per
`doc/design/DECISION-SHEET.md`, "Per-change snapshot verification
demoted to dormant (Jul 13 2026)".

## Risks

- The flag-off path goes through the same `IssueDetail.tsx` file, so a
regression there would affect current users. Mitigation: the classic
markup renders through the same components as before behind explicit
flag conditionals, and the full UI suite passes.
- The transcript adapter interprets stored run-log formats, including
legacy entries without `toolUseId`. Malformed logs degrade to generic
tool rows rather than crashing.
- The new view changes no server behavior other than one additive
instance setting; it is additive and default-off. Overall risk with the
flag off is low.

## Model Used

- Claude (Anthropic), model id `claude-fable-5` (Claude Fable 5),
extended thinking enabled, agentic tool use (file editing, shell, test
execution) via Claude Code / Claude Agent SDK.

## 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 Fable 5 <noreply@anthropic.com>
2026-08-01 02:26:47 -07:00
DottaandPaperclip 1ee1275f11 fix(adapters): persist ACPX process identity for hot restart (#9838)
## Thinking Path

> - Paperclip is the open source control plane people use to manage AI
agents for work.
> - Local agent heartbeats need durable process identity so the server
can supervise them.
> - The ACPX runtime owns the child process used by `codex_local`
sessions.
> - ACPX did not expose the child PID and start time to the Paperclip
adapter.
> - Warm ACPX runtimes can also serve a later heartbeat without a new
spawn event.
> - A hot restart could therefore classify a live Codex run as lost
because its heartbeat row had no process identity.
> - This pull request forwards ACPX spawn identity, reuses it for
compatible warm heartbeats, and fails closed when identity cannot be
persisted.
> - The benefit is reliable hot-restart adoption for eligible local
Codex runs.

## Linked Issues or Issue Description

No matching public GitHub issue was found.

**What happened?**

A `codex_local` heartbeat could run through ACPX without a persisted
`processPid` or `processStartedAt`. A Paperclip hot restart then had no
durable identity for the live ACP child. Recovery could classify the run
as `process_lost` even while the child was still alive.

**Expected behavior**

ACPX reports the real child PID and start time before the first prompt.
A compatible warm runtime reports the same known identity to each later
heartbeat that reuses the child. ACPX stops the child if the identity is
invalid or persistence fails. Hot-restart recovery can then adopt the
live run.

**Steps to reproduce**

1. Start a `codex_local` heartbeat through the ACPX execution lane.
2. Keep the run active during a Paperclip hot restart.
3. Inspect the heartbeat row before this change.
4. Observe that the process identity can be null and recovery cannot
adopt the live child.

**Reproduced on**

- Paperclip `master` before this change.
- Linux source deployment.
- `codex_local` with ACPX `0.12.0`.

## What Changed

- Add an awaited `onAgentSpawn` lifecycle hook to the patched ACPX
runtime.
- Forward the ACP child PID and start time through the adapter `onSpawn`
callback.
- Keep a mutable callback sink for cached runtimes so a later respawn
updates the current heartbeat.
- Reuse the last known process identity when a compatible warm heartbeat
reuses the existing child.
- Kill the ACP child and fail session startup when the PID is invalid or
identity persistence rejects.
- Add ACPX and heartbeat recovery tests for callback ordering, warm
reuse, failure cleanup, durable row identity, and hot-restart adoption.
- Document the one-time drain required when an installed pre-fix run
already lacks process metadata.

## Verification

-
`PAPERCLIP_HOME="$PAPERCLIP_RUN_SCRATCH_DIR/test-home-execute-escalated"
pnpm exec vitest run
packages/adapter-utils/src/acpx-engine/execute.test.ts` — 89 passed.
-
`PAPERCLIP_HOME="$PAPERCLIP_RUN_SCRATCH_DIR/test-home-recovery-escalated"
pnpm exec vitest run
server/src/__tests__/heartbeat-process-recovery.test.ts` — 92 passed.
-
`PAPERCLIP_HOME="$PAPERCLIP_RUN_SCRATCH_DIR/test-home-remote-smoke-escalated"
pnpm exec vitest run
packages/adapter-utils/src/acpx-engine/remote-spawn-smoke.test.ts` — 3
passed.
-
`PAPERCLIP_HOME="$PAPERCLIP_RUN_SCRATCH_DIR/test-home-ci-repro-escalated"
pnpm exec vitest run
server/src/__tests__/heartbeat-dependency-scheduling.test.ts` — 6
passed.
- `pnpm --filter @paperclipai/adapter-utils typecheck` — passed.
- Reverse and forward dry-run application of `patches/acpx@0.12.0.patch`
— passed.
- `git diff --check` — passed.
- `git diff --exit-code origin/master...HEAD -- pnpm-lock.yaml` —
passed.
- `git diff --exit-code origin/master...HEAD -- .github/workflows` —
passed.

## Risks

- Runtime risk is low to moderate. ACPX now awaits process-identity
persistence during child startup.
- ACPX kills the child when persistence fails. This prevents an
unsupervised process, but it makes that heartbeat fail visibly.
- A compatible warm heartbeat reuses the identity of the existing ACP
child. Regression tests verify that identity is persisted before the
next prompt.
- The change updates the vendored ACPX patch. Package installation must
apply that patch.
- There are no schema, migration, public API, UI, workflow, or lockfile
changes.

> 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

- OpenAI Codex used GPT-5.3-Codex for the earlier implementation.
- OpenAI Codex used GPT-5 for the lifecycle-hook revision and the
current fail-closed review fix. The runtime did not expose a more
specific snapshot ID or context-window size. Both runs used reasoning,
repository tools, and code 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
- [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-07-31 14:43:46 -07:00
Nicky LeachandPaperclip 53bcf3897f feat: sync @-mentioned projects into remote sandboxes (confined sandbox transport, flag ON) (#10564)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The run layer must move project context into the sandbox that
executes the agent
> - Local sandboxes already stage referenced projects for @-mentions
> - Remote confined sandboxes dropped the whole referenced set, so the
agent lost needed files and paths
> - This pull request keeps the confined sandbox transport aligned with
the local behavior for referenced projects
> - It does this behind a remote-only flag that defaults on, while SSH
keeps the old drop-only path
> - The benefit is that remote runs can read the same referenced project
context that local runs already provide

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting. This change touches server orchestration, sandbox
transport, and observability.

**Problem or motivation**

A run can @-mention another project. Local targets stage each referenced
project and give the agent a path. Remote confined sandboxes dropped the
full referenced set, so the agent could not read those project files or
paths.

**Proposed solution**

Enable referenced-project sync for the confined sandbox transport behind
`PAPERCLIP_MULTI_PROJECT_WORKSPACE_SYNC_REMOTE`, which defaults on. Keep
the SSH transport out of scope and keep it dropping referenced projects.
Repoint each referenced workspace hint at its staged
`project-<projectId>` sandbox directory. Publish
`PAPERCLIP_WORKSPACES_JSON` on the confined sandbox lane. Count each
per-project remote staging failure as a `staging` failure in the
requested-vs-synced metrics.

**Alternatives considered**

Keep the remote path drop-only. That keeps the gap open. Move the change
into SSH too. That expands scope beyond the target transport and adds
risk.

**Roadmap alignment**

No matching item in `ROADMAP.md` showed up in this review.

**Additional context**

The change lands in three commits. The first commit opens the gate for
the confined sandbox transport. The second commit repoints the workspace
hints and publishes the workspace map. The third commit records
per-project staging failure data.

## What Changed

- Opened remote referenced-project sync for the confined sandbox
transport behind `PAPERCLIP_MULTI_PROJECT_WORKSPACE_SYNC_REMOTE`.
- Repointed referenced workspace hints to the staged
`project-<projectId>` sandbox directories and published
`PAPERCLIP_WORKSPACES_JSON`.
- Counted per-project remote staging failures as first-class `staging`
failures in the requested-vs-synced observability.

## Verification

- The pushed ref
`refs/heads/feat/sync-referenced-projects-remote-sandbox` resolves to
the authorized submit SHA.
- `git log --oneline
origin/master..origin/feat/sync-referenced-projects-remote-sandbox`
shows exactly the three expected commits.
- The handoff reports server typecheck clean, adapter-utils typecheck
clean, and the listed unit tests passing.
- The handoff also reports no open review comments and no Greptile score
yet.

## Risks

- The change touches authorization and sandbox path handling, so
regressions could block remote runs or expose the wrong project context.
- The new flag defaults on, so any bug in the remote path affects normal
remote use.
- SSH stays out of scope, so the two transport paths must remain
distinct.

## Model Used

OpenAI GPT-5 via Codex. Tool use enabled. Context window not reported in
this run.

## 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
- [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-07-31 13:31:17 -07:00
Nicky LeachandPaperclip 9f7565f4ce feat: activate OpenTelemetry spans on the sandbox start path (#10536)
## Thinking Path

> - Paperclip moves agent work through sandboxed execution and
control-plane services.
> - The sandbox start path now has a no-op span seam.
> - This change turns that seam on when OTLP export is configured.
> - It keeps the default path unchanged when export is off.
> - The result is structured startup traces with low-cardinality
attributes and explicit parent links.
> - The benefit is better observability without changing normal
behavior.

## Linked Issues or Issue Description

No public GitHub issue exists for this change.

### Problem
The sandbox start path has a tracer seam, but it stays a no-op unless
the OTLP export path is active.

### Proposed solution
Enable the server tracer on sandbox bring-up, open a root span, parent
each startup boundary to that root, and keep the export path opt-in
behind `OTEL_EXPORTER_OTLP_ENDPOINT`.

### Alternatives considered
- Keep the start path as a no-op. I rejected that path because it leaves
sandbox start opaque when OTLP export is already configured.
- Add broad attributes for commands and paths. I rejected that path
because the span allowlist must stay low-cardinality.

### Roadmap alignment
This follows the current OTel sandbox-start work and keeps the default
path unchanged.

## What Changed

- Add a root sandbox startup span and child spans for each named startup
boundary.
- Keep concurrent bridge spans parented to the root span.
- Inject the server tracer through the adapter deps without
OpenTelemetry imports in the engine.
- Attach host-received provider duration attributes only when the values
are finite.
- Keep span attributes inside the allowlist and keep command, path, id,
and error text out of span data.

## Verification

- The pushed branch already passed `pnpm --filter
@paperclipai/adapter-utils exec tsc --noEmit`.
- The pushed branch already passed `pnpm exec vitest run
packages/adapter-utils/src/acpx-engine/`.
- The pushed branch already passed `pnpm --filter @paperclipai/server
exec vitest run src/__tests__/environment-execution-target.test.ts
src/__tests__/instrumentation.test.ts`.
- The pushed branch already passed `pnpm --filter @paperclipai/server
exec tsc --noEmit`.

## Risks

- OTel export changes trace volume when the endpoint is set.
- The allowlist limits trace detail, so new fields need care.
- The change stays no-op when OTLP export is off.

## Model Used

- OpenAI GPT-5, tool use enabled.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with 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 linked existing issues or described the issue in-PR
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
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
- [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-07-30 21:20:58 -07:00
Nicky LeachandPaperclip 6a267e0328 feat(sandbox): stage referenced projects into the run sandbox (#10469)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Runs can reference more than one project
> - Each referenced project must land in its own sandbox tree so one run
does not mix files across projects
> - The anchor workspace must keep its own git history and overlay rules
> - Fail closed on sync and confinement errors, and keep the other
projects alive
> - This pull request stages referenced projects into isolated project
directories under the run sandbox root
> - The benefit is safer multi-project runs with clear failure isolation

## Linked Issues or Issue Description

No public issue exists.

Related PR: #10448.

## What Changed

- Thread additional referenced-project sources through the run prepare
path and the runtime layers.
- Stage each referenced project into its own `project-<projectId>`
directory under the runtime root.
- Keep the anchor workspace history and overlay semantics unchanged.
- Fail closed on confinement or sync errors for one project, and keep
the other projects running.
- Keep the path inert by default behind the multi-project workspace-sync
kill-switch.
- No documentation update was needed for this code-only runtime change.

## Verification

- `pnpm --filter @paperclipai/adapter-utils exec vitest run
src/sandbox-file-sync.test.ts src/command-managed-runtime.test.ts
src/sandbox-managed-runtime.test.ts src/remote-managed-runtime.test.ts`
- `pnpm --filter @paperclipai/adapter-utils exec tsc --noEmit`
- The branch points at `871378e4064a95adc8e4647e442ec1359768de61` on
`origin/feat/stage-referenced-projects-into-sandbox`.
- GitHub CI is green on PR #10469.

## Risks

- A referenced project can skip if confinement or sync fails.
- A new runtime tree layout can affect tools that assume one project
root.
- The kill-switch keeps the path inert until operators enable it.

## Model Used

OpenAI Codex, GPT-5, tool use enabled.

## 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 or
confirmed no docs update was needed
- [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-07-29 16:09:57 -07:00
a3b293e26d perf(adapter-utils): parallelize the two Daytona sandbox bridge setups (#10334)
## Thinking Path

> - Paperclip runs AI-agent work through adapter-backed execution paths.
> - Sandbox-backed local adapters pay startup overhead in two host-side
bridge setups.
> - Those setups were happening serially even though most of the work is
independent.
> - The remaining dependency is only the merged env that must reach the
process-session launch.
> - This pull request overlaps the independent setup work while
preserving that launch-time dependency.
> - The benefit is lower end-to-end startup latency without changing
execution semantics.

## Linked Issues or Issue Description

No public GitHub issue exists for this change.

Problem / motivation:
- Sandbox-backed local adapter startup waited for two largely
independent bridge setups in series, so users paid roughly the sum of
both setup times.
- The only hard dependency is the merged Paperclip env that must reach
the process-session launch.

Proposed solution:
- Start the paperclip callback bridge and the process-session bridge
concurrently.
- Keep the env merge as the single sequencing point before
process-session launch.
- Stop whichever bridge started if either startup path fails.
- Keep the concurrent bridge telemetry duration-only so shared runner
counters are not double-counted.

Alternatives considered:
- Keep the bridges serial, which preserves the current telemetry shape
but leaves the startup latency unchanged.
- Move env merging earlier, which would complicate launch ordering and
risk changing runtime semantics.

Roadmap alignment:
- This is the approved Daytona start-speedup work, focused on bridge
startup concurrency rather than broader runtime behavior.

## What Changed

- Started the paperclip callback bridge and the process-session bridge
concurrently in `execute.ts`.
- Added a memoized env finalizer so the process-session launch still
waits for the merged paperclip env at the correct moment.
- Updated `execution-target.ts` so the process-session bridge can accept
a deferred env resolver and consume it only at launch time.
- Added tests that cover the overlapped launch path and the
failure-cleanup path when one bridge start fails.

## Verification

- `pnpm --filter @paperclipai/adapter-utils exec vitest run
src/acpx-engine/execute.test.ts`
- `pnpm --filter @paperclipai/adapter-utils test`
- `pnpm --filter @paperclipai/adapter-utils exec tsc --noEmit`
- `git log --oneline
origin/master..origin/perf/parallelize-daytona-bridge-setups`
- `git diff --stat
origin/master...origin/perf/parallelize-daytona-bridge-setups`

## Risks

- A regression in the launch-time env merge could change what the
process-session bridge sees at startup.
- The concurrent start/cleanup logic could leak a bridge if the stop
paths were incorrect, which is why the failure cleanup test matters.
- This is low-to-moderate risk because the change is isolated to
adapter-utils startup plumbing and is covered by targeted tests.

## Model Used

OpenAI Codex, GPT-5, tool-using code execution mode.

## 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
- [ ] 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Harold Kim <harold@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 15:26:11 -07:00
197718bc00 feat(acpx): per-step round-trip + provider-latency attribution for sandbox startup (#10222)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Sandbox-backed runs need more precise startup observability so
operators can see where time is spent before an adapter is invoked
> - Aggregate startup timing hides which boundary is actually slow,
especially in remote execution where the bottleneck can move between
host round-trips, provider-boundary latency, and handshake phases
> - This pull request keeps the existing startup timing channel additive
while attributing the latency to the specific startup steps that caused
it
> - The benefit is better diagnosis of sandbox startup regressions
without changing control flow or introducing a schema migration

## Linked Issues or Issue Description

Refs: #10204

This PR extends the existing sandbox run-startup timing observability
with per-step round-trip and provider-latency attribution for the
Daytona startup path. It keeps the event payload additive and free-form,
and it leaves the control flow, database schema, and external adapter
interfaces unchanged.

## What Changed

- Added per-step round-trip counting for the host-to-sandbox execute
seam
- Added provider-boundary duration accumulation for the Daytona execute
and re-fetch steps
- Split the ACP handshake timing into `createRuntimeMs` and
`ensureSessionMs` while preserving the warm-handle skip
- Kept the startup timing payload additive and did not add a schema
migration

## Verification

- `adapter-utils` acpx-engine and startup-timing suites: pass
- Daytona plugin suite: pass with mocked SDK and injected-clock duration
assertions
- `server` environment-execution-target suite: pass
- `tsc --noEmit` for adapter-utils and server: pass
- Git validation: fetched
`origin/feat/sandbox-start-step-timing-attribution`, confirmed it
matches the authorized submit SHA
`53c573266e618af05c57a0720aaa9d9e0452de61`, and confirmed the branch
contains only the expected commit on top of `origin/master`
- Searched GitHub for duplicate or related PRs/issues; found one closely
related merged PR and no open duplicate on this branch
- Checked `ROADMAP.md`; the broad sandbox-agent roadmap section does not
call out this specific startup-timing attribution work as a duplicate

## Risks

- Low risk: the change is additive and only enriches existing timing
data
- Downstream consumers that assume aggregate-only startup timing may
need to tolerate the additional per-step fields
- The finer spawn/initialize/session split remains a follow-up in the
external ACP client because that hook is not available here yet

## Model Used

OpenAI GPT-5, tool-using coding agent

## 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Harold Kim <harold@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 09:27:00 -07:00
DottaandClaude Fable 5 b996b71a38 Deduplicate wake-payload issue descriptions and compact resume deltas (#10216)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Heartbeat wake payloads and the task-context markdown are the two
channels that deliver an issue's brief into an agent's prompt
> - #10151 fixed wake-prompt-only adapter lanes waking without the issue
description by adding it to the structured wake payload
> - That left the description delivered twice per prompt on lanes that
also inject the task-context markdown, and re-delivered in full on every
resume wake, permanently bloating persistent-session context
> - This pull request makes the task markdown the single description
carrier on lanes that use it, and omits the description from
non-assignment resume deltas on all lanes while keeping it for
assignment-shaped and recovery wakes
> - The benefit is that every lane receives the brief exactly once when
it needs it, and long-lived sessions stop re-paying the full brief in
tokens on every wake

## Linked Issues or Issue Description

Refs #10151

Related prior work: #2883, #8402 (earlier description-delivery attempts
referenced by #10151). I searched the PR list for open work on
wake-payload description handling and found none besides the merged
#10151.

**Bug:** After #10151, adapters that inject the `Paperclip task context`
markdown (ACPX engine lanes, claude-local CLI, hermes server and
gateway) receive the issue description twice in a single prompt — once
in the wake prompt's `Issue description:` block and once in the task
markdown. Separately, resume deltas re-send the full description (up to
12k characters) on every wake even though the persistent session already
received it.

**Expected behavior:** The description appears exactly once per prompt
on every lane, and resume deltas only carry it when the resuming session
may not have seen the brief (assignment-shaped or recovery wakes),
leaving an explicit fetch breadcrumb otherwise.

**Reproduction:** Wake a claude-local or ACPX agent on an issue with a
description and inspect the assembled prompt: the description text
appears in both the wake-payload block and the task-context block. Wake
the same session again via a comment: the full description is present
again in the resume delta.

**Affected version:** Current `master` (with #10151 merged).

**Deployment mode:** Adapter-backed heartbeat execution, local and
sandboxed lanes.

## What Changed

- `renderPaperclipWakePrompt` accepts `suppressIssueDescription`; the
four task-markdown lanes pass it so the task markdown stays the single,
uncapped description carrier there.
- Non-assignment resume deltas omit the description and emit `- issue
description: omitted from this resume delta; fetch the issue if you need
the latest brief`. Assignment-shaped reasons (`issue_assigned`,
`issue_reopened_via_comment`, `issue_recovery_action_restored`,
`issue_tree_restored`) and recovery wakes still deliver the full brief.
- `buildPaperclipTaskMarkdown` gains `includeDescription`; the server
now also publishes `context.paperclipTaskMarkdownCompact` (description
stripped, directives and wake comment kept), and the new
`selectPaperclipTaskMarkdown` helper picks the right variant under the
same resume rules, falling back to the full markdown when no compact
variant exists (version skew safety).
- The wake prompt's description block now carries the same user-authored
trust framing the task markdown already had.

## Verification

- `npx vitest run packages/adapter-utils/src/server-utils.test.ts
packages/adapters/claude-local/src/server/acp.test.ts
packages/adapters/codex-local/src/server/acp.test.ts
server/src/__tests__/heartbeat-context-summary.test.ts` — 137 tests
passed, including new coverage for suppression, resume omission plus
breadcrumb, assignment-shaped resume inclusion, compact-variant
building, variant selection, and an end-to-end ACPX prompt-assembly test
asserting the description appears exactly once on fresh wakes and not at
all on comment resumes.
- `npx vitest run` in `packages/adapters/hermes` — 59 tests passed,
including a gateway execute-level test asserting the brief is sent
exactly once on fresh runs and not re-sent on stable-session resumes.
- `tsc --noEmit` in `packages/adapter-utils`,
`packages/adapters/claude-local`, `packages/adapters/hermes` — clean;
`server` matches the `master` baseline exactly (pre-existing plugin-sdk
resolution errors only, none in touched files).
- Pre-existing failures confirmed identical on clean `master`:
claude-local `execute.remote.test.ts` / `test.probe.test.ts`,
adapter-utils `mcp-isolation.integration.test.ts` (requires a newer
local Claude CLI).

## Risks

- Behavioral shift, prompt-only: a resumed session woken by a comment on
an issue it never handled (rare — assignment wakes normally precede
comment wakes) would not get the inline description; the breadcrumb plus
the standard issue-fetch path covers it.
- Additive context key (`paperclipTaskMarkdownCompact`); older adapters
ignore it and newer adapters fall back to the full markdown when it is
absent, so mixed-version deployments degrade to current behavior.
- No schema, migration, or API changes; the structured wake-payload JSON
shape is unchanged.
- Known follow-up deliberately out of scope: openclaw embeds the raw
wake-payload JSON (which still contains the description) in prompt text
for machine parsing. The hermes-gateway lane is handled: it detects
stable-session resumes (issue/agent session-key strategy plus a stored
prior session id), compacts the task markdown, and omits the description
from its prompt-embedded JSON copy.

> This is a focused correctness/efficiency fix to existing wake plumbing
and does not overlap with planned roadmap feature work.

## Model Used

- Anthropic Claude Fable 5 (`claude-fable-5`), extended thinking
enabled, with repository tool use, shell execution, and local test
execution via 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)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
(execution-workspace branch, same convention as merged #10202)
- [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
(code-level docs; no user-facing docs affected)
- [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

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 16:35:07 -05:00
762ce5b4ef feat(acpx): per-step timing observability for sandbox run-startup (#10204)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Sandbox-backed runs need observable startup behavior so operators
can see where time is spent before an adapter is invoked
> - The current startup path only surfaced aggregate timing, which makes
it hard to identify the slow boundary in the bring-up sequence
> - That gap matters because sandbox startup latency is often dominated
by one specific step, and aggregate timing hides the bottleneck
> - This pull request adds per-step startup timing events for the named
sandbox bring-up boundaries
> - The benefit is more precise observability with no control-flow
change and no schema migration

## Linked Issues or Issue Description

### Subsystem affected
Cross-cutting (multiple of the above)

### Problem or motivation
Sandbox run startup only exposed aggregate timing. That makes it hard to
identify which bring-up boundary is responsible for slow starts,
especially in remote or sandboxed execution where the bottleneck can
move between workspace setup, skill reconciliation, bridge setup, and
adapter handshake.

### Proposed solution
Emit a structured timing event for each named startup boundary before
the adapter is invoked, so the existing run-event stream carries
per-step duration data. This keeps the event path additive and lets
operators see which step dominates startup latency without changing
control flow or introducing a schema migration.

### Alternatives considered
- Keep only the aggregate startup duration: simpler, but it hides the
bottleneck and makes regression analysis much harder.
- Add a new telemetry sink or schema field: rejected because the
existing run-event payload already carries structured event data and
does not need a new storage path.
- Log unstructured text for each step: rejected because it is harder to
query and aggregate than a structured `step` + `durationMs` event.

### Roadmap alignment
This fits the roadmap direction around cloud / sandbox agents and
enforced outcomes by improving observability for sandboxed execution
without changing the control plane model. The roadmap section is broad,
but it does not call out this specific startup-timing work as a planned
duplicate.

### Additional context
This PR is intentionally additive. It records timing for the named
startup boundaries in the existing event stream and leaves the bridge,
database shape, and adapter invocation order unchanged.

## What Changed

- Added a `measureStartupStep` helper that times a startup step, emits
one structured `run.startup.step` event, and rethrows failures after
recording duration
- Wrapped the seven sandbox bring-up boundaries in `execute.ts` so the
structured timing covers each named step before adapter invocation
- Added unit coverage for the helper and integration coverage for the
startup-step events in the adapter-utils execute path
- Kept the event path additive, with no bridge change and no database
migration

## Verification

- `tsc --noEmit` for `@paperclip/adapter-utils`
- `pnpm test` in `packages/adapter-utils` equivalent suite coverage: 292
passed, 4 skipped
- Adjacent server event/log-store suites: `run-log-store.test.ts` and
`heartbeat-run-log.test.ts` passed (11 total)
- Git validation: fetched `origin/feat/sandbox-startup-step-timing`,
confirmed it matches the authorized submit SHA, and confirmed
`origin/master..origin/feat/sandbox-startup-step-timing` contains the
expected single commit
- Searched GitHub for duplicate or related open PRs/issues and found no
overlapping open items
- Checked `ROADMAP.md`; the roadmap covers sandboxed environments
generally, but does not call out this specific startup-timing
observability work as a planned duplicate

## Risks

- Low risk: the change is additive and only emits additional structured
events
- If downstream consumers assume startup events are aggregate-only, they
may need to ignore or account for the new `run.startup.step` entries
- Timing is measured via the injected clock and event emission happens
in a `finally`, so failures still report duration before rethrowing

## Model Used

OpenAI GPT-5, tool-using coding agent

## 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Harold Kim <harold@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-24 12:50:37 -07:00
Nicky Leach b517b887ad fix(acpx): decouple host proxy spawn cwd from in-sandbox remoteCwd (#10122) 2026-07-23 16:48:52 -07:00
Nicky LeachandPaperclip d36ea13e08 feat(acpx): stage once per remote ACP session - compatible resume reuses staged runtime, no cross-session credential reuse (#10089)
## Thinking Path

> - Paperclip coordinates autonomous agent work across isolated
company-scoped sessions
> - The ACP remote lane stages workspaces and managed home state inside
the sandbox so sessions can resume safely
> - If a compatible resume re-staged everything every time, it would
waste work and risk inconsistent session reuse behavior
> - If an incompatible resume reused the wrong staged runtime, it could
cross session boundaries or leak credentials
> - This pull request keeps the session fingerprint as the scoping key
and adds a staged-runtime cache keyed to that fingerprint
> - Compatible resumes now reuse the already staged runtime while
incompatible fingerprints stage fresh
> - The benefit is faster safe resumes without weakening session
isolation or credential separation

## Linked Issues or Issue Description

### Problem or motivation
The ACP remote lane currently needs to preserve safe resume behavior
without repeatedly re-staging work that is already valid for the same
session. The failure mode to avoid is letting one session reuse another
session's staged workspace or credentials.

### Proposed solution
Keep the session fingerprint as the scoping key and add a staged-runtime
cache keyed to that fingerprint. When the fingerprint matches, reuse the
already staged workspace and managed home. When the fingerprint changes,
stage fresh.

### Alternatives considered
- Always restage on resume: safest mechanically, but wastes work and
breaks the compatible-resume optimization.
- Reuse without fingerprint scoping: too risky because it could cross
session boundaries.

### Roadmap alignment
This is a narrow implementation change for the ACP remote lane and does
not duplicate any broader roadmap item I could find in `ROADMAP.md`.

### Additional context
The change preserves the existing session fingerprint contents and codex
auth copy-back cadence while adding tests for compatible reuse,
incompatible fresh staging, no cross-session credential reuse, and
failed-turn eviction.

## What Changed

- Added a staged-runtime cache in the ACP remote execution path keyed by
the session fingerprint.
- Reused the existing staged workspace and managed home for compatible
resumes.
- Kept incompatible fingerprints on the fresh staging path.
- Preserved the existing session fingerprint contents and codex auth
copy-back cadence.
- Added tests for compatible reuse, incompatible fresh staging, no
cross-session credential reuse, and failed-turn eviction.

## Verification

- `pnpm exec vitest run
packages/adapter-utils/src/acpx-engine/execute.test.ts` (74/74 pass,
including the active-turn lease regression)
- `pnpm exec tsc -p packages/adapter-utils/tsconfig.json --noEmit`
- Verified the PR touches only
`packages/adapter-utils/src/acpx-engine/execute.ts` and
`packages/adapter-utils/src/acpx-engine/execute.test.ts`

## Risks

- A cache eviction bug could cause an unavailable or partially staged
runtime to be reused.
- If the fingerprint scoping regressed, a session could incorrectly
reuse another session's state.
- The change is isolated to the ACP remote lane, but it still affects
resume behavior for that path.

## Model Used

OpenAI Codex, GPT-5, reasoning-capable coding agent, tool-enabled
session.

## 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
- [ ] 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-07-23 10:13:44 -07:00
e1cc63328a feat(acpx): per-adapter managed-home seed (x3) + Codex auth copy-back for remote ACP lane (#10073)
## Thinking Path

> - Paperclip is a control plane that orchestrates AI agents and adapter
execution for human operators.
> - Agents run across local and remote execution contexts, and
reliability in remote sessions depends on consistent adapter
bootstrapping.
> - The ACP path must prepare per-adapter runtime homes so managed
credentials and config are available in sandboxed runner environments.
> - Before this change, the new remote ACP lane did not yet consistently
stage managed-home paths for all affected adapters or restore Codex auth
state on teardown.
> - We added a shared per-adapter seam in the ACP engine, then wired
Codex, Claude, and Gemini remote lanes to seed managed homes and remap
to in-sandbox locations.
> - Codex additionally reuses the existing atomic auth restore flow to
copy auth back on teardown, matching CLI behavior.
> - This improves remote runner parity with existing CLI behavior and
avoids credential drift in shared-code-path executions.

## Linked Issues or Issue Description

- This change continues the ACP remote managed-home work by completing
per-adapter remote bootstrapping and Codex auth restore behavior for the
remote ACP lane.
- It specifically covers: `acpx-engine`, `codex-local`, `claude-local`,
and `gemini-local`.
- Related prior work in this repo: PR #10070.

## What Changed

- Add a per-adapter remote managed-home seam
(`prepareRemoteManagedHome`) in `acpx-engine` and thread it through ACP
execution options.
- Implement Codex ACP preparation to:
- stage `CODEX_HOME` (auth/config/skills) into a sandboxed remote home,
  - repoint `CODEX_HOME` to the in-sandbox path,
- and wire teardown copy-back through the existing managed-auth restore
path.
- Implement Claude ACP preparation to seed a sanitized `config-seed`
into `CLAUDE_CONFIG_DIR` and remap that directory to the sandbox root.
- Implement Gemini ACP preparation to seed `~/.gemini/skills`, set
`HOME` to managed runtime root, and preselect API-key auth in
`settings.json`.
- Keep local and runner-less ACP→CLI behavior unchanged by only invoking
the remote managed-home seam when running in remote ACP mode.
- Preserve existing authorization and activity boundaries in the shared
engine and adapter layers.

## Verification

- `git log --oneline
origin/master..origin/feat/acp-remote-managed-home-seed` confirms only
the expected 4 commits.
- `tsc --noEmit` is clean in `adapter-utils`, `codex-local`,
`claude-local`, and `gemini-local`.
- Vitest selection used during validation passed (120 tests across the
ACP-related suites).

## Risks

- If remote sandbox teardown occurs after token rotation but before
restore timing, Codex credentials can become stale and require re-auth
on next startup.
- Partial provisioning of managed-home assets would cause adapter
bootstrap failures in runner-backed ACP sessions.
- This change is scoped to execution-path behavior; it should not affect
CLI behavior.

## Model Used

None — human-authored.

## 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
- [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: Harold Kim <harold@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-23 06:42:52 -07:00
8a3cc86531 feat(acpx): stage workspace + route in-sandbox cwd for remote ACP lane (#10070)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The shared ACP engine
(`packages/adapter-utils/src/acpx-engine/execute.ts`) is responsible for
launching local and remote agent processes via the ACP protocol
> - On runner-backed remote sandbox (Daytona) targets, `buildRuntime`
never crossed the CLI's staging seam: it never called
`prepareAdapterExecutionTargetRuntime`, left `runtimeRootDir: null` in
both the paperclip and process-session bridges, and handed the agent the
**HOST filesystem path** as the `session/new` cwd — meaning
Claude/Gemini silently operated on a path that does not exist inside the
sandbox (Codex additionally crashes on its HOST home path, addressed in
a follow-up PR)
> - The fix must cross the staging seam for remote sandboxes, thread the
real `runtimeRootDir` through both bridges, and bind the in-sandbox
workspace path as the session cwd — without touching local ACP runs or
the runner-less ACP→CLI fallback
> - This pull request introduces a `stageAcpRemoteRuntime` helper that
calls `prepareAdapterExecutionTargetRuntime` for runner-backed remote
runs, captures `{ workspaceRemoteDir, runtimeRootDir, assetDirs,
restoreWorkspace }`, and reuses the in-sandbox `workspaceRemoteDir` as
the single `sessionCwd` across `session/new`, the fingerprint,
compatibility check, persistence, `ensureSession`, the process-session
bridge cwd, and the error path
> - The benefit is that remote ACP runs now operate in the correct
in-sandbox cwd and receive a non-null `runtimeRootDir` in both bridges —
fixing silent wrong-cwd degradation for Claude/Gemini on Daytona
targets; this is PR 1 of 3 and seeds no credential material

## Linked Issues or Issue Description

No public GitHub issue exists for this change. Inline description
follows the feature request template:

### Subsystem affected

packages/adapters — agent adapter implementations

### Problem or motivation

The shared ACP engine
(`packages/adapter-utils/src/acpx-engine/execute.ts`) never crossed the
CLI's staging seam on runner-backed remote sandbox targets. It never
called `prepareAdapterExecutionTargetRuntime`, always passed
`runtimeRootDir: null` to both the paperclip and process-session
bridges, and handed the agent the **HOST filesystem path** as the ACP
`session/new` cwd. As a result, Claude and Gemini silently operated on a
cwd that does not exist inside the sandbox; Codex crashed with a fatal
error on the HOST `CODEX_HOME` path (that crash is in a follow-up PR).

### Proposed solution

Gate on `usesRunnerBackedSandbox` (`kind === "remote" && transport ===
"sandbox" && runner`). For runs that pass the gate, call
`prepareAdapterExecutionTargetRuntime` via a new `stageAcpRemoteRuntime`
helper that ships the workspace into the sandbox and captures `{
workspaceRemoteDir, runtimeRootDir, assetDirs, restoreWorkspace }`.
Thread the real `runtimeRootDir` into both bridges. Bind a single
`sessionCwd` (the in-sandbox `workspaceRemoteDir`) and use it at every
cwd-keyed session site (`session/new`, fingerprint, compat, persist,
`ensureSession`, process-session bridge, error path) so a warm/resumable
session is reused rather than invalidated. For local runs and the
runner-less ACP→CLI fallback, `sessionCwd` resolves to the HOST cwd —
byte-identical to the previous behavior.

### Alternatives considered

Patching each per-adapter bridge individually — rejected because the bug
is in the shared engine layer and the fix belongs there so all three
adapters (Codex, Claude, Gemini) benefit without per-adapter
duplication.

### Roadmap alignment

Internal correctness fix enabling remote ACP to work as designed; no new
user-facing features. This is PR 1 of 3 in a sequential chain: PR 1
(this PR) stages the workspace and routes the cwd; PR 2 adds per-adapter
home seeding and copy-back; PR 3 wires session-lifecycle restore.

## What Changed

- **`packages/adapter-utils/src/acpx-engine/execute.ts`** — added
`stageAcpRemoteRuntime` helper that calls
`prepareAdapterExecutionTargetRuntime` for runner-backed remote
sandboxes and returns `{ sessionCwd, runtimeRootDir, stagedRuntime }`;
`buildRuntime` now uses this helper to derive `sessionCwd` (in-sandbox
`workspaceRemoteDir` for remote; HOST cwd unchanged for
local/runner-less) and threads the real `runtimeRootDir` to both the
paperclip bridge and process-session bridge; `stagedRuntime` is stashed
for the follow-up credential PR
- **`packages/adapter-utils/src/acpx-engine/execute.test.ts`** — new
engine-level unit tests: staging seam crossed with no credential asset,
non-null `runtimeRootDir` in both bridges, in-sandbox `session/new` cwd,
warm-handle reuse after the cwd change, local-unchanged; 60 tests green
- **`packages/adapters/codex-local/src/server/acp.test.ts`** — new
per-adapter test: runner-backed remote asserts `ensureSession` cwd ==
`workspaceRemoteDir`; runner-less sandbox falls back to CLI
- **`packages/adapters/claude-local/src/server/acp.test.ts`** — same
per-adapter coverage for Claude
- **`packages/adapters/gemini-local/src/server/acp.test.ts`** — same
per-adapter coverage for Gemini

## Verification

- `adapter-utils` typecheck clean; `codex/claude/gemini-local` typecheck
clean
- Engine units (`acpx-engine/execute.test.ts`): 60/60 green — staging
seam crossed with no credential asset, non-null `runtimeRootDir` to both
bridges, in-sandbox `session/new` cwd, warm-handle reuse after cwd
change, local unchanged
- Per-adapter ACP test suites (`codex/claude/gemini-local`
`acp.test.ts`): 103 tests green — runner-backed remote asserts
`ensureSession` cwd == `workspaceRemoteDir`; runner-less sandbox falls
back to CLI
- CI green on PR (in progress)

## Risks

This is PR 1 of 3 in a strictly sequential chain; it seeds **no
credential material** (no `assets`, no `installCommand`). The
per-adapter home seeding is deferred to PR 2, which consumes the
`stagedRuntime` object stashed here. The `restoreWorkspace` callback is
carried on `stagedRuntime` for PR 3's session-lifecycle wiring (see the
`stageAcpRemoteRuntime` function comment). Local ACP runs and the
runner-less ACP→CLI fallback are untouched — `sessionCwd` resolves to
the HOST cwd for those paths, preserving existing behavior. The
`stageAcpRemoteRuntime` helper is gated on `usesRunnerBackedSandbox`, so
there is no regression risk for local or CLI-lane runs.

## Model Used

Claude Sonnet 4.6 (`claude-sonnet-4-6`) via Paperclip ACPX engine —
extended context, tool use enabled, co-authored with Paperclip agent
orchestration.

## 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/...`,
`feat/...`) 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
- [ ] 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: Harold Kim <harold@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-22 22:24:30 -07:00
DottaandPaperclip d31a28828b fix(acpx): support Windows agent spawning (#9980)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Local Claude, Codex, Gemini, and custom ACP adapters run through the
shared embedded ACPX engine
> - That engine wrapped every local agent command in a generated Bash
script to inject environment variables and filter child stderr
> - Windows cannot directly spawn that Bash wrapper, and npm/pnpm ACP
binaries are exposed through `.cmd` shims there
> - ACPX 0.12 already supports per-session child environment variables,
so the wrapper is unnecessary
> - This pull request registers agent commands directly, injects env
through ACPX session options, captures child stderr in-process, and adds
a real Node ACP spawn smoke on Ubuntu and Windows
> - The benefit is one cross-platform spawn path with a reusable smoke
test instead of parallel shell-wrapper implementations

## Linked Issues or Issue Description

Fixes #9941.
Refs #9428 and #9771.

**What happened**

ACPX-backed local agents failed to start on Windows because Paperclip
registered a generated POSIX `.sh` wrapper as the agent command. Windows
also needs the `.cmd` npm/pnpm shim when resolving built-in ACP
binaries, and symlink creation can fail with `EPERM` for seeded
auth/skill files.

**Expected behavior**

The same ACPX engine path should spawn a real ACP agent on Windows and
Linux, forward Paperclip/runtime env without mutating `process.env`,
preserve filtered/unfiltered child stderr behavior, and fall back to
copies where Windows symlinks are unavailable.

**Steps to reproduce**

Run a local ACPX adapter on Windows with the prior wrapper path. ACPX
attempts to spawn the generated `.sh` file and the agent never
initializes.

**Deployment mode**

Local Paperclip adapters using
`packages/adapter-utils/src/acpx-engine/`.

## What Changed

- Removed generated Bash agent/env wrappers and registered local
commands directly with ACPX.
- Passed the resolved child environment through ACPX
`sessionOptions.env`, including resume retry paths.
- Added a minimal `acpx@0.12.0` package patch exposing child stderr
callbacks and allowing documented uppercase env-map keys in persisted
session options.
- Moved stderr tee/filter behavior in-process: raw stderr remains in the
per-run file while benign `nes/close` noise is omitted from live stderr.
- Preferred `.cmd` ancestor binaries on Windows and added `EPERM` copy
fallbacks for Codex auth seeding and Gemini skill materialization.
- Added a real Node ACP echo-agent spawn smoke that can run directly on
any supported platform.

## Verification

- `pnpm exec vitest run
packages/adapter-utils/src/acpx-engine/execute.test.ts
packages/adapter-utils/src/acpx-engine/spawn-smoke.test.ts` — 57 passed.
- `pnpm --filter @paperclipai/adapter-utils typecheck` — passed.
- `node --test scripts/acpx-patch-packaging.test.mjs
scripts/release-lib.test.mjs` — 10 passed.
- Full canary release dry run under Node 24.18.0 / npm 11.16.0 — passed
in an isolated scratch clone.
- `git diff --check` — passed during implementation verification.
- One-time GitHub Actions proof: [Ubuntu ACPX spawn
smoke](https://github.com/paperclipai/paperclip/actions/runs/29924348927/job/88937774579),
[Windows ACPX spawn
smoke](https://github.com/paperclipai/paperclip/actions/runs/29924348927/job/88937774558),
and [Canary Dry
Run](https://github.com/paperclipai/paperclip/actions/runs/29924348927/job/88937774497)
passed on head `f345ac69f2`; the dedicated smoke jobs are intentionally
not retained in the recurring PR workflow.

## Risks

- The ACPX stderr callback and env persistence exemption are carried as
a pnpm dependency patch until ACPX exposes/fixes those behaviors
upstream.
- Child stderr is synchronously appended to preserve ordering and
failure diagnostics; unusually high-volume agent stderr could briefly
block the Node event loop.
- The Windows-specific `.cmd` resolution and symlink `EPERM` branches
are proven by the standalone smoke test and the linked one-time
`windows-latest` run rather than a permanent CI gate.

> 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

- OpenAI GPT-5.4 via Codex CLI, medium reasoning, repository/tool
execution enabled; context-window size is not exposed in this session.

## 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>
2026-07-22 10:22:29 -05:00
5ed0b74b34 fix(runtime): scope PAPERCLIP_ env-binding strip to reserved keys (#9974)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent runs get their environment from user/adapter/project/routine
env bindings resolved by the server heartbeat, plus `PAPERCLIP_*`
runtime vars (identity, wake, workspace, API access) injected by the
harness
> - The heartbeat stripped **every** `PAPERCLIP_`-prefixed binding
before resolution, so legitimately user-named keys (e.g. cloud provider
token bindings like `PAPERCLIP_CLOUD_PROD_PROVIDER_RAILWAY_*`) were
silently dropped and never reached the run env
> - At the same time, several adapters honored an explicitly configured
`PAPERCLIP_API_KEY` over the harness-minted run token, which is exactly
the one key config must never control
> - This pull request replaces the blanket prefix strip with a precise
three-rule policy: never accept `PAPERCLIP_API_KEY` from config, always
let harness-assigned runtime vars win, and let every other
`PAPERCLIP_*`-named user binding flow through
> - The benefit is that user secrets with a `PAPERCLIP_`-style name work
like any other binding, while runtime identity and API credentials stay
fully harness-controlled

## Linked Issues or Issue Description

**Bug description** (no public issue exists):

- **What happened:** Env bindings whose key starts with `PAPERCLIP_`
(e.g. a cloud provider token a user deliberately named
`PAPERCLIP_CLOUD_PROD_PROVIDER_RAILWAY_TOKEN`) were silently stripped by
the server before secret resolution, so the spawned agent never received
them. No error, no access event — the variable just never appeared.
- **Expected behavior:** A user-named `PAPERCLIP_*` binding should reach
the run env unless the harness itself uses that key. Only
`PAPERCLIP_API_KEY` should be categorically rejected, and
harness-assigned runtime vars (`PAPERCLIP_RUN_ID`, `PAPERCLIP_AGENT_ID`,
wake/workspace vars, …) should always win over config.
- **Steps to reproduce:** Configure an agent/project env binding named
`PAPERCLIP_<ANYTHING>` (plain or secret_ref), run a heartbeat, and
inspect the spawned process env — the key is absent.
- **Deployment mode:** local server, any local adapter.

Related prior PRs (different, save-time/API-layer blanket-ban approach;
this PR supersedes that direction with a runtime allow-except-reserved
policy): Refs #8239, Refs #8439.

## What Changed

- `server/src/services/heartbeat.ts`: the pre-resolution strip now
removes only `PAPERCLIP_API_KEY` (hard denylist) instead of every
`PAPERCLIP_`-prefixed binding; other `PAPERCLIP_*` keys flow into
binding resolution. Low-trust inline-sensitive-env checks now also cover
those keys.
- `packages/adapter-utils/src/server-utils.ts`: new
`isForbiddenConfigEnvKey()` helper; the shared
`refreshPaperclipWorkspaceEnvForExecution` merge drops
`PAPERCLIP_API_KEY` from config and keeps harness-assigned `PAPERCLIP_*`
keys authoritative.
- `packages/adapter-utils/src/acpx-engine/execute.ts`: removed the
explicit-`PAPERCLIP_API_KEY`-from-config allowance; the run token
(`authToken`) is now always applied; config `PAPERCLIP_API_KEY` is
ignored.
- All local adapters (`claude-local`, `codex-local`, `cursor-local`,
`gemini-local`, `grok-local`, `opencode-local`, `pi-local`) plus
`cursor-cloud`, `hermes`, and the server `process` adapter: removed
`hasExplicitApiKey`-style allowances so the harness token always wins,
and guarded the remaining unguarded env-merge loops (claude-local inline
loop, process adapter) with the same policy.
- Tests updated/added: heartbeat binding-strip test now asserts the
three-rule policy; adapter-utils merge tests assert the
`PAPERCLIP_API_KEY` ban and `PAPERCLIP_*` pass-through; acpx engine
tests moved credential fixtures to `authToken` and assert config
`PAPERCLIP_API_KEY` is ignored while other `PAPERCLIP_*` config keys
forward and still bust the session fingerprint on rotation.

## Verification

- `pnpm vitest run packages/adapter-utils/src/server-utils.test.ts
packages/adapter-utils/src/acpx-engine/execute.test.ts` — 127 passed
- `pnpm vitest run server/src/__tests__/heartbeat-project-env.test.ts
server/src/__tests__/heartbeat-local-environment.test.ts
server/src/__tests__/claude-local-execute.test.ts
server/src/__tests__/codex-local-execute.test.ts
server/src/__tests__/cursor-local-execute.test.ts
server/src/__tests__/gemini-local-execute.test.ts` — 68 passed
- Adapter package execute suites and the server tests touching API-key
fixtures (`heartbeat-run-log`, `redaction`,
`effective-run-config-fingerprints`, `agent-permissions-routes`) —
green. Three pre-existing sandbox/SSH fixture failures reproduce
identically on clean `master` on this host and are unrelated.
- `pnpm --filter <pkg> typecheck` for server, adapter-utils, and all
nine touched adapter packages — all pass.

## Risks

- Behavioral change: a deployment that relied on configuring a static
`PAPERCLIP_API_KEY` in adapter config env loses that override — by
design; the harness-minted run token is now the only source. When no run
token exists, no API key is injected at all.
- `PAPERCLIP_*`-named user bindings now reach binding resolution and run
envs; a key that collides with a harness runtime var is still discarded
at merge time, so runtime identity/wake/workspace vars cannot be
spoofed.
- Low risk otherwise: no migrations, no API surface changes.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic Claude 5 family,
Mythos-class tier), extended thinking enabled, agentic tool use (file
edits, shell, test runner) via Claude Agent SDK.

## 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
- [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

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 07:01:57 -05:00
DottaandPaperclip 5a5c918705 fix(acpx): configure Codex models at startup (#9700)
Move Codex ACPX model, reasoning effort, and fast-mode settings into CODEX_CONFIG startup config so arbitrary model IDs avoid ACP session picker validation.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-07-16 11:41:48 -05:00
DottaandPaperclip 0ecae2cd7e fix(adapters): forward resolved adapter env to local agents, keep runtime env authoritative (#9617)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Local coding adapters (Claude, Codex) run each agent heartbeat in a
spawned child process; Paperclip resolves adapter-configured env —
including `secret_ref` bindings — into the env for that process
> - The resolved adapter env was not being forwarded reliably: config
env could overwrite Paperclip's own runtime vars, and a warm/resumable
ACP session could keep serving stale env because its fingerprint ignored
the resolved env
> - This mattered because a configured API key or other secret could be
silently absent from the agent shell, and a resumed session would never
pick up an updated value — while a config binding could also override
runtime identity/wake vars
> - This pull request keeps Paperclip-managed `PAPERCLIP_*` runtime env
authoritative over config, and folds a stable hash of the applied
adapter env into the session fingerprint so an env change forces a fresh
launch
> - The benefit is that adapter-configured env (plain values and
resolved secrets) reliably reaches the agent process, updates are picked
up on the next launch, and runtime identity can never be clobbered by
config

## Linked Issues or Issue Description

No public GitHub issue exists, so the underlying bug is described inline
following the bug-report template.

**What happened**

Env keys configured on a local adapter (plain values and `secret_ref`
bindings, resolved server-side into plain strings) did not reliably
reach the spawned agent process. Two distinct gaps: (1) when merging
config env into the process env, a config key in the reserved
`PAPERCLIP_*` namespace could overwrite a Paperclip-managed runtime
variable (identity, wake, workspace, API access); (2) a warm-handle /
resumable ACP session computed its reuse fingerprint from
`secretManifestHash` only, which misses plain-value edits and
same-version secret rotations — so a resumed session kept serving stale
env and never re-launched with updated values.

**Expected behavior**

Non-`PAPERCLIP_*` adapter env (plain + resolved secret values) is
forwarded to the agent process; a change to any applied forwarded value
invalidates a warm/resumable session so the next launch sources the
latest env; configured `PAPERCLIP_*` entries can never override
Paperclip runtime env, while an explicitly configured
`PAPERCLIP_API_KEY` (stable per-run config) is still honored and its
rotation also busts the session.

**Steps to reproduce**

Configure an adapter with an env key (e.g. a `secret_ref` API key) and a
resumable ACP session. On resume, the updated env value is not sourced;
separately, a `PAPERCLIP_*` config key overrides the runtime value.

**Deployment mode**

Local adapters (Claude / Codex) via the shared adapter-utils execution
path.

## What Changed

- `packages/adapter-utils/src/server-utils.ts`: add
`isPaperclipRuntimeEnvKey` and, in
`refreshPaperclipWorkspaceEnvForExecution` (used by all local adapters),
skip a `PAPERCLIP_*` config key when Paperclip has already assigned it
this run; all other keys still forward.
- `packages/adapter-utils/src/acpx-engine/execute.ts`: apply the same
`PAPERCLIP_*` non-override rule (via the shared helper) when merging
config env, capture the applied config env in `resolvedAdapterEnv`, and
fold a stable `adapterEnvHash` of it into the session fingerprint so an
env change forces a fresh launch. Per-wake `PAPERCLIP_*` runtime vars
are assigned earlier and never enter that map, so they stay out of the
hash; stable configured `PAPERCLIP_*` values (e.g. an explicit
`PAPERCLIP_API_KEY`) are included so rotating one busts the session.
- Added unit/integration tests for plain + secret forwarding,
`PAPERCLIP_*` non-override, the explicit-API-key path, fingerprint
refresh-on-env-change vs. stable-across-wakes, and rotation of a
configured `PAPERCLIP_API_KEY`.

## Verification

- `npx vitest run packages/adapter-utils/src/acpx-engine/execute.test.ts
packages/adapter-utils/src/server-utils.test.ts` → 2 files, 111 tests
passing (includes the new cases).
- Tests assert: forwarded plain/secret values appear in the spawned
wrapper `.env`; a `PAPERCLIP_*` config key does not override the runtime
value; changing an applied forwarded env value (including a rotated
`PAPERCLIP_API_KEY`) changes `configFingerprint`, while a new wake with
the same config env keeps it stable.

## Risks

Low risk. Behavior change is limited to (a) config env no longer
overriding `PAPERCLIP_*` runtime vars — a security-positive tightening —
and (b) a resumable session re-launching when its applied config env
changes, which is the intended fix. Per-wake `PAPERCLIP_*` churn is
deliberately excluded from the fingerprint so normal sessions still
resume across heartbeats. Existing sessions get a new fingerprint once
on first deploy (the added `adapterEnvHash` field), which is expected.
No secret values are logged (key-name redaction in the adapter plus
manifest-driven redaction of `meta.env`).

## Model Used

Claude Opus 4.8 (Anthropic), model id `claude-opus-4-8`, extended
reasoning with tool use.

## 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
- [ ] 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-07-15 11:26:47 -05:00
DottaandPaperclip 931eec3fbf feat(mcp) [split 4/8]: wire gateway runtime and Smoke Lab (#9559)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Governed MCP access spans contracts, runtime enforcement, adapters,
UI surfaces, and operator verification
> - The parity reference PR #9534 is too large for effective automated
or human review
> - The feature therefore needs a linear stack whose individual diffs
stay below the 100-file review limit
> - This pull request is split 4/8 and focuses on gateway runtime, Smoke
Lab, plugins, and server wiring
> - The benefit is a standalone, testable review boundary while
preserving byte-for-byte parity at the top of the stack

## Linked Issues or Issue Description

- Related parity reference: #9534
- Problem: The policy core needs runtime execution, endpoint guards,
route registration, heartbeat integration, and adapter MCP injection to
become operational.
- Proposed solution: Adds the remaining server routes/wiring/consumers,
runtime tests, adapter-utils MCP contracts, and Claude/Codex injection
implementations required by the server layer.
- Alternatives considered: keeping #9534 as one 403-file review, or
rewriting the feature to manufacture seams; both were rejected in favor
of path extraction plus compile-driven boundary moves.
- Roadmap alignment: this advances the existing governed MCP/tool-access
work already represented by #9534; it does not introduce a separate
roadmap initiative.
- Stack position: base branch is `pap10341-split/03-server-tool-access`.
- Merge policy: merge bottom-up, in order, only after the complete
eight-PR stack has been reviewed and the top-of-stack parity gate
remains empty.
- Requested review: SecurityEngineer for gateway, endpoint guard, token
issuance, and runtime wiring; Greptile on every PR.

## What Changed

- Adds the remaining server routes/wiring/consumers, runtime tests,
adapter-utils MCP contracts, and Claude/Codex injection implementations
required by the server layer.
- Keeps this PR below 100 changed files and independently typecheckable.
- Preserves the final tree from #9534 when combined with the other seven
stack levels.

## Verification

- `pnpm typecheck`
- Changed server test set — 26 files, 382 tests passed
- Affected server adapter tests — 38 tests passed after concrete adapter
boundary move
- Adapter-utils and Codex focused tests — 76 tests passed

## Risks

- Remote endpoint validation, token handling, and runtime supervision
are security-sensitive and can fail closed or deny legitimate access if
misconfigured.
- Stack risk: merging out of order can expose incomplete layers;
mitigate by following the documented bottom-up merge policy.
- Parity risk: later edits to an intermediate branch can drift from
#9534; mitigate by re-running the empty top-of-stack diff before merge.

> 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

- OpenAI Codex, exact model ID `gpt-5.4`; runtime-managed context
window; medium reasoning with repository, shell, Git, GitHub CLI, and
code-execution tools enabled.

## 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] Internal references are omitted except the execution-plan link
explicitly required for this coordinated split stack
- [x] My branch name describes the change 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


## Stack Coordination

- Internal execution plan:
[PAP-13874](/PAP/issues/PAP-13874#document-plan)
- Parity reference: #9534
- Stack: #9556 → #9557 → #9558 → #9559 → #9560 → #9561 → #9562 → #9563
- Merge bottom-up only after full-stack review and an empty parity diff
at #9563.

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-14 15:07:30 -05:00
9e7e84e3fe fix(adapters): propagate ACP-lane usage and cost into spend telemetry (#9471)
## Thinking Path

> - Paperclip is the open source control plane for running and governing
AI-agent companies.
> - Adapter executions feed token usage, billing identity, and run cost
into the control plane's spend telemetry.
> - The default ACP execution lane for local Claude and Codex adapters
did not propagate per-turn usage or cost, so paid runs could be recorded
with zero spend and no tokens.
> - Claude CLI result events could also undercount output tokens by
reading only the main-loop usage block instead of the complete per-model
ledger.
> - The shared executor needs to distinguish per-run usage from
session-cumulative usage so the server does not apply the wrong delta
heuristic.
> - This pull request captures ACP usage and cumulative-cost deltas,
resolves adapter billing identity, uses Claude's complete model-usage
ledger, and preserves per-run usage in server normalization.
> - The benefit is accurate token and cost accounting across the default
paid Claude and Codex execution paths.

## Linked Issues or Issue Description

### What happened?

Paid `claude_local` and `codex_local` runs using the default ACP engine
can complete successfully while the control plane records zero or null
cost and missing token usage. Claude CLI result parsing can additionally
undercount output tokens when subagent or sidechain usage is present.

### Steps to reproduce

1. Run a paid Claude or Codex local adapter through the ACP engine.
2. Complete a turn that reports usage and cumulative cost through ACP
status/events.
3. Inspect the execution result and normalized run telemetry.

### Expected behavior

The execution result contains per-turn token usage, a per-run USD cost
delta, and the correct billing identity. Server normalization records
those per-run values without applying a session-cumulative delta a
second time.

### Actual behavior before this change

ACP execution results returned no usage and `costUsd: null` with unknown
billing. The server therefore recorded zero spend and no tokens for paid
runs. Claude CLI parsing could use an incomplete usage block.

## What Changed

- Capture ACP usage from runtime status and `usage_update` events,
reporting it as `usageBasis: per_run`.
- Convert agent-reported cumulative ACP cost into a per-turn delta,
including counter-reset and no-report safeguards.
- Add a shared billing-identity resolver and map Claude and Codex
authentication/provider modes to control-plane billing types.
- Prefer Claude result-event `modelUsage` totals so subagent and
sidechain tokens are included.
- Skip the server's session-cumulative usage delta when an adapter
explicitly reports per-run usage.
- Add regression coverage for usage capture, event fallback, cost
resets, stale reports, billing identities, model-usage totals, and
server spend normalization.

## Verification

- `pnpm exec vitest run
packages/adapter-utils/src/acpx-engine/execute.test.ts
packages/adapters/claude-local/src/server/parse.test.ts
packages/adapters/claude-local/src/server/acp.test.ts
packages/adapters/codex-local/src/server/acp.test.ts
server/src/__tests__/costs-service.test.ts
server/src/__tests__/monthly-spend-service.test.ts` — 6 files, 126 tests
passed.
- `pnpm --filter @paperclipai/adapter-utils typecheck` — passed.
- `pnpm --filter @paperclipai/adapter-claude-local typecheck` — passed.
- `pnpm --filter @paperclipai/adapter-codex-local typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- A broader Claude-local suite has a pre-existing rate-limit
classification failure in `test.probe.test.ts`; it also fails on clean
`master` and is unrelated to this change.

## Risks

- Cost reporting depends on the agent's cumulative counter semantics;
reset handling falls back to the post-turn amount and is covered by
regression tests.
- Incorrect billing-mode inference could misclassify spend;
provider/auth mappings mirror each adapter's existing CLI behavior and
have focused tests.
- The new `usageBasis` contract changes server normalization only when
adapters explicitly opt into `per_run`; existing adapters retain prior
behavior.
- No database migration, workflow, lockfile, or UI changes are included.

> 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

- Implementation commit: Anthropic Claude Fable 5, tool-enabled coding
workflow (exact context window and runtime configuration were not
recorded in the commit metadata).
- PR preparation and verification: OpenAI Codex, tool-enabled coding
agent (runtime model ID and context window are not exposed to this
session).

## 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
- [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: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-12 20:37:22 -05:00
9cde4e128c feat: run ACP sessions in sandbox execution targets (#9390)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent adapters (Claude, Codex, Gemini) default to the ACP engine
lane, which needs a live bidirectional stdio session with the agent
process
> - Sandbox execution targets only exposed one-shot command execution,
so every ACP-capable adapter refused remote targets and fell back to the
CLI lane with a "supports only the local Paperclip host" warning
> - Running agents in sandboxes is a core deployment mode, and losing
ACP there means losing streaming updates, structured events, and
default-lane parity with local runs
> - This pull request adds a provider-agnostic process-session bridge
that relays the ACP stdio session into the sandbox over the existing
sandbox runner contract, and updates the adapters to use it
> - The benefit is that the default ACP lane now behaves the same on the
local host and in any sandbox provider, with CLI fallback reserved for
targets that genuinely cannot host a bidirectional session

## Linked Issues or Issue Description

No existing public issue covers this; inline description following the
feature request template:

**Problem or motivation**

Configuring an ACP-capable adapter (e.g. Claude) with a sandbox
environment made every run fall back to the CLI lane with the warning
"Claude ACP currently supports only the local Paperclip host, but this
run targets a remote environment." The ACP engine only knew how to spawn
a local subprocess, while sandbox providers only expose one-shot command
execution — so there was no way to hold the bidirectional stdio session
ACP requires.

**Proposed solution**

Add a process-session bridge in `adapter-utils`: a local ACPX-spawnable
proxy script connects to a token-authenticated loopback TCP server,
which relays JSON-framed stdin/stdout/stderr events to and from a small
relay script executed inside the sandbox via the provider's ordinary
runner. Claude/Codex/Gemini adapters now treat sandbox targets with a
runner as ACP-capable, resolve agent commands against the remote target,
and fall back to CLI only when the sandbox exposes no bidirectional
path. The sandbox callback bridge injects a run-scoped API endpoint and
bridge token so the agent inside the sandbox can reach Paperclip
(including work-product handoffs) without ever receiving the host run
JWT.

**Alternatives considered**

A provider-specific lane was prototyped first: Daytona minting SSH
access metadata at lease time, converted into an SSH execution target.
It was dropped because it only worked for providers able to advertise
SSH, added per-provider surface area, and left every other sandbox
provider on the CLI fallback. The merged design rides the one-shot
runner contract all providers already implement; a regression test pins
that sandbox targets stay on the bridge lane even when lease metadata
advertises SSH access.

**Roadmap alignment**

Directly advances the "Cloud / Sandbox agents" roadmap item — agents
running in remote and sandboxed environments keep the same control-plane
behavior as local ones. No overlap with other planned core work.

## What Changed

- `packages/adapter-utils/src/execution-target.ts`: new
`startAdapterExecutionTargetProcessSessionBridge()` plus helpers —
writes a token-authenticated local proxy script (spawnable by ACPX) and
a remote relay script synced into the sandbox, with a loopback TCP
server streaming JSON-framed stdio between them; events emitted before
the ACP client attaches are buffered so none are lost.
- `packages/adapter-utils/src/acpx-engine/execute.ts`: the ACP engine
can execute against remote sandbox targets through the bridge instead of
requiring a local subprocess, including remote cwd/env shaping.
- `packages/adapter-utils/src/sandbox-callback-bridge.ts`:
sandbox-scoped API bridging extended to allow work-product handoffs; the
sandbox payload env carries a bridge token, never the host run JWT.
- `packages/adapters/claude-local`, `codex-local`, `gemini-local`
(`src/server/acp.ts`): default-lane selection no longer rejects all
remote targets; command resolution is remote-aware
(`ensureAdapterExecutionTargetCommandResolvable`,
`resolveAdapterExecutionTargetCwd`); the fallback reason is now scoped
to sandboxes that expose only one-shot execution.
- `server/src/__tests__/environment-execution-target.test.ts`: pins that
sandbox targets resolve to the bridge lane, including when lease
metadata advertises SSH access.
- Non-sandbox remote targets (e.g. SSH) keep the CLI lane: the ACP
engine's remote transport is sandbox-only, so default-lane selection
falls back for those targets across all three adapters, and tests
covering CLI-specific remote behavior pin `engine: "cli"` explicitly.
- The bridge authenticates loopback connections before they can own the
session or receive buffered output (token required, idle unauthenticated
peers dropped), and remote event writes are serialized so the exit event
always lands after stdout/stderr have drained.
- Daytona plugin: formatting-only residue from the earlier iteration; no
functional change.

## Verification

- `vitest run` over the touched suites —
`packages/adapter-utils/src/acpx-engine/execute.test.ts`,
`packages/adapter-utils/src/execution-target-sandbox.test.ts`,
`packages/adapter-utils/src/sandbox-callback-bridge.test.ts`, the three
adapter `acp.test.ts` files, and
`server/src/__tests__/environment-execution-target.test.ts` — 102 tests
pass.
- End to end: with a Claude agent configured on a Daytona sandbox
environment, the primary-model test now selects the default ACP lane (no
fallback warning), and the full round trip (wake → sandbox execution →
API bridge → comment post) was exercised twice from inside a live
sandbox.

## Risks

- Behavioral shift: adapters that previously always fell back to CLI on
sandbox targets now default to ACP there; `engine=cli` still pins the
CLI lane explicitly.
- The bridge relays stdio as JSON lines over loopback TCP guarded by a
per-session random token; the remote relay runs inside the sandbox under
the provider's runner. Providers with slow one-shot execution will see
higher session startup latency — the CLI fallback remains for genuinely
incapable targets.
- No schema or migration changes.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic) — extended thinking
enabled, agentic tool use via the Claude Agent SDK harness;
implementation iterated with local Vitest verification.

## 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 (no
shipped docs describe the old local-only ACP limitation)
- [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: Cody <noreply@paperclip.ing>
Co-authored-by: Cody <cody@paperclip.local>
2026-07-10 17:13:53 -07:00
Devin FoleyandPaperclip eedc7ddef2 Make ACP the default engine for local adapters (#9238)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Adapter packages are the bridge between the control plane and local
agent harnesses such as Claude Code, Codex, and Gemini CLI.
> - ACP support was concentrated in a separate `acpx_local` adapter,
which made ACP feel like a separate agent choice instead of an execution
capability of the harness adapters.
> - Claude, Codex, and Gemini now have ACP-capable harnesses, so the
native adapter should own ACP selection, fallback, config, transcript
parsing, and environment diagnostics.
> - The standalone ACPX adapter still needs a compatibility path for
existing rows, but it should not be offered as an active adapter for new
agents.
> - This pull request moves the shared ACP runtime into
`@paperclipai/acpx-engine`, wires Claude/Codex/Gemini local adapters to
prefer ACP when prerequisites are available, and retires `acpx_local` to
a tombstone.
> - The benefit is one adapter per harness, richer ACP transcripts by
default where possible, and a migration path for existing Claude/Codex
ACPX agents.

## Linked Issues or Issue Description

Closes #5932 — the broken default `acpx_local` Claude path is replaced
by native `claude_local` ACP support, existing Claude/Codex ACPX rows
migrate to native adapters, and new agents no longer choose the
standalone ACPX adapter.

Refs #4893 — original merged ACPX local adapter runtime that this PR
replaces with native per-harness ACP engines.
Refs #6590 — prior ACPX-Claude seamlessness work folded into the new
native Claude ACP path.
Refs #197 — related open generic ACP/Kiro adapter work; this PR does not
close it because Kiro/custom generic ACP remains a separate adapter
decision.
Refs #7018 — related Kimi-specific `acpx_local` shell failure; this PR
retires the built-in standalone adapter but does not add a native Kimi
adapter.
Refs #8864 — related ACPX prompt/API guidance PR; this PR moves runtime
guidance into the shared/native ACP engine path instead of the old
standalone adapter.
Refs #8881 — related `acpx_local` POSIX shell failure from the old
`acpx` pin; this PR updates ACP dependencies but does not claim
custom/OMP ACP support as a first-class native adapter.
Refs #8964 — related open `acpx_local` stderr cleanup PR; this PR makes
the old runtime path obsolete for new agents but keeps it as a
non-closing reference.

Problem description:

- The standalone `acpx_local` adapter duplicates Claude/Codex agent
choices that already have first-class local adapters.
- ACP should be an execution engine capability of each harness adapter
when the underlying harness supports ACP.
- Existing `acpx_local` agents should either migrate to native harness
adapters or fail with an explicit retirement message instead of silently
falling back to the process adapter.

## What Changed

- Added `@paperclipai/acpx-engine` as the shared ACP execution,
session-codec, CLI formatter, and UI parser package.
- Wired `claude_local`, `codex_local`, and `gemini_local` to auto-select
ACP by default when prerequisites pass, with `engine=cli` opt-out and
`engine=acp` strict mode.
- Added ACP config schema/UI fields, environment checks, session-codec
preservation, transcript parsing, and adapter capability metadata for
the native adapters.
- Retired `acpx_local` to a server tombstone, removed its
UI/package/runtime image surface, and added a migration for existing
Claude/Codex ACPX agents.
- Updated package manifests, lockfile, release tooling, docs, Kubernetes
sandbox defaults, and tests.

## Verification

- `corepack pnpm --filter @paperclipai/acpx-engine typecheck`
- `corepack pnpm --filter @paperclipai/adapter-claude-local typecheck`
- `corepack pnpm --filter @paperclipai/adapter-codex-local typecheck`
- `corepack pnpm --filter @paperclipai/adapter-gemini-local typecheck`
- `corepack pnpm --filter @paperclipai/acpx-engine exec vitest run`
- `corepack pnpm --filter @paperclipai/adapter-claude-local exec vitest
run src/server/acp.test.ts src/server/execute.acp-fallback.test.ts
src/ui/build-config.test.ts`
- `corepack pnpm --filter @paperclipai/adapter-codex-local exec vitest
run src/server/acp.test.ts src/ui/build-config.test.ts`
- `corepack pnpm --filter @paperclipai/adapter-gemini-local exec vitest
run src/server/acp.test.ts src/ui/build-config.test.ts
src/ui/parse-stdout.test.ts`
- `corepack pnpm --filter @paperclipai/plugin-sdk ensure-build-deps &&
corepack pnpm --filter @paperclipai/server exec tsc --noEmit`
- `corepack pnpm --filter @paperclipai/server exec vitest run
src/__tests__/adapter-routes.test.ts
src/__tests__/adapter-session-codecs.test.ts
src/__tests__/adapter-models.test.ts`
- `corepack pnpm --filter @paperclipai/ui typecheck`
- `corepack pnpm --filter @paperclipai/ui exec vitest run
src/adapters/metadata.test.ts
src/adapters/adapter-display-registry.test.ts
src/components/AgentConfigForm.test.ts
src/components/AgentConfigForm.render.test.tsx
src/components/transcript/RunTranscriptView.test.tsx`
- `node --test scripts/bootstrap-npm-package.test.mjs
scripts/release-package-map.test.mjs
scripts/verify-release-registry-state.test.mjs`

Note: the server typecheck script calls `pnpm` internally; this dev
shell exposes pnpm through Corepack only, so I ran the two script steps
manually with `corepack pnpm`.

## Risks

- Migration changes existing `acpx_local` Claude/Codex agents to native
adapter types and clears old ACPX task sessions/runtime state.
- Custom ACP commands remain on the retired tombstone and will need a
separate future adapter/plugin path.
- ACP auto-selection depends on local Node and ACP server command
prerequisites; remote and unsupported environments fall back to CLI
unless `engine=acp` is explicit.
- `@paperclipai/acpx-engine` is a new public package and needs npm
trusted-publishing bootstrap before release automation can publish it.

> 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

OpenAI GPT-5 via Codex coding agent. Exact hosted model build and
context-window size are not exposed in this runtime. Tool use included
shell execution, repository editing, GitHub CLI operations, and local
test/typecheck 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
- [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-07-08 19:05:03 -07:00