489 Commits
Author SHA1 Message Date
5a92d53f7d fix(adapter-utils): handle closed child stdin safely (#14057)
Handle closed child stdin without losing the child result.

Keep each child stdin error handler scoped to that child. Expected EPIPE
and stream-destroyed failures no longer crash the host. The child close
event remains the source of its exit result.

Verification: 2,024 tests passed and 12 live/platform cases skipped across
87 complete adapter-utils suites. The final 140-test server-utils suite
passed. The regression reproduces the EPIPE crash on unchanged master.

Final qualification: all applicable CI passed and exact-head Greptile Apex
scored 5/5 with no unresolved findings.

Co-Authored-By: Grzegorz Golaś <[email protected]>
Co-Authored-By: Devin Foley <[email protected]>
Co-Authored-By: Paperclip <[email protected]>
2026-11-07 03:01:03 -08:00
DottaandClaude Opus 5.5 c45c7cb854 fix(workspaces): ignore killed-transfer scratch in sparse checkpoints (#19730)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A native run in a remote sandbox copies its workspace back to the
host after the turn. A sparse checkpoint copies only changed entries,
and the host validates each entry against the sandbox's own manifest
> - Every Daytona transfer stages a `.paperclip-upload-<uuid>` scratch
file at the sandbox workspace root and removes it only in the same
process
> - A control-plane kill during any transfer leaves that scratch behind.
The sparse checkpoint then recorded it as a workspace file, but
providers never export it. Every later copyback and every saved-work
recovery attempt failed with "Workspace checkpoint payload mismatch"
> - The failed recovery then left an accepted Retry showing "Paperclip
is finishing the saved result" with no run
> - This pull request stops recording transfer scratch, makes the
checkpoint export an exact copy, names the failing entry, and gives a
Retry behind a spent recovery an honest reason and a working action
> - The benefit is that a restart during a transfer no longer makes a
task's saved work permanently unrecoverable

## Linked Issues or Issue Description

Refs #19721 (RC-50: the killed copyback owner; this PR covers the
separate RC-50b cause)

**What happened?**

Restart-chaos waves s12/t01 and s14/t05 (Claude on Daytona). After a
SIGKILL of the control plane, the native result was accepted, and then
workspace copyback failed with "Workspace checkpoint payload mismatch".
In s14 all 6 saved-work recovery attempts failed the same way. A Retry
accepted during recovery kept `retryWait.reason =
finalizing_saved_result` after the recovery budget was spent. No run was
created.

**Steps to reproduce**

1. Start a native Claude run in a Daytona sandbox.
2. SIGKILL the control plane while any provider transfer is in flight.
3. Let the run finish its turn and copy its workspace back.

**Expected behavior**

Copyback succeeds. Provider scratch is not workspace content. A Retry
behind a spent recovery names the failure and can start a new recovery.

**Actual behavior**

The s14 sandbox (6f4a0b6a) still held
`/home/daytona/paperclip-workspace/.paperclip-upload-d99fe36b-5acc-437b-96ad-85d5ff9e79c4.tar`.
Its mtime was 03:29:23, the second of the K4 SIGKILL. The Daytona
provider stages transfer scratch at `remoteDir`, which is the lease
`remoteCwd` (the workspace root). The runtime workspace wipe
deliberately preserves `.paperclip-upload-*`. The checkpoint helper
(`workspace-checkpoint.ts`, `checkpointMain`) recorded the stale tar.
The ordinary export excludes `.paperclip-upload*`, so the host received
nothing for that entry. `validateCheckpointPayload` then threw "payload
mismatch" on every attempt. The file-mode hypothesis was disproved: the
sandbox umask is 022 and the scratch is mode 644.

## What Changed

- `packages/adapter-utils/src/exclude-patterns.ts`: new
`isSandboxTransferScratchPath`, which matches a root-level
`.paperclip-upload-*` entry. This agrees with the wipe's preserve rule
in `buildWorkspaceTarExtractCommand`.
- `packages/adapter-utils/src/workspace-checkpoint.ts`: the sandbox
checkpoint helper skips that scratch. A mismatch error now names the
entry and its recorded, baseline and received forms (kind, octal mode,
short hash; never contents).
- `packages/adapter-utils/src/sandbox-managed-runtime.ts`: the
checkpoint export requests `preserveSourceEntries: true`. The checkpoint
output is an exact copy that the host validates entry by entry. An
ordinary export applies the host umask and drops `._*` and
`.paperclip-upload*` names at any depth. That gives the same permanent
mismatch for a legitimate user file such as `._summary`,
`notes/.paperclip-upload-plan.md`, or a `0775` script.
- `server/src/services/explicit-native-continuation.ts` and
`task-interruption-presentation.ts`: when the run's saved-work recovery
is exhausted, a waiting Retry reports `saved_work_recovery_failed` with
Retry available, instead of `finalizing_saved_result`. The existing task
Retry path starts one new recovery generation.
- `doc/execution-semantics.md`: a short additive note.

## Verification

- `packages/adapter-utils/src/workspace-checkpoint-capture.test.ts` uses
the real checkpoint helper and an export that behaves like Daytona's
ordinary export:
- Before the fix: `Workspace checkpoint payload mismatch at
".paperclip-upload-d99fe36b-….tar": sandbox recorded file mode=644 …;
baseline absent; received absent`. That is the wave error.
  - After the fix: the scratch is not recorded, and validation passes.
- A second case shows that reserved-looking user names and `0775` modes
need the exact export.
- `packages/adapter-utils/src/workspace-checkpoint.test.ts` is an
end-to-end `prepareSandboxManagedRuntime` restore through a provider
model. In that model, ordinary exports drop reserved names and apply the
umask, and preserve exports copy exactly. Two red checks:
- Without `preserveSourceEntries`, it fails with the mismatch on
`._summary`.
- Without the scratch skip, the stale tar leaks into the host workspace.
- Green: the agent file, the `._summary` file and the `0775` mode
arrive, and no scratch arrives.
- `server/src/services/workspace-recovery-preparation-gates.test.ts`,
"names a spent saved-work recovery behind an accepted Retry, and the
next Retry finishes it" (s14 shape):
  - Red: `finalizing_saved_result` after 6 failed attempts.
- Green: `saved_work_recovery_failed` with `canRetry: true`. The next
Retry starts a new generation, recovery completes, and the Retry
continues in exactly one new run.
- Suites: adapter-utils checkpoint, capture, sandbox-managed-runtime,
seed-reuse and managed-seed (168 passed). Server preparation-gates (47),
explicit-native-continuation and task-interruption-presentation (455),
workspace-recovery admission and retry-copyback, and
native-workspace-finalizer. `tsc --noEmit` is clean for adapter-utils
and server.
- Live evidence: I started the stopped s14 sandbox read-only, listed its
workspace, and stopped it again. No Daytona replay was run. The runner
content ID of this checkout matches no built image.

## Risks

- A user file named `.paperclip-upload-*` directly at the workspace root
is no longer captured by a sparse checkpoint. Providers already drop
that name from every ordinary export, and the runtime already reserves
it at the root.
- The checkpoint export now uses the provider's exact-copy mode. A
workspace file with setuid, setgid or sticky bits is refused by that
mode, as it is for the existing exact-copy transfers. The ordinary
export would have stripped those bits and failed validation anyway.
- The stale scratch tar stays in the sandbox. It is ignored, not
deleted, because a concurrent transfer can own a file with the same
prefix.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, through Claude Code
with tool use (shell, file edits, test runs, embedded Postgres, a
read-only Daytona sandbox listing).

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

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

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-06 22:46:16 -06:00
DottaandClaude Opus 5.5 f09b4704b6 fix(recovery): keep the agent's work when Retry continues after exhausted or blocked recovery (#19666)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Task recovery restarts agent work after a control-plane restart or a
failed run
> - When automatic recovery runs out or is blocked, the user clicks
Retry
> - Retry then started a fresh isolated attempt cloned from committed
HEAD, so the agent lost its uncommitted files, its task branch and the
worktree Git link
> - A restart must never lose the agent's work
> - This pull request makes Retry keep the task's own workspace when
that is safe, and tells the agent where the old files are when a fresh
attempt is still required
> - The benefit is that a Retry continues the real work instead of
starting over

## Linked Issues or Issue Description

**What happened?**
After `native_restart_recovery_blocked` or an exhausted recovery
generation, a Retry ran in
`isolated-workspace-attempts/<company>/<issue>/<run>/workspace`. That
directory was a detached clone of the base ref. It did not contain the
uncommitted edits from the issue's git worktree. It was not on the task
branch. The agent was not told where the old files were. A restart-chaos
wave graded this as an R2 failure (`pre_fault_files_lost`). A second
defect: when a restart hit the bootstrap of a fresh attempt, preparing
the same run again failed with
`isolated_task_directory_binding_mismatch`, so the Retry could not
finish.

**Expected behavior**
Retry continues in the task's own workspace with its edits and `.git`
intact when no other writer can hold it. When a fresh attempt is really
needed, the old directory stays as it was and the agent gets its path
and its changed files. Preparing the same attempt again after a restart
is idempotent.

**Paperclip version or commit**
`master` at `e482f0f632`, restart-chaos waves on 2026-11-06.

**Steps to reproduce**
1. Start a native task in a git worktree. Let the agent write an
uncommitted file.
2. Kill the control plane so recovery ends in
`native_restart_recovery_blocked` or an exhausted generation.
3. Click Retry.
4. The successor runs in a fresh clone without the uncommitted file or
the task branch.

Root cause:
- `server/src/services/fresh-workspace-continuation.ts`
`canSelectFreshWorkspace` returns a fresh intent for every task Retry
that has a system recovery record.
- `server/src/services/explicit-native-continuation.ts` (fresh selection
in the source loop) then always persisted `fresh_isolated_workspace`.
- `server/src/services/fresh-workspace-seed.ts`
`materializeFreshWorkspaceSeed` clones the project source at the base
ref with `--no-local`, checks out a detached commit and removes
`origin`. Uncommitted work and the branch are not carried over.
- RC-35: `server/src/services/heartbeat/workspace-preparation.ts`
rejected any reusable existing workspace while `fresh` was true,
including the attempt directory that the same run had already created
and bound.

## What Changed

- New continuation mode `reuse_issue_workspace`. At Retry admission,
after all stop proofs pass, `retainedIssueWorkspaceForRetry` checks that
the task's bound workspace is the single local directory the failed run
used, owned by the task, not a shared workspace, not under remote
storage authority, with no other lease, failed cleanup or running
workspace operation, a settled recovery generation, no workspace-export
failure, and a readable Git state rooted at that directory. Then the
Retry continues there with a fresh session. Otherwise the existing fresh
attempt is kept.
- The reuse decision consumes the generation's one alternative attempt
(`freshAttemptRunId`), sets the Retry strategy to `continue`, and
records `issue.retry_workspace_reused`.
- Environment selection pins the reused workspace binding and fails with
`retry_workspace_binding_mismatch` if the binding changed before
dispatch.
- Fresh fallback: the run's continuation context gets `priorWorkspace`
(path, branch, bounded list of uncommitted files). Git status is read
with optional locks disabled. The prompt tells the agent to inspect it
and carry over the work. The old directory is not changed.
- RC-35: preparing the same fresh attempt again after a restart reuses
the attempt directory that this run already bound.
- Reuse checks run Git through the shared workspace scan scheduler with
short deadlines, because admission can hold the issue lock.
- `doc/task-recovery.md` and `doc/execution-semantics.md` describe the
new contract.

## Verification

- `server/src/services/retry-workspace-continuity.test.ts` (embedded
Postgres, real git worktree with uncommitted edits):
- exhausted-generation Retry and blocked-recovery Retry continue in the
same worktree, `.git` file and branch intact. Both failed before the fix
(readiness was `fresh_isolated_workspace`).
- Blocked recovery without a generation and duplicate holds for one run
also reuse the worktree. A running operation orphaned by a restart of
the stopped source does not block reuse.
- The fresh attempt is kept when the workspace is unsafe: live lease,
running workspace operation, corrupt `.git`, missing directory, remote
authority, shared workspace, running export generation. `priorWorkspace`
is omitted for the unsafe writer and shared cases.
- `workspace-preparation.test.ts`:
- A `reuse_issue_workspace` Retry prepares in the original worktree with
its uncommitted file and branch.
- RC-35 red/green: re-preparing the same fresh attempt reuses its bound
directory and keeps files written before the restart. Another run still
gets `isolated_task_directory_binding_mismatch`.
- The fresh fallback sets `priorWorkspace` with the old path and
`unpublished.txt`.
- `environment-selection.test.ts`: the new mode pins the binding and
rejects a changed binding or cwd.
- `packages/adapter-utils/src/server-utils.test.ts`: the prompt names
the retained directory and its files.
- Regression files run locally: `workspace-preparation`,
`environment-selection`, `task-retry`, `quarantined-task-retry`,
`explicit-native-continuation`, `workspace-recovery`.
- Restart-chaos (`tests/restart-chaos`, local, B1+B5, codex+claude,
K1/K4/K6, concurrency 2, rc06-masked):
- Final head: wave `w-20261107T014633-s7206` on `c6776283f3`: 8/8 PASS,
all 8 restart-crossing. 5 tasks needed a Retry (t03-t07: codex B1 K6,
codex B1 K1, codex B5 K6, codex B5 K1, claude B5 K6). All five finished
in the original git worktree with `.git` and every pre-fault file
present (`workspaceMovedTo` empty, `lostFiles` empty); no "Retry keeps a
fresh attempt" log line.
- Wave `w-20261107T004829-s7205` on `29373a6b6a`: 8/8 PASS, all 8
restart-crossing. 3 tasks needed a Retry (t02, t06, t07). All three
finished in the original git worktree with `.git` and every pre-fault
file present; no "Retry keeps a fresh attempt" log line. (`cc178492ef`
only makes the operation filter stricter for run-less jobs.)
- Wave `w-20261107T000412-s7204` on `d197082107`: 8/8 PASS, 7
restart-crossing. 3 tasks needed a Retry (t02, t04, t06). All three
finished in the original git worktree (`workspaceMovedTo` is empty), R2
passed with `.git` and every pre-fault file present, and no "Retry keeps
a fresh attempt" log line appeared.
- Wave `w-20261106T231416-s7203` on the first commit: 6/6 PASS, but its
t01 Retry still moved to `isolated-workspace-attempts`. That instance
was not kept, so the exact rejection was not recorded. The likely cause
is a `running` workspace operation that a killed server left on the
stopped source (the source restarted three times). The second commit
treats such restart orphans as not live and logs the rejection reason. A
`--replay` of that t01 on `d197082107` finished in the original worktree
with its `.git` file and no attempt directory.
- Review follow-ups in `d197082107`, `29373a6b6a`, `cc178492ef` and
`c6776283f3` (run-less workspace jobs still block reuse; red/green
covered by the `running_operation` case): holds are grouped by source
run; the readiness fallback (blocked recovery without a generation) also
reuses the task workspace; Git probes go through the shared scan
scheduler with 10-15 s deadlines; `priorWorkspace` is only set for a
task-owned directory with no other writer; the reuse rejection reason is
logged; `priorWorkspace` uses the same task-owned, settled,
single-writer predicate as reuse; Git probes share one budget per
workspace; the RC-35 check is a pure path check; renames are parsed from
either porcelain column.

## Risks

- A Retry now writes into the old directory instead of a new one. The
checks above prevent a second writer. A stale binding fails the run with
`retry_workspace_binding_mismatch`; it does not write to another
directory.
- If the cause of the failure was inside the workspace itself, reuse
could repeat it. Workspace-export failures, restore failures and
required workspace recovery keep the fresh attempt.
- A workspace operation of the stopped source that started before the
source settled and before the current server process no longer blocks
reuse (a restart orphan). Operations with no run, of another run, or
started in the current process still block it.
- `priorWorkspace` is an optional new field in the continuation
envelope. Older adapters ignore it.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used through Claude
Code with tool use (shell, file edits, test runs).

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-06 20:27:22 -06:00
DottaandClaude Opus 5.5 02a2299173 feat(codex): default Codex to gpt-6.1-sol, half the gpt-5.6-sol price (#19623)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Codex agents run through Paperclip Runner or the legacy
`codex_local` harness, and the runner E2E suites run paid Codex cells
> - The shared Codex default and many selections preferred `gpt-5.6-sol`
> - The reviewed OpenAI rate card in
`server/src/services/codex-pricing.ts` (`openai-standard-2026-09-30`)
prices `gpt-6.1-sol` at $2 input / $0.10 cached / $10 output per 1M
tokens, and `gpt-5.6-sol` at $4 / $0.40 / $20
> - So every default run and every paid Codex eval cell cost twice as
much as necessary
> - This pull request moves every place that prefers or selects
`gpt-5.6-sol` to `gpt-6.1-sol`, and keeps historical facts, rate cards
and compatibility fallbacks on `gpt-5.6-sol`
> - The benefit is half the token price for new default Codex agents and
for the Codex E2E and TestOps cells

## Linked Issues or Issue Description

**What existing behavior does this improve?**
The default Codex model for new agents and the Codex model that the
runner E2E suites and TestOps plans select.

**Subsystem affected**
Adapters (`adapter-utils`, `codex-local`), server native runtime
fallback, UI AI connection pools, Paperclip Runner devtools and live
evals, runner E2E catalog and TestOps mappings.

**Current behavior**
New Codex agents without an explicit model store `gpt-5.6-sol`. The
runner E2E `runner-codex` profile, the Reliability `queue-steer` plan
cell, the Product regression TestOps mapping and the ACPX Codex
qualification selection all use `gpt-5.6-sol`.

**Proposed behavior**
They use `gpt-6.1-sol`. Existing agents that store a model keep it.
`gpt-5.6-sol` stays selectable.

**Reason and benefit**
`gpt-6.1-sol` costs half as much per token on the in-repo reviewed rate
card. The current OpenAI pricing page (retrieved 2026-11-06) shows the
same short-context rates and also lists long-context rates of $4 / $0.20
/ $15 for `gpt-6.1-sol` against $8 / $0.80 / $30 for `gpt-5.6-sol`.

**Breaking changes**
None for stored configurations. See Risks.

## What Changed

Changed defaults and selections (all now `gpt-6.1-sol`):

- `PAPERCLIP_RUNNER_DEFAULT_MODELS.codex` in
`packages/adapter-utils/src/paperclip-runner-permissions.ts`. This also
changes `DEFAULT_CODEX_LOCAL_MODEL`,
`resolvePaperclipRunnerModel("codex", …)`, runner config normalization,
`codex_local` → Paperclip Runner conversion, the UI new-agent and
agent-config defaults, the composer default model and the public MCP
effort lookup.
- `codex-local` `models`: the 5.6 entry is now the literal
`gpt-5.6-sol`, because it used to be `DEFAULT_CODEX_LOCAL_MODEL`. The
list order and contents do not change.
- AI connection pool: a new OpenAI member profile uses
`PAPERCLIP_RUNNER_DEFAULT_MODELS.codex` instead of a hard-coded
`gpt-5.6-sol`.
- Paperclip Runner devtools ACPX Codex default and preset, the
`runner-codex-sol` local provider smoke, and the `acpx-codex-sol` live
workflow candidate.
- `tests/acpx-qualification-models.ts` Codex selection (runner E2E and
runner acceptance ACPX Codex profiles).
- Runner E2E: the `runner-codex` profile follows the default, so all
native Codex cells move. This includes the `runner-codex` cells among
the 52 standard `everyday-workflows` cells and the seven Codex
reliability cells. `runner-codex-mini` stays on `gpt-5.4-mini`. The
Reliability plan `queue-steer` `steeringProfile` is now
`['runner-codex', 'gpt-6.1-sol']`. Its executionId
(`everyday-workflows.runner-codex.daytona.queue-steer`), its fullName
and its TestOps case identity do not change. No separate profile is
necessary.
- `product-regression-testops.mjs`: the declared model for every
`runner-codex` case is now `gpt-6.1-sol` (it was already that for
`configured-repository`).
- `tests/runner-e2e/installed-cli.ts`: the credential-free installed
startup proof opens a session on the default model.
- `tests/runner-e2e/scheduled-prices.json`: add a `gpt-6.1-sol` price
(`openai-standard-2026-11-06`, including 30m/12h cache writes and
long-context rates from the OpenAI pricing page). The weekly scheduled
Codex cells need an exact price for their model, or admission fails
closed.
- Storybook: the composer model picker fixtures, the connection pool
stories and host, and the agent settings preview show `gpt-6.1-sol` as
the default.
- Docs: `doc/DEVELOPING.md` (the Codex conversion default) and
`tests/runner-e2e/EVERYDAY-WORKFLOWS.md` (local matrix, reliability
extras, `queue-steer` row).
- Tests that assert the default now expect `gpt-6.1-sol`. New
assertions: the default has the `0.159.0` CLI floor, the compatibility
fallback is floor-free, the `queue-steer` plan cell runs `runner-codex`
on `gpt-6.1-sol`, and all native Codex everyday cells (standard and
reliability) use `gpt-6.1-sol`.

Deliberately kept on `gpt-5.6-sol`:

- Rate cards: `server/src/services/codex-pricing.ts` and
`packages/paperclip-runner/src/evals/model-pricing.ts`. Old receipts
need them. (The runner eval rate card has no `gpt-6.1-sol` entry. An
unpriced candidate is reported as unpriced, not as zero. I did not add a
rate there because that card has its own version and source.)
- The remote Codex CLI compatibility fallback. A new
`CODEX_LOCAL_COMPATIBILITY_FALLBACK_MODEL = "gpt-5.6-sol"` replaces the
default in `codexModelFallbackCandidates`. `gpt-6.1-sol` needs Codex
0.159.0 or newer, so it cannot be the floor-free last resort. Fallback
behavior is unchanged.
- `tests/runner-e2e/provider-usage.ts`: the implicit request
cache-policy attribution stays qualified only for `gpt-5.6-sol`. I did
not verify that policy for `gpt-6.1-sol`. A `gpt-6.1-sol` response that
omits the write-TTL split keeps its cache-write cost unknown, which is
the existing behavior for unqualified models.
- `tests/runner-e2e/scheduled-measurements.json`: historical
`gpt-5.6-sol` measurements. They no longer match the current suite
definitions, so the forecast reports them as historical only. The
forecast tests now build their synthetic cohorts on the current
definition hash.
- Model catalogs that list `gpt-5.6-sol` as available (`codex-local`,
`cursor-local`, `opencode-local`, Speko voice diagnostics, Pi picker
fixture).
- The `gpt-5.6` → `gpt-5.6-sol` alias for old stored configs.
- Dated or evidence docs: `doc/plans/*`, `doc/adapter-model-audit-*`,
`QUALIFICATION-*`, `doc/connections/MEMORY.md`,
`doc/connections/TASK-REVIEWS-VERIFICATION.md`,
`tests/runner-e2e/CHAT-FEATURES.md`, the Slack eval evidence, and the
S49 soak signature text (the soak failure happened on `gpt-5.6-sol`).
- Test fixtures that parse, price, migrate or round-trip an existing
`gpt-5.6-sol` value (Rust runner tests, driver tests, pricing tests,
stored-config tests, stories that show an existing agent).

## Verification

- `npx vitest run` on every server, UI, adapter-utils and codex-local
test file that mentions `gpt-5.6-sol` or the default, plus all of
`packages/adapter-utils` and `packages/adapters/codex-local`: 5,483
passed. Three timeouts under load (`sandbox-managed-runtime`,
`acp.quota`) passed when rerun.
- `packages/paperclip-runner`: `vitest run` for `src/eval`, `src/evals`,
`src/drivers/acpx`, `src/drivers/codex`, `src/cli`, `src/contracts`,
`src/backends`: 2,641 passed.
- `node --test tests/runner-e2e/reliability-testops.test.mjs
tests/runner-e2e/product-regression-testops.test.mjs`: 28 passed.
- `node tests/runner-e2e/run-unit.mjs`: all model, catalog, reliability,
scheduled and TestOps tests pass. Failures remain only in
`copilot-protection-flow`, `denied-target-isolation-flow`,
`pi-controls-flow`, `workflow-target` and `unit-launcher`. These five
also fail on unmodified master in this local environment (filesystem
watch and git environment), and they do not touch models.
- `vitest run --config tests/runner-acceptance/vitest.config.ts`: 19
passed.
- `pnpm test:e2e:runner -- --list --suite everyday-workflows --group
reliability`: the seven `everyday-workflows.runner-codex.daytona.<case>`
cells show `gpt-6.1-sol` with `OPENAI_API_KEY,DAYTONA_API_KEY`.
`--matrix-json` gives 16 reliability cells with the same credentials as
before. `--list --all` shows no `gpt-5.6-sol` cell.
- Typecheck: `adapter-utils`, `adapter-codex-local`, `server`, `ui`,
`paperclip-runner` (TypeScript), and `pnpm test:e2e:runner:typecheck`.
- `pnpm check:token-gates`: clean.
- Not run: paid cells, Rust tests (no Rust change), the browser suites,
and the full `pnpm -r typecheck` / `pnpm build` (CI runs them).

## Risks

- Agents without an explicit model change model on their next save or
normalization, and Paperclip Runner rows with a blank model use
`gpt-6.1-sol` at execution. Stored models, including `gpt-5.6-sol`, are
never rewritten. No migration is necessary.
- `gpt-6.1-sol` needs Codex CLI 0.159.0 or newer with ChatGPT sign-in.
The runner pins `@openai/[email protected].0`. On a remote target with an
older image CLI, the existing compatibility check falls back to
`gpt-6-sol` and then `gpt-5.6-sol`, and that probe now runs for
default-model agents too. A legacy `codex_local` agent created with the
new default on a host with an older Codex CLI gets the existing
actionable "upgrade Codex" Test result.
- Qualification: the native Codex profile uses `adapter_constant` model
qualification, so it follows the default. No qualification gate is
dropped. The ACPX Codex qualification selection moves to `gpt-6.1-sol`.
Its next qualification run uses the new model. The provider-usage
cache-policy attribution stays limited to `gpt-5.6-sol`.
- Runner E2E history: the Codex cells change their suite definition
hashes, so earlier `gpt-5.6-sol` results are a separate cohort and do
not count as current.
- The `gpt-6.1-sol` scheduled price uses the file's existing `expiresAt`
(2026-11-19). It needs the same review as the other entries before then.
- No `.github/` change. The Reliability queue-steer cell keeps the
`runner-codex` profile id, so the existing workflow credential and setup
steps still match.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- Claude Opus 5.5 (Anthropic), model ID `claude-opus-5-5`, running in
Claude Code with tool use (shell, file edits, web fetch of the OpenAI
pricing page).

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

Related: #19604 (commits the Reliability TestOps identities; this PR
changes only `steeringProfile` in the same file).

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

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-06 15:35:34 -06:00
DottaandPaperclip c6573962f8 fix(workspaces): recover full sandboxes through read-only export (#19465)
## Thinking Path

> - Paperclip preserves agent work across failed runs and server
restarts.
> - A remote workspace must have a durable save before its source can be
released.
> - Portable capture normally writes a Git mirror and a full archive
inside the sandbox.
> - A full sandbox can prevent capture before the provider's streaming
download starts.
> - Retrying that write cannot create the required free space.
> - This change transfers a checked standalone source and seals its
archive on the controller.
> - The source remains retained unless the existing durable save
completes.

## Linked Issues or Issue Description

Related: #18644 reduced temporary capture space. #18172 added the
provider's disk-free directory transfer. This repair connects portable
capture to that transfer when the archive itself cannot be created.

**What happened?**

Workspace capture returns `remote_workspace_sandbox_disk_full` after
provider work has finished. File recovery repeats the same sandbox
write.

**Expected behavior**

Recover a stable standalone workspace without requiring another full
archive on the source disk. Preserve staged changes, untracked work,
confined links and the original source.

**Steps to reproduce**

1. Create a standalone Git repository with a commit, staged changes and
an untracked result.
2. Use the existing real-command transport fixture. Inject ENOSPC into
the sandbox helper write or archive write.
3. Run portable capture. Before this change it rejects the helper
failure.
4. With the change, restore the saved archive after removing the fixture
source. Check the commit, staged content, untracked result and Git
connectivity.

## What Changed

- Attempt one controller-side capture after an exact sandbox disk-full
failure.
- Read a source inventory without installing a helper. Use the existing
provider bulk transfer with confined links and runtime exclusions.
- Compare the transferred inventory with source fingerprints before and
after sealing.
- Reject copied Git pointers, alternates and administrative symlinks.
Parse config as data without includes; allow only portable keys before
controller Git normalization.
- Preserve socket exclusions. Retain the source and remove incomplete
controller staging after failure.
- Update execution semantics and focused regression coverage. No
database migration or new execution authority.

## Verification

- TDD red on base `2f35eb1080`: the new full-sandbox test failed with
`remote_workspace_sandbox_disk_full` before production edits.
- Green: 202 transport and archive tests passed before the additional
Git configuration guards. At head `8541086cb5`, all 116 transport tests
and the PostgreSQL durability acceptance pass. The final `e24e173ec9`
exclusion guard passes those 116 cases plus its new Git-history
regression across the full run and focused rerun. Environment: macOS
arm64, Node 26.4.0; real Node/Git commands and filesystem transfer.
- Review regressions were also red before their fixes: an internal Git
config symlink bypassed the check, and `core.attributesFile` could
select a controller path. Git administrative symlinks and nonportable
config now fail before normalization. The focused safety selection
passes 11 cases.
- Cases cover helper and archive ENOSPC, commit/staged/untracked
continuity after source removal, confined links, sockets, changed
source, corrupt or extra copied files, unsafe Git metadata, retained
diagnostic text and failed download.
- Real PostgreSQL acceptance passes: normal workspace save recovers one
sandbox ENOSPC, commits the encrypted snapshot, clears its writer, loses
the original allocation, then independently reloads the saved result
twice. The first acceptance attempt used an incorrect expected status
(`available`); the documented state is `committed`. No production change
was needed for that correction.
- Review regressions for untracked Python/Node environments and
excessive Git roots failed before their fixes. Dependency exclusions now
match normal capture. Controller parsing starts only after
whole-tree/repository budgets and layout checks; refspec/settings
budgets and a shared two-minute verification/normalization deadline
bound further work. The excessive-root test verifies zero Git
subprocesses.
- Real provider-boundary red/green: Daytona direct tar and
capacity-fallback PTY transfers first dropped `._result.txt`. The new
optional mapping contract preserves all user names and excludes only
current transfer scratch. Kubernetes implements the same contract.
Explicit caller exclusions and archive validation remain in force.
- Standalone Linux qualification on Node 24.22.2/Debian bookworm: fresh
shared/SDK build; Daytona and Kubernetes typecheck and build; Daytona
file-sync/capacity/diagnostics 68/68 pass; Kubernetes file-sync 31/31
pass, including the actual `/proc` case. Qualification was refreshed
again on the final `be6f7feacc` source tree: current shared/SDK rebuilt;
all 815 staged source/config/manifest hashes match the delivered tree
before and after testing. An initial container run as root bypassed a
read-only file test; the same source passes as uid 1000. Installing zstd
enabled eight initially skipped cases.
- A further review regression proved `.git/pyvenv.cfg` could exclude Git
history as a dependency. The inventory now handles `.git` before
dependency filtering, matching normal capture. The full capture/restore
test verifies the original commit and saved work. The first green
attempt incorrectly compared command-result object identity; correcting
the test to compare stdout passed with no production change.
- Further red/green regressions cover read-only private-copy cleanup, a
regular file replaced by a FIFO before open, case aliases of Git
administrative metadata, and permission preservation under restrictive
umask. Nonblocking no-follow file handles verify identity before and
after reading. Cleanup changes only private controller directories.
Provider extraction preserves ordinary permissions, refuses special mode
bits before writing, and does not adopt source ownership, ACLs,
attributes or flags. Final macOS Daytona tests pass 65/65; the final
Linux qualification above was repeated on these exact provider sources.
- Focused strict TypeScript compilation of the inventory and archive
modules passes.
- The first sandboxed archive run failed three Unix socket fixtures with
EPERM. The permitted run outside that filesystem sandbox passed all
tests.
- Initial full local server typecheck could not resolve the unbuilt
Runner package and produced cascading errors. Exact-head `e24e173ec9` CI
typecheck, build, Runner and browser checks pass. That CI attempt failed
two server shards: the unchanged retained-cleanup backlog test timed out
at 15 seconds and passes locally unchanged; ten cache-reclaim fixture
oracles failed because directory fallback consumed their sealed-archive
counters/faults. Final commit `caed4e4f83` isolates the intended
unavailable-directory boundary while preserving archive counts, recovery
status, integrity and no-second-reclamation assertions. Those ten
failures reproduced locally before the fixture edit. The full affected
file passes 394/395; its sole other HTTP socket-hang-up case passes
unchanged in an isolated rerun. This is combined coverage of all 395
cases, not a single complete green run. The final branch integrates
master `53645a0104` at `98ea07225592930cbb0d18bcf250bc64f6bb13b4`,
preserving upstream workspace phase timings. After integration: 117
transport cases, the PostgreSQL durability acceptance, the existing
object-storage/timing regression, and 12 affected cache cases pass. The
expanded recovery file now has 421 cases. Final-head
`be6f7feacc45c1e32a26a2417e973f1e3752fd8d` CI passes all 55 applicable
checks (three skipped, one neutral review), including the expanded
recovery suite, typecheck, builds and Runner coverage. [CI
result](https://github.com/paperclipai/paperclip/actions/runs/42251345554).
- Provider exclusion regressions were red through four real Linux
transfer paths: Daytona direct archive, direct safe-link repair,
capacity PTY repair, and Kubernetes streaming. A root socket and nested
regular file share a basename; literal glob-character files/subtrees
also survive. Preserve mode now anchors tar exclusions as literal
root-relative paths and escapes `find -path` pruning. Default callers
keep glob semantics. Receipt-count regressions were also red before
correcting preserved-output counting. All 31 Kubernetes and 68 Daytona
tests, builds and typechecks pass on the final source with 815 matching
inputs.
- The final semantics documentation explicitly records special mode
bits, the stricter config allowlist (including innocuous unlisted keys),
and provider qualification limited to Daytona/Kubernetes; these address
the optional current-head review observations without broadening
controller Git authority.
- No Daytona credential is available in this session. Provider transfer
is a local filesystem boundary in these tests. Hosted task recovery and
rollout remain unverified.

## Risks

- Recovery reads the source repeatedly and transfers the standalone
tree. It can take longer than normal capture.
- Linked worktrees, initialized submodules, Git administrative symlinks,
nonportable Git configuration, special mode bits and alternate object
stores remain retained if the normal capture cannot finish. This
fallback does not follow their pointers onto the controller.
- A provider without usable directory export, a changed source, or
insufficient controller space cannot produce a receipt. The original
source stays retained.
- The server, SDK and affected provider plugins must be built from this
revision together. An older provider can fail inventory verification on
names it omits; it cannot publish an incomplete save. No merge,
publication or deployment is included.

## Model Used

OpenAI GPT-6 family through Codex, with reasoning, code editing, shell
tools and automated tests. The exact serving model ID and context window
are 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 references)
- [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 <[email protected]>
2026-11-06 11:00:32 -06:00
DottaandPaperclip 6ec4180ea1 fix: preserve ordinary diagnostic text during environment secret redaction (#19461)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Run diagnostics help operators find the cause of a failed agent run.
> - The failure reporter treated unknown environment settings as
credentials.
> - It replaced every use of those values in diagnostic text.
> - A flag set to `1`, `2`, or `safe` removed parts of dates, model
names, versions, and normal words.
> - This pull request preserves explicit ordinary settings and keeps
unknown values private.
> - Operators can read the failure details while real credentials stay
masked.

## Linked Issues or Issue Description

**What happened?**

Run-failure contexts contained redaction markers inside timestamps,
model names, driver versions, and the word "safely". Provider failure
diagnostics used the same broad environment-value rule.

**Expected behavior**

Ordinary flags, counts, paths, regions, and mode labels leave diagnostic
text readable. API keys, URL passwords, and declared secret bindings
remain masked.

**Steps to reproduce**

1. Set ordinary environment settings to `1`, `2`, and `safe`.
2. Report a failed native run with a timestamp, `gpt-6.1-sol`,
`phase6-v2`, and "safely" in its details.
3. Inspect the run failure report. Before this fix, unrelated fragments
are replaced.

Related work: #13668 addresses a separate model-ID false positive.
#11311 and #15524 address credential capture at other boundaries. This
change keeps the existing command scanner and registered-secret
replacement.

## What Changed

- Add a shared environment selector in adapter-utils. Keep unknown
values private. Exempt explicit ordinary process settings, typed flags
and counts, mode labels, and key selectors.
- Use the same selector for run-failure reports and inherited provider
failure environments. Keep one policy for both consumers.
- Keep configured provider bindings private because that API does not
carry separate secret-reference metadata.
- Preserve short declared secrets, unknown opaque values, encoded
credentials, and inherited Bedrock tokens. Add regressions for ordinary
`SORT_KEY` values and both Bedrock consumers.
- Add screenshot-shaped regressions, real database reporter coverage,
and credential tests.
- Document the policy in `doc/observability.md`. The optional monitoring
gate stays in place.

## Verification

Linux, Node v25.9.0, pnpm 9.15.4. The published tree matches the tested
local tree `cb236f072cca195e4c2cfb4034c7cc06ea7adbc5`.

- Before the fix, four diagnostic regressions failed. The output
reproduced the damaged timestamp, model, version, and word.
- Review regressions: six tests failed on the first revision. They
proved the missed Bedrock token, opaque inherited-value exposure, and
key-selector false positive. All pass after the correction.
- 555 focused tests pass on the final source tree.
- `cd server && pnpm exec vitest run
src/services/__tests__/run-failure-diagnostics.test.ts
src/__tests__/redaction.test.ts src/__tests__/redact-sensitive.test.ts
src/__tests__/run-secret-redaction.test.ts`: 206 passed.
- `cd server && pnpm exec vitest run
src/services/__tests__/run-failure-report.test.ts`: 289 passed, with
real embedded PostgreSQL.
- `pnpm exec vitest run
packages/adapter-utils/src/command-redaction.test.ts
packages/adapter-utils/src/acpx-engine/terminal-session-failure.test.ts`:
60 passed.
- `pnpm --filter @paperclipai/adapter-utils typecheck`: passed.
- `git diff --check`: passed.
- The first reporter attempt could not load the unbuilt plugin SDK. It
passed after `pnpm --filter @paperclipai/plugin-sdk ensure-build-deps`.
- The filtered install omitted the CLI tsx loader, so `pnpm verify
--explain` could not start. The equivalent verifier entry points ran
with the installed server loader and returned exit 2: broader CI
coverage is required for the observability document.
- Full local build/typecheck and browser suites were not duplicated.
[Final-revision
CI](https://github.com/paperclipai/paperclip/actions/runs/42231412547)
passed on `013960a4c6fa6279e1f28b0f6af5f036d7cad5a7`, attempt 1. This
includes typecheck, build, general/server/chat tests, serialized suites,
Runner checks, eight E2E shards, and the canary dry run.
- [Greptile
review](https://github.com/paperclipai/paperclip/pull/19461#issuecomment-6686592783)
scored this head 5/5 with zero new findings. All four inline threads are
resolved. The security rescan and Snyk status passed. The PR has no
merge conflicts.

## Risks

- Ordinary-setting exemptions need a known name or a name/value type
rule. Unknown opaque values remain private. Declared secret bindings and
recognizable credentials override these exemptions.
- A real short secret still masks matching fragments. Removing that
protection would expose the secret.
- Existing stored redaction markers cannot restore their original text.
This fix applies when new diagnostics are produced.
- No migration, dependency, workflow, authorization, or default
monitoring changes.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. This change is a focused bug
fix.

## Model Used

OpenAI GPT-6, through Codex, with shell execution and file tools. The
host does not expose the exact runtime variant or context window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have 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 <[email protected]>
2026-11-06 09:01:55 -06:00
Devin Foley e82210386f fix(workspaces): tolerate interrupted managed clone staging beside nested repositories (#19374)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A task workspace can hold managed nested repositories in
`.paperclip-repositories/<name>`. Sandbox and SSH runs read each nested
repository before they stage the workspace.
> - The server stages a new nested clone in
`.paperclip-repositories/<checkout>.clone-XXXXXX` and renames it when
the clone is complete. If the process stops during the clone, this
staging directory stays.
> - Workspace preparation keeps `.clone-` entries on purpose. But the
snapshot reader and the SSH listing reject every directory name that has
a dot. One leftover staging directory makes every later sandbox or SSH
run on that workspace fail with `Invalid project repository directory`.
> - This pull request makes both readers skip only server-owned
`<name>.clone-<suffix>` directories. Sandbox and SSH staging also
exclude these directories from the upload and from the restore.
> - The benefit is that an interrupted clone no longer blocks all later
runs on the workspace. Other unexpected entries still fail closed.

## Linked Issues or Issue Description

Refs #17382

**What happened?**
A hosted staging run failed before the agent started. The error was
`Error: Invalid project repository directory` from
`readManagedWorkspaceRepositories`, through `readGitWorkspaceSnapshot`
in `prepareSandboxManagedRuntime`. The server had restarted about five
minutes before the run. The run was an automatic retry.

**Expected behavior**
A leftover clone staging directory from an interrupted clone does not
stop later runs. The sandbox or SSH host does not receive the partial
clone. The restore does not delete or change the host staging directory,
because a concurrent preparation can own it. An unexpected directory
that is not a staging directory still fails closed.

**Steps to reproduce**
1. Create a Git workspace with a managed nested repository at
`.paperclip-repositories/backend`.
2. Create the directory `.paperclip-repositories/backend.clone-Ab12Cd`
with a `.git` directory and a file in it. This is the state after an
interrupted clone.
3. Call `readGitWorkspaceSnapshot` on the workspace, or start a sandbox
or SSH run on it.
4. Before this change, the call throws `Invalid project repository
directory`.

**Additional context**
`materializeManagedProjectWorkspace` creates the staging directory with
`fs.mkdtemp(\`${cwd}.clone-\`)`. Its cleanup does not run when the
process stops. `prepareProjectRepositoryWorkspaces` already skips
entries that contain `.clone-` when it retires stale entries. #17382
moved the common nested path to shared worktrees, but the fallback path
still stages in place. #18534 protects nested `.git` directories during
copy-back. It does not cover staging directories.

## What Changed

- `git-workspace-sync.ts`: Add `PROJECT_REPOSITORY_STAGING_PATTERN`
(`^[a-zA-Z0-9_-]+\.clone-[a-zA-Z0-9_-]+$`).
`readManagedWorkspaceRepositories` skips a directory that matches it.
Add `listProjectRepositoryStagingPaths`, which lists these directories.
- `ssh.ts`: `listLocalProjectRepositories` skips the same directories.
`prepareWorkspaceForSshExecution` excludes the staging paths from the
upload when no completed repository exists, and returns them as
`excludedPaths`.
- `remote-managed-runtime.ts`: Add `excludedPaths` to the SSH workspace
baseline excludes. The restore does not download them, and the merge
does not change them on the host.
- `sandbox-managed-runtime.ts`: Add the listed staging paths to the
workspace archive excludes and to the restore excludes.
- New test file `managed-clone-staging.test.ts`:
- The snapshot and the SSH listing skip the staging directory and keep
the nested repository.
- An unexpected entry such as `backend.backup` still fails in both
readers.
- A sandbox round trip does not ship the staging directory, restores the
nested repository change, and keeps the host staging directory.
- An SSH round trip with only a staging directory does not ship it,
restores a tracked-file change, and keeps the host staging directory and
its `.git/HEAD`.

## Verification

- `cd packages/adapter-utils && PATH=$PATH:/usr/sbin npx vitest run
src/managed-clone-staging.test.ts`: 4 pass, with a real sshd.
- Red: with the source changes reverted, the staging tests fail. Before
the change, the reader fails with the exact production message `Invalid
project repository directory`. Before the SSH fix, the SSH round trip
failed with `expected 'present' to be 'absent'`. Without the restore
exclude, the sandbox restore deletes the host staging directory.
- `npx vitest run src/ssh-fixture.test.ts
src/remote-managed-runtime.test.ts src/ssh-command-managed.test.ts
src/managed-clone-staging.test.ts src/git-workspace-sync.test.ts
src/sandbox-managed-runtime.test.ts`: 218 pass. `ssh-fixture.test.ts`
ran all 25 tests with a real sshd. 3 tests in
`git-workspace-sync.test.ts` fail (two "rejects a checkout during
integration" cases and "does not inherit a poisoned GIT_CONFIG_GLOBAL").
They fail in the same way on unmodified master in the test sandbox, so
they are environment failures.
- `npx tsc --noEmit -p packages/adapter-utils`: passes.

## Risks

- Low risk. The skip matches only the exact staging name shape that the
server creates. Every other unexpected name still fails closed.
- The excludes apply only to directories that exist when staging starts.
A staging directory that appears during the run is not in the baseline,
so the restore does not touch it.
- A user directory with a name like `name.clone-x` in
`.paperclip-repositories` is now ignored and not rejected. This
directory belongs to the server, so this is the expected behavior.

> 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

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with
tool use (shell, file edits, test runs, GitHub connector).

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

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-11-06 06:48:13 -08:00
Devin FoleyandClaude Opus 5.5 33269e1fec fix(observability): report directory-merge lock holder diagnostics (#17755)
A directory-merge lock timeout (ERR_WORKSPACE_RESTORE_LOCK_TIMEOUT) already
carries allowlisted holder evidence, but server Sentry capture dropped it.

- Add directoryMergeLockTimeoutDiagnosticsFor in adapter-utils: closed
  labels, booleans and bounded durations; no owner PID or paths.
- server captureException: lock timeouts get an event-local
  directory_merge_lock context and error_code=workspace_restore_lock_timeout
  tag; default grouping and message unchanged.
- run-failure-diagnostics restoreLock* fields use the shared reader
  (same allowlist and order).
- Document the record in doc/workspace-restore-locks.md.

Diagnostics only; does not change lock acquisition, timeouts or retries.

Refs PAPERCLIP-BACKEND-4Q

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-05 16:48:46 -08:00
Devin FoleyandPaperclip 8080620fc9 fix(workspaces): size archive deadlines and preserve resume budgets (#19364)
Give large workspace archives a bounded per-transfer deadline without changing
provider settings. Preserve configured post-upload command limits and the saved
provider budget when resuming an exact retained sandbox.

Verification: 341 focused Core/SDK/adapter tests and 588 Daytona tests passed.
Workspace and provider typecheck/build passed. Hosted CI: 54 successful checks,
two intended Storybook skips. Greptile 5/5 with no unresolved review threads.
The bounded full local run passed all six chat shards and stopped at twenty
minutes during general server suites; that full local run remains incomplete.

Co-Authored-By: Paperclip <[email protected]>
2026-11-05 16:09:32 -08:00
DottaandClaude Opus 5.5 22281d4b3a feat(task-chat): approve harness-proposed plans (Codex plan mode, Claude ExitPlanMode) (#19291)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents run through the native runner. Codex and Claude Code each
have their own plan mode, and each produces a proposed-plan document.
> - Paperclip already has a planning work mode, an issue `plan`
document, and a `request_confirmation` approval gate for plans.
> - These parts were not connected. The Codex proposed-plan item was
transcript text only. The server path never put Codex into plan mode.
Claude was never put into plan mode, and its ExitPlanMode plan was
dropped. A planning run could close its task as done with no approval.
> - This pull request turns a harness's own proposed plan into the
issue's Plan document and the existing approval gate, for Codex and
Claude.
> - The benefit is that users approve or send back the agent's real plan
from the task chat. Approve resumes the agent. Request changes sends the
reason back to the agent.

## Linked Issues or Issue Description

Refs #19252

**Subsystem affected**
Native runner provider events (Codex and ACPX Claude), the runner Codex
provider (Rust), server native plan synchronization, and the task chat
UI.

**Problem or motivation**
Codex and Claude both produce a proposed-plan document in their native
plan mode. Paperclip did not use it. In live tests on the server path, a
Codex planning run was not in Codex plan mode, saved its own
`implementation-plan` document, and marked the task done without
approval. Claude planning runs were not in Claude plan mode, and the
ExitPlanMode plan text never reached Paperclip.

**Proposed solution**
Add a canonical `plan.proposed` event for a harness's proposed plan. Put
Codex and Claude into their native plan mode for planning runs. Save a
completed proposal as the issue `plan` document with the existing
`request_confirmation` gate. Agents can also propose a plan on a
standard task; the work mode stays standard.

**Alternatives considered**
Reuse `plan.updated`. Its schema is the within-turn checklist and is not
the durable Plan document, so a separate event keeps the two clear.
Stronger prompt text without native plan mode. Live tests showed that
the model kept its own document workflow.

**Roadmap alignment**
This reuses the issue plan document, planning work mode and approval
gate that already exist. It adds one canonical provider event.

## What Changed

- `packages/paperclip-runner/protocol/schemas/*`: add the
`plan.proposed` event (`paperclip.plan.proposed.v1`: `proposalId`,
`status`, `markdown` up to 60k characters, `source`). Regenerate the
schema bundle, validators and manifest.
- `packages/paperclip-runner/src/provider-events.ts`: map the Codex
`plan` item and a `<proposed_plan>` block in a completed Codex message
to `plan.proposed`. Add `claudePlanProposalEvent`.
-
`packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs`:
send Codex's native `collaborationMode: plan` on `turn/start` for a
read-only (planning) Codex run. Before this change the server path never
sent it.
- `runner-core/src/acpx_event_payload.rs`: admit `plan.proposed` as ACPX
rich display activity.
- `drivers/acpx/codex-runtime-adapter.ts`: set Claude Code to native
plan mode for a read-only run. Capture Claude's ExitPlanMode plan from
its permission request, record it, and decline the native "Ready to
code?" prompt.
- `drivers/acpx/codex-acpx-driver.ts`, `cli/acpx-runtime-sidecar.ts`:
emit the captured proposal as a rich event. Complete that turn instead
of reporting it as cancelled.
- `backends/codex-native-backend.ts`: per-harness plan-mode
instructions, and a short opt-in hint for Claude on standard tasks.
- `server/src/services/native-runtime/native-session-executor.ts`:
synchronize a completed `plan.proposed` into the `plan` document and a
`request_confirmation` in planning and standard mode. Do not change the
issue status during the run (that revoked the run's lease). The run
finalizer turns the pending confirmation into the review wait.
- `server/src/services/heartbeat-run-summary.ts`: keep a
`<proposed_plan>` block out of the posted reply.
- UI: new `plan_proposal` provider family. The task chat shows a live
"Writing plan" card while a proposal streams and a one-line record when
it completes. Resolved plan reviews keep a receipt ("Approved Plan v1",
"Requested changes to Plan v1: reason"). Plan previews skip section
headings.
- Storybook: `Tasks and conversations/.../Task chat/Proposed plan
approval` shows the journey.
- `doc/SPEC-implementation.md`: describe harness-proposed plan approval.

## Verification

- `cargo test -p paperclip-runner-core --lib` (new:
`read_only_codex_turns_use_native_plan_collaboration_mode`)
- `cd packages/paperclip-runner && npx vitest run
src/provider-events.test.ts
src/drivers/acpx/codex-runtime-adapter.test.ts`
- `cd server && npx vitest run
src/__tests__/native-plan-proposal-sync.test.ts
src/__tests__/heartbeat-run-summary.test.ts`
- `cd ui && npx vitest run
src/components/transcript/native-run-events.test.ts
src/components/task-chat`
- Live test on a local worktree instance with real harnesses:
- Codex (`gpt-5.6-sol`), planning task: Codex asked one question, then
produced a native plan item. The task showed Plan v1 with Approve plan /
Request changes and moved to in review. Approve changed the work mode to
standard. Codex then implemented the plan and its tests passed.
- Codex, Request changes with a reason: Codex woke in planning mode and
produced Plan v2 that includes the change.
- Claude (`claude-opus-5-5`), planning task: Claude was in plan mode.
ExitPlanMode became Plan v1 and the turn completed. Approve resumed
Claude in standard mode, and Claude implemented the plan.
- Claude, standard task that asks for a plan first: Claude stopped
before code changes and asked for plan approval. The work mode stayed
standard.
- Storybook: open `Proposed plan approval` and check each stage.

## Risks

- Codex planning runs now really use Codex plan mode on the server path.
Codex can ask a clarifying question before it proposes. This matches
Codex plan mode.
- Claude planning runs now use Claude plan mode. A declined ExitPlanMode
ends the Claude turn; the runner reports it as completed. Claude's tool
row for that request records a rejection from Claude's side.
- Codex does not stream the proposed plan on the server path yet, so the
live card shows a placeholder until the plan completes.
- A `<proposed_plan>` block in any completed Codex message on a standard
task now asks for approval. This is the documented opt-in path.

> 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

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), via Claude Code with
tool use (shell, file edits, browser). Long context. No extended
thinking mode was used.

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

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

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 20:53:33 +00:00
Nicky LeachandPaperclip 31bf6e4482 Pin plugin Runner cleanup to its physical task (#19300)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Plugin Runner owners manage a physical task through an environment
lease.
> - The existing replacement path can assign a new physical task to that
same lease.
> - A delayed old owner can then stop the replacement or mark it
stopped.
> - Cleanup must keep the original physical task identity across RPC and
database writes.
> - This pull request pins that identity and keeps cleanup available to
the new owner.

## Linked Issues or Issue Description

Refs #19134 and #19157. This fix is based on master and does not enable
repeated stopped-source recovery. A search found no duplicate fix.

**What happened?**

The maintained `createOwnedSandbox` replacement path changes
`providerLeaseId` on one lease. An old task owner uses that mutable
lease for a later Stop. A stopped-state response already in flight can
also update the new task's flags.

**Expected behavior**

The old owner must address its original task. Its late cleanup writes
must not alter the replacement. The replacement's own owner must still
stop and complete it.

**Steps to reproduce**

1. Create a plugin task owner for a stopped task.
2. Replace that task through the existing describe/startup path on the
same lease.
3. Resume an old checkpoint or a held old status response.
4. Observe a Stop for the replacement or an incorrect stopped flag
before this fix.

## What Changed

- Require each task owner to keep the physical ID returned by its
description. Reject a rebind to a different task.
- Carry the ID through the existing internal task context after startup
ends. Reject a changed physical task before RPC dispatch.
- Match the physical task in stopped-state, completion and
credential-handoff writes.
- Hold the exact lease row through the retained-session database update
and local checkpoint save.
- Cover the maintained replacement path with real PostgreSQL tests.
Preserve positive cleanup for the replacement owner. Document this
boundary.

## Verification

- Both replacement regressions failed on unmodified master `10a7d039c8`:
delayed old-owner Stop targeted the replacement, and a held old status
response marked it stopped.
- Six focused files passed 160 tests: task authority, task runtime, warm
tasks, execution target, credential writeback and task owner.
- Four selected executor factory cases passed. They cover task-only
providers and the physical task binding after the startup context ends.
- Runner TypeScript, plugin SDK and adapter-utils builds passed. Server
TypeScript check passed. Diff and added-line secret scans passed.
- All 15 setup-probe cases passed with the actual Runner and fake
provider binaries. The first local attempt lacked cargo on PATH. A
sandboxed native attempt timed out in seven process cases; the unchanged
run with loopback permission passed all 15. A separate initial DB
integration attempt was denied a loopback listener; its
permission-correct run passed.
- Review found three missed constructors in the existing SDK/Core
lifecycle fixture. They now use the actual describe result. All four
cases in that real PostgreSQL and worker RPC file passed.
- The first GitHub attempt failed in the unchanged GitHub recovery
pagination case in `chat-channels.integration.test.ts`: its zero-request
assertion observed a configuration GET. That exact case passed locally
without code or assertion changes. The one failed-job-only retry passed;
the initial failure log is retained.
- [GitHub workflow
42082587576](https://github.com/paperclipai/paperclip/actions/runs/42082587576)
passed on its second attempt. All CI checks completed, and Snyk passed.
Standard Greptile scored exact head `fb914d98b99e` 5/5 with no
unresolved threads. The branch is mergeable and ready for review.

## Risks

The task context change is internal. It adds no plugin wire field,
schema or Runner mode. A stale owner now gets an error and cannot clean
up a replacement. A new owner can still clean up that task. The retained
checkpoint write holds one existing lease row during a bounded local
file write.

Repeated stopped-source incarnation admission, runtime pins and
deployments are outside this change.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), high reasoning. Used repository
tools, TypeScript and real PostgreSQL tests.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with version and capability
details
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have described the issue in-PR using the bug template
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change
- [x] I have run the focused tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented risks
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no unresolved findings
- [x] I will address reviewer comments before requesting merge

---------

Co-authored-by: Paperclip <[email protected]>
2026-11-05 12:14:09 -08:00
DottaandClaude Opus 5.5 86a9d3ec6c feat(task-chat): tool rows that say what ran and how it went (#19265)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The task chat shows each tool call an agent makes as a short
activity row.
> - Real Claude runs showed Bash rows named "Terminal" with no command
and Write rows stuck at "Preparing file…". No row showed an exit code or
a duration, and a tool refused by policy looked like any other failure.
> - Codex already reported exit codes, durations and parsed command
actions, but the UI dropped them, and every Codex read looked like a
shell command.
> - This pull request gives both harnesses one row grammar: a verb icon,
the real subject, an exit chip and a duration.
> - The benefit is that a reviewer can see what ran and how it went
without opening each row.

## Linked Issues or Issue Description

Refs #19259 (this branch includes its `_meta` passthrough commit; merge
#19259 first or together).

**Problem or motivation**
Claude tool rows hide what ran: the runner keeps the first ACP title
("Terminal", "Preparing file…") and drops the refined one. `exitCode`
and `durationMs` are null on every Claude tool event. The UI ignores
`tool.execution.progressed`, `exitCode` and `durationMs` for every
harness.

**Proposed solution**
Read real tool facts in the runner (Claude `_meta.claudeCode`, raw input
and result text; Codex `commandActions`), carry them in the canonical
tool event, and render one row grammar in the task chat.

**Alternatives considered**
Enable Claude's `terminal_output` client capability to get
`terminal_exit`. That changes how command output arrives, and Claude's
own exit code is derived from the error flag anyway, so this PR reads
the error flag and the "Exit code N" text instead.

## What Changed

- **Runner (TS):** `createAcpxToolEventNormalizer` keeps the latest
concrete title instead of the first placeholder. It merges tool facts
across the lifecycle and measures the duration. New
`acpxToolDetailFromRuntimeEvent` reads the native tool name, the command
or pattern, the description, the exit code and a "Permission denied"
reason. Exit 0 is inferred only for Claude `Bash`, where a non-zero exit
is always a failed result.
- **ACPX sidecar:** reads the facts before `rawInput` is dropped and
sends a bounded `toolDetail`. A file path argument becomes an attested
workspace-relative location.
- **runner-core (Rust):** validates `toolDetail`.
`normalize_acpx_tool_call` uses the native name, exit code, duration,
subject, description and blocked reason. Codex `commandExecution` gets
its operation, a single-file target and a subject from `commandActions`.
The shell wrapper is removed, and a single search reads as `"TODO" in
src`.
- **Protocol:** `toolExecution` gains the optional fields `subject`,
`description` and `blockedReason`. Generated validators and the manifest
are regenerated.
- **UI:**
- `nativeRunEventsToTranscript` handles `tool.execution.progressed` and
passes a canonical `detail` on `tool_call` entries.
- The task-chat row shows `$ command`, a path or a pattern, a red `exit
N` chip (exit 0 only for commands) and a duration (0 is hidden).
  - Three or more consecutive reads fold into "Read N files a, b, c +1".
  - A refused tool shows "Blocked" with its reason.
- **Storybook:** `Tasks/Tool rows` compares the data recorded today with
the new runner data for Claude and Codex.

## Verification

- `cd packages/paperclip-runner && npx vitest run
src/provider-events.test.ts src/cli src/drivers/acpx` (1,957 pass)
- `cd packages/paperclip-runner/runner && cargo test -p
paperclip-runner-core --lib provider_events`
- `cd ui && npx vitest run src/components/transcript
src/components/task-chat` (973 pass)
- `pnpm check:token-gates`, `pnpm --filter paperclip-runner build`
(includes the protocol coverage and replay-golden checks)
- Live runs on a local instance with real harnesses:
- Codex (gpt-5.6-sol): the rows read `$ node --test` with `exit 1` and
then `exit 0`, plus real durations. A three-file `sed` shows as one
command and does not claim a single file.
- Claude (claude-sonnet-5-5 through ACPX): the rows show `Bash` and
`Edit` instead of "Terminal" and "Preparing file…". Commands show their
real text, `exit 0` and durations, and edits show `Edited temp.js`.
- Not seen live: a real Claude `Write`, a folded read run and a Claude
policy denial. Unit and Rust tests cover them.

## Risks

- The tool-execution schema gains only optional fields, so older events
stay valid. Older runs keep rendering with what they recorded.
- Commands now cross the sidecar boundary as `subject` (bounded to 4,000
characters). Codex already recorded commands as the tool name, and
server redaction still applies.
- The tool lifecycle now keeps the latest concrete title. A provider
that retitles a call to something less useful would show the newer
title.

## Model Used

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

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

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

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 13:57:04 -06:00
Devin FoleyandClaude Opus 5.5 7f5d0793bd fix(daytona): record a bounded errno when a sandbox command fails (#19297)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Daytona sandbox provider copies the agent's workspace back to
the host after each turn (syncOut)
> - When the sandbox `tar` fails in the `archive_create` step, the
restore diagnostic records only `exitCode: 2` and `errorCode: "unknown"`
> - The command output is not sent across the plugin RPC boundary, so an
operator cannot tell a full disk from a permission error or a path that
is too long
> - This pull request maps the system error text at the end of a failed
sandbox command's output line to the existing allowlisted `errorCode`
> - The benefit is that the next failure records the real errno, and the
output text stays private

## Linked Issues or Issue Description

Refs #18172
Refs #18212

**What happened?**
A hosted Daytona run failed its workspace restore. The `archive_create`
step exited 2 on all three attempts. The run log recorded
`{"step":"workspace_transfer","transferStep":"archive_create","transferFailureKind":"command_failed","exitCode":2,"errorCode":"unknown"}`.
Two causes are possible. The sandbox disk was full, or a path was longer
than PATH_MAX, for example from deeply nested GitHub helper config
directories before #18212. The diagnostic cannot tell these causes
apart.

**Expected behavior**
A failed sandbox command records a bounded errno such as `ENOSPC`,
`EACCES` or `ENAMETOOLONG` when its output shows one. The output text
itself must not be recorded.

**Steps to reproduce**
1. Run `performSyncOut` against a sandbox whose `tar -c` exits 2 with
`tar: ./x: Cannot open: Permission denied`.
2. Read the environment sync diagnostic from the thrown error.
3. See that `errorCode` is `unknown`.

**Paperclip version or commit**
`master` at 3fca8a1.

## What Changed

- `packages/plugins/sandbox-providers/daytona/src/file-sync.ts`:
`assertSandboxCommandOkWithOutput` maps the strerror suffix of a failed
command's output to an allowlisted errno. It reads only the last 16 KiB
in memory. A line must end with `: <message>`. Fatal causes are checked
first (ENOSPC, EIO, ENAMETOOLONG, EACCES, EPERM, ENOTDIR, EISDIR,
ENOENT), so a warning about a vanished file does not hide the error that
stopped `tar`. The error message, capacity fallback (#18172), restore
and retain-on-failure behavior do not change.
- `packages/plugins/sdk/src/environment-sync-error.ts` and
`packages/adapter-utils/src/workspace-restore-diagnostics.ts`: add
`ENAMETOOLONG` to both allowlists. Both sides still validate
independently.
- Tests: 7 new Daytona cases, plus one allowlist case each in the SDK
and adapter-utils.

## Verification

- `vitest run --config
packages/plugins/sandbox-providers/daytona/vitest.config.ts
packages/plugins/sandbox-providers/daytona/src/file-sync-diagnostics.test.ts`:
18 passed. Without the `file-sync.ts` change, the 7 new cases fail with
`errorCode: "unknown"`.
- `packages/plugins/sdk`: `vitest run
tests/environment-sync-error.test.ts`: 20 passed.
- `packages/adapter-utils`: `vitest run
src/workspace-restore-diagnostics.test.ts`: 50 passed.
- `tsc --noEmit` passes for the SDK and adapter-utils. `git diff
--check` is clean.
- I did not run the Daytona package typecheck or its full suite locally
because the sandbox disk was full. CI covers them.

## Risks

- Low risk. The change adds a diagnostic field only. Control flow, error
messages and the capacity fallback regex do not change.
- Localized `tar` output does not match, so it stays `unknown`. That is
the current behavior.
- `ENAMETOOLONG` is a new allowlisted code on both sides. It is a fixed
string and carries no path or output text.

## Model Used

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

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

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

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 11:34:41 -08:00
DottaandClaude Opus 5.5 1eff07f29e feat(task-chat): show Claude subagents as a collapsible tree in the runner turn (#19264)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The task chat shows what an agent does in a run. The Paperclip
Runner sends this activity as canonical provider events.
> - Claude runs subagents through its Agent (Task) tool. A run that
delegates showed a confusing burst of unrelated tool calls.
> - The subagent data was already sent, but it was lost: ACPX dropped
`_meta`, the hardened sidecar dropped `rawInput`, and async subagents
report "launched" as the tool result.
> - This pull request maps each Agent call to a canonical `delegation`
event, links each child tool to its subagent, and shows subagents as a
collapsible card or tree.
> - The benefit is that operators can see what each subagent was asked,
what it is doing now, which tools it used, and what it returned.

## Linked Issues or Issue Description

Refs #19252 (the docked plan card used the same canonical-event pattern)
Refs #19259 (ACPX `_meta` passthrough, merged). This PR builds on it.

**What happened?**
When a Claude agent used its Agent tool, the task chat showed one
generic "Task" row and then the subagents' tool calls as flat rows in
the parent turn. The subagent type, prompt, progress and result were not
visible.

**Expected behavior**
Each subagent shows as its own card with its type, description, prompt,
live progress summary, nested tool rows and result. Subagents that start
together show as one tree with status dots.

**Steps to reproduce**
1. Assign a Claude (Paperclip Runner, ACPX) agent a task that asks it to
launch three Explore subagents in parallel.
2. Open the task while the run is live.
3. Before this change, the subagents' tool calls appear flat in the
parent turn.

## What Changed

- **claude-agent-acp patch:** Under the isolated runner context, the
patch turns on the SDK option `agentProgressSummaries`. It forwards each
subagent's `task_progress` summary and its terminal state and summary
onto the spawning tool call as `_meta.claudeCode.subagentProgress` and
`subagentState`. Native subagent sessions stay off, because ACPX cannot
follow child ACP sessions.
- **Runner:** The new file `claude-subagent-lineage.ts` tracks each
Agent/Task call across ACP update deltas. It maps the call to
`delegation.started|updated|completed` events and stamps child tools
with `parentItemId`. An async subagent stays running after its "Async
agent launched" result until its own state settles.
- **Sidecar path:** `acpx-runtime-sidecar.ts` reads the lineage before
it drops `_meta` and `rawInput`. Delegations go out as schema-checked
rich events. Child tools carry `parentToolCallId`, which Rust
(`acpx_event_payload.rs`, `provider_events.rs`) maps to `parentItemId`.
- **Contracts:** The provider-event schema adds optional `prompt` and
`result` to delegation children and optional `parentItemId` to tool
executions. The protocol modules are regenerated. ACPX Claude now
advertises the `delegation` event family.
- **UI:** `native-run-events.ts` keeps delegation payloads and the tool
parent link. `buildTurnTimelineRows` groups adjacent delegations into a
new `subagent_group` row, nests child tool rows (plain and
provider-activity shapes), and hides the spawning call's own tool row.
The new `TaskChatSubagentCard.tsx` renders the card and tree. A new size
token caps the result height.
- **Storybook:** The new story `Tasks and conversations/…/Task
chat/Subagents` has 7 states.

## Verification

- `pnpm --filter @paperclipai/paperclip-runner exec vitest run src/cli
src/drivers/acpx src/provider-events.test.ts
src/claude-subagent-lineage.test.ts src/protocol` (2069 passed)
- `cargo test --release --locked -p paperclip-runner-core --lib` in
`packages/paperclip-runner/runner` (488 passed, including the new
parent-link test)
- `node --test test/protocol-contract.test.mjs
test/acpx-sidecar-contract.test.mjs
test/acpx-codex-package-contract.test.mjs
test/acpx-rich-extensions-package-contract.test.mjs` (48 passed)
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/task-chat src/components/transcript`, including the new
`subagent-timeline.test.ts`
- `check:protocol-types`, `check:protocol-manifest`,
`check:acpx-profiles` and `pnpm check:token-gates` pass.
- **Live test:** A local test-drive instance ran a Claude Opus 5.5 agent
on the Paperclip Runner (hardened sidecar path). The agent ran real
tasks that launched three parallel Explore subagents. The live view
showed "Running 3 subagents · 2 done" with a progress summary. The
settled view showed "Ran 3 subagents", each with its prompt, nested
tools and its own result text.

## Risks

- `agentProgressSummaries` makes Claude Code generate a short progress
summary for each running subagent about every 30 seconds. This adds a
small model cost to runs that delegate.
- The adapter patch reads `task_notification` and terminal
`task_updated` messages. A future claude-agent-acp version can change
these. The patch hunks then fail to apply, and the dependency bump must
update them.
- If a delegation event is lost, child tools stay at the top level as
before. Nothing is hidden without a matching delegation.
- Codex is not changed. Codex subagents need `multi_agent`, which
Paperclip keeps off on purpose. Changing that is a separate policy
decision.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use
(shell, file edits, browser automation). It did the investigation,
implementation, tests and live test.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 18:58:35 +00:00
Devin FoleyandPaperclip 088f85c5fe fix: rename the managed environment to Paperclip Sandbox (#19296)
## Thinking Path

> - Paperclip manages AI agents and their execution environments.
> - Managed installations supply a default sandbox through managed
configuration.
> - Most selectors show the configured environment name.
> - A fallback label, two errors, and examples still use Paperclip
Computer.
> - This pull request changes that stock copy to Paperclip Sandbox.
> - Existing and custom environment names continue to come from the
stored row.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The managed environment label in agent settings and the errors shown
when the managed sandbox is unavailable.

**Subsystem affected**

The agent configuration UI, runner selection, and environment
reconciliation tests.

**Current behavior**

Fallback copy and current examples call the managed sandbox Paperclip
Computer. The actual environment name is already configurable.

**Proposed behavior**

Use Paperclip Sandbox for stock copy and current examples. Keep
configured names unchanged in selectors, including legacy names and
custom names.

**Reason and benefit**

The name identifies this environment as a managed sandbox and removes
ambiguity.

**Breaking changes**

The displayed fallback and error text change. Error codes, execution
behavior, and environment identity remain the same. No migration is
needed.

**Additional context**

No duplicate rename issue or pull request was found. This is a copy
update, with no new roadmap feature.

## What Changed

- Update the managed fallback label and two unavailable-sandbox error
messages.
- Update current test and Storybook fixtures without changing story IDs
or behavior.
- Test configured names, fallback copy, and the unavailable-sandbox
message.
- Add a PostgreSQL regression test for a rename that retains the
environment ID, instance default, agent binding, environment variables,
and lease.

## Verification

- `pnpm -r typecheck` passed.
- `pnpm build` passed.
- Focused coverage: 956 cases across nine changed suites. The new
fixture initially tried to insert an existing singleton; its corrected
upsert passed with all 52 environment-service cases. The updated runner
suite also passed all 243 cases.
- The two UI suites passed all 189 cases, including configured
legacy/custom names and the missing-default fallback.
- `pnpm check:token-gates` passed.
- `node scripts/storybook-catalog.mjs --check` passed. The catalog is
structural evidence only.
- `node --test scripts/check-maps.test.mjs
scripts/storybook-catalog.test.mjs
scripts/storybook-feature-index.test.mjs` passed.
- Full `pnpm test:run` is in progress. No live provider or browser
journey is claimed by these tests.
- CI passed on the current head after one server-shard rerun. The first
attempt hit a 10 ms deadline assertion in the unchanged status-card
timing test; its isolated local run and the complete rerun shard passed.
The original failure and rerun are retained in the PR comment.
- Reviewed the diff for secrets, personal data, and run artifacts.

## Risks

Low risk: runtime edits change three display strings. Managed
reconciliation is unchanged. The regression test exercises the existing
rename against PostgreSQL. The configured name still controls normal
display, so this code change alone does not rename existing database
rows. A managed configuration update uses the existing reconciliation
path. Roll back the copy by reverting this commit.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) through Codex in T3 Code, xhigh
reasoning, with 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, version, and capabilities
- [x] I have checked ROADMAP.md; this copy update does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs
- [x] I have described the issue in the PR with the enhancement template
fields
- [x] No internal or instance-local issue references
- [x] The branch name describes the change and contains no internal
ticket ID
- [x] Focused tests pass locally
- [x] Relevant tests were added or updated
- [x] Relevant copy and examples were updated; no API or schema
documentation changed
- [x] Risks are documented
- [x] All CI gates passed on the current head, including the server
11/12 rerun and aggregate verify
- [ ] Full local test suite completed (still running; six chat shards
passed)
- [x] Greptile is 5/5 on the current head with no unresolved findings
- [x] I will address all reviewer comments before requesting merge

Co-authored-by: Paperclip <[email protected]>
2026-11-05 10:23:43 -08:00
DottaandClaude Opus 5.5 c6996799c4 feat(task-chat): show an inline diff for every file edit (#19269)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The task chat shows what an agent did during a run, including each
tool call
> - Codex and Claude both report the patch for every file edit, but
Paperclip dropped it. Claude's ACP `diff` content was never read. The
Codex `fileChange` item became a generic item with no patch. Codex
`turn/diff/updated` had no native-runner mapping
> - A reviewer could not see what an agent changed without opening the
workspace
> - This pull request carries a bounded patch on each edit call and
shows it in the edit row. Each turn that changed files ends with a
"Changed in this turn" card
> - The benefit is that a reviewer can check every change in the
conversation, with the same view for Codex and Claude

## Linked Issues or Issue Description

Refs #19252 (precedent: canonical `plan.updated` events as a docked plan
card).

**Subsystem affected**
Paperclip runner (Rust and TypeScript canonical event mapping), PRP
provider-event schema, and the task-chat UI.

**Problem or motivation**
Edit rows say "Edited a file" with no content. Claude Edit/Write `diff`
content is lost after the ACPX sidecar. The Codex native runner drops
`fileChange.changes` and `turn/diff/updated`. The existing
workspace-changes card showed a raw text patch in a dialog, and only on
the in-process Codex path.

**Proposed solution**
Add an optional, bounded `fileChanges` list to
`paperclip.tool.execution.v1`. Fill it from Codex `fileChange` items and
Claude ACP `diff` content. Map Codex `turn/diff/updated` to
`workspace.change.updated` on the native runner. In the UI, show `+N −M`
on each edit row, open a compact colored diff, and end the turn with the
change set.

**Alternatives considered**
A runner-side git diff at turn end. It needs workspace access and extra
cost on every turn. The harness patches are already available. For
Claude, the UI builds the turn card from the edit rows instead of new
per-turn runner state.

## What Changed

- Protocol: optional `fileChanges[]` on `toolExecution` in
`provider-event.schema.json`, with the regenerated schema bundle,
validators and manifest. Paths are workspace-relative. A patch is at
most 32 KiB, and a call has at most 32 files.
- Runner (Rust): new `tool_file_changes.rs`. A Codex `fileChange` item
now also emits `tool.execution.*` with `fileChanges`; the native item
event stays for the recovery and progress projections.
`turn/diff/updated` becomes `workspace.change.updated`.
`normalize_acpx_tool_call` re-validates the sidecar's `fileChanges`.
`normalize_codex_notification_in` resolves absolute paths against the
session cwd.
- Runner (Rust): `has_bounded_rich_display_shape` accepts schema-valid
workspace diffs. Without this change, durable storage cut each patch
string to a 4 KiB preview. Patches use the uncapped secret redactor.
- Runner (TS): new `tool-file-changes.ts`. The ACPX sidecar computes
`fileChanges` from ACP `diff` content with the working directory. The
in-process Codex and ACPX mappers do the same, with an optional
`context` argument.
- UI: new `TaskChatDiffView` (colored rows, hunk labels, a height cap,
"Show N more lines"). Edit rows show counts and the diff. Collapsed
activity groups show edit totals. The workspace card is now "Changed in
this turn", with in-place per-file diffs, and it sits at the end of the
turn. When a harness sends no whole-turn diff (Claude), the card is
built from the edit rows. A change set is marked complete when its run
ends.
- UI: `native-run-events.ts` maps `workspace.change.updated` and
attaches each edit's latest patch to its row (the `diff` transcript
entries now carry `toolUseId`).
- Design tokens: `--diff-add-*`, `--diff-remove-*` and
`--sz-edit-diff-max-h` come from the status icon hues.
- Storybook: `Tasks/Edit diffs` story. Docs: `doc/run-log-events.md`.

## Verification

- Real runs on a local instance (`test-drive`, native runner):
- Codex (`gpt-5.6-sol`): the "Applied a patch" rows show `+9` and `+35
−2` with their diffs. The turn ends with "Changed in this turn", 2
files, `+42`, from `turn/diff/updated`.
- Claude (`claude-opus-5-5` via ACPX): the Write and Edit rows show
`+5`, `+14 −1` and `+9` with correct hunks. The turn card is built from
the edit rows.
- Codex model note: with `gpt-5.4`, pinned Codex 0.160 reports "model
metadata not found". The model then wrote files through the shell, so no
`fileChange` items occurred.
- `pnpm check:token-gates`: all gates clean.
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/task-chat src/components/transcript src/adapters`: 1073
passed.
- `packages/paperclip-runner`: `vitest run src/provider-events.test.ts
src/tool-file-changes.test.ts src/cli/acpx-runtime-sidecar.test.ts`
passed. `check:protocol-types` and `check:protocol-manifest` match.
- `cargo test -p paperclip-runner-core --lib provider_events
tool_file_changes`: passed. `cargo fmt --check`: clean.
- Storybook: open `Tasks and conversations/…/Tasks/Edit diffs`.

## Risks

- Larger payloads: an edit call can carry up to 96 KiB of patch in Rust,
and the sidecar keeps 48 KiB under its 128 KiB frame. Above the budget,
a file keeps its counts and drops its patch (`diffTruncated`).
- Redaction: each patch goes through the shared secret redactor. Before
this change, a 4 KiB preview cap also applied.
- Claude turn card: it joins each edit's patch in order. It is not a net
diff. For a file the turn created, the counts are net and exact. For an
existing file, the counts are summed over its edits.
- The workspace card moves to the end of the turn. One adapter test
changed to match.
- Local only: four Linux cgroup `task_memory` Rust tests fail on macOS.
They do not touch these modules.

## Model Used

Claude Opus 5.5 (`claude-opus-5-5`), via Claude Code with tool use
(shell, browser and file editing) and a long context window.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 17:39:07 +00:00
DottaandClaude Opus 5.5 b0fa2ef9bf feat(task-chat): show web searches, model switches, waits and verification checks as dedicated rows (#19262)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents run through the native runner. The runner turns harness
activity into typed canonical events such as `research.*`,
`model.route.changed`, `wait.*` and the run's own verification checks.
> - The task chat flattened these events into generic tool rows. A web
search showed as "Ran Research eventType: research.started, summary: …".
The run's verification checks were dropped completely.
> - Live tests found a second break: the Rust runner core forwarded
Codex `webSearch` items without their query, action or results. The
canonical `research.*` events therefore had `query: null`.
> - This pull request keeps the full payload for these families (as
#19252 did for plans), adds small dedicated rows, and keeps the
web-search fields through the durable runner.
> - The benefit is that users see what the agent searched for, which
model actually ran, how long a wait lasts, and whether each check
passed, without opening the run log.

## Linked Issues or Issue Description

Refs #19252 (same pattern for the plan card)

**Subsystem affected**
Task chat activity rows (UI), the native run-event transcript mapping,
and the Rust runner core Codex normalizer.

**Problem or motivation**
Provider activity that the runner already maps to canonical events
reaches the task chat as unreadable generic rows. Verification checks
never appear. For Codex, the web-search query and action are lost before
they reach the canonical event.

**Proposed solution**
Keep `research`, `model_identity`, `artifact`, `review` and `wait`
events as `provider_activity` entries with their full payload. Project
the runner's `verification` items as a new `verification` family. Render
dedicated rows: a web search with its quoted query and source-domain
chips, a model switch as `from → to · reason`, a wait with a live
countdown, and green/red check lines. Keep bounded web-search and sleep
fields in the Rust normalizer.

**Alternatives considered**
A separate "Checks" block beside the final answer. We kept the checks in
the activity group, because the group summary already leads with "Check
failed" when a check fails.

## What Changed

- `ui/src/components/transcript/native-run-events.ts`: keep research,
model, artifact, review and wait events as `provider_activity` with the
full payload. Map `item.completed` items of kind `verification` to the
new `verification` family. Context, hook, safety and delegation stay on
the generic path, because other work items own them.
- `packages/adapter-utils/src/types.ts`,
`ui/src/components/task-chat/task-chat-model.ts`,
`ui/src/lib/issue-chat-messages.ts`: add the `verification`
provider-activity family.
- `ui/src/components/task-chat/task-chat-activity-presentation.ts`: add
labels: "Searched the web “query”", "Opened a web page host/path",
"Switched models a → b · reason", "Waiting 30s" / "Waited 30s", and
"Check passed" / "Check failed" / "Check not run".
- `ui/src/components/task-chat/TaskChatRunnerActivityGroup.tsx` and
`TaskChatProtocolActivityRow.tsx`: show up to three source-domain chips
under a finished search, a live countdown for a running wait, and a
check line with a tinted icon and its detail.
- `ui/src/components/task-chat/completed-activity-summary.ts`: the group
summary leads with "Check failed" when a check fails, and says "Switched
models" for a reroute.
- `ui/src/components/task-chat/transcript-adapter.ts`: verification
detail keys, and a `startedAt` timestamp that stays across lifecycle
merges (used by the countdown).
-
`packages/paperclip-runner/runner/crates/runner-core/src/provider_events.rs`:
keep bounded `query`, `action` and http(s) `results` for Codex
`webSearch` items, and `durationMs` for `sleep` items. Other item types
do not change.
- `ui/storybook/stories/tasks/task-chat-provider-activity.stories.tsx`:
new story. Its fixtures are persisted run events that go through the
production transcript path.

## Verification

- `pnpm --filter @paperclipai/ui exec vitest run
src/components/transcript src/components/task-chat
src/components/TaskChatThread.test.tsx src/adapters/paperclip-runner
src/lib`: 275 files and 3669 tests pass.
- `cargo test -p paperclip-runner-core`: all tests pass, including the
new `keeps_bounded_web_search_and_sleep_identity`.
- UI typecheck is clean. `pnpm check:token-gates` is clean. `cargo fmt
--check` is clean.
- Storybook: open "Tasks and conversations / … / Task chat / Provider
activity". It shows a settled run, a live search, a live wait countdown
and a narrow layout.
- Live test on a local worktree instance with a real Codex agent
(gpt-5.6-sol, Codex app-server 0.160). The task asked for a web search,
a parser with tests, a pause, and two checks. The task chat showed
"Searched the web “ISO 8601 week date first week Thursday conversion
calendar date”" and two green check rows ("node --test week-date.test.js
· 8 tests passed; 0 failed.", "node --check week-date.js"). The group
summary read "Ran checks". Before the runner fix, the same search had
`query: null`.

Not verified live:
- The source-domain chips. Codex 0.160 sends `results: null`, because it
keeps search results on the server. The chips appear only when a
provider reports sources. Unit tests and the story cover them.
- The wait countdown and the model-switch row. In the live run Codex
paused with a shell `sleep`, not a native `sleep` item, and no reroute
occurred. Unit tests and the story cover both.
- Claude. Its WebSearch and WebFetch calls arrive as generic tool
executions, because ACPX drops the tool metadata. They show as tool
rows, as before. The ACPX `_meta` work is separate.

## Risks

- Low risk. The change only affects how canonical events display. The
generic path stays for the families that this PR does not change.
- The Rust normalizer now persists the web-search query and the source
URLs/titles in the run events. This data was already intended for the
canonical `research.v1` event, and it is bounded (4,000 characters per
text field, 64 results, http(s) URLs only).
- A search that Codex reports with `action: other` and no query shows as
"Searched the web" with no subject.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use
(shell, file edit, browser automation) and extended reasoning.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 16:37:05 +00:00
Devin FoleyandPaperclip 5fdfd3f17a fix(recovery): bound repeated saved-record response bodies (#19168)
## Thinking Path

> - Paperclip manages agent tasks and their saved execution history.
> - A continuation carries completed API receipts to prevent duplicate
work.
> - Repeated saves can copy large record bodies into each new provider
prompt.
> - The resulting command can exceed the durable PRP frame limit before
useful work starts.
> - This change projects large successful saved-record responses as
compact references.
> - The original receipts and authenticated user direction remain
available.

## Linked Issues or Issue Description

**What happened?**

A task with many saved document and comment responses can fail with
`command_payload_too_large`. Same-run recovery can then show a runner
identity failure that hides this first cause. Completed response bodies
repeat content already stored in task records.

**Expected behavior**

Continue from saved records with completed-action evidence that fits the
runner command. Keep user messages, answers and approvals intact. Keep
the full original receipts.

**Steps to reproduce**

1. Save 39 successful document, comment and task-write responses in a
run's API receipt ledger.
2. Give each response a large record body.
3. Build and render a continuation, then queue it through the real
durable PRP journal.
4. Before this fix, the regression throws `command_payload_too_large`
with a 4.2 MB prompt.

Related work: #16980 pages oversized assigned tool results. This change
addresses earlier response bodies repeated in a later continuation. It
does not change tool-result paging or protocol limits.

## What Changed

- Project response data over 4 KiB for three known successful
record-write operations. Keep record, revision and status metadata, HTTP
outcome, receipt identity and a data digest.
- Keep small, failed, unknown, mismatched and foreign-company results in
their existing form.
- Explain the reference format in shared prompt guidance and the
execution contract.
- Add database and real-journal regression coverage. Verify the original
receipt ledger remains unchanged.

The continuation service owns the projection. The shared adapter helper
only explains the reference to native and legacy prompt consumers. There
are no new dependencies, provider calls, timers or background scans.

## Verification

- The unfixed database regression fails at the real PRP command limit
with `command_payload_too_large`.
- Continuation builder, native execution input and native continuation
suites: 118 tests pass.
- Shared prompt consumers: 142 tests pass.
- Module boundaries and diff whitespace checks pass.
- Greptile: 5/5 on `8109e7361c5b5e38f9aad35af9bf4ad7bdc5f338`, with no
unresolved review threads. All CI checks pass.
- Full workspace `pnpm -r typecheck` and `pnpm build`: passed.
- Hosted verification: pending deployment. The source fix does not
itself resume the affected hosted task.

Local setup failures remain accounted for: the fresh checkout lacked an
ignored root SDK link and embedded PostgreSQL library symlinks after an
offline install that skipped lifecycle scripts. The maintained
workspace-link helper, SDK build and platform hydration script repaired
those prerequisites. A later test expectation assumed JSON insertion
order across PostgreSQL JSONB; it now checks the digest against the
unchanged stored receipt. These failures did not change runtime limits
or recovery guards.

## Risks

Large responses from these three successful operations now use explicit
saved-record references in continuation context. Agents must read the
record or source receipt when full content is needed. This is not a
universal prompt-size limit: user history, unknown operations and other
inputs retain their existing representation. No migration, permission,
credential, provider-stop or replay policy changes.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), xhigh reasoning, through Codex in T3
Code. Used for investigation, implementation and local verification. No
subagents.

## 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 linked existing issues or described the issue in-PR
following the issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal task
id
- [x] I have run the targeted tests locally and they pass
- [x] I have added tests and updated relevant documentation
- [x] I have considered and documented risks
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open findings
- [x] I will address review comments before requesting merge

Co-authored-by: Paperclip <[email protected]>
2026-11-04 19:01:08 -08:00
Devin FoleyandClaude Opus 5.5 15114bb9a0 fix(workspaces): keep durable seed copies above the free-space reserve (#18999)
persistDurableSeedArchive now checks the source file and applies the shared workspace free-space reserve before copying, so a large seed cannot fill the instance volume.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-04 15:29:55 -08:00
3cdb367369 fix(acpx): report how the sandbox agent process session ended (#16366)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Hosted `claude_local` runs use the ACPX engine. The agent process
runs in a sandbox, and a host-side process session bridge carries the
ACP stream.
> - Some hosted turns fail with only "The connection to Claude was
lost." (`acpx_turn_failed`, terminal session category `connection`). The
failure report shows one pending `execute` tool. It does not show why
the agent process went away.
> - The bridge already records how the session ended: the remote exit
code and signal, or a host proxy close. But it writes that evidence only
to the run log. The run failure report never receives it.
> - Without it, we cannot tell an OOM kill (`SIGKILL`) or a provider
process exit from a bridge or proxy close. Each cause needs a different
fix, so we must not guess.
> - This pull request keeps the first terminal evidence on the bridge
handle and copies it, as closed values, into the run failure report's
execution context.
> - The benefit is that the next recurrence names the cause, and the
correct fix can follow.

## Linked Issues or Issue Description

**What happened?**

Hosted `claude_local` runs on staging and production failed with "The
connection to Claude was lost." One run failed about 4 minutes in, 7–10
seconds after its last ACP event. Another failed after about 3.5 hours
with no ACP events for most of that time. Both had one pending `execute`
tool. The report had no information about how the sandbox agent process
ended.

**Expected behavior**

The run failure report says how the sandbox agent process session ended.
Examples: the remote process exited with a code, the remote process was
killed by a signal, or the host proxy closed.

**Steps to reproduce**

1. Start a hosted ACP `claude_local` run on a sandbox target.
2. Kill the agent process in the sandbox while a tool runs, for example
with `kill -9`.
3. See that the run log has an `ACP process session terminal:` line, but
the run failure report has no matching field.

**Paperclip version or commit**

`master` at 1b58ac6.

## What Changed

- `startAdapterExecutionTargetProcessSessionBridge` keeps the first
terminal evidence it records (`source`, `outcome`, `exitCode`,
`signal`). The new optional handle method `readTerminalEvidence()`
returns it. The run log line does not change.
- The closed source and signal labels move to
`acpx-engine/constants.ts`. The bridge and the server reader use the
same list.
- The ACPX engine adds `acpProcessTerminalSource`,
`acpProcessTerminalOutcome`, `acpProcessTerminalExitCode` and
`acpProcessTerminalSignal` to the turn activity diagnostics in
`resultJson`. It leaves out an unknown exit code or signal.
- `collectRunFailureDiagnostics` copies these fields into the execution
context. It accepts only closed labels and an integer exit code from 0
to 255. If the source is not a closed label, it drops all the fields.
- `doc/observability.md` documents the new fields next to the other ACP
activity fields.
- Tests: bridge evidence for a remote exit, a missing command, and an
authenticated proxy close; the engine summary helper; and the server
reader with valid and invalid values.

## Verification

- `vitest run src/acpx-engine/ src/execution-target-sandbox.test.ts` in
`packages/adapter-utils`: 22 files, 579 tests pass.
- `vitest run src/services/__tests__/run-failure-diagnostics.test.ts` in
`server`: 121 tests pass.
- `tsc --noEmit -p packages/adapter-utils`: passes.
- I type-checked the changed server files with a narrow config. The only
errors came from workspace packages that were not built locally
(`@paperclipai/plugin-sdk`, `@paperclipai/paperclip-runner`). A full
server `tsc` ran out of memory in my sandbox. CI runs the full check.

## Risks

- This change adds diagnostics only. It does not change retry, recovery,
or classification behavior.
- Every new field is a closed label or a bounded integer. No message,
command, path, payload, or provider text goes into the report.
- The host records a `proxy_close` from a socket close event. If the
turn finalizes before that event, the fields are absent for that run.
When the fields are absent, the report has the same data as it has
today.
- `readTerminalEvidence` is optional on the handle type, so other handle
implementations and test doubles still compile.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) through Claude Code, with tool use
and code execution. It read the repository, traced the failure path,
edited the code, and ran the tests.

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

Related: #16360 bounds hung MCP tool calls (ACP kind `other`). This
change is separate. It covers the sandbox agent process ending while a
tool runs.

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

---------

Co-authored-by: devinfoley <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: devinfoley <[email protected]>
Co-authored-by: Paperclip <[email protected]>
2026-11-04 12:15:43 -08:00
commitperclip[bot] a00109aa35 fix: Preserve sanitized ACP JSON-RPC error details and numeric codes in run errors, summaries, and acpx.error records (#18087)
> [!NOTE]
> Opened by Prospector's automated issue-fix pipeline. A maintainer
reviews and decides; nothing here merges on its own.

> [!WARNING]
> This change crosses a trust boundary. See Risks.

## Thinking Path

- Issue #16034 reports a defect in paperclipai/paperclip.
- Root cause, as the fixing agent read it: emitAcpxFailure in
packages/adapter-utils/src/acpx-engine/execute.ts built the run error,
summary, and acpx.error message from Error.message alone. It ignored the
JSON-RPC error's 'data' explanation and numeric 'code', so every ACP
phase failure (create runtime, ensure session, configure session, turn)
showed only 'Internal error'.
- This pull request: Preserve sanitized ACP JSON-RPC error details and
numeric codes in run errors, summaries, and acpx.error records

## What Changed

- `packages/adapter-utils/src/acpx-engine/execute.ts`: This change is
kept from the earlier attempt. The shared failure handler reads the
explanation from a string 'data', 'data.details', or 'data.message'. It
appends that text to the message unless the message already contains it,
and runs the result through sanitizeTerminalSessionFailure. A numeric
JSON-RPC code is recorded as errorMeta.rpcCode. Errors without these
fields keep their old message.
- `packages/adapter-utils/src/acpx-engine/execute.test.ts`: The existing
harness keeps the earlier it.each case, which covers all four phases,
all three detail shapes, redaction, and rpcCode in the acpx.error
record. A new case in the same harness covers the checks that only the
separate file had: detail text already in the message is not repeated,
and a plain Error stays unchanged with no rpcCode.
- `doc/run-log-events.md`: Documents the change to the acpx.error
record: which detail formats are read and in what order, the rule
against repeating detail text, the redaction and 4,096-character title
limit applied through the existing sanitizer, and the optional integer
'rpcCode' field.

## Verification

- Reproducing test(s), added in this PR:
`packages/adapter-utils/src/acpx-engine/execute.test.ts`
- They fail on the base commit and pass with the fix, each twice in a
fresh sandbox.
- The repository's compile command passes with the fix applied.
- 9 existing test file(s) near the change passed.
- The full test suite passes except for the 11 file(s) that already fail
on the base commit.

## Risks

- Written by an automated pipeline, not a person: review it as you would
an outside contribution.
- Risk tier of the touched paths: 2.
- The scope reviewer found it also changes, beyond the report: Plain
Error, non-object throw, string or Node errors with string codes
(ENOENT), and data that is empty, missing, non-string or malformed
- The scope reviewer found it also changes, beyond the report:
messageOverride callers (turn timeout, handshake guard)
- It crosses a trust boundary. Check each crossing against what the
issue asked for, and against who asked:
- data-exposure
(packages/adapter-utils/src/acpx-engine/execute.ts:3626): Text the ACP
child process controls (JSON-RPC error 'data', 'data.details' or
'data.message') is now added to the run error message and summary that
users see; it passes through sanitizeTerminalSessionFailure redaction
first.
- logging (packages/adapter-utils/src/acpx-engine/execute.ts:3651): The
'acpx.error' run-log record now carries the provider's explanation in
'message' and a new integer 'rpcCode' field, both supplied by the child,
on every failure route that calls emitAcpxFailure without a
messageOverride, including the handshake-guard route that suppresses
stderr.
- data-exposure
(packages/adapter-utils/src/acpx-engine/execute.ts:3639): The numeric
JSON-RPC error code from the child is now copied into
'classified.errorMeta.rpcCode', which reaches the adapter result's
errorMeta and the places that store or show it.

## Model Used

- opus (Anthropic Claude, via Claude Code), writing and checking the
change in an isolated sandbox.
- opus (Anthropic Claude, via Claude Code), reviewing it independently.

## Linked Issues

Fixes #16034

A search of paperclipai/paperclip's open and closed pull requests found
no other pull request that names #16034.

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

<!-- prospector:issue-fix v1 issue=16034 base=5cfa2bf26d3e
report=1b07160d9492357f -->

Co-authored-by: commitperclip[bot] <284812584+commitperclip[bot]@users.noreply.github.com>
2026-11-03 14:43:24 -08:00
DottaandPaperclip 27d35314be feat: connect experimental Cursor Cloud to native Runner (#18069)
## Thinking Path

> - Paperclip helps people manage AI agents at work.
> - Native Runner owns execution and task authority.
> - Operators need an explicit native Cursor Cloud choice.
> - Tools/files need company, task, session and run scope.
> - This connects setup and lifecycle to Runner Core.
> - Legacy agents retain their selected runtime/history.

## Linked Issues or Issue Description

Depends on #18068. Managed-only API placement and API-owned Stop
containment are in #18310. Refs #5664, #11803, #17299, and #17471. No
duplicate native integration was found.

## What Changed

- Add explicit native setup, Test, saved settings and dispatch with
no-prompt account/catalog/callback checks.
- Scope MCP to company, agent, task, session and run through
authenticated HTTPS. Register verified artifacts/work products; preserve
unpriced accounting, completion and budgets.
- Connect Question/Plan continuation, cold handoff and admitted fresh
sessions; recheck waiting admission under the task lock.
- Present audited Stop intent while preserving physical containment
proof; bound typed Stop metadata. Preserve title edits through
preference/navigation readiness.
- Select one live/history representation per run to prevent duplicate
settled cards. Integrate upstream task/read-state/retry/schema contracts
and repair two test fixtures.

## Verification

Core #18068 merged normally as `3910352c923a`. Product head
`5a60f541c982` has passing repository CI/build/browser checks, including
required `ci / verify` and `ci / e2e`, plus a fresh exact-head Greptile
5/5 review with zero unresolved threads. Two non-required external app
checks remain pending: Paperclip Review is queued waiting for its
assigned agent; Superagent Security Scan is reviewing the diff. These
are not claimed completed. The normal merge endpoint enforces the
repository rules; no policy or status override is used. The dependent
Top PR is addressing its fresh deferred-Stop review finding.

Product `5a60f541c982` passed 270 UI and 20 selected
Cloud/native/disk-full tests, UI/server typechecks, tokens and maps.
Top’s clean normal merge passed tokens/maps/diff checks; its matrix is
pending. Earlier receipts retain Core’s 47 Rust, 4 compiled-native
retirement and 19 settlement controls, normal Runner build, and runtime
9af build/recursive typecheck. Reproduced loading/shutdown CI fixture
failures passed 7/7 browser and 121/121 unit tests, including a
broken-unsubscribe negative control. No timeout/oracle was relaxed.

The actual-parent restack adopts 75 mainline paths, including
schema/permission and generic runtime changes, beyond the prior two
fixture repairs and client projection fix. The full difference from
hosted `9af1a5de42a2` is therefore larger than four files; backend
source also differs. The merged config-read checks match Core391, and
the Cursor placement/runtime/relay/selection modules match frozen9af;
selected affected regressions pass. Paid proof keeps its actual 9af
source/image identity. The separately tested live/history card fix is
not claimed deployed on that host.

On 9af, original saved settings survived reopening and fresh no-prompt
Test authenticated catalog/model/binding; no new Save is claimed. Fresh
Linux admission passed. With `claude-sonnet-4-5`, Artifact downloaded
exact 27 bytes and matched 121 canonical identities in complete
S3-served history. First Stop’s strict quiet FAILED on a 10.373-second
gap despite independent cancellation/no late artifact. One targeted
unchanged-sampler retry retained all 24 samples: 35.076 seconds quiet /
maximum gap 4.584 seconds, 67 ordered canonical identities, and separate
CANCELLED/artifact-absent status after the original 180-second deadline.
The pre-reload duplicate settled card is disclosed. Exact-ID cleanup for
the three new provider agents is awaiting authorization, so full grades
and the fresh Artifact’s post-cleanup download remain blocked. A
historical deleted-provider artifact was newly downloaded on 9af;
original F226 Question/Plan/reconnect and cleanup proof keep their
identities.

Retained failures: stale fixture 132/133; E8 48/135 with 87 reset-hook
failures; Mac 17 SQL failures/skips; earlier
title/startup/custody/Stop/deletion failures with unknown causes/bounded
recoveries; `dea023` Stop’s warning/50 duplicate identities; pre-repair
ACK and original Core CI failures. WSP’s 251-assertion, 7/8-suite
attempt failed physical PostgreSQL teardown; the unchanged wrapper rerun
passed 128. Retry alone establishes no cause or full local-suite pass.
All paid attempts remain recorded; vendor dollar cost is UNKNOWN.

## Risks

Experimental: Cursor independently acknowledges neither the effective
model nor instruction digest before the first prompt. Explicit values
are submitted and checked through behavior; unpriced provider dollar
cost stays UNKNOWN.

Callbacks need authenticated reachable HTTPS. Scope, revocation,
budgets, completion and cleanup remain authoritative. Operator Stop
intent is separate from physical containment. Saved legacy agents retain
their selected runtime. Prior shared title-save/deletion failures remain
disclosed.

## Model Used

OpenAI Codex, GPT-6 family, assisted with reasoning, repository tools,
code execution, browser verification, and delegated code review. The
serving model’s exact API ID and context window are not exposed. No
value is inferred from the model catalog.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used and the limits of available model
metadata
- [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 linked public issues or described the issue in this PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch describes the change and contains no internal ticket ID
- [x] I have run the scoped local tests reported above; pending gates
are stated separately
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented the risks
- [x] All required CI gates pass on the current head
- [x] Fresh Greptile is 5/5 with no unresolved findings on the current
head
- [x] I will address all reviewer comments before requesting merge

---------

Co-authored-by: Paperclip <[email protected]>
2026-11-03 09:01:39 -06:00
Nicky Leach 3a7bb4dac6 perf(runner): activate shared retained startup context (#18405) 2026-11-02 15:12:57 -08:00
DottaandPaperclip b9e94b85a6 feat(observability): trace Daytona lease acquisition and resume (#18626)
## Thinking Path

> - Paperclip manages work performed by AI agents.
> - The server prepares a remote execution environment before the agent
can respond.
> - OpenTelemetry traces show the time spent in each preparation phase.
> - A retained Daytona sandbox can take minutes to resume from an
archive.
> - The provider worker previously exposed no child timings for this
operation.
> - This pull request adds bounded lease phase spans under the existing
worker trace.
> - Operators can separate provider startup from lookup, teardown waits,
and workspace verification.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Daytona environment acquisition and resume tracing. A long worker
invocation currently hides the operation that consumed its time. Related
tracing foundation: #10999. No duplicate open PR was found.

**Subsystem affected**

The Daytona sandbox provider and the server's existing plugin span
validation boundary.

**Proposed behavior**

Show lookup, start or recovery, teardown waits, workspace verification,
and fresh acquisition setup as timed child spans. Group verification
steps beneath one parent. Record the bounded provider state before
start, including archived.

**Reason and benefit**

An operator can identify which step delayed the first agent response.
SDK startup includes its internal readiness wait; this change does not
claim to measure provider queue time separately.

**Breaking changes**

The opt-in trace schema gains fixed span names and one closed state
attribute. Execution, authorization, resource ownership, deadlines, and
retry policies keep their existing contracts.

## What Changed

- Add lease phase spans through the existing invocation tracer.
- Add the fixed span names and state values to host validation. Define
the state key in the shared startup attribute contract.
- Preserve the exact work result or error if tracing fails. Never repeat
work through the tracing wrapper.
- Test worker receipts for archived and warm resume, recovery,
concurrency, failure, disabled tracing, and cleanup.
- Document the waterfall and its attribution limits.

## Verification

- Daytona plugin suite: 525 passed, 7 skipped. The expanded tracing
suite separately passes all 12 cases.
- Host span validation and instrumentation tests: 46 passed, 3 skipped.
- Standalone Daytona TypeScript check passes.
- Module boundary and whitespace checks pass.
- Full recursive typecheck and full build pass.
- The full local Vitest attempt at
b007a8e64f stopped in chat shard 1: 337
passed, 2 failed, 1478 skipped. One retry-notice case exceeded 15
seconds. The next case then reported that fake timers were no longer
installed. A focused rerun of all four retry-notice cases passed. This
is not a full local suite pass.
- Final head 8e1f81523b passes CI and
Greptile 5/5, with no unresolved review threads. The CI chat shards pass
despite the preserved local timeout attempt.
- The expanded lease tracing suite passes 15 tests; host/shared tracing
suites pass 66 tests. Disabling the wait flag makes the new teardown
test fail, and restoring it passes. Standalone plugin typecheck passes.
- The tests drive the real plugin worker RPC and invocation tracer with
a controlled provider fixture. They validate receipts and host parent
IDs. They do not establish deployment or Honeycomb ingestion. Inspect a
new deployed run to verify the exported waterfall.

## Risks

- A new provider phase must be added to the closed host allowlist or it
appears as other.
- SDK-internal start and readiness work remains one measured operation.
- Older workers without withSpan run normally without these new detail
spans.
- Receipt budgets and clock validation can omit detail from very large
or invalid receipts.

## Model Used

OpenAI Codex, GPT-6, with repository tools and code execution. The exact
runtime model ID and context window were 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
- [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 <[email protected]>
2026-11-02 11:32:03 -06:00
AlexFrontegg 8424a51018 fix(redaction): scan long letter/digit runs in linear time (#16999)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip removes secrets from run logs, tool results and
diagnostics with `redactSensitiveText` before it stores or shows them.
> - The JSON field-name patterns in that function start with an
unanchored `[A-Za-z0-9_-]*`. Each start position inside a long run of
letters and digits scans to the end of the run, so the cost grows with
the square of the run length.
> - Base64 of text is one long run of letters and digits. A 40 KB base64
blob of source code in a shell command takes about 5 s to redact, and a
large Gmail RAW tool result does not finish.
> - The redaction runs on the main event loop, so one such chunk blocks
every request and every run on the instance.
> - This pull request makes these scans start only at the start of a
run. The scans become linear, and the redacted output does not change.
> - The benefit is that large base64 payloads no longer freeze the
server, and every secret that was redacted before is still redacted.

## Linked Issues or Issue Description

Fixes #15393. The tool content guard runs `redactSensitiveText` on tool
results. A Gmail RAW body is base64url of a MIME message, which is one
long letter/digit run. `validateToolContent` on a Gmail-shaped result
with a 68 KB `raw` field takes 59 s on master and 19 ms with this
change; a 5.4 MB `raw` field takes 0.65 s.

Thanks @ts-beat for the clear report in #15393.

Refs #12309 (false positives in the same patterns; this pull request
does not change which text matches).

I searched issues and pull requests for "redaction",
"redactSensitiveText", "quadratic", "backtracking", "hangs the server"
and "100% CPU". #15393 reports the hang. No open pull request fixes it.

**Subsystem affected:** secret redaction (`server/src/redaction.ts`,
`packages/adapter-utils/src/command-redaction.ts`), used by run logs,
tool content guards and run diagnostics.

**Problem:** `compactRunLogChunk` redacts each run log chunk before it
truncates the chunk. A shell command such as `printf '%s' '<base64>' |
base64 -d > file.ts` with base64 of source text makes one long
letter/digit run. Base64 of ASCII text contains `+` or `/` only where
every third byte is `>`, `?`, `~` or DEL, so random base64 does not show
the problem. One `redactSensitiveText` call on such a tool-call line
takes (local measurement):

| Blob | Before | After |
| --- | --- | --- |
| 4 KB | 66 ms | 1.1 ms |
| 8 KB | 272 ms | 0.5 ms |
| 16 KB | 1.1 s | 1.0 ms |
| 40 KB | 5.4 s | 2.5 ms |
| 1 MB | not measured | 36 ms |

The `\b`-anchored command patterns (`KEY=value`, JWT, `Authorization`)
and the CLI option pattern have the same quadratic cost on
dash-separated runs, for example base64url data.

**Proposed solution:** start the unanchored field-name scans only where
no name character precedes them (`(?<![A-Za-z0-9_-])`). A prefixed name
such as `myApiKey`, `x_auth_token` or `GITHUB_TOKEN` still matches,
because the scan from the start of the run absorbs the prefix.

**Alternatives considered:**

- Truncate or cap the input before redaction. Rejected: a cut can split
a secret and leave part of it unredacted, and the scan stays quadratic
below the cap.
- Run the scan in a worker thread with a deadline, as #15393 suggests.
That limits the damage, but the CPU cost stays, and a timeout must then
drop or block the content. It is still useful as an additional defense.

## What Changed

- `server/src/redaction.ts`: `JSON_SECRET_FIELD_TEXT_RE` and
`ESCAPED_JSON_SECRET_FIELD_TEXT_RE` start a field name only where no
name character precedes it. The lookbehind comes after the optional
opening quote, so quoted keys behave as before. The `Authorization` word
scan uses the same start instead of `\b`.
- `packages/adapter-utils/src/command-redaction.ts`: the `KEY=value`
pattern uses the same start. The JWT pattern starts only at the first
word character of a run. A CLI option name can no longer span `--`, so
each `--` starts its own short scan.
- Redaction still runs before truncation. `compactRunLogChunk` does not
change.

Why the output does not change: when an old pattern could match from the
middle of a run, it could also match from the start of that run, because
the `[A-Za-z0-9_-]*` prefix absorbs the extra characters. The engine
tries the start of the run first. So the old patterns never matched from
the middle of a run, and the new start condition only skips positions
that cannot produce a match. The old JWT pattern also always matched
from the first word character of a run. A CLI option name always ends at
the end of its run, so the match from the last `--` in the run ends at
the same place.

## Screenshots

No UI change.

## Verification

- New tests:
- prefixed secret field names in plain, single-quoted and escaped JSON
are still redacted, also when the name is glued to the end of a long
run;
- a 48 KB base64-of-source tool call through `compactRunLogChunk` stays
intact, the secret in the same command is redacted, and the call
finishes within 1 s;
- `redactSensitiveText` and `redactCommandText` handle 48-120 KB
letter/digit, dash-separated, `--`-separated and base64url runs within 1
s, and redact the secret that follows each run;
- `x--token`, `--x--token`, `a-b--api-key=`, `prefix-MY_API_KEY=` and
`--<jwt>` are still redacted, and `my-token is here` is not changed.
- Without the change, the timing tests fail: 9.3 s, 9.8 s, 32 s and 60 s
against the 1 s budget. The correctness tests pass with and without the
change.
- Mutation check: I reverted each of the six pattern changes one at a
time. Each revert makes at least one test fail.
- Differential fuzzing: 1.1 million random inputs built from secret
names, quotes, escaped quotes, dashes, dots, colons and JWT parts gave
the same output from the old and new `redactSensitiveText`,
`redactCommandText` and `redactDiagnosticText`.
- `cd server && npx vitest run <files>`: all 33 server test files that
use the redaction helpers, with embedded Postgres, 1730 passed.
- `npx vitest run --project @paperclipai/adapter-utils
packages/adapter-utils/src/command-redaction.test.ts`: 25 passed.
- `npx vitest run --project @paperclipai/adapter-utils`, complete suite:
80 of 82 files passed in the parallel run. The 2 other files
(`github-launcher-environment`, `sandbox-managed-runtime`) had 5 s
timeouts in git-based tests and passed on rerun.

## Risks

- Lookbehind assertions need ES2018. `redactCommandText` also runs in
the UI bundle. Node and current browsers support lookbehind, and the
server (`server/src/log-redaction.ts`) and the UI
(`ui/src/components/task-chat/task-chat-attachments.ts`) already use it.
- A change to a redaction pattern could leak a secret. The reasoning
above, the unchanged existing tests and the differential fuzzing show
the same output as before.
- Not changed here, found while checking: `redactDiagnosticText`
backtracks exponentially on an escaped JSON secret value made of many
escaped backslashes with no closing quote, and `redactSensitiveText`
redacts a value only up to its first quote of either kind
(`{"apiKey":"abc\"tail"}` keeps `tail"`). Both behave the same before
and after this pull request.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) by Anthropic, in Claude Code, with
tool use (shell, code search, and test runs) and extended thinking. A
human directed the design choices.

## 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
2026-11-02 10:49:08 -06:00
Devin FoleyandClaude Opus 5.5 978ffb6e11 fix(adapter-utils): tolerate entries removed during workspace snapshots (#18426)
Treat ENOENT from lstat, readlink, content hashing or opendir of a listed non-root entry as an absent entry during workspace snapshot walks. A concurrent restore can rename its temporary merge file between the directory listing and the read. A missing capture root and all other errors still fail.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 21:53:19 -07:00
DottaandPaperclip 59da15066a feat(runner): deliver native Kimi Code (#18391)
Deliver native Kimi Code setup, exact models, scoped credentials, verified packages and runtime packs. Preserve current upstream release attestations and restore verified same-run remote retry dispatch without weakening process ownership.

Co-Authored-By: Paperclip <[email protected]>
2026-10-30 22:26:15 -05:00
DottaandPaperclip b866fa4bdc feat(runner): add pinned Kimi Code native runtime (#18028)
Add the pinned Kimi Code runtime, native instruction and model admission, scoped credentials, and pending-candidate recovery contracts. Retain current master release attestations and provider-pack compatibility.

Co-Authored-By: Paperclip <[email protected]>
2026-10-30 21:58:12 -05:00
Devin FoleyandPaperclip 81ecaf0654 fix(server): bound failed lifecycle verification (#18383)
Failed lifecycle verification could allocate another test environment every minute indefinitely. Count failed host verification tests separately from claims, persist a repair wait for confirmed configuration failures or three generic failed tests, and clear it through authorized retry or execution configuration changes.

Preserve typed temporary execution-target failures through adapter checks so timeouts, transport failures and interrupted probes keep automatic recovery. Retain existing readiness, authorization, termination and cleanup gates. Document the policy and add real PostgreSQL persistence and actual OpenCode adapter regressions.

Verified 398 focused tests (one Windows-only test skipped on macOS), repository-wide typecheck and build, structural checks, fully green current-head CI and Greptile 5/5 with no unresolved comments.

Co-Authored-By: Paperclip <[email protected]>
2026-10-30 15:29:43 -07:00
Nicky LeachandPaperclip 6d61990692 fix: allow managed Git helpers to select a private interpreter (#18337)
## Thinking Path

> - Paperclip manages AI agents and their execution environments.
> - Managed Git helpers run next to those agents.
> - Some environments keep their Node interpreter outside the agent
PATH.
> - The current generator always emits an env-based Node shebang.
> - This pull request lets the environment select an absolute
interpreter path.
> - Existing callers keep the same default behavior.

## Linked Issues or Issue Description

Refs #18212.

**What happened?**

A managed helper can fail when Node exists only as a private runtime
binary. The caller must currently alter generated source or expose that
runtime through PATH.

**Expected behavior**

The source generator accepts an optional interpreter path. The
environment can keep its own runtime private.

**Steps to reproduce**

Generate a helper in an environment whose PATH has no usable Node. Run a
local Git command through that helper. The env-based shebang cannot
select the private interpreter.

## What Changed

- Add optional `interpreterPath` to `githubLauncherSource`.
- Preserve `/usr/bin/env node` when the option is absent.
- Reject relative paths, whitespace and NUL bytes.
- Document the option and test actual execution with an unusable Node on
PATH.

## Verification

- The affected adapter-utils package typecheck passed.
- All 26 tests in `packages/adapter-utils/src/github-launcher.test.ts`
passed.
- The new test runs a generated helper as a child process. It proves
that the selected interpreter bypasses a failing PATH-selected Node and
that the helper body stays unchanged.
- Invalid-path tests passed. Existing broker and local-Git tests passed.
- Full checks run in GitHub CI per the user's request. No full local
suite was run.

## Risks

The option changes only the shebang. Callers own interpreter
installation and immutability. Default behavior, PATH, credentials and
permission policy stay unchanged. No schema or workflow changes.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), high reasoning, with source
inspection and targeted execution. The host does not expose an exact
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 (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 <[email protected]>
2026-10-30 09:29:51 -07:00
Nicky Leach fba0ab8162 fix(runner): preserve GitHub helpers and Codex probe policy (#18212) 2026-10-30 07:42:10 -07:00
DottaandPaperclip 6da112ab40 perf: avoid flushing separate Git cache seed archives (#18308)
## Thinking Path

> - Paperclip manages work done by AI agents.
> - Native sandbox runs prepare a workspace before the agent can
respond.
> - A shared seed cache avoids repeated Git copies and archive
preparation.
> - Cache publication also flushes a branch-neutral Git seed that is
separate from recovery archives.
> - This cache can be rebuilt. Each reader checks the archive bytes
before reuse.
> - This change omits that separate cache file flush and measures the
remaining preparation gaps.
> - The benefit is less work before agent execution and clearer timing
evidence.

## Linked Issues or Issue Description

Refs #18240.

**What existing behavior does this improve?**

Workspace seed cache publication and latency diagnostics for native
sandbox preparation.

**Current behavior**

Publication flushes both cache archives to disk under the company lock.
Slow storage can delay the publishing run and cache readers. A cache hit
also combines private copying, hashing, and last-used bookkeeping in one
span. Early controller preparation has gaps before environment
selection.

**Proposed behavior**

Keep the existing flush for recovery-backed workspace and Git archives.
Omit the flush only for a separate branch-neutral Git cache archive.
Reject an alternate cache file that is the recovery file or its
hardlink. Keep archive checksums, private copies, atomic publication,
quotas, and company locking. Measure cache read steps and the existing
early preparation operations.

## What Changed

- Skip the flush only for the separate branch-neutral Git seed used by
the company cache.
- Preserve the existing flush for recovery-backed archives, including
hardlink storage. Reject cache-only files that alias recovery bytes
before or during publication.
- Add a regression with a blocked cache-only Git flush. The deployed
code waits; the new code publishes verified cache bytes without waiting.
Test storage failure for both recovery-backed archives and same-path,
hardlink, and replacement alias cases.
- Add cache copy, hash, and bookkeeping spans beneath seed lookup.
- Add timing around existing agent, conversation, runtime, issue,
identity, trust, and task context preparation. Keep their execution
order.
- Update the trust-order guard to recognize the timed call while
continuing to assert trust resolution precedes workspace selection and
credential setup.
- Document the cache contract and the new spans. Keep the old flush span
in the allowlist for compatibility with older observations.

## Verification

- The blocked-flush regression fails on the old code and passes with
this change.
- Focused tests (`vitest run` on the six affected cache, timing, and
native-sync suites) cover cache corruption, private-copy integrity,
quota and eviction races, branch binding, per-run recovery, native
workspace synchronization, trace parentage, and tracing disabled or
failing.
- 115 focused tests and both typechecks pass after rebase on current
master. The tests include recovery storage failures and an archive
replaced with a recovery hardlink during verification.
- All seven trust tests pass with the actual service and migrations,
using a fresh database on an existing local PostgreSQL server. The local
embedded server could not allocate host semaphores, so only its test
startup helper was substituted; assertions and service code were
unchanged.
- A real SDK export with a retained public seed verifies that cache-read
detail spans have the lookup as their parent. The endpoint-off control
produces no spans.
- Current revision `28dea63432d462cad160906f8dfc2d21c81d3446`: all 53
checks pass, two optional checks are skipped, Greptile is 5/5, and no
review threads are open.
- No new staging latency claim is made by this PR. Hosted measurement
remains necessary after delivery.

## Risks

A crash or power loss can discard the cache-only Git seed. A later read
rejects missing or corrupt cache bytes and prepares the workspace again.
Recovery-backed archives retain their previous flush behavior. The
change adds no flush to warm per-run persistence. Hashes and
private-copy checks remain required. The new spans use the existing
opt-in exporter and closed names; they record no task content.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use, and code
execution. The session does not expose the exact model SKU or
context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have 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 <[email protected]>
2026-10-30 09:13:08 -05:00
DottaandPaperclip ed8fae8dbb fix(workspaces): preserve completed shallow sandbox exports (#18221)
## Thinking Path

> - Paperclip coordinates agent work and preserves its results.
> - Remote execution copies completed workspace changes back to the
host.
> - An agent can fetch a published branch with shallow Git history.
> - A full Git bundle omits the shallow boundary metadata and can fail
host connectivity checks.
> - This pull request carries the boundaries through the full-bundle
fallback and imports through normal Git fetch.
> - Recovery must also recognize completed native work on a reusable
lease without treating it as ordinary executor reuse.
> - Completed work can be saved without another provider turn, while
existing host history remains connected.

## Linked Issues or Issue Description

**What happened?**
Sandbox finalization can fail with a missing parent commit after an
agent checks out a separately fetched shallow branch. Re-exporting a
full bundle repeats the failure because Git bundles do not encode
shallow boundaries.

**Expected behavior**
Import the completed branch and saved files. Preserve existing host
ancestry and concurrent host edits. Keep normal Git connectivity and
workspace merge checks.

**Steps to reproduce**
1. Prepare a sandbox from one host commit.
2. Fetch a different published branch with depth one and check it out.
3. Save a result file and restore the sandbox workspace.

**Paperclip version or commit**
Reproduced against master before this repair. The regression fails with
the missing-parent error before the production change.

Related public context: #15268 covers rewritten history integration.
This repair handles shallow bundle transport before integration.
Searches found no equivalent open repair.

## What Changed

- Export shallow boundaries beside the full bundle when the delta import
lacks history.
- Unpack into a temporary shared bare repository. Keep parent
connections already available on the host and import with Git's shallow
fetch protocol.
- Reject malformed boundary values and retain existing connectivity,
branch, and concurrent-work merge guards. Keep boundary probes within 15
seconds without consuming the separate 60-second Git import budget.
- Let source-bound preservation recover reusable leases only for the
finalizer-selected, accepted successful native result and matching
completion contract. Retain provider-stop, physical ownership and
host-writer checks; ordinary reusable sources remain blocked.
- Classify invalid Git metadata separately from omittable unsafe
archives. Keep native finalization failed and preserve service-owned
file recovery; carry the sender's dedicated rejection across the command
boundary without transient retries.
- Share the failure allowlist across bounded run/activity projections
and cancelled-preservation selection. Migration `0416` rebuilds the
matching partial index; task chat retains the non-retryable recovery
notice.
- Add real Git, filesystem sandbox and database recovery regressions.
Update execution semantics and affected verification maps.

## Verification

- Red: real filesystem sandbox restore failed with the same
missing-parent/revision-walk error before the export repair.
- Green: 240 focused Git, workspace restore, sandbox transport and
native-finalization tests pass. Coverage includes both transfer paths,
malformed/oversized metadata, a genuinely shallow host with its original
boundary intact, and concurrent host work with both merge parents
verified.
- Red/green: a 14-second simulated probe left the final real Git fetch
only 45,893 ms before the budget split. It now retains over 55 seconds
after the probes; a 16-second probe exhausts its own budget before host
import.
- Red/green: an oversized read progress report was retried three times
before its distinct metadata classification. It now aborts the first
read before payload consumption, leaves the host tip unchanged and does
not retry.
- Red/green: the real metadata error was swallowed by best-effort export
and native finalization incorrectly returned success. Both regressions
now pass; all 240 focused Git/restore/native-finalization tests pass. A
PostgreSQL/filesystem fault-injection test also verifies retained files,
failed run, service-owned recovery barrier, no preservation receipt and
no new wake.
- Red/green: duplicate metadata produced 47 Git process starts before
deduplication; the bounded-work regression now passes. Oversized byte
and boundary inputs fail before Git probes.
- Recovery red: both reusable lease policies exhausted recovery despite
a selected accepted successful result. The assembled
PostgreSQL/filesystem path now passes for retained and both reusable
policies, including idempotent re-admission after exhausted automatic
recovery, saved files, no agent wake, and once-only existing-result
finalization.
- Source verification controls reject ordinary reusable leases,
rejected/failed/unselected results, another physical allocation owner,
and an active host writer without changing recorded state.
- The 78-test PostgreSQL/filesystem recovery/source suite passed before
the metadata classification correction. Its new persisted
metadata-failure regression also passes. Full workspace typecheck and
build pass at final head `69f10daab2`.
- `node scripts/check-maps.mjs` and `git diff --check` pass.
- Full local `pnpm test:run` attempts did not finish. They are not
counted as passes. Early focused runs exceeded 5-second and 30-second
fixture deadlines; the final serial suite passed all 176 tests with a
30-second per-test budget in 56.8 seconds.
- Red/green: both bounded PostgreSQL projections dropped the new code in
oversized results. All 19 source/projection cases now pass, plus an
actual index-predicate test that excludes a metadata repair hold and
still selects ordinary cancelled work.
- Red/green: an oversized sender `.git/shallow` lost its error type
before host validation. The real transport test now verifies one typed
rejection, no shallow-payload read, and an unchanged host tip. All 465
command/sandbox/task-chat tests pass, and token/migration gates pass.
- Full typecheck/build pass after these final contract corrections at
`69f10daab2`; all 20 PostgreSQL source/projection/index contracts pass.
All current-head CI checks passed; Greptile is 5/5 with no unresolved
review threads. Merged #18230 supplies the CI-cache and Linux
shell-fixture baseline repairs; their duplicate changes have been
removed from this PR. The final Runner lane passed after one justified
rerun of an unchanged authentication fixture timeout (all 11 fixture
tests also pass locally). Previous CI failures and incomplete local runs
remain recorded; live deployment and retained-run recovery remain
separate verification.

## Risks

- The fallback creates a temporary shared bare clone. Metadata is
limited to 64 KiB and 256 unique boundaries, duplicate/parent probes are
cached, and Git import has a 60-second command budget plus a separate
15-second boundary-probe budget. It runs only after a missing-history
import failure.
- Incorrect boundaries could hide real host ancestry. The importer
retains available parent connections, and regressions cover that case.
- The full-bundle fallback can transfer a bundle twice. This preserves
the existing fast delta path; removing that extra transfer is outside
this correctness repair.
- Preservation eligibility expands only for completed native exports on
reusable leases. It creates no provider turn and leaves ordinary
reusable leases ineligible. Database/filesystem regressions cover result
selection and competing physical/host ownership.
- The migration rebuilds one partial index; no tables, columns or user
records change. There are no agent execution admission, model, trust,
recovery permission, workflow or lockfile changes.

## Model Used

OpenAI Codex, GPT-6, with reasoning, code execution, and browser tools.
The exact serving variant and context-window size are 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
- [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 <[email protected]>
2026-10-29 20:02:53 -05:00
DottaandPaperclip 2aa3ea47e3 Reduce restored workspace overhead and trace cache publication (#18240)
## Thinking Path

> - Paperclip runs agents in isolated workspaces.
> - A native sandbox turn must restore its workspace before the provider
starts.
> - Restored Git metadata can cause expensive work in the provider's
first snapshot.
> - Cache replay also hashes the same archives twice, and workspace
helpers open sessions that do not need output.
> - This pull request removes that repeated work and refreshes the
restored index before OpenCode starts.
> - It also adds cache-publication timings so the next measurement can
separate quota checks, lock waiting, and archive IO.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Native sandbox workspace preparation and copyback.

**Current behavior**

Cache replay validates shared archives, copies them, and validates them
again. Exit-status-only helpers use persistent sessions. A restored
checkout can make OpenCode's first snapshot recheck much of the
repository. Cache publication has an unexplained interval after archive
verification.

**Proposed behavior**

Validate the private archive copy that staging will consume. Use the
existing one-shot command path for helpers that only need an exit
status. Refresh the Git index after workspace transfer for a new native
OpenCode sandbox. Keep the refresh inside the awaited inbound barrier
and use the existing command timeout. Add nested publication spans.

**Reason and benefit**

Two matched local component pairs reduced preparation plus provider time
from 8.563/8.553 seconds to 7.313/7.511 seconds, including the refresh
cost. Two alternating remote copyback pairs reduced the median from
9.110 to 7.943 seconds. These are component results. They do not
establish an end-to-end hosted improvement.

Related: #18181 and the earlier closed #18195. This implementation uses
the existing fully awaited transfer boundary. It does not use an
optional preparation deadline.

## What Changed

- Materialize cache hits into caller-owned staging and validate those
exact bytes once. Remove incomplete copies on failure.
- Select the existing one-shot path for exit-status-only run and removal
helpers. Output-reading helpers keep their existing behavior.
- Refresh restored Git stat metadata before OpenCode starts. Enable this
only for newly acquired native OpenCode host-current workspaces. Skip
recovery and retained sandboxes. Keep dirty files unstaged, accept Git's
dirty-worktree status, and propagate real command errors.
- Trace cache capacity checks, eviction, the body after lock
acquisition, archive copy, sync, and hashing. Keep the operator endpoint
gate and bounded phase names.

## Verification

- On the rebased branch: 202 focused tests pass: 152 workspace
seed/cache/runtime tests, 27 managed command tests, and 23 native
workspace/tracing tests. Adapter TypeScript build and server typecheck
pass.
- Tests cover corrupted copies, cache changes after private
materialization, dirty working trees, quoted paths, recovery, the
awaited refresh barrier, real command failure, and company-lock
contention.
- A real-filesystem probe through the server phase mapper verified
parentage and endpoint-off behavior. A controlled lock wait stayed
outside the locked-publication span. No external export was used.
- A full local Core/Daytona replay on the pre-rebase implementation
saved its synthetic reply. It retained the pinned large public
workspace, all 209 skill files, positive Git context, and 66 tool
schemas. Both model calls stayed on sandbox loopback. Index refresh took
0.644 seconds and the first provider snapshot took 0.499 seconds. The
run took 162.004 seconds, including large laptop uploads; it is not a
hosted acceptance result or a matched end-to-end comparison. The failed
installer attempt remains recorded separately. Normal cleanup removed
all owned sandboxes.
- The current head passed 53 CI checks, with two optional skips.
Greptile is 5/5 on the exact head, with no open review threads. No
deployment or hosted latency claim is included.

## Risks

- Index refresh adds one awaited command. It uses the existing timeout.
A real Git failure stops preparation.
- Cache hits need temporary disk space for a private copy. Existing
corruption fallback and per-run recovery archives remain.
- One-shot command transport must preserve exit status and timeout
behavior across providers. The agent's native session and output-reading
helpers are unchanged.
- Local component gains are small relative to the remaining hosted
latency. Batch the fixes with the diagnostic spans; measure the actual
deployed path before claiming success.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use, and code
execution. The session does not expose the exact model SKU or
context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have 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 <[email protected]>
2026-10-29 19:32:28 -05:00
DottaandPaperclip 54cc522e86 feat(runner): add gated native Antigravity ACP execution (#18188)
## Thinking Path

> - Paperclip manages agents and their work.
> - Native Runner must bind each provider turn to the selected task,
account, model, and instructions.
> - Antigravity needs native ACP admission and physical process
ownership across continuation.
> - A saved conversation must not reuse an old process owner or silently
switch its effective model.
> - This pull request adds the native candidate behind exact operator
admission.
> - The default release gate remains closed while qualification is
pending.

## Linked Issues or Issue Description

Builds on merged #18187. Product account setup and the candidate
producer are in dependent #18189. The repeatable browser journey is in
#18190. Refs: #12503, #14938; those proposals use legacy CLI adapters.

**Agent or provider**

Google Antigravity ACP 1.4.3.

**Why this adapter is useful**

Users need to set up an Antigravity agent and run real work through
their managed Google subscription.

**How the agent is invoked**

Native Paperclip Runner uses the pinned Antigravity ACP runtime. Exact
operator admission is required while qualification remains pending.

## What Changed

- Add native Antigravity ACP model and instruction admission,
selected-account homes, and fenced refresh ownership.
- Carry exact candidate authority from the control plane through Runner,
Rust, the sidecar, and each current run. Clamp daemon lifetime to the
absolute permission expiry, including replacement owners.
- Preserve a rotated OAuth refresh token before later identity
verification can fail, including failed or cancelled startup admission.
Make private lease-close retries safe.
- Bound native probe retirement and retry the exact original owner; do
not delete credential homes before physical proof.
- Preserve Gemini API-key recovery when shared Google subscription
capability is added.
- Resume the same tagged native conversation with fresh per-turn process
ownership and physical native retirement.
- Include native manifests in isolated Rust image stages, release cache
keys, and dev rebuild detection. The workflow changes only extend
existing cache input lists.
- Keep current master provider behavior and append Grok 13, Pi 35, and
Copilot 84 input identities. Preserve prior identities, including saved
Copilot 82 and 83 runs.

## Verification

- Runner TypeScript main checks, profile generators, Rust formatting,
and offline workspace checks pass. The native backend integration target
passes 28 tests.
- The complete transport and cleanup group passes 387 tests without
unhandled rejections. These checks use local scripted children and
actual process and port ownership.
- Saved-profile decoding, native-execution parsing, installation, and
selected-credential controls pass 208 focused tests. All 109
pre-existing profile identity files remain byte-for-byte unchanged.
Copilot, Grok, and shared TypeScript/Rust generation checks pass.
- The final recovery fixture passes all seven cases. After the master
rebase, the combined recovery and owned-Node-runtime group passes all 10
cases. Each expiry case sets one 45-second absolute grant before
startup. It includes the deliberate six-second cold delay, four existing
eight-second command budgets, and active-turn headroom. The test proves
an active turn before expiry and then checks the daemon, owned process
group, child processes, and all three native port fences. The 60-second
test deadline does not renew provider authority.
- The consolidated model-discovery and pending-release group passes 22
tests. Google credential compatibility and auth recovery pass 25 tests,
including existing Gemini API-key recovery. The full product settings
test group remains intact in #18189.
- Earlier failures remain in their original CI runs and local records.
They exposed the Copilot 82 decoder gap, an expired five-second fixture
bootstrap ticket, and a fixture grant consumed before activation on
loaded CI. Each repair has a separate result. Production ticket limits
and admission behavior are unchanged by the final test commits.
- An unrelated decision-expiry check failed in [the earlier CI
run](https://github.com/paperclipai/paperclip/actions/runs/40941986698/job/123688668997).
Its owning code is unchanged from the base. The unchanged suite passes
20 tests on isolated PostgreSQL. A controlled database rollback
reproduces the earlier wake: clean startup schedules the 120-second
decision expiry, while rollback reconciliation leaves a 30-second
continuation-coalescing wake. The decision stays open, with no wake
delivery or error. The fresh final-head server shard passes; the failed
result is preserved.
- Final native commit `ec6bc7e4d77f5ae0cc4946dbba6c662e95a87f5b` passed
`ci / verify` and `ci / e2e`. Fresh Greptile and Paperclip reviews both
score 5/5, with no open findings. Antigravity retains the recorded
native `.5` closure and exact low-model profile. This PR does not claim
global or cross-platform qualification.

## Risks

- The new transport carries private managed credentials. Exact
ownership, account generation, model, instructions, and native
retirement are fail-closed.
- Only local macOS ARM64 has a verified complete Antigravity runtime.
Default Antigravity task execution remains disabled without current
trusted operator admission. The Google server sign-in lane and
production candidate producer ship in #18189; this native slice does not
expose the subscription catalog choice.
- Shared launch inputs change provider identities. New identities are
append-only; existing recorded identities remain available.

## Model Used

OpenAI Codex (GPT-6). The host did not expose the exact model revision
or context limit. The model used tools for code edits, tests, and
review.

## 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 <[email protected]>
2026-10-29 16:36:12 -05:00
AlexFrontegg 57658fcb30 fix(codex): pass the run's GitHub broker capability to shell commands (#17605)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - When an execution has managed GitHub, Paperclip gives it token-free
`git` and `gh` launchers. Each launcher call gets the run's credential
from the broker with a per-run capability
(`PAPERCLIP_GITHUB_BROKER_TOKEN`).
> - The launchers run inside the agent's shell commands and read the
capability from their environment.
> - #17216 limits the environment of Codex shell commands to an
allowlist, and it keeps every `PAPERCLIP_GITHUB_*` name out.
> - So on legacy Codex through ACP, the launchers do not get the
capability, report `capability_missing`, and continue without
credentials.
> - The native runner already passes the same names to its Codex
provider (`GITHUB_CREDENTIAL_ENVIRONMENT_KEYS` in
`packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs`).
> - This pull request adds the broker capability to the legacy Codex
shell allowlist when Paperclip set it for the run.
> - The benefit is that managed `git` and `gh` work again for legacy
Codex agents.

## Linked Issues or Issue Description

Fixes #17599
Refs #16549, #17216

## What Changed

- `packages/adapter-utils/src/codex-shell-environment.ts`: the shell
allowlist also includes `PAPERCLIP_GITHUB_BROKER_TOKEN` and
`PAPERCLIP_GITHUB_BRIDGE_TOKEN` when they have a value in the run's
environment. Only the names enter
`shell_environment_policy.include_only`. The values stay out of the
Codex config and the arguments.
- `packages/adapter-utils/src/codex-shell-environment.test.ts`: a new
test for the capability. The "runtime transports" test no longer lists
the broker token, because the launchers need it in the shell.

## Verification

- `pnpm --filter @paperclipai/adapter-utils exec vitest run
src/codex-shell-environment.test.ts`: 5 passed. The new test fails
without the change.
- Before the change, on a deployment with a legacy Codex agent that has
managed GitHub, `gh pr view` in a shell command fails with `Paperclip:
GitHub capability_missing; continuing without managed credentials`, and
the shell has the broker URL but not the broker capability. I will add
the result after the change is deployed there.

## Risks

- Codex shell commands can now read the run's broker capability. The
broker accepts it only while the run is live (`captureRunIdentity`
requires `status === "running"`), and it returns the credential that the
managed `gh` command receives anyway.
- Agents cannot set these names in their configuration.
`stripForbiddenEnvBindings` removes them
(`server/src/services/heartbeat/run-preparation.ts`) before the server
adds the run's own values.
- No change for runs without managed GitHub: Paperclip does not set the
capability, so the allowlist does not add it.

## Model Used

- Provider and model: Anthropic Claude Opus 5.5 (`claude-opus-5-5`),
used through Claude Code.
- Capabilities used: tool use, code execution (local tests and type
checks), and repository search.
- A human reviewed the diagnosis and the change before this pull request
was opened.

## 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)
- [ ] 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
2026-10-29 13:18:49 -07:00
Nicky Leach e2cfdc7e0c feat(plugins): expose GitHub credential broker to task providers (#18178) 2026-10-29 10:52:44 -07:00
DottaandPaperclip 9f3fc2094f Fix shared workspace seed capacity and trace deferred copyback (#18181)
Apply the existing company quota to company-shared workspace seed buckets. Preserve individual seed limits and recheck size after verification.

Trace each deferred native workspace finalization check and its resumed copyback under the same hashed run ID. Keep existing admission, ownership and retry behavior.

Co-Authored-By: Paperclip <[email protected]>
2026-10-29 12:44:34 -05:00
DottaandPaperclip 27fbdb2a74 perf(workspaces): reuse verified seeds across task branches (#18159)
## Thinking Path

> - Paperclip manages AI agents and their workspaces.
> - Remote runs stage a recoverable copy of the selected host workspace.
> - New tasks use separate workspace IDs and Git branches.
> - The verified seed cache currently treats those identities as
different content.
> - This causes repeated clones and seed verification when task files
and Git commits are identical.
> - This pull request shares immutable seed content within a company and
applies each task's branch to a private archive.
> - It also removes credential broker calls for two local Git snapshot
commands.

## Linked Issues or Issue Description

**What happened?**

New remote tasks rebuild the same Git seed under different workspace IDs
and branches. In a measured staging run, preparation took 29.7 seconds.
Git clone took 6.1 seconds and optional seed publication took 7.8
seconds. Publication overlapped the 4.4-second transfer, so those values
cannot be added. Local `diff-files` and `write-tree` commands also
request GitHub credentials.

**What did you expect?**

Reuse a verified seed when filesystem contents, Git HEAD, origin, and
nested repository layout match. Preserve each task's branch, recovery
archive, ignored files, and copyback behavior. Keep local snapshot
plumbing independent of GitHub credential capture.

**Steps to reproduce**

1. Configure a remote native environment and a Git project workspace.
2. Create two tasks from the same source commit with different task
branches and workspace IDs.
3. Inspect the workspace preparation spans. Both tasks currently clone
and publish the same seed content.

**Paperclip version and deployment mode**

Reproduced on Core `25f1070fbdabd96b26cdf199e624a77905f3ca37`, with a
managed remote sandbox and native Runner. The local comparison used a
public repository at a fixed commit with roughly 13,000 entries.

Refs #17886, #16398, #16399, #17357. The Git launcher extension builds
on Jamie Buchanan's work in #17886. The other PRs establish the seed
verification, recovery, and eviction contracts preserved here. A search
found no open PR for sharing these verified seeds across task branches.

## What Changed

- Use a company-scoped cache bucket for native host-current preparation.
Keep the existing bucket and company storage limits and eviction rules.
- Remove branch names from the shared generation identity. Keep file
contents, Git HEAD, origin, and nested layout in that identity. Keep the
default workspace-scoped identity unchanged for other callers.
- Store a detached Git archive in the shared cache. Copy it and append
the selected branch metadata before upload and private per-run
persistence. Existing durable-seed replay can read the private archive
without the new cache option.
- Forward the scope through the execution target and both
command-runtime paths.
- Treat `diff-files` and `write-tree` as local Git commands. Network
commands, unknown commands, and managed commit identity keep their
existing credential path.
- Test new workspace IDs, named and detached branches, nested
repositories, dirty and ignored files, symlinks, recovery, corrupt
caches, refused publication, and copyback.

## Verification

- Current head `6087bac274d50804d1b332646e43f3b8d41d798f`: 53 checks
passed, two optional checks skipped, Greptile 5/5, no unresolved review
threads. Linux CI passed.
- Adapter-utils TypeScript 7.0.2 typecheck passed.
- Server package typecheck passed, including its Runner and plugin build
prerequisites.
- 140 seed-cache, seed-reuse, and sandbox-runtime tests passed after
rebasing.
- 44 Git launcher and command-runtime tests passed after rebasing.
- 30 native workspace preparation, warm history, and
retention-configuration tests passed. All 56 retention tests also passed
(5.72 seconds) after repairing missing library aliases inside the
installed macOS Postgres package. That local dependency repair changed
no repository source. The earlier startup timeouts occurred before test
assertions.
- The new seed reuse regression failed before the change because the
second branch cloned again. It passes through the execution target and
both command-runtime paths after the change.
- Clean local preparation comparison: two alternating measured pairs,
with an existing verified company seed and new task branches. Baseline:
19.63 and 9.85 seconds. Shared cache: 5.18 and 4.80 seconds. Median:
14.74 to 4.99 seconds. Each baseline cloned and published; each
shared-cache run skipped both. Branch and file checks passed. The
complete comparison loop took 58 seconds.
- The comparison used real Git, archives, snapshot workers, and local
file transport. It used no model calls. It does not establish a complete
staging response time. The first generation still pays the cold
preparation cost.

## Risks

- A shared cache hit requires an identical generation that is still
present. The company bucket retains the existing 16-generation/1 GiB
bucket limit; busy companies may miss more often.
- The branch binding appends entries to a private tar archive. Tests
cover the host platform; Linux CI must also pass before merge.
- A cache miss, corruption, or capacity refusal must retain the private
recovery path. Existing tests exercise those cases.
- Git plumbing can still encounter repository-specific filters or
missing objects. This extends the existing local-command policy by two
commands; it does not grant network credentials to them.
- The latest measured real staging reply took 77.8 seconds. This change
targets a bounded part of that path and does not claim the
under-15-second response goal is met.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and repository tools. The
session does not expose the exact model revision or context window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have 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 <[email protected]>
2026-10-29 09:19:23 -05:00
DottaandPaperclip b81b870732 Add QA smoke journeys and OpenCode run traces (#18098)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Product E2E journeys verify saved application behavior across
deployments.
> - Task creation and questions need reusable smoke selections and exact
run attribution.
> - Operators also need to inspect the OpenCode execution behind a
measured question.
> - Company observability does not currently support native OpenCode.
> - This change adds the journeys and a bounded projection of normalized
OpenCode run events.
> - The projection follows the configured capture policy and retains the
current run identity across warm reuse.

## Linked Issues or Issue Description

Related foundation: #17262. Related alternative: #17989 exports metadata
from OpenCode's native OTLP stream. This change uses normalized run
events to support the existing content switches and per-run linkage. It
does not forward the native stream. The two PRs overlap in launch
registration and must be reconciled before both land.

**What existing behavior does this improve?**

Reusable Product E2E smoke selections and company observability for
native OpenCode.

**Current behavior**

The journey catalog lacks reusable task creation and attached-agent
question checks. Native OpenCode is marked unsupported for company
observability, so a successful destination connection test does not
prove that agent traces will appear.

**Proposed behavior**

Add explicit task-create and ask-question selections. Verify saved state
and exact company, issue, agent, model, environment and successful run
attribution. Export normalized OpenCode turns, tools and usage to the
configured OTLP destination. Honor each capture switch and redact known
secrets.

**Reason and benefit**

CI can run a small measured smoke selection, retain its screenshots and
checks, and link results to traces belonging to the same run.

**Breaking changes**

Existing selectors remain unchanged. OpenCode export is opt-in through
existing company settings. Legacy OpenCode stays unsupported.

## What Changed

- Add reusable task creation and question journeys, configured-agent
capability, owned-fixture checkpoints and strict evidence validation.
- Add an OpenCode-owned projection of normalized run events. No provider
SDK or raw payload schema enters heartbeat coordination.
- Extend the private launch union and reserved environment allowlist for
native OpenCode. With export disabled, no receiver request runs.
- Keep destination headers in the parent wrapper and out of the actual
provider child environment and configuration.
- Bound each turn to 64 tool and 64 usage entries, 8 KiB per text field
and a 1 MiB request. Use a three-second request deadline, four
concurrent exports, and no retries or idle worker.
- Pass the rotated runner authority to each proxy turn. A retained
process no longer attributes later turns to its original launch run.
- Update settings copy, observability documentation and capability maps.

## Verification

- `pnpm -r typecheck` and `pnpm build` passed after the implementation
changes.
- Adapter launch/redaction: 9 tests passed. Projection and actual proxy:
12 tests passed before the review fix; the expanded projection suite
passed all 12 cases after the fix. The proxy test exercises two runs in
one process against a real local HTTP receiver and a fake provider.
- Full OpenCode driver/projection suite: 72 passed, one existing
response_wake test timed out at five seconds. A focused check then
passed both existing omitted-continuation cases and the new trace export
case. The timeout cause remains unconfirmed.
- Runner package TypeScript and Rust `cargo check` passed. Map and UI
token checks passed.
- Prior journey validation: 273 tests across 12 files and the journey
TypeScript check passed.
- Broad local `pnpm test:run` encountered native-runtime integration
failures with repeated embedded PostgreSQL initialization failures and
was stopped. All 16 failing cases passed in an isolated run outside the
filesystem sandbox (961 unrelated cases unselected). The broad local run
is not a clean acceptance result. A later focused database-backed
chat/heartbeat run passed all six selected observability cases,
including native OpenCode launch and successful allocation (1,809
unrelated cases were unselected).
- All CI gates passed for `21085f880d8babc9efeb22d0623db99613d33093`,
including packaged-runner, clean-install, browser and build checks:
https://github.com/paperclipai/paperclip/actions/runs/40884658995 .
Greptile reviewed the same commit at 5/5 with no unresolved findings.
- Verified OTLP JSON acceptance and indexing with an explicitly
synthetic transport marker. No live OpenCode model trace or deployed
smoke acceptance is claimed yet.

## Risks

Export is best effort. Abrupt termination, recovered in-flight turns
without a new submitted event, limits or receiver failure can lose
traces. Export failure does not change task success. The projection
represents observed normalized events, not raw native model spans or
hidden reasoning. Known-secret redaction is not an OS isolation
boundary. UI changes can break journey locators. Deployment adapters
retain credential, ownership and cleanup responsibility. Both the
controller and native runner bundle must be updated before live
qualification.

## Model Used

OpenAI Codex, GPT-6, with reasoning, code execution and browser tools.
The exact serving model ID and context size 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 (targeted suites; broad
local limitation documented above)
- [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 <[email protected]>
2026-10-29 08:57:12 -05:00
DottaandPaperclip 7d8d1fbc1d fix(adapter-utils): provide a default author for local commits (#18094)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/209/changes/paperclip/209
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents use managed launchers for Git and GitHub commands.
> - These launchers exclude host credentials and host Git identity.
> - A checkout without an author cannot save a local commit when managed
GitHub identity is absent.
> - A local author default belongs in the launcher that owns this
isolation.
> - This pull request supplies a private Paperclip default and preserves
explicit and managed identity selection.
> - Agents can save local work without manual author setup or extra
GitHub access.

## Linked Issues or Issue Description

**What happened?**

An ordinary `git commit` fails with `Author identity unknown` in a fresh
checkout without author settings or a usable managed GitHub identity. An
agent must set its own author to continue.

**Expected behavior**

The managed launcher supplies a default local author. Repository and
command settings override that default. A current managed identity
retains its existing precedence.

**Steps to reproduce**

Run the managed launcher in a fresh Git repository without author
settings or broker credentials. Add a file, then run `git commit -m
work`.

**Paperclip version or commit**

Reproduced at `063cfd43813cabf18417605fb9eefb9aacf95c8d` on Linux with
Node 25.9.0 and pnpm 9.15.4.

**Deployment mode**

Managed sandbox execution. The shared launcher also serves local and SSH
execution that selects managed access.

Related: #16662 changes precedence for a connected GitHub account. #8945
assigns identity to managed worktrees. This repair supplies the missing
anonymous launcher default and keeps the current managed precedence. It
does not require either change.

### Hypothesis and thesis for a fix

**Hypothesis:** The launcher removes inherited author variables and host
Git config. Without a broker identity or explicit repository settings,
it has no author. Four real-Git cases reproduce the reported error on
the base revision.

**Thesis for a fix:** Put `Paperclip <[email protected]>` in a
private Git config for each operation. Git treats it as global config,
so repository and command settings can override it. The existing broker
environment still takes precedence. Do not store a person's identity in
shared checkout config, where it could remain after steering or
revocation. The default grants no GitHub access.

## What Changed

- Write the default author to the existing private operation directory
with mode `0600`. Remove it when the operation exits.
- Point managed Git at this file while keeping host global/system
config, credentials and SSH identity excluded.
- Add real-Git regressions for missing, absent, unavailable and rejected
access. Preserve explicit overrides, identity changes and revocation
controls.
- Verify two shell commits through normal launcher staging and a commit
after anonymous heartbeat cleanup. Check saved files, commit metadata
and temporary config cleanup.
- Update identity documentation and the two run map recipes.

## Verification

- Red: the four new local-commit cases fail on the base launcher with
`Author identity unknown`.
- Green: all 80 focused tests pass in one run across
`github-launcher.test.ts`, `github-launcher-environment.test.ts`,
`heartbeat-github-launchers.test.ts` and `native-github-access.test.ts`
(23.31 seconds).
- A controlled filesystem failure first made the candidate discard valid
managed credentials. After the correction, the same real launcher
process retains them. Default-author file failure does not change
operation-directory readiness.
- Review found a second failure: a partial write can leave invalid Git
config. The real-process test failed before the repair with `fatal: bad
config line` from `git status`. The launcher now selects the default
file only after the write succeeds. Both write-failure cases retain
managed credentials, allow status, require identity when no default is
available, and save a commit with explicit identity. The test reads the
stored commit metadata with real Git.
- Real shells, generated launchers and Git create and inspect saved
commits. Sandbox transport and broker replies use controlled fixtures.
No live cloud rollout or GitHub write acceptance is claimed.
- The isolated test process excludes inherited task launcher paths, Git
config and broker authentication variables. An initial run exposed those
test-environment inputs; the isolated replay passed. The heartbeat
assertion now checks the private default file and its cleanup instead of
expecting `/dev/null`.
- Adapter-utils typecheck and build pass. Map validation and `git diff
--check` pass.
- Conflict repair on 2026-10-29: merged master
`fa5e70faf47ef2ca20bf2daf95b7503c94469b89` into the PR branch. Resolved
19 conflict paths by retaining master for unrelated files. All seven Git
identity files remain byte-for-byte equal to the reviewed fix. The final
diff is seven files, 145 additions and 15 deletions.
- At head `c501e3f28369d404c91082b89188dc150c9362a9`, all 80 focused
tests pass again (37.53 seconds). Adapter-utils typecheck and build
pass. Map validation and migration checks pass. The PR diff passes `git
diff --check`.
- Full workspace typecheck and build were attempted again. Both stop
because this sandbox has no Rust `cargo`. The duplicate full local
Vitest run was stopped after about five minutes without a final result
while full CI was running. The earlier full local attempt was stopped
without a final result. No local full-suite pass is claimed.
- GitHub now reports the PR as mergeable. All current-head checks have
completed. [Full CI run
40878973679](https://github.com/paperclipai/paperclip/actions/runs/40878973679)
passes, including typecheck, build, Rust, test shards and all eight E2E
shards. The exact-head check inventory has 54 successes, one neutral
Paperclip Review and two Storybook skips. Snyk passes. Greptile and
Paperclip each reviewed `c501e3f28369d404c91082b89188dc150c9362a9` at
5/5 with no actionable findings. All review threads are resolved. GitHub
reports the PR as clean and mergeable.
- The prior CI run had one failed E2E test: `a lost creation response
requires reload and cannot blindly replay commands`. Its YAML parser
received page text that starts with `Skip to Main Content`. The new run
passes E2E shard 7/8 and the full E2E gate. The prior failure remains
part of this record. A passing run does not establish the earlier
failure's cause. This change does not alter that test or the agent
configuration UI.

## Risks

- Commits without explicit or managed identity now use the generic
Paperclip author. Operators can still set repository or command
identity.
- `git config --global` writes to temporary operation config and does
not persist. The documentation states this behavior.
- If the private config directory cannot be prepared, commits still need
explicit identity. Existing redacted diagnostics and credential
isolation remain.
- One small synchronous file write is added to a directory the launcher
already creates. No new subprocess, network request, persistent identity
or authority cache is added.

> 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, based on GPT-6, with reasoning, shell execution and tool
use. The runtime does not expose the exact model ID or context window
size to this 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
- [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 <[email protected]>
2026-10-29 08:56:12 -05:00
Jamie Buchanan 3339582810 fix(adapter-utils): stop asking the GitHub broker on every local git call (#17886)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent runs use `git` and `gh` through the managed GitHub launcher.
> - The launcher asks the broker for credentials before every `git` and
`gh` call. It also asks for local commands that do not use the network,
such as `git status`.
> - Each request runs a transaction and an UPDATE in the database. On a
busy install this gives hundreds of thousands of writes each day and
long COMMIT waits.
> - This pull request stops the request for local `git` commands and
writes the identity summary only when it changes.
> - The benefit is fewer broker requests and fewer database writes. The
credential rules do not change.

## Linked Issues or Issue Description

- Fixes #17879

## What Changed

- The managed `git` launcher does not ask the broker for local
subcommands (`status`, `diff`, `log`, `rev-parse`, `add`, `config`,
`branch`, `checkout` and others). It reads past global options such as
`-C` and `-c`. An unknown subcommand or option still asks. `commit` and
`var` still ask, because the managed author comes from the broker
answer.
- The launcher keeps no broker answer between invocations, so no GitHub
credential is written to a file. The suggestion in the issue to reuse
one answer per run is not part of this pull request, because the reused
answer would have to rest on disk.
- `resolveGitHubOperationCredentials` writes
`run_identity_contexts.github` only when the summary changes.
- `doc/execution-github-identity.md` describes the behavior.

## Verification

- New launcher tests run real `git` against a fake broker that counts
POSTs. A 12-command sequence (init, status, config, rev-parse, add,
diff, commit, log, branch, show, status, diff) made 12 POSTs before and
1 POST after. The one POST is `commit`.
- The same tests check that `var`, `remote`, `fetch`, an alias, and an
unknown option still ask, and that `git var GIT_AUTHOR_IDENT` returns
the managed author.
- A new test covers the unchanged-summary write.
- `vitest run` on the launcher, launcher environment, callback bridge,
github-operation-credentials, run-identity, heartbeat-github-launchers,
native-github-access, openapi-routes, run-trust-preset,
http-log-redaction, skill-source-github-access and chat-channels tests:
12 files, 1901 passed, 1 skipped.
- `vitest run --project @paperclipai/adapter-utils` on `src`: 1903
passed, 12 skipped, 1 failed. The failure is
`sandbox-callback-bridge.test.ts`, a file this PR does not change. It
passes alone (89 of 89, three runs).
- `tsc --noEmit` passes in `packages/adapter-utils` and in `server`.
- Three deliberate breakages each made a test fail: `var` back in the
local list, the broker always asked, and the summary write skip removed.
- Not run: the full server suite and `pnpm build` (the local machine has
no `cargo` for the runner binary).

## Risks

- In a partial clone or an LFS repository, a local command that must
fetch a missing object runs without a token.
- A `git` command that is not local (`commit`, `fetch`, `push`) and
every `gh` command still asks the broker once.
- No migration. No credential is stored at rest.

## Model Used

- Claude Sonnet 5.5 (`claude-sonnet-5-5`, Anthropic) through Claude
Code, with tool use (shell, file edits, test runs), wrote the code and
tests.
- Claude Opus 5.5 (`claude-opus-5-5`, Anthropic) through Claude Code
wrote the issue text and this description.
- A person asked for the change and submits it.

## 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
2026-10-28 09:06:14 -05:00
DottaandPaperclip f60ac29cf1 fix(runner): migrate Grok Build with native reasoning effort (#17922)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Harness selection maps supported agents to Paperclip Runner.
> - The native Runner already supports Grok Build, but its migration
mapping excludes Grok.
> - Grok agents also have a thinking-effort setting that the native
execution contract does not carry.
> - This pull request adds the migration path and preserves explicit
Grok effort through startup and resume.
> - Operators can upgrade an existing Grok agent without silently losing
its model, account, or effort.

## Linked Issues or Issue Description

**What happened?**

Existing Grok Build agents have no Runner selector or upgrade action.
Adding only the mapping would route conversion through the Codex
fallback and lose Grok execution semantics. Native execution also
ignores the legacy reasoning-effort field.

**Expected behavior**

A qualified Grok agent can explicitly upgrade. The saved model, account,
instructions, history, and supported effort remain intact. Unsupported
legacy settings fail before saving. The provider must acknowledge the
selected effort before inference.

**Steps to reproduce**

1. Open the runtime page of an existing Grok Build agent on a qualified
Linux x64 or macOS ARM64 computer.
2. Observe that the Runner selector and upgrade action are absent.
3. Compare the native execution input with the saved legacy
reasoning-effort setting.

Related work: #13882 introduced native Grok Build. #15422 covers the
earlier default-runner work. This change extends the current shared
selection contract.

## What Changed

- Add the Grok harness mapping, qualified platform limits, upgrade
action, and company-scoped computer qualification.
- Translate Grok settings to ACPX without changing the selected account
or explicit model. Resolve the legacy default model to the native
default. Preserve the effective legacy search policy. Reject
incompatible command, argument, permission, and turn-limit settings.
- Add native execution input v10 for explicit Grok effort and search
policy. Preserve historical schemas. Enforce disabled search through
Grok requirements and bind it across startup and recovery.
- Apply and verify ACP `reasoning_effort` before prompting. Bind the
value to provider descriptors, checkpoints, runtime identity, and resume
checks.
- Use the Grok catalog and effort choices in the task composer. Apply
task model and effort overrides while retaining the agent account and
search policy. Update runtime controls, portability/default handling,
documentation, and capability maps.
- Advance Pi and Copilot source attestations for changed shared
transport and recovery code. Keep historical identities intact and
update all Rust integration fixtures.

## Verification

- Migration and review regressions were reproduced before the fixes.
Coverage includes the Grok mapping, default model, explicit effort,
implicit search policy, task overrides, and incompatible legacy
settings. The broad migration, route, immutable-input, and composer
check passed 478 tests. The final catalog and task-input check passed
135 tests.
- Runner startup, recovery, search policy, and profile checks passed 361
tests, with one platform skip. The final source-identity,
search-admission, and transport event-pump check passed 89 tests.
- All 757 Rust workspace tests passed for the broad implementation.
After the final source attestation update, all 55 ACPX profile/session
tests passed.
- `pnpm -r typecheck` and `pnpm build` passed for the implementation.
Server and Runner typecheck passed after the final production change.
Server `tsc --noEmit` passed after the final test-fixture change. Docker
context integrity, token gates, maps, generated protocol data, and
replay goldens passed.
- The v10 startup fixture initially used an ambient local Grok login and
failed on CI. An explicitly empty credential home reproduced all four
failures. The fixture now supplies a synthetic key and a private empty
home. A separate case checks that missing credentials fail closed. All
seven selected Grok v10 and Claude v9 startup cases pass without host
credentials.
- The full local suite could not complete because embedded PostgreSQL
failed before database-backed fixture setup. The broad executor-suite
attempt hit the same limit and was stopped. Neither attempt is counted
as passing. Current-head CI must verify those paths before merge.
- Earlier CI found missing Rust input fields, stale source attestations
and exact fixture pins, a missing Docker context entry, and an outdated
import expectation. Those failures were fixed and checked locally. The
CI logs retain the original failures.
- Different earlier CI runs also failed unchanged cleanup pagination,
device-login timeout, and chat lost-response recovery cases. Later
attempts passed the cleanup and login cases without changes to those
implementations. Their causes remain unproven. The original evidence
remains in [the first
run](https://github.com/paperclipai/paperclip/actions/runs/40563402840),
[the login
run](https://github.com/paperclipai/paperclip/actions/runs/40571128328),
and [the chat
run](https://github.com/paperclipai/paperclip/actions/runs/40573072163).
A passing retry does not establish a root-cause fix.
- Both reviews rate current head
`16fba9b4cdf12bc7945e6628912e79da42959f35` 5/5. All current-head CI
gates pass in [run
40576580699](https://github.com/paperclipai/paperclip/actions/runs/40576580699).
All 60 check runs completed cleanly, including the four intentional
skips and the neutral 5/5 Paperclip review. The legacy security status
is successful. No review threads remain unresolved.
- Staging migration and a real shell task are pending deployment. This
PR does not claim full Grok provider qualification. Reviewers can verify
the operator path by upgrading a qualified Grok agent, checking its
saved model and effort, and running a shell task through native
execution.

## Risks

- Explicit effort and search policy require the matching Runner and
sidecar build. Older immutable execution inputs keep their existing
schemas.
- Providers that omit or reject the selected effort fail before a
prompt. Unsupported effort does not fall back silently.
- Existing agents retain their saved runner until an explicit change.
New Grok agents use native execution only on qualified platforms.
- Grok token and cost reporting remain unverified, as before.

## Model Used

OpenAI Codex, GPT-6 family. The exact serving model ID and
context-window capacity are not exposed in this session. Used reasoning,
source editing, shell execution, and browser inspection.

## 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 the focused suites pass; the full
local suite has the environment limitation recorded above
- [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 <[email protected]>
2026-10-27 15:28:42 -05:00
commitperclip[bot] 2d72f2c4c0 fix: Serialize ACPX session record saves per record to stop overlapping renames (EPERM on Windows) (#16925)
> [!NOTE]
> Opened by Prospector's automated issue-fix pipeline. A maintainer
reviews and decides; nothing here merges on its own.

## Thinking Path

- Issue #15346 reports a defect in paperclipai/paperclip.
- Root cause, as the fixing agent read it: ACPX saves one session record
from several writers (live checkpoint timer, turn end, finalize, close)
with no coordination; each save renames a temp file onto the same record
file, and on Windows overlapping renames onto one file fail with EPERM.
All of these saves go through Paperclip's session-store wrapper, which
forwarded them concurrently.
- This pull request: Serialize ACPX session record saves per record to
stop overlapping renames (EPERM on Windows)

## What Changed

-
`packages/adapter-utils/src/acpx-engine/ephemeral-session-environment.ts`:
Every ACPX save in Paperclip passes through this wrapper, so it now puts
all saves of one record id in a single queue (a promise chain shared by
all wrappers in the process). Saves of one record never overlap and keep
their order; a failed save still returns its own error and does not
block the next one; saves of different records still run in parallel.

## Verification

- Reproducing test(s), added in this PR:
`packages/adapter-utils/src/acpx-engine/ephemeral-session-environment.test.ts`
- They fail on the base commit and pass with the fix, each twice in a
fresh sandbox.
- 3 independent attempts wrote 3 reproductions; this fix passes every
one of them.
- The repository's compile command passes with the fix applied.
- 3 existing test file(s) near the change pass.
- The full test suite passes except for the 246 file(s) that already
fail on the base commit.

## Risks

- Written by an automated pipeline, not a person: review it as you would
an outside contribution.
- Risk tier of the touched paths: 2.
- The scope reviewer found it also changes, beyond the report: save()
called when no earlier save of that record is pending
- The scope reviewer found it also changes, beyond the report:
persistedStore.save throwing synchronously
- The scope reviewer found it also changes, beyond the report: Records
with missing/undefined acpxRecordId
- The scope reviewer found it also changes, beyond the report: save()
for different record ids

## Model Used

- opus, sonnet (Anthropic Claude, via Claude Code), writing and checking
the change in an isolated sandbox.

## Linked Issues

Fixes #15346

Other pull requests that name #15346:
- #15370 (closed): fix(acpx): serialize session file saves and clean
failed temporary writes

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

<!-- prospector:issue-fix v1 issue=15346 base=1c07b5903b1b
report=4fe3fefc8864e00f -->

Co-authored-by: commitperclip[bot] <284812584+commitperclip[bot]@users.noreply.github.com>
2026-10-26 20:55:37 -07:00
Devin FoleyandPaperclip 188bb14ba0 fix: evict stale workspace seed generations instead of freezing (#17357)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Native runs in remote sandboxes upload the workspace as seed
archives, and a per-company cache keeps recent seeds so a later run with
the same workspace state can reuse them
> - The cache only ever added seeds; at its limits it stopped accepting
new ones and never removed old ones
> - On one instance it had filled to its 8 GiB cap with week-old seeds
and froze, so it held disk without helping new runs
> - This pull request expires seeds unused for 7 days and evicts the
least recently used seeds when a new one needs room
> - The benefit is a cache that stays useful within the same limits and
gives back space held by stale seeds

## Linked Issues or Issue Description

Bug report described in-PR (bug report template).

**What happened?**
On one instance, the company's seed cache
(`native-workspace-sync/generations/<company>/`) sat at its 8 GiB cap:
209 generations across 169 workspaces. 107 generations (8.2 GB) had not
been touched for 7 days or more. Because publishing stops at the limits,
new workspace states were no longer cached, while seeds for old states
and deleted workspaces stayed forever.

**Expected behavior**
Stale seeds are removed, and a new seed can replace the least recently
used ones when the cache is full.

**Steps to reproduce**
1. Run enough native sandbox runs across workspace states to reach a
seed cache limit.
2. New seeds are no longer published, and nothing is ever removed.

**Paperclip version or commit:** `master` at 837add0caa.
**Deployment mode:** any with native remote-sandbox runs.
**Agent adapter(s) involved:** native sandbox runtimes
(`packages/adapter-utils` seed cache).

## What Changed

`packages/adapter-utils/src/workspace-seed-cache.ts`:
- **Use tracking:** on a verified hit, `readWorkspaceSeedGeneration`
takes the company lock, confirms the receipt is still the one it
verified, and touches it, so its mtime records the last use. Eviction
holds the same lock from reading timestamps to removing, so a hit is
either recorded first and protected, or already gone and reported as a
miss. The reader accepts a `companyDirectory` option, which the sandbox
runtime now passes as it already does for publishing.
- **Idle expiry:** `publishWorkspaceSeedGeneration`, under the existing
company lock, first removes generations unused for
`WORKSPACE_SEED_CACHE_EVICTION.idleMs` (7 days).
- **LRU eviction:** if the new seed still does not fit, it evicts the
least recently used generations until it does: within the workspace for
workspace limits, across the company for company limits.
- **What is never evicted:**
- a generation used within `minIdleMs` (1 hour), since a run copies a
cache hit shortly after the lookup;
  - the generation being published;
- abandoned `.pending-*` directories, which still count toward the
limits as before.
- **Removal:** renames to `.evicted-<uuid>` before deleting, and removes
leftovers of an interrupted eviction. All empty workspace directories
are removed, so they no longer hold workspace slots.
- **Identity check:** publication rechecks the company directory's
identity under the lock before evicting, so a directory replaced during
verification is never touched.
- **Verification:** optional verification still runs only when the seed
can fit after eviction. A dry eviction plan decides this before
verification, and the real eviction happens under the lock.
- `publishWorkspaceSeedGeneration` accepts `eviction` and `now` options
for tests; the limits are unchanged.

No code comments were added.

## Verification

- `pnpm exec vitest run src/workspace-seed-cache.test.ts
src/workspace-checkpoint.test.ts` (in `packages/adapter-utils`): 58
passed. With `sandbox-managed-runtime.test.ts`: 168 passed. New cases:
- **Idle expiry:** generations unused for 7 days are removed on publish,
emptied workspaces are removed, newer ones stay.
  - **Use tracking:** a cache hit protects an otherwise idle generation.
- **Workspace LRU:** the least recently used generation of a full
workspace is evicted.
- **Company LRU:** company pressure evicts across workspaces, but never
a generation used within the last hour.
  - **Verification:** it runs only when eviction can make room.
- **Pending directories:** abandoned ones are never evicted, and
verification is skipped when they fill the cache.
  - **Leftovers:** remnants of an interrupted eviction are removed.
- **Concurrent read:** an eviction paused just before removing a seed
makes a concurrent read report a miss, never a hit whose files vanish.
- **Replaced directory:** a nonempty company directory swapped in during
verification keeps its seeds.
- **Empty workspace:** an abandoned empty workspace directory no longer
holds the only workspace slot.
- **Mutation checks:** removing any of these fails at least one case:
  - the touch on read;
  - idle expiry;
  - the recent-use guard;
  - the plan before verification;
  - leftover cleanup;
  - the pending-directory guard;
- the read lock (the concurrent-read case then returns a hit whose files
are gone);
  - the identity recheck;
  - removing only workspaces that had generations.

The existing `workspace-checkpoint` end-to-end test caught an earlier
version that ignored pending directories when deciding whether to
verify.
- `pnpm exec vitest run src` (in `packages/adapter-utils`): all seed
cache and checkpoint tests pass. Two runs on a heavily loaded machine
each had unrelated failures (`github-launcher.test.ts`,
`acpx-engine/execute.test.ts`). One run also failed a seed cache test
once; I could not reproduce that in 21 further runs, including 6 in
parallel.
- `pnpm exec vitest run native-workspace-sync` (in `server/`): 23
passed.
- `pnpm run typecheck` (in `server/`, after building
`packages/adapter-utils`): pass.
- Not run: server build and boot smoke.

## Risks

- **First publish after deploy:** on an instance with a full cache of
old seeds, it removes them, about 8 GB on the instance above. The
removal is a rename plus a recursive delete under the company lock.
- **Eviction during use:** a run that looked up a seed more than an hour
earlier and has not yet copied it could see it evicted. It would fail
that preparation and retry. In practice the copy follows the lookup
within the same preparation.
- **No new settings:** the 7-day and 1-hour thresholds are constants
exported as `WORKSPACE_SEED_CACHE_EVICTION`.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through Claude Code
with tool use (shell, file edits, tests, read-only measurements on a
live instance).

## 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: Paperclip <[email protected]>
2026-10-23 14:05:34 -07:00
DottaandPaperclip 3bd709df8b Operationalize Product E2E accounting, tracing, and scheduling (#17009)
## Thinking Path

> - Paperclip manages agents and the work they perform.
> - Product E2E tests exercise real agents, tools, setup and cleanup.
> - Repeated runs need retained costs and traces, including failed
attempts.
> - A passing retry must not hide earlier spending or feedback.
> - Recurring work also needs admission limits, private reports and
failure detection.
> - This change supplies those controls while keeping paid activation
behind existing gates.

## Linked Issues or Issue Description

Refs #16996 and #17158. This is the consolidated landing of the reviewed
eval operations stack: #17019, #17024, #17025, #17027, #17030, #17033,
#17035, #17038, #17043, #17044, #17048, #17050, #17051, #17092, #17096,
#17100, #17107, #17109, #17112, #17115, #17117, #17121, #17123, #17127,
#17130, #17133, #17134, #17137, #17140, #17143, #17154. The repository
permits squash merges only. A local rehearsal showed that squashing each
slice into master causes repeated conflicts in dependent slices. This PR
preserves their commits and lands their combined implementation in one
reviewed integration. Superseded slices will close after this merge.

## What Changed

- Retain every attempt's cost receipt, including setup, failures,
interruptions and retries. Separate reported amounts, estimates, missing
costs and reserved capacity in reports and dashboards.
- Add bounded request admission, exact model pricecards, gateway error
accounting, cache policy and reconciliation evidence. Refuse unsupported
routes and requests that cannot fit the allowance.
- Integrate Braintrust gateway traces and OTLP correlation, with secret
filtering and experiment imports. Record phase timing, including setup,
feedback capture and cleanup.
- Collect complaint and suggestion evidence after each attempt,
including recovery after failures. Retain explicit review dispositions
independently from the product grade.
- Add gated daily and weekly selections, measured forecasts,
setup/readiness checks, wall-time limits and retained monitor
notifications. Include declared non-model overhead in admission while
reporting the existing subscription separately.
- Keep recurring history private, bound uploads and retention, and use
isolated archive environments. Keep public publication an explicit
operation. Report and publication jobs install only the committed
lockfile, with scripts disabled and isolated stores; stale pins fail
closed.
- Integrate current master, preserve its Product E2E journey billing and
privacy checks, and test that shared setup receipts do not double-count
estimated agent usage. Preserve upstream fixture repairs.

## Verification

- Complete integration baseline
`0649a5ce7544ab20825c2183d2f421157db49f24` includes master
`a3e1d915a7110a4e115b46396a3f79e6dfb4d05e` and every original stack head
as ancestors.
- On that integration baseline, `pnpm test:e2e:runner:unit`: 4,012
Vitest tests passed, two skipped, across 193 files; all 137 Node
source-contract tests passed.
- Focused billing and Product E2E journey contract checks: 24 tests
passed. They cover estimates, unknown receipts, single-owner setup
costs, browser-only execution and private publication guards.
- Monitor and workflow wiring: all 96 Node tests passed. `node
scripts/check-token-gates.mjs`: all gates clean.
- `pnpm test:e2e:runner:typecheck` passes after the standard Runner
TypeScript build refreshed local declarations. The first attempt
rejected the new master `runner_prepared_launch` union member because
this checkout retained an older generated declaration; the source union
already contained that member.
- Current head `492b0708a1c531a023d9ac10f0f3dc6383cc1754` removes
dependency re-resolution from archive-authorized reporting jobs after
the security scan flagged it. All 15 workflow-security tests pass after
updating the old two-install assertion.
- Hosted [CI run
39965460180](https://github.com/paperclipai/paperclip/actions/runs/39965460180)
passes on the current repaired head, including typecheck, build, general
tests, Runner checks, browser shards and canary dry run. Greptile is 5/5
on this exact head with no unresolved threads. The security scan passes
after the fixed lockfile-resolution thread was resolved.
- Paperclip Review returned a terminal neutral check but an incomplete
0-file assessment on the repaired head: its platform blocked both diff
responses as prompt-injection content. This is not a successful code
review. No bypass or fabricated pass was attempted; current-head
Greptile, security and hosted CI are independent gates.
- No paid test, reservation release or recurring-run activation
accompanies this landing. Live route qualification and invoice
reconciliation remain separate evidence.

<details>
<summary>Retained earlier validation failures</summary>

Earlier broad local runs had two 15-second test timeouts (CLI poll
cadence and Retry validation), one embedded-database setup timeout, and
later PostgreSQL semaphore exhaustion. The exact timed-out tests passed
separately without larger timeouts; the broad-run causes were not
established. Those local failures remain distinct from subsequent hosted
CI results. The independent #17158 fix records its Muse fixture
collision, its UUID correction and hosted verification. Local Muse
execution was also blocked by macOS embedded-PostgreSQL startup. No
historical failure is treated as a successful paid qualification.

</details>

## Risks

- Admission is a local control, not a provider invoice cap. Missing
provider receipts or resource charges retain unknown amounts and holds;
estimates do not become billed dollars.
- The bounded path admits only documented local suites and provider
routes. Ordinary legacy or separate Runner protocol campaigns have
different controls. Merging this code does not qualify additional
routes.
- Recurrence still needs the existing enable gates and current
qualification/allowance evidence. Archive permissions and resources must
match the documented private-environment contract.
- Legacy reports may lack complete attempt or phase evidence. Reports
preserve that limitation and do not infer zero usage from missing data.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository tools and code
execution. The exact deployment variant and context-window size are not
exposed by 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 (targeted checks;
full-suite host limitation disclosed above)
- [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 <[email protected]>
2026-10-23 07:18:37 -07:00
DottaandPaperclip 042051904d fix(codex): deliver configured service secrets to shell tools (#17216)
## Thinking Path

> - Paperclip lets operators assign tools and credentials to agents.
> - Legacy Codex starts provider processes and builds a separate shell
environment policy.
> - Both ACP and CLI removed every variable whose name contained KEY,
SECRET, or TOKEN.
> - This also removed service credentials that the operator explicitly
assigned, such as SLACK_BOT_TOKEN.
> - This pull request uses the resolved configuration to admit assigned
service secrets by name.
> - Every Codex launch keeps a bounded shell allowlist, including
launches without an agent identity.
> - Assigned scripts can use their credentials without adding unassigned
host secrets to shell tools.

## Linked Issues or Issue Description

Related: #16255. That PR fixes project-level exclude patterns. This PR
fixes the removal of explicitly assigned service secrets and the
fallback to an inherited policy when no agent identity is present.

**What happened?**

A Codex task could not run a Slack posting script. SLACK_BOT_TOKEN was
present in its resolved launch environment, but Paperclip removed its
name from shell_environment_policy.include_only. A live removal check
also showed that an identity-free launch without an explicit policy
could inherit an unassigned host token.

**Expected behavior**

An explicitly assigned service credential reaches shell commands.
Removing that binding removes its shell access on the next launch.
Unassigned host credentials stay outside the allowlist. Codex login
credentials and observability headers keep their separate provider
paths.

**Steps to reproduce**

1. Configure a Legacy Codex agent with a synthetic SLACK_BOT_TOKEN
value.
2. Run a shell command that checks whether the variable exists without
printing its value.
3. Before this fix, the variable is absent. Repeat through ACP and CLI.
4. Remove the binding and run a second turn. Check that neither the
removed token nor an unassigned host token reaches the command.

**Paperclip version or commit**

Reproduced in the executor regressions before the fix on master at
4dfcb4c9b8. The final change is rebased on 25f2d05282.

**Deployment mode**

Source checkout. Local macOS arm64 provider acceptance with Codex CLI
0.160.0 and Codex ACP 1.6.2, gpt-5.6-sol, and an existing ChatGPT login.

## What Changed

- Added one shared Codex shell-name policy for Legacy ACP and CLI
execution.
- Admit explicitly resolved service-secret bindings. Keep Codex login
keys, observability credentials, and private runtime transport tokens
outside that opt-in.
- Reject glob-shaped environment names so one configured name cannot
match other credentials.
- Always emit the bounded policy. Do not fall back to host inheritance
after a binding is removed.
- Updated executor regressions, the identity contract, and the paired
verification maps.

## Verification

- Red before the fix: executor regressions failed because
SLACK_BOT_TOKEN was absent in ACP and CLI, with and without agent
identities.
- Green: 267 tests passed across the two executor suites and the shared
shell-policy suite. Coverage includes host/provider isolation,
unresolved and removed bindings, glob names, identity delivery, and
names-only metadata.
- The complete Codex adapter source suite passed after the CI follow-up:
571 tests passed, 1 skipped across 31 files. Its typecheck also passed.
- The first CI run caught four stale expectations in ACP config and SSH
launch tests. Those now assert the bounded policy and the existing
command/resume behavior. No production code changed in that follow-up.
- Live CLI and ACP acceptance: run a Python presence check through each
real adapter, remove the synthetic Slack binding, and run again. The
assigned token was present only on the first turn. The unassigned host
token and OpenAI API key were absent on both turns. CLI retained its
session; ACP correctly invalidated its session after the configuration
changed. Workspace proof files persisted.
- An earlier live CLI check failed after binding removal because the
no-policy fallback exposed the synthetic host token. The final change
removes that fallback. Both live checks then passed.
- Full pnpm -r typecheck and pnpm build passed. The changed packages
were compiled again after the safety correction.
- node scripts/check-maps.mjs and git diff --check passed.
- The local pnpm test:run attempt reported failures in native
warm-session tests and was interrupted. That full-suite result is not a
pass; PR CI must establish the final repository-wide gate. One named
native failure passed alone, and the complete native executor file then
passed separately (774 tests). The original broad-run failure cause
remains unconfirmed.
- After rebase, one Gemini skill-root case timed out at five seconds
while the broad run was active. The case passed alone, and the full
focused set then passed again at the PR head (267 tests). The earlier
timeout remains part of the verification record.
- An initial broad adapter run failed because sandbox restrictions
denied loopback listeners. Running the same suites with loopback access
passed.

## Risks

- Explicitly assigned service secrets now reach shell commands. This is
the intended behavior change: assigning a credential authorizes the run
to use it, and a model-directed command can read it. This is not a
broker that hides assigned credentials from the model. Operators must
assign only credentials and service permissions authorized for the run.
Configuration and secret-access authorization remain with their existing
owners.
- Superagent initially flagged this intended assigned-credential
exposure as a P2. The inline reply records the trust-model rationale and
the documentation states it explicitly. The security scan on the final
documented revision passed without any check override.
- The launch policy contains names, not values. Log redaction remains in
place. Provider login credentials and observability headers remain
excluded from the service-secret opt-in.
- Identity-free Codex runs now also receive a bounded policy. This
removes reliance on the operator's inherited shell policy.
- Existing explicit project exclude patterns retain their behavior;
#16255 addresses that separate case.
- The change has not been deployed to a shared instance.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository tools, and code
execution. The exact served model ID and context-window size are 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
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run the focused tests locally and they pass; the full-suite
limitation is recorded above
- [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 <[email protected]>
2026-10-22 18:17:34 -05:00
DottaandPaperclip 734c265339 Default new Pi agents to Paperclip Runner (#17142)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Users choose a harness during agent setup.
> - Claude, Codex, and OpenCode already receive their qualified Runner
default.
> - Pi has a qualified native integration but new Pi agents still use
the direct adapter.
> - This change gives new Pi agents the native default on supported
targets.
> - Saved agents keep their runner and compatible settings. Legacy
remains available in Advanced.

## Linked Issues or Issue Description

Refs #17120, #14921, #14922, #14923, #14924, and #14956. Related generic
default work: #15422. This PR is the separate Pi follow-up to the merged
OpenCode change.

**Current behavior**

Choosing Pi for a new agent saves the direct Pi adapter. The native Pi
integration remains behind the generic experimental flag.

**Proposed behavior**

Default new Pi agents to ACPX Pi on macOS ARM64/x64 and GNU/Linux x64
with supported glibc. Keep unsupported targets and explicit Legacy
selections on the direct adapter. Require an explicit provider/model
before test or hire. Permit model-free inactive setup drafts.

**Reason and benefit**

Users choose Pi once and receive its qualified runner. Saved agents
retain their identity and settings. Conversions reject unsupported
legacy thinking and launch settings instead of dropping them.

## What Changed

- Add Pi to the shared platform/default contract and server creation
selection.
- Admit native Pi without the generic experimental flag.
- Preserve provider models, credentials, workspace settings, exact
thinking, and saved native policies.
- Add Pi runner controls, branding, setup resume, onboarding, built-in
configuration, and import handling.
- Add managed Pi OpenRouter routing and matching native provider API-key
accounts. Keep subscription and custom gateway exclusions. Clear
alternate credentials and reject custom Pi provider overrides.
- Keep Pi draft models empty instead of applying Claude's default model.
- Verify setup against the pinned Pi runtime. For native Pi on the
official OpenRouter route, reverify the API key live without requiring a
global Legacy Pi CLI.
- Match Pi’s bounded controller close window to its native snapshot
cleanup and lifetime verification, preserving explicit caller deadlines
and the mandatory drain/suspension proof.
- Bind Pi sidecars to the package that owns the pinned native assets,
including source and vendored server layouts. Other providers retain
their existing package authority.
- Keep protocol-owned finish/block tools out of the ACPX forwarded
catalog. The trusted bridge installs these tools itself; the complete
Rust authorization catalog and terminal receipt ledger remain intact.
This fixes actual Pi session startup and applies to other ACPX
consumers.
- Advance Copilot’s shared-source attestation to v58 because it pins the
shared files touched by Pi. Preserve immutable v57 bytes and historical
decoding; Pi’s own release identity is unchanged.
- Add regression coverage and update the paired maps and developer
documentation.

## Verification

- Passed: full repository typecheck and full production build on final
head 656d304783, including the generated attestation repair.
- Passed: focused shared/server/UI suites covering default selection,
platform fallback, conversion preservation, inactive drafts, managed
account compatibility, credential isolation, OpenRouter routing/model
IDs, exact thinking controls, and saved-account selection. Relevant
batches include 375 server/conversion tests, 267 account/routing tests,
and 110 UI setup/account tests.
- Passed: 179 attestation, historical-profile, qualified-profile, and
native-execution contract tests after CI identified stale Copilot source
hashes. The identity generator check passes; the only delta after the
passing Pi browser run is this Copilot attestation/history repair.
- Passed: 145 managed-account tests, including actual service
preparation and API-key adoption/rejection for native Pi without the
Legacy CLI; 6 pinned-installation probe tests.
- Red then green: an actual task failed session.open because its
54-operation catalog included the bridge-owned terminal tools. The
regression with those tools failed before the filter; all 399 Rust
library tests pass after it. The full product retry exposed an
additional package-authority mismatch. Four authority tests failed
before that repair; 11 authority checks and 14 installation/asset checks
pass afterward. Direct real Pi session initialization with the corrected
package root and catalog succeeds. No model prompt was sent by that
diagnostic.
- Red then green: real Pi posted Hello from Pi but its controller closed
before native cleanup could finish. The cleanup-budget test failed with
the old default and passes with the bounded Pi window. All 46 selected
suspension, drain, and package-authority tests pass after building their
native fixture.
- Passed: token gates, paired map checks, Rust formatting, and git diff
--check.
- Passed real browser acceptance on macOS ARM64, generic native runner
flag disabled, source a433748a12 (Pi code unchanged afterward): the UI
selects Paperclip Runner by default, retains Pi and the selected
model/account, and verifies the pinned runtime plus live OpenRouter key.
A fresh UI-created task, assigned to the UI-created native Pi agent,
posted exactly `Hello from Pi` and reached Done without retry or
restart. The native run and workspace finalization succeeded; the
response and Done persisted after Reload.
- Evidence: 8 preserved browser attempts, including the final passing
18-step journey with all screenshots. Prior failures remain recorded.
The earlier failed checkpoint run recovered on server restart; that
recovery is separate from the fresh final task’s clean success.
- Preserved browser failures: missing account control/inherited Claude
draft model; incompatible managed-account/model-prefix handling; generic
npm probe for Pi; Legacy CLI hello check; duplicate terminal tools and
wrong provider-package authority at actual native session.open; a real
Hello from Pi comment followed by premature checkpoint cleanup timeout.
Each failure has retained screenshots/logs and a corresponding focused
repair. The fresh final task proves these repairs end to end.
- The exact Runner Vitest shard was also run locally after the
attestation fix: the stale-hash failure is gone; 1,833 tests passed, 10
skipped, and two Codex retained-control settlement scenarios failed with
`native_cleanup_maintenance_unproven` during terminal ACK/suspension
replay. Both scenarios passed in an isolated rerun (2/2), and the same
19-test file passed in the preceding Linux CI run. The precise
full-shard timing cause is not established. These Codex scenarios are
outside the changed Pi paths; the original local failures are retained;
no complete local Runner-shard pass is claimed. The corresponding Runner
shard now passes on the final head in hosted Linux CI. All 60 final-head
check runs are now terminal and clean (55 successful, 1 neutral, 4
skipped), and the legacy Snyk status is successful.
- Local full repository tests were attempted but not completed:
provisioning briefly confused a workspace symlink, then a later run was
stopped before browser-driven repairs. No full local-suite pass is
claimed. Final-head hosted CI is green and fresh Greptile review is 5/5
with all review threads resolved; earlier jobs canceled by runner
shutdown are not counted as a pass.

## Risks

- Pi cleanup has a finite 45-second default window, accounting for the
native 30-second snapshot cleanup and 5-second lifetime fence. Other
providers and explicit caller deadlines retain their prior behavior.
- Pi requires explicit operator setup with `paperclipai runtime setup
pi` on the execution host. This PR does not add automatic downloads.
- Linux ARM64, Windows, musl, and unqualified targets retain Legacy.
- Legacy thinking levels minimal, medium, and xhigh cannot be translated
exactly. Native max thinking, warm lifecycle, or restricted permissions
cannot be dropped during a return to Legacy.
- Copilot’s bound profile advances to v58 with exact current launch
admission; v57 remains decodable for recovery. No historical identity is
rewritten.
- Existing agents and immutable run descriptors are not migrated. Cloud
Pi availability is unchanged.

## Model Used

OpenAI GPT-6 through Codex with reasoning, code editing, command
execution, and browser use. The exact serving model ID and
context-window size are 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
- [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 <[email protected]>
2026-10-21 17:10:00 -05:00
8b873e0112 fix(adapter-utils): skip the materialize lock for an already-current skill (#17061)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A remote Claude ACP run stages the selected runtime skills into the
sandbox. The host archives the materialized skills directory with tar
and uploads it.
> - Concurrent runs of one agent share that skills directory. The
directory key is the selected skill set and its content.
> - Each run calls `materializePaperclipSkillCopy` for each skill. The
function always created and removed a sibling `<skill>.lock` directory,
even when the copy was already current.
> - The archive step lists the directory entries first and then gives
the names to tar. When a concurrent run removed its lock between these
two steps, tar failed with `Cannot stat` and the whole run failed as
`adapter_failed`.
> - This pull request checks the materialized fingerprint before it
takes the lock. An up-to-date copy now leaves no transient entries in
the shared directory.
> - The benefit is that concurrent runs of one agent no longer fail each
other's skill staging.

## Linked Issues or Issue Description

**What happened?**
A remote `claude_local` run failed before the agent started. The skill
staging step ran `tar -c ... -T entries.nul` on the shared
runtime-skills directory. Tar stopped with `tar: <skill>.lock: Cannot
stat: No such file or directory` and exit status 2. Another run of the
same agent was materializing the same skills at the same time.

**Expected behavior**
A run that finds its skills already materialized does not change the
shared skills directory. Concurrent runs can archive that directory
safely.

**Steps to reproduce**
1. Materialize a skill into a skills directory.
2. Call `materializePaperclipSkillCopy` again in a loop for the same
skill.
3. At the same time, call `createTarballFromDirectory` on the skills
directory in a loop.
4. Before this change, most archives fail with `Cannot stat` on
`<skill>.lock`.

**Agent adapter(s) involved**
`claude_local` (ACP, remote sandbox). The function is shared, so
`grok_local` and the Codex and Gemini ACP skill paths also benefit.

## What Changed

- `packages/adapter-utils/src/server-utils.ts`:
`materializePaperclipSkillCopy` hashes the source and checks the
materialized sentinel before it takes the lock. When the copy is
current, it returns without creating the lock directory or a temporary
copy. The existing check inside the lock stays, so a copy that is
missing or out of date still uses the lock as before. The source is
hashed once per call, the same as before.
- `packages/adapter-utils/src/server-utils.test.ts`: a new test holds a
live lock from another owner, then materializes an already-current
skill. The call must return at once and must not change the lock or the
directory.

## Verification

- `cd packages/adapter-utils && npx vitest run
src/server-utils.test.ts`: 132 passed.
- Red/green: without the fix, the new test times out because the call
waits on the live lock. With the fix, it passes.
- `npx vitest run src/acpx-engine/execute.test.ts -t skill`: 15 passed.
- `npx tsc --noEmit -p tsconfig.json` in `packages/adapter-utils`:
passes.
- Real-dependency race check (a temporary script, not committed). The
script used real files and the system tar. It ran
`materializePaperclipSkillCopy` for 5 skills in a loop while
`createTarballFromDirectory` archived the same directory for 8 seconds:
- Before the fix: 1994 of 2307 archives failed with `tar: skill-1.lock:
Cannot stat: No such file or directory`.
  - After the fix: 0 of 2021 archives failed.
- Assumption under test: the sentinel check outside the lock is safe.
The sentinel is written into the temporary copy before the atomic
rename, so a matching sentinel means a complete copy. The lock never
protected readers: the caller already reads `SKILL.md` after the lock is
released.
- Remaining limit: the first materialization of a new skill set still
uses the lock. A concurrent archive of the same directory is unlikely
then, because a run archives the directory only after it has
materialized every skill in it.

## Risks

- Low risk. The change only skips the lock when the result is already
correct. A missing, damaged, or out-of-date copy uses the same locked
path as before.
- No change to the materialized layout, the sentinel format, or the lock
format.

> 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

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with
tool use (shell, file edits, test runs). The exact 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

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

---------

Co-authored-by: devinfoley <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-21 00:14:53 -07:00
Nicky LeachandPaperclip e5351b240a Verify native Runner setup through plugin tasks (#17029)
## Thinking Path

> - Paperclip manages work for AI agents.
> - An environment plugin can own a Runner task.
> - The merged launch-preparation contract lets the execution owner
build the provider configuration.
> - Core still needs a task process owner and a setup test that uses
that configuration.
> - This PR connects the task API to the Runner process-owner contract.
> - Operators can test a plugin environment before Core sends it task
work.

## Linked Issues or Issue Description

**Subsystem affected**

Runner, plugin SDK, and agent environment tests.

**Problem or motivation**

A task-only environment cannot use the shell-based setup path. Core must
verify the Runner hello exchange and process cleanup through dedicated
task operations.

**Proposed solution**

Add a lease-bound task driver and use it for a bounded setup probe. Keep
heartbeat admission and task execution in #16978. This PR follows the
merged launch-preparation contract in #16997.

**Alternatives considered**

A provider declaration alone cannot prove that Runner and the provider
connect. A shell bridge would give the plugin an unnecessary command
interface.

## What Changed

- Pass structured Runner launch data to process owners. Support an
external connection and explicit detach.
- Add typed launch authentication checks and checkpoint operations.
Check lease, run, project, and retained-task authority.
- Connect launch preparation, PRP, termination proof, and credential
handoff to the task driver. Reject local environment overrides during
installed-account setup. Pass saved-account identity and credential home
directly from Core.
- Run a short-lived setup probe through the existing agent environment
test API. Require a verified provider hello and cleanup. Keep saved
Copilot account checks metadata-only through a dedicated task operation.
- Update the Copilot source identity because the shared transport source
changed. Keep provider behavior and the earlier identity fixture.

## Verification

- CI passes on `f987adec7`: 55 successful checks and 5 expected skips.
Standard Greptile and Apex are both 5/5 with no open findings. The
branch merges cleanly with current master.
- Repository typecheck and build pass after the rebase onto the
workspace-provider contract. Server typecheck and build also pass after
the final setup fix.
- The 110 focused lease, SDK, setup, Copilot, and route cases pass after
the rebase. All 14 setup cases pass after the final change. These
include a real local Runner with a test provider. The saved-agent
model-discovery case also passes.
- Apex found that Core's relative workspace marker failed the driver
path check, and non-Codex sessions omitted the observed model. Both
failures were reproduced and fixed. The probe leaves physical workspace
selection to plugin preparation. The transport reads each provider's
observed model and exposes it to the driver. All 14 setup cases, 6
transport peer cases, and both existing real Runner/OpenCode cases pass.
Runner build and server typecheck pass.
- The coverage audit removed two redundant cases, repeated fixture code,
and two extra real Runner launches. It kept authorization, credential
selection, cancellation, lost replies, termination proof, checkpoint
identity, and provider setup checks.
- The Copilot source-identity check passes. The added-line secret and
PII scan passes. The unmerged v52 attestation was regenerated after the
shared transport fix.
- CI on `13faeff02` caught one stale Copilot digest assertion after
attestation regeneration. The literal now matches the candidate
attestation, and all 49 installation cases pass. That CI lane otherwise
passed 1,818 cases.
- An earlier CI run hit an unchanged UI toast timing test. The separate
fix is tracked in #16952. Current CI passes that shard. CI also caught a
stale model-discovery assertion in this change; the assertion is fixed.
- The local full-suite run was stopped when master changed. The final CI
run supplies exact-head full-suite validation. No live Cloud or provider
test was run.

## Risks

- Native hello tests require a saved Core account or the installed
account. Local environment overrides are unsupported for
installed-account probes and fail before the provider runs.
- A native task provider must declare launch preparation and
authentication-check version 1. It must keep the checked configuration
fixed through submission. Core rejects providers that cannot make this
guarantee.
- A lost task reply does not prove process termination. Core retains
grants until the provider proves termination and Core saves required
state.
- This PR does not enable native heartbeat dispatch through plugin
environments. Cloud deployment and live provider validation remain
separate.

## Model Used

OpenAI Codex, GPT-6. The exact model ID and context window are not
exposed in this session. Tool use includes code editing, local tests,
and the GitHub CLI.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have described the issue in-PR following the relevant issue
template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal issue
details
- [x] I have run focused 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 <[email protected]>
2026-10-20 18:13:09 -07:00