Commit Graph
1895 Commits
Author SHA1 Message Date
DottaandPaperclip ebe8910c29 Merge synchronized native tracing into portable controller runtime
Include the safe evidence-reader module in portable inventory and preserve current master checkbox behavior plus release completion waits.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-26 09:35:35 -05:00
DottaandPaperclip edc7642773 Merge reviewed evidence repairs into native tracing controller
Apply the same bounded reader to newly introduced OTLP evidence. Preserve failed finalization accounting and relay closure.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-26 09:32:51 -05:00
DottaandPaperclip 13f31e2ec5 fix(evals): bound retained proof reads and credential exclusion
Reject nonregular and oversized evidence without blocking. Share the safe reader across execution, archive and report. Exclude configured private keys and JSON config credentials, reject hidden nested tool definitions, and constrain retained attempt directory names.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-26 09:27:03 -05:00
DottaandPaperclip 00f787c1e8 Merge origin/master into controller evidence branch
Preserve the integration parser and cleanup repairs already contained on this branch.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-26 09:22:19 -05:00
DottaandPaperclip a1f77821b9 perf(tasks): show recent conversation before historical activity (#17593)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A task page must show the instruction and current conversation
quickly.
> - The current page waits for related task data and historical run
transcripts.
> - These reads grow with task history, even when the reader only needs
the latest answer.
> - This pull request gives cold opens a small initial view and loads
older history on demand.
> - Messages and runs observed while the page stays open remain visible.
> - The benefit is a readable task before historical diagnostics finish
loading.

## Linked Issues or Issue Description

Related: #14667 and #16229. The first changes the reveal gate and scroll
anchors. The second adds general history pagination. This PR adds a core
task read and a folded recent-conversation view. It preserves the
default full-read API contracts. The shared files will need coordination
if either related PR lands first.

**What existing behavior does this improve?**

Cold opening an ordinary task with a long conversation or large run
logs.

**Current behavior**

Task enrichment and historical transcripts delay the readable
conversation. A cold open can fetch several megabytes of tool activity
before revealing saved messages.

**Proposed behavior**

Show the task instruction, recent saved replies, latest human input, and
the latest run's conversation data first. Fetch older messages and full
run activity when the reader chooses **More messages**. Retain new
messages while the page stays open.

## What Changed

- Add an authorized core issue view that skips related enrichment. Keep
enrichment on the existing detail read. Share task mutation, rollback
and invalidation updates with the core cache.
- Add a bounded recent-comment read. Preserve the first human
instruction when no description exists, including legacy rows with a
NULL author type. Keep company and task access checks and comment
redaction.
- Add optional recent activity and run-summary views. Fetch original
input receipts for visible comments separately so delivered messages
stay out of the queue. Reconcile watched unfinished runs after a
reconnect beyond the preview window, removing deleted runs after
reconciliation.
- Filter the latest native run's initial events to assistant messages,
request lifecycles and terminal results. Full event reads remain
available on expansion. Legacy fallback reads use a small tail extended
to the first complete record. HTTP cancellation aborts the storage read.
- Retain observed messages across cursor catch-up and new runs. Apply
local cancellation and deletion to both caches and their task aliases.
Refetch locally seeded history pages on expansion. Preserve expansion
across identifier and UUID links. Keep deep links, classic tasks and
Agent Chat on their existing full-history path.
- Update API documentation, design guidance, capability maps and
regression coverage.

## Verification

- Final acceptance on `9ea0b15ae44d9f6d2c9a9f824b4835f299f92979`: all CI
gates passed, including server/UI tests, all eight browser shards,
runner checks, full typecheck and build. [Final CI
run](https://github.com/paperclipai/paperclip/actions/runs/40326990328).
GitHub reports 55 successful checks, two intentionally skipped checks,
and one neutral Paperclip Review check described below. No checks are
pending or failed; Greptile is 5/5 and no review threads remain open.
- October 26 merge update: merged current `master`. The only conflict
was in task-page imports; both the compact-history hook and the new
live-update hooks remain. All CI tests and build jobs passed on
`044fd6733d`. Fresh review found canceled-comment cache drift, legacy
human-comment filtering, send-then-expand truncation, and partial legacy
log records. These defects were fixed in `af3d66a5bd`.
- Review-fix verification: 470 tests pass across ten affected suites,
including all six PostgreSQL cold-open tests without a helper override.
Cancellation/delete and send-then-expand regression tests failed before
the fixes and pass afterward. Coverage includes task identifier/UUID
reopen, stale in-flight reads, legacy NULL author types, local-file and
object-storage tails cut inside UTF-8 text, route forwarding, and
request cancellation. Full workspace typecheck and production build,
token gates, map validation and diff checks pass.
- Follow-up review found that a failed expanded legacy read lost its
record-alignment state on retry. `9ea0b15ae4` commits the expanded
window only after a successful read. The new regression fails before the
fix and passes afterward; all 19 transcript tests, UI typecheck, UI
build and token gates pass. Greptile reviewed this commit at 5/5 with no
actionable findings, and all review threads are resolved.
- The final Paperclip Review check ended neutral because its execution
did not complete; no passing Paperclip assessment is claimed. Its
earlier four concrete findings were fixed and regression-tested.
Greptile and the security scans completed successfully on the final
commit.
- The preceding `af3d66a5bd` CI run was canceled when superseded by the
retry fix. Its server shard 11 check briefly reported failure, but the
job API records cancellation and its test step passed all 1,750 tests.
This superseded run is not used as final CI acceptance.
- 549 focused tests pass on the final implementation across nine task
UI, activity-route, OpenAPI and PostgreSQL suites. These cover pending
history, desktop/mobile visibility, expansion, retained runs, message
bursts, cancellation recovery and queued-comment removal in both views.
- Legacy expansion keeps the latest answer visible while fetching and
deduplicating older output.
- Five PostgreSQL tests pass for core access denial, recent comments,
missing-description instructions, event paging and bounded activity/run
reads. macOS could not start another PostgreSQL cluster because it
exhausted its semaphore limit. These tests used a separate disposable
database on an existing local server through a temporary test setup
override. The helper was restored afterward.
- Full workspace typecheck and build pass with the final state fixes, as
do token gates and capability-map validation.
- Token gates, capability-map validation and `git diff --check` pass.
- Browser proof used a disposable task with 80 comments and six large
run histories. The cold view showed the instruction and recent exchange.
**More messages** revealed older comments. Navigation away and back
preserved expansion.
- Local HTTP sample: core 50 ms, recent comments 24 ms, recent run
summaries 17 ms. The latest native conversation response was 839 JSON
characters / 8 ms; its full event response was 1,099,005 / 86 ms.
Browser content paint was 1.34 seconds with development assets cached.
These are local observations, not staging latency claims.
- The first CI run passed every job except four board-send browser cases
and the review threshold. Those browser fixtures only mocked the old
comments endpoint; they now serve both recent and full reads, retaining
all message and attachment assertions. Both initial review findings were
fixed with regression tests. The next CI run passed every job. The
deeper review identified core-cache mutation, older delivery receipt and
retained run-status gaps; those are now fixed with regression coverage.
Final review is 5/5 with zero unresolved findings. Final-head CI
initially failed one unchanged runner shutdown test
(`runnerd-codex-transport.test.ts`, timeout-attachment case) with
`NativeSessionCloseUnrecoverableError`; all other CI jobs passed. The
same runner code passed on 971a762f80. A targeted local reproduction
also failed during provider drain. The failure is outside the changed
task loading paths; its underlying cause remains unresolved. The single
failed-job retry passed on the same head; this demonstrates recovery,
not a root-cause fix. All CI gates passed on the previous implementation
head `9004f94f28`: [CI run and
retry](https://github.com/paperclipai/paperclip/actions/runs/40192334629).
- `pnpm test:run` was attempted and stopped after repeated embedded
PostgreSQL startup failures. The native session suite recorded 15
database-related failures; other database suites could not start. No
complete local suite pass is claimed.

## Risks

- The compact view intentionally omits older diagnostics until
expansion. Existing open questions and approvals still use the current
interaction and run paths.
- Previously observed comment and activity rows remain cached while
open. The newest preview and incoming cursor pages refresh; this does
not add a general historical-edit synchronization protocol.
- Recent views are optional. Existing full API callers retain their
response shape and behavior. No migration is required.
- A legacy tail can exceed the requested byte limit by the prefix of its
first log record. Backward reads stop at the preceding record boundary
and honor cancellation.
- Staging has not been deployed or qualified with this change. Instance
startup latency is separate from task history loading.
- The related open PRs touch the same task-history files.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository editing, shell
tools and browser testing. The exact serving model identifier 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 — 549 affected task tests;
the full local suite and unrelated runner failure are 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 <noreply@paperclip.ing>
2026-10-26 08:11:39 -05:00
DottaandPaperclip 41c42895fa feat(evals): enforce fixed Runner request allocations (#17452)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Runner evals execute real provider harnesses against maintained
fixtures.
> - Turn-level usage arrives after requests have already spent money.
> - Parallel cells need distinct allowances before requests start.
> - Missing output must preserve the remaining allowance and any known
charges.
> - This pull request adds a controller that reuses the existing request
ledger and gateway.
> - It makes a bounded local Runner attempt reviewable before hosted
scheduling is enabled.

## Linked Issues or Issue Description

Refs #17440. That PR added the explicit Runner gateway route. This PR
owns request admission and retained campaign accounting for that route.

The existing hosted matrix is not enabled or changed here. Its global
allocation, rerun rejection, and private evidence archive remain
separate integration work.

## What Changed

- Validate a trusted manifest with exact request, CLI, package, daemon,
model, and source identities. Assign each selected cell a distinct
allowance. Count operating overhead once. Reject allocations above the
campaign limit before launch.
- Run native Codex or ACPX Claude through the shared request proxy and
append-only ledger. Pass only the proxy credential to the child. Refuse
local attempt reuse and preserve existing output files.
- Close gateway admission on cancellation. Retain the ledger, receipts,
route descriptor, child result, and cleanup evidence outside the
disposable workspace.
- Recompute campaign accounting from retained ledgers. Missing or
invalid final evidence holds the full cell allowance while preserving
known charges. Reports distinguish provisional and incomplete costs.
- Accept namespaces containing only local function/custom tools. Disable
web search in the controlled Codex profile. Continue to reject hosted
tools, including tools hidden inside a namespace.
- Add controller tests to the existing Runner static test path and
document this source-checkout entry point.

## Verification

- 20 controller tests pass. They use real child processes, local HTTP,
and the actual ledger. Cases cover reservations before dispatch,
concurrent cell isolation, caps, cancellation, replay rejection, lost
artifacts, changed receipts, and Braintrust Claude routing. Upstream
responses are synthetic.
- 52 shared gateway tests and 55 Runner CLI tests pass. The namespace
acceptance and web-search configuration regressions failed before the
repair and pass afterward.
- Runner TypeScript/Rust checks, Runner build, shared E2E typecheck, and
`git diff --check` pass.
- The first actual-harness probe found that Codex advertised both a
local tool namespace and web search. It reached no upstream transport.
That failure is retained; the compatibility repair does not allow paid
hosted tools.
- An earlier test failure showed that separate dynamic imports created
different admission-error classes. The shared runtime bridge now loads
one module graph. Another regression showed that a missing final report
hid known ledger charges; the collector now preserves those charges.
- The repaired actual-harness probe passed its control checks for native
Codex and ACPX Claude: each reached exactly one synthetic upstream
request after the ledger reservation; both retained the unresolved hold
and route evidence; both recorded clean process exit. A scan of 41
retained files found no synthetic upstream credential. Deliberate
provider rejection preserved the failed Codex turn and Claude timeout;
these are not semantic passes. Prices, operating bounds, and the package
archive identity in this probe were synthetic.
- Review regressions reproduced lost request/receipt evidence, lost
admission metadata, executable replacement after verification, and a
SIGTERM-resistant descendant surviving the CLI. The repairs now pass.
Executable bytes are staged before launch; a trusted Node resolution
hook preserves relative imports. A symlink-path regression also failed
before its repair and now passes. The final staged native probe repeated
both one-request checks successfully.
- Full current-head
[CI](https://github.com/paperclipai/paperclip/actions/runs/40115932485)
passed on `4781487215116afd10a415d4db53b1ec8e004dd5`: all 58 check runs
and the legacy status are successful, neutral, or skipped. Greptile and
Paperclip both reviewed this commit at 5/5. All five finding threads are
fixed and resolved. No paid provider request ran during this change.

## Risks

- This is a trusted source-checkout controller, not a published package
command. It needs the repository's installed shared cost dependencies.
- The manifest declares reviewed prices and full-lifecycle bounds.
Synthetic tests do not qualify real prices, invoices, storage, or
Braintrust metering.
- The attempt-directory guard is local. Hosted fanout still needs unique
global allocations, rerun rejection, and durable private retention. Do
not enable the existing matrix on this change alone.
- Forced child termination can leave cleanup unverified. That attempt
remains incomplete and holds its full allocation. These checks do not
claim a host security boundary against an adversarial target.
- The current private publisher does not archive the separate controller
directory. Native trace relay, private report integration, and live
qualification remain open.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use, and code
execution. This runtime does not expose the exact model ID 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 <noreply@paperclip.ing>
2026-10-26 07:53:57 -05:00
Devin FoleyandPaperclip 1a3f604097 fix(db): honor the prepared-statement opt-in for ORM queries (#17673)
Drizzle calls Postgres.js unsafe queries, which disable preparation unless
per-query options enable it. The existing client-level opt-in therefore
does not prepare ORM queries.

Honor explicit true across root and child clients. Preserve defaults,
per-query options, lazy execution, and driver error handling. Document the
pooler capability requirement.

Test with 92 selected database and connection checks, workspace typechecks,
server and database builds, and a read-only hosted ORM protocol probe.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 18:17:43 -07:00
Devin FoleyandPaperclip 34409a92c4 fix(native-runtime): stop failover backups from carrying throwaway provider state (#17618)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Native runner sessions on managed sandboxes keep a failover backup
of provider state, so a replacement sandbox can resume the session
> - Each backup copies the provider's runtime directory, with a
per-directory sha256 manifest, minus `excludeEntries`
> - Codex already excludes credentials and re-downloadable plugin
caches; OpenCode excluded nothing, and ACPX excluded only per-agent
credentials
> - OpenCode also leaked a temporary HOME on every launch, because
removing it failed on the read-only skill snapshot inside and the error
was swallowed
> - So every backup carried every earlier launch's HOME (with its npm
cache) plus re-installable plugin `node_modules`
> - This pull request removes those homes correctly, sweeps homes left
by earlier versions, and excludes the throwaway entries from OpenCode
and ACPX backups
> - The benefit is much smaller backups and live session directories,
while session history is still backed up and restored

## Linked Issues or Issue Description

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

**What happened?**
On one instance, current native failover backups totalled 24 GB across
about 300 sessions. For OpenCode sessions:
- `opencode/<session>/home-XXXXXX/`: 170 per-launch HOME directories
across 67 sessions, 9.3 GB. Each held `.npm` (about 111 MB of npm
cache), `.claude` (the skill snapshot) and `.local`.
- `opencode/<session>/config/opencode/node_modules`: 68 sessions, 4.3 GB
of OpenCode plugin installs.

One ACPX session also carried `acpx/<session>/data/pnpm/store` (229 MB
in the current backup, several GB in older copies).

Cause of the leaked homes: `startRuntime` in the OpenCode driver creates
a fresh `mkdtemp` HOME for each launch and seals the assigned skills
inside it (directories `0555`, files `0444`). `close()` and the
startup-failure paths removed it with `rm(..., { recursive: true, force:
true }).catch(() => undefined)`. That fails with `EACCES` on the sealed
directories, and the failure was ignored, so each launch left its HOME
behind.

**Expected behavior**
Per-launch homes are removed when the runtime closes, and backups hold
only state needed to resume a session. Caches that the provider
re-creates are left out.

**Steps to reproduce**
1. Run an OpenCode native session with at least one assigned skill on a
managed sandbox, over several turns or runs.
2. Inspect `<session>/` in the OpenCode runtime directory: one
`home-XXXXXX` per launch remains.
3. Inspect `failover-backups/current/opencode/<session>/`: the same
homes and `config/opencode/node_modules` are in the backup.

**Paperclip version or commit:** `master` at e335265568.
**Deployment mode:** any with native OpenCode/ACPX sessions on managed
sandboxes.
**Agent adapter(s) involved:** OpenCode (native), ACPX.

## What Changed

- OpenCode driver
(`packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts`):
- `removeIsolatedHome` unseals the skill snapshot with the existing
`releaseMaterializedNativeRuntimeSkills`, then removes the HOME. Close
and every startup-failure path use it. It only unseals when `.claude` is
a real directory, not a symlink.
- Per-launch homes are now created under `<session>/homes/home-XXXXXX`,
so one exact path can be excluded.
- When a session opens, `home-*` directories left at the session root by
earlier versions are removed. History is not in them: OpenCode keeps it
in `XDG_DATA_HOME` (`<session>/data/opencode/opencode.db`), and HOME
only holds caches, state locks and the skill copy.
- Exports `opencodeRuntimeSessionDirectoryName` (the existing
session-directory naming) and `OPENCODE_ISOLATED_HOMES_DIRECTORY` for
the server.
- `resolveNativeHarnessPersistenceProfile`
(`server/src/services/native-runtime/native-session-executor.ts`):
- OpenCode excludes `<session>/homes` and
`<session>/config/opencode/node_modules`.
- ACPX (every agent) excludes `acpx/<session>/home/.npm` and
`acpx/<session>/data/pnpm/store`, in addition to the existing per-agent
entries.
- `excludeEntries` are matched as exact relative paths (wildcards are
rejected), so the session directory is computed in the same way as the
existing ACPX entries.
- Exposes the two new exports through the server's runner vendor shim.

No code comments were added.

## Verification

- `pnpm exec vitest run src/drivers/opencode
src/drivers/runtime-context-materializer` (in
`packages/paperclip-runner/`): 108 passed. The 1 failure, `uses provider
structure rather than prose length to select a post-tool answer`, also
fails on unmodified `master`.
- The existing skill-tree test, with and without a startup retry, now
asserts that homes are created under `homes/` and that `homes/` is empty
after close.
- New test: a sealed legacy `home-*` at the session root is removed when
the session reopens, and `data/opencode/opencode.db` is kept.
- `pnpm exec vitest run src/services/native-runtime/
src/__tests__/native-harness-backup-expiration.test.ts` (in `server/`):
5080 passed. `plugin-runner-probe` could not run locally because of a
cargo manifest-path problem in this environment; it is unrelated to this
change.
- Profile tests cover the new OpenCode and ACPX exclusions, including a
session id that needs sanitizing.
- New checkpoint/restore round-trip tests for OpenCode and ACPX. They
use `syncRemoteRunnerDirectoryOut` for the backup and
`stageRemoteRunnerDirectory` for the replacement. Excluded caches are
absent from both the backup and the replacement, while history, config
and workspace files are restored and the live directory is unchanged.
- **Mutation checks**, each failing its test:
- removing the skill unsealing (homes leak again; 3 runner tests fail);
  - removing the legacy sweep (1 test fails);
  - emptying the OpenCode/ACPX exclusion lists (4 server tests fail).
- Checked by hand against the bundled OpenCode 1.18.34 binary, with the
driver's environment:
- launching with a fresh HOME and no `config/opencode/node_modules`
still lists earlier sessions;
  - OpenCode reinstalls the plugin dependencies on launch.
- `pnpm run typecheck` in `server/` and `packages/paperclip-runner/`:
pass.
- Not run: server build and boot smoke.

## Risks

- **Rollout:** existing backups still contain legacy `home-*`
directories. After a restore, the sweep removes them when the session
opens, and the next backup no longer carries them.
- **First launch after restore:** OpenCode reinstalls its plugin
dependencies and npm repopulates its cache. This needs network access to
the npm registry, the same as a brand-new session.
- **Crash leftovers:** a HOME left behind by a hard crash (the runner
killed before `close()`) stays under `homes/` until the session
directory is removed. It is still excluded from backups. Homes under
`homes/` are not swept on open, to avoid touching a HOME that a
concurrently running runtime might still use.
- **pnpm store:** dropping the pnpm store from ACPX backups means the
next `pnpm install` in a replacement sandbox downloads packages again.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through Claude Code
with tool use (shell, file edits, tests, and running the real OpenCode
binary to check what state it reads on resume).

## 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 <noreply@paperclip.ing>
2026-10-25 08:51:39 -07:00
DottaandPaperclip aec86c794b fix(runner): preserve native work through controller outages (#17594)
Keep remote native work under its authenticated connection lease through controller outages. Hold durable ACPX tools through reconnection, align the native Claude HTTP MCP client budget, and wait for the recovered handler before dispatch. Preserve Stop, cancellation, exact identity and artifact checks.

Verified with real-process regressions and a real Claude Opus 5.5 Daytona workflow: 125 seconds offline during useful work, original process and lease retained, no failed native tool calls, queued follow-up completed, independently verified ZIP and committed workspace receipts. All current-head CI gates pass and Greptile is 5/5 with all findings resolved.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 09:01:24 -05:00
Devin FoleyandPaperclip efa2724f9a fix(db): pace bounded response capture output (#17564)
Close database response measurement at its existing deadline, then emit the bounded metadata at 64 records per second. Preserve group indices, counts, privacy, and query/transaction outcomes. Stop the unreferenced timer when the output queue is empty.

Validation: 32 capture and real PostgreSQL tests, full build and typecheck, exact-head Greptile 5/5 (medium), full green CI, and no unresolved comments. A 2,200-query fixture reconciles all 2,048 groups and its summary; dedicated-pool coverage awaits deferred output.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 00:11:41 -07:00
Devin FoleyandPaperclip a12329b8a9 fix(db): complete bounded response attribution (#17537)
Raise bounded database-response attribution to 2048 groups and add numbered 128-entry log pages. Preserve the overflow bucket and exact completed-call and byte totals. Document complete page retrieval and validation.

Verified with 30 focused tests, the full build, and typecheck. All CI checks pass. Exact-head Greptile is 5/5 at Medium impact with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 19:34:31 -07:00
Devin FoleyandPaperclip 71f06d84dc fix(db): keep bounded response captures attributable (#17522)
Keep more caller groups in one bounded database response capture and verify public caller paths in the production bundle. All local checks and CI passed; Greptile scored the exact head 5/5 with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 17:34:41 -07:00
DottaandPaperclip d0c8f0b090 fix: explain project access denials and add an authorized repair path (#17509)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Low-trust agents may read only the projects in their allowed scope.
> - Workspace setup correctly stops a task outside that scope.
> - The task previously showed a generic failure, and agent settings did
not offer an additive repair.
> - This pull request explains the denial and lets authorized operators
add projects while preserving other restrictions.
> - The saved task then offers an explicit retry. Saving access does not
start work.

## Linked Issues or Issue Description

**What happened?**
A project access denial appeared as an ordinary failed attempt.
Operators could not add a second allowed project from agent settings.

**Expected behavior**
The task should explain the denied access and link to a working
permission editor. The editor should preserve existing scope and
restrictions, enforce operator authority, and retain drafts after a
failed save.

**Steps to reproduce**
1. Assign a low-trust agent to a task whose project is outside its
allowed projects.
2. Let workspace setup deny project access.
3. Open the task, follow Manage project access, add the project, save,
reload, and return to the task.
4. Confirm that the saved message remains and Retry task is a separate
action.

## What Changed

- Record a typed, non-retryable project denial before provider dispatch.
Project denials do not enter automatic continuation or provider comment
policy.
- Project current and historical setup denials into a safe task notice.
Keep unrelated failures, prior assignees, and approval/pause/budget
gates separate. Recheck current project authority, including standard
trust and root-issue grants. Accepted retries keep their working state.
- Add board-only project-access reads and additive saves under the agent
row lock. Enforce company scope, agent-management authority,
pending-approval restrictions, and an audit record for new additions.
- Add the production notice and searchable multi-project editor,
including read-only, saving, cancellation, and failed-save states.
Preserve the return-to-task query through canonical agent redirects.
- Register board-only project-access routes in OpenAPI with a strict
request schema.
- Add production Storybook examples and API/persistence/concurrency
regressions. Retain the approved design snapshot and update the behavior
maps and implementation contract.

## Verification

- Passed: `pnpm -r typecheck`, `pnpm build`, `pnpm build-storybook`,
`pnpm check:token-gates`, `node scripts/check-maps.mjs`, and `node
scripts/storybook-catalog.mjs`.
- Passed: 11 permission/projection tests and 30 run-completion tests
against an isolated PostgreSQL database. These cover historical
failures, concurrent additions, idempotent audit, preserved
restrictions, foreign projects, read-only users, agent self-grants, and
no automatic wakeup.
- Passed: all 27 OpenAPI route tests, including route coverage and the
new board-only request/response contract.
- Browser: the real app displayed a historical denial; Manage project
access opened the editor; saving retained the original project and
survived reload; Return to task showed the ready notice. Saving created
one audit event and zero runs. Explicit Retry returned 202 and reached
the separate sandbox requirement, which remained enforced. No external
provider was used.
- All five production Storybook interaction checks pass: blocked task,
save, cancellation, failed-save recovery, and read-only settings. These
use fixture callbacks; durable authorization is covered separately
above.
- `pnpm test:run` was attempted locally but stopped after repeated
embedded PostgreSQL startup failures caused by macOS semaphore
exhaustion. Full-suite CI is required before merge. The separate UI
suite passed all 9,095 tests. The 41 focused server regressions also
passed after rebasing onto the latest master. A further 26
recovery-classification tests pass, including a red-to-green regression
for automatic continuation after a project denial.

## Risks

- No schema migration. The new endpoint only adds same-company project
IDs and preserves the rest of the stored policy.
- The historical notice recognizes one exact setup denial message. It
does not display arbitrary run errors or disclose a different source
project's identity.
- The retry still passes all current admission and isolation checks. The
notice does not promise that other execution requirements are satisfied.
- Related open work: #13309 covers inherited secret restrictions; #11234
covers the broader repository catalog. This change does not replace
either policy surface.

## Model Used

OpenAI Codex (GPT-6), with code execution and browser tools. The precise
runtime model ID and context-window size 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
#` 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 (focused server and full UI
suites; full-suite infrastructure limit 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 <noreply@paperclip.ing>
2026-10-24 16:16:29 -07:00
DottaandPaperclip 4e719ac4c4 fix(evals): align portable receipt bounds before publication
Also cover createdFromIssueId in the list and count UUID rejection tests from the master merge.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 16:03:27 -07:00
Devin FoleyandPaperclip a3675ddcdf Add bounded database response size diagnostics (#17504)
Count database response value sizes by public caller and static table for one explicit UTC window of at most 60 seconds. Keep diagnostics off by default and exclude SQL, parameters, returned values, errors, and credentials from output. Preserve lazy execution, transaction boundaries, and no-replay behavior.

Verification: full build and recursive typecheck; 63 focused database and PostgreSQL tests; eight physical disconnect tests with capture enabled.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 15:39:28 -07:00
DottaandPaperclip 2ccf99aec5 fix(evals): keep stdin imports free of CLI side effects
Also include receipt bytes and the receipt file in reported artifact totals.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 15:34:21 -07:00
DottaandPaperclip e3e1bc1887 fix(evals): include controller receipts in artifact limits
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 15:29:19 -07:00
DottaandPaperclip 8c9d2a0e4a feat(evals): package a portable Runner controller runtime
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 15:27:35 -07:00
DottaandPaperclip 1461bb48e1 fix(evals): retain cancellation and native failures through root export
Close pending OTLP forwarding on cancellation through finalization. Inspect native receipts before building the root, and label its status as preceding the root export.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 14:41:15 -07:00
DottaandPaperclip 9b7e77cf6c feat(evals): retain bounded native trace evidence in the Runner controller
Bind native exports and a separate root trace to each attempt and its reviewed operating allowance. Replay private receipts after worker deletion and retain holds on incomplete exports.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 14:30:03 -07:00
Dotta 39d53931f9 Combine current master with retained controller evidence for native tracing
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/runner-controller-evidence:
  fix(evals): retain annotated failed controller attempts
  fix(evals): bind archived results and complete catalog allocations
  fix(evals): stop surviving children after controller CLI exit
  feat(evals): retain portable Runner controller evidence
  fix(evals): finish process group cancellation after CLI exit
  fix(evals): resolve staged CLI imports through canonical paths
  fix(evals): retain accounting proof and pin launched bytes
  fix(evals): qualify local Codex tool namespaces
  feat(evals): enforce fixed Runner request allocations

# Conflicts:
#	packages/paperclip-runner/docs/runner-protocol-live-evals.md
2026-10-24 14:11:24 -07:00
DottaandPaperclip 044628f59e Add explicit native tracing to the Runner eval CLI (#17483)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Runner Evals exercise real providers against a mock control plane.
> - The controlled gateway route creates a disposable provider home.
> - That route had no explicit native trace configuration.
> - Codex skips normal home preparation for a preconfigured home.
> - This change validates and applies the existing observability launch
contract before provider creation.
> - Operators can collect native evidence without passing vendor keys to
the child.

## Linked Issues or Issue Description

Refs #17440. Related: #17452 and #17469. This PR is based directly on
master and does not include those pending controller changes.

**Current behavior**

The eval-session CLI cannot pass an explicit native observability
launch. Passing environment alone cannot configure Codex in the
disposable gateway home.

**Proposed behavior**

Accept an opt-in descriptor bound to the gateway attempt. Validate the
existing provider launch contract, add a scoped relay token in memory,
and apply it only to the owned provider home and environment.

**Reason and benefit**

A controller can route native evidence through its bounded local relay
while the CLI keeps configuration distinct from verified ingestion. This
is the CLI contract; hosted controller integration remains separate
work.

## What Changed

- Add a strict, bounded descriptor reader for native Codex and ACPX
Claude. Bind campaign, execution, attempt, project, trace, span, and
mock authority identifiers.
- Reuse Runner observability materialization and redaction. Preserve the
model gateway route. Reject upstream destinations, extra environment
fields, credentials in headers, and unknown exporter settings.
- Add `--observability-config`, requiring an explicit gateway. Keep
direct and unconfigured paths unchanged.
- Retain secret-free configured/unverified metadata in success and
failure artifacts.
- Document the contract and controller responsibilities. No workflow,
runtime dependency, paid schedule, or public report changes.

## Verification

- Eval CLI regression selection: 81 passed before the test-only
relocation. The 17 new cross-package observability tests now pass from
`tests/runner-e2e/runner-eval-observability.test.ts`; their
integration-suite typecheck also passes. They use the actual shared
launch generator, real disposable homes, CLI success/failure paths, and
credential redaction. The first run exposed two test setup references
introduced by a broad edit; both were corrected and its failure log
remains retained.
- Runner TypeScript build and release daemon build passed. Full `pnpm -r
typecheck` passed.
- Actual native Codex and ACPX Claude reached a local receiver with
authenticated OTLP protobuf and the expected Braintrust parent. Codex
sent four log and two trace batches; Claude sent three log and one trace
batch. All recorded processes exited, disposable homes were removed, and
29 retained files contained neither synthetic credential.
- The local model endpoint deliberately returned 401. Codex retained a
failed terminal turn; Claude retained a provider timeout. These are
route/export proofs, not semantic eval passes. Native retries reached
only the fake endpoint. No paid provider or Braintrust request was made.
- The first local probe failed before provider launch because its
TypeScript loader path was wrong. It was corrected to the installed
loader; both attempts remain retained.
- Full repository build passed at initial head 7b75c0349f. The full
local test suite encountered repeated embedded PostgreSQL startup
failures before database-backed test bodies. It was stopped after five
affected suites (exit 143), so it is incomplete. Its log remains
retained. The same host had independently demonstrated PostgreSQL
semaphore exhaustion earlier in this task; this run did not collect that
lower-level error. Full hosted workflow 40131255078 passed on final head
23a172af32, including the database-backed
suites, Runner, build, typecheck and browser checks. Greptile is 5/5
with zero open threads. The secondary Paperclip reviewer ended neutral
without an assessment; it is not counted as a successful review. A
follow-up header-validation repair uses a map without a prototype and
passes the added negative regression. No exporter behavior changed.
- The CLI still reports native ingestion as unverified. No complete
capture, destination indexing, vendor billing bound, hosted admission,
or paid recurrence qualification is claimed.

- Package-boundary validation found two new imports from the standalone
Runner tests into other repository packages. Those tests now live in the
repository integration suite and use the actual launch generator there.
Its first run failed on a TOML parser resolved from the wrong package;
resolving that dependency from its Runner owner fixed the test. Three
pre-existing boundary/tracked-import findings remain in unchanged files;
this PR adds none.

## Risks

- This descriptor is intentionally narrow. Changes to the shared launch
contract must be reviewed against its validator and integration tests.
- The local relay receives native content under the selected capture
policy. Its controller must enforce ingestion quotas, shutdown, private
retention, and qualified cost bounds.
- Descriptor validation runs once before launch. It reads at most 128
KiB and refuses symlinks and non-regular files. Existing owned-home
config writing remains synchronous and small.
- Relay credentials are held in child memory. Persisted diagnostics and
artifacts use the existing redactor. The descriptor itself must remain
secret-free.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use, and code
execution. This runtime does not expose the exact model ID 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 <noreply@paperclip.ing>
2026-10-24 14:07:28 -07:00
Dotta 7d4eeb5728 fix(evals): retain annotated failed controller attempts 2026-10-24 13:04:11 -07:00
DottaandPaperclip 4d6673d8ce fix(evals): bind archived results and complete catalog allocations
Compare immutable outcomes and accounting with the controller's reconstructed output. Validate provider, model, profile and driver for every selected allocation, including missing cells.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 12:51:01 -07:00
DottaandPaperclip b28901e420 fix(evals): stop surviving children after controller CLI exit
Retain the full allocation and bound inherited pipes when the CLI exits before its owned provider process group. Cover both retained and absent artifacts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 12:45:39 -07:00
DottaandPaperclip 99e07e675d feat(evals): retain portable Runner controller evidence
Replay archived ledgers through the existing verifier, preserve conservative holds, and keep private bundles out of public reports.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 11:13:45 -07:00
DottaandPaperclip 1a4275856f Merge reviewed Runner controller for evidence integration
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/runner-controlled-budget:
  fix(evals): finish process group cancellation after CLI exit
  fix(evals): resolve staged CLI imports through canonical paths
  fix(evals): retain accounting proof and pin launched bytes
  fix(evals): qualify local Codex tool namespaces
  feat(evals): enforce fixed Runner request allocations
2026-10-24 10:48:10 -07:00
DottaandPaperclip 0eb2c964a7 fix(agents): allow runner qualification with agent configuration permission (#17448)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent owners configure the harness and computer used for that work.
> - A legacy runner upgrade must first qualify an unverified sandbox.
> - The upgrade called an instance-admin probe, so a company owner with
agent configuration permission received HTTP 403.
> - This pull request qualifies the saved computer through the agent's
existing configuration authorization.
> - Company owners can complete the upgrade while general instance
environment management stays restricted.

## Linked Issues or Issue Description

Refs #17361. Related: #17122.

**What happened?**

Upgrade runner displayed for an existing Claude agent on a Daytona
Paperclip Computer. Clicking it returned “Instance admin access
required” for a cloud company owner. The agent remained on the legacy
runner.

**Expected behavior**

A caller permitted to update the agent can qualify that agent's saved
computer and upgrade. Instance-admin access is not required for this
agent-scoped operation.

**Steps to reproduce**

1. Sign in as a cloud company owner with `agents:configure` and no
instance-admin role.
2. Open an existing legacy Claude agent on an unqualified Daytona
snapshot.
3. Select Upgrade runner.

## What Changed

- Add `POST /api/agents/{id}/qualify-runner` with the existing
`agent_config:update` guard.
- Reuse the server's effective environment resolver. The submitted
environment ID only detects stale state; it cannot select a different
computer.
- Preserve company binding checks, check binding again at lease
acquisition, and record agent-scoped audit activity.
- Return qualification status and the lease receipt without
instance-level provider metadata or raw provider errors.
- Use the new endpoint for the existing-agent upgrade. Keep the generic
environment probe restricted to instance admins.
- Disable Upgrade runner when an unsaved computer change resolves to a
different target, with a save-first explanation. Other draft settings
still accompany an upgrade.
- Add real-database authorization regression tests and update the API
contract and access maps.

## Verification

- Red: the existing UI called the instance-wide probe and failed the new
agent-scoped call assertions in eight upgrade cases.
- Green: 349 related route, provider qualification, runner selection,
and UI tests passed.
- Green: 12 authorization cases passed with real PostgreSQL, real HTTP
routes, real saved role grants, and a simulated provider boot. They
cover the cloud owner, removed grants, foreign agent/computer, inherited
targets, stale target, invalid body, archived/local target, redacted
failure, audit scope, and unchanged agent settings.
- Review regression: two cases first reproduced the enabled upgrade
button after an unsaved computer change; both now pass, along with all
168 related UI tests. Qualified/unqualified computers and restoring the
saved selection are covered.
- All 26 OpenAPI cases passed; the 12 real-database authorization cases
also passed after rebase.
- The host exhausted PostgreSQL semaphores. For the 12 cases, a
temporary local-only setup used disposable PostgreSQL 18 in Docker
instead of the embedded database launcher. The committed tests use the
normal embedded database helper. No authorization decisions were mocked.
- `pnpm -r typecheck`, `pnpm build`, `pnpm check:token-gates`, map
validation, module-boundary validation, and `git diff --check` passed.
- `pnpm test:run` was attempted but did not establish a full local pass:
the host could not start embedded PostgreSQL clusters. All current-head
CI gates passed, including the full suite in CI. The commitperclip
review workflow timed out before reporting any executed steps on its
first attempt and passed on one retry.
- Fresh staging acceptance remains pending deployment. Verify persisted
native configuration and a successful runtime test on the existing
Daytona computer after merge. Earlier live Daytona proof from #17361 is
separate evidence.

## Risks

- Qualification can start a fresh, temporary sandbox. It uses the
existing probe lifecycle and cleanup contract.
- The new path must remain scoped to the saved agent and its company.
Denial and redaction tests cover that boundary. General instance probe
authorization is unchanged.
- No migration, role change, credential change, or automatic agent
upgrade is included.

## Model Used

- OpenAI GPT-6 through Codex, with reasoning, code execution, and
browser tools. The exact deployed model variant and context-window size
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 <noreply@paperclip.ing>
2026-10-24 10:31:40 -07:00
DottaandPaperclip 7ae81c9fa7 fix(slack): support rich questions and complete review delivery (#17255)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Questions and review decisions must work in the conversation where a
task started.
> - Slack omitted question context and did not publish several review
kinds.
> - Some answers saved in Paperclip also lost their authorized
continuation or their final Slack reply.
> - A settled card and a successful run did not prove that the user
received the result.
> - This pull request preserves complete questions and follows the
existing answer and delivery receipts through continuation.
> - Users can read the request, submit an authorized decision, and
receive the resumed task's result in the same conversation.

## Linked Issues or Issue Description

Refs #17045 and #17218.

**What happened?**

Slack question cards could omit instructions and editable starting text.
Checkbox reviews, item verdicts, task suggestions, and connection
requests had no publication path. Complex question answers submitted in
Paperclip could fail continuation authorization. Several review
continuations completed without delivering their result to Slack.

**Expected behavior**

Show the actual question and context. Use native Slack controls when
they preserve the form contract. Otherwise provide an explicit Paperclip
review link. Persist one authorized response, resume through the
existing delivery path, settle the card, and deliver the useful result
to its originating conversation.

**Steps to reproduce**

1. Start a task in Slack and ask a text or choice question with
instructions and help text.
2. Submit native text and choice responses, then submit a complex form
through Paperclip.
3. Accept a checklist, submit item decisions, select suggested tasks, or
decline a connection request.
4. Inspect the saved answer, continuation run, original Slack card, and
final Slack reply independently.

## What Changed

- Preserve full prompts, context, descriptions, recommendations, help,
and editable starting text. Use direct buttons for short choices, inline
searchable selects for longer lists, and native checkboxes with explicit
submission for multiple choices.
- Add native custom-answer and validated-text modals, ordinary
confirmation buttons, checklist decisions, per-item verdicts with
reasons, suggested-task creation/dismissal, and connection decline. Keep
governed approvals, permission grants, and forms that cannot be
represented faithfully in the full Paperclip review surface.
- Keep Slack-specific canonical-validation feedback in its owning
platform module. Shared callbacks delegate without interpreting provider
presentation.
- Reuse the existing canonical interaction transactions, callback
receipts, publication queue, and continuation delivery. Share ordinary
confirmation handling across Telegram and Slack. Recheck current
authorization and commit response receipts atomically.
- Settle cards with the saved answer and remove stale controls. Ignore
passive Slack link callbacks so opening a task does not display a false
answer error.
- Validate exact multi-select receipt mappings during native
continuation. Recover automation-origin connection replies from their
published prompt after validating the durable connection receipt.
- Keep oversized multi-select handoffs free of unused native tokens, and
retain the complete Paperclip form for 101–128 choices plus Other. Make
the original-conversation audience explicit before submitting a Slack
modal.
- Add rollback, provider serialization, callback, continuation, and
negative authorization regressions. Update the connector playbook and
feature/access maps. No React or application UI changes are included.
- Add a durable Q01–Q11 qualification index and provider coverage
matrix. Require distinct functional, good-experience, and
best-available-control assessments; real unanswered/answering/settled
evidence; saved results, continuation and navigation proof; and retained
failures. Mark other providers unaudited rather than assuming Slack
parity.

## Verification

Candidate: `e0017d446b1016a38d65382366cd1408cce33039`. The
eleven-core-format live Slack gallery is pinned to
`c3d458b2a06b660448940fd8cf33c5906f0058d5`; subsequent boundary fixes
and the sharing notice have automated verification, not new live
screenshots.

- Real Slack UI testing covered all 11 formats: text, closed choice,
long single select, multiple choices, choices with a custom answer,
pattern-validated text, confirmation, checklist, item verdicts,
suggested tasks, and connection requests. Each has
unanswered/answering/answered screenshots as applicable, an inspected
saved result, one continuation, and an individual experience review in
the local HTML gallery.
- The dedicated private QA app/channel used signed callbacks to the
local candidate. A separately approved password-protected, read-only
preview proved connection-review, suggested-task review, and
created-task navigation. Anonymous access, writes, and credential routes
were denied. No OAuth grant was attempted.
- Real testing exposed and repaired a multi-select continuation failure
and a misleading error on link-button clicks. Failed attempts remain in
the evidence. The final navigation replay opened the exact pending
connection request and declined it in Slack without granting access.
Suggested-task creation produced the selected child with the correct
parent and description.
- Final review-fix verification: **32 form tests**, **28
provider/form-rendering tests**, **30 publication tests**, and **171
native continuation/authorization tests** passed. Earlier **3
atomic-resolution rollback**, **9 targeted
callback/publication/Telegram**, and **28 review-continuation** cases
passed. The final Slack/Teams modal subset also passed. The
automation-origin connection cases, 101/128 custom-choice cases, and
oversized multi-select continuation reproduced before their fixes and
passed afterward; failing logs remain retained. These counts describe
relevant runs, not a reliability percentage.
- CI on `b5885b85fd` exposed a test fixture that assumed every Slack
modal block was an input; the new sharing notice correctly renders a
non-input block. The fixture now selects input blocks safely. The exact
signed-modal correction/one-continuation test passed locally, together
with 14 Slack form/modal tests. The failing CI log and its specific
cause remain recorded.
- `pnpm -r typecheck` and `pnpm build` passed. Server typechecking was
repeated after the final source changes. Map and module-boundary checks
and `git diff --check` passed.
- The full `pnpm test:run` was attempted and stopped after repeated
embedded PostgreSQL semaphore exhaustion on this host. Its 1,395-test
chat suite found two stale native-control assertions; both were
corrected and re-tested in a focused run. Focused database suites used
separate disposable databases on existing local PostgreSQL. The full
suite is not claimed green; new-head CI must complete before merge.
- Final-head CI passed on `e0017d446b1016a38d65382366cd1408cce33039`:
all test/build jobs and required `ci / verify` and `ci / e2e` gates are
green
([run](https://github.com/paperclipai/paperclip/actions/runs/40115430194)).
Greptile and Ponytail both reviewed this exact head at 5/5, the security
scan passed, and all review threads are resolved.
- All three QA app callbacks were restored to staging. Both temporary
tunnels and local proxies were closed. The local endpoint was archived
and its local credential copies removed. The candidate app was stopped.

Limits: provider execution used a deterministic Claude protocol fixture,
not an LLM. This is local real-provider UX proof, not deployed staging
or broader Codie/Claude qualification. The public preview intentionally
blocked ancillary artifact routes, so its destination pages could show a
partial-history warning. The complete permission-grant flow and every
oversized/mixed-form branch were not driven manually. Earlier fixture,
test, and provider failures remain recorded.

## Risks

- New reply routes require durable origin proof. Incorrect receipt
matching could lose a reply or route it incorrectly. Tests cover
missing, forged, mismatched, and revoked authority.
- Governed forms keep their Paperclip resolution checks. This change
does not grant new approval authority to Slack.
- Existing completed review results can become deliverable through the
repaired publication path without another provider run.
- No schema, lockfile, workflow, public task-status model, or
runtime-instruction change is included. This branch is not deployed.

## Model Used

OpenAI Codex, GPT-6 family, with code execution and tool use. The exact
backend 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 linked existing issues or 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
- [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 on the new head
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
on the new head
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-24 10:29:15 -07:00
DottaandPaperclip 4781487215 fix(evals): finish process group cancellation after CLI exit
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 10:23:49 -07:00
DottaandPaperclip 5201836602 fix(evals): resolve staged CLI imports through canonical paths
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 10:20:59 -07:00
DottaandPaperclip e796f67710 fix(evals): retain accounting proof and pin launched bytes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 10:19:15 -07:00
DottaandPaperclip 966f3a2b27 fix(evals): qualify local Codex tool namespaces
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 10:08:58 -07:00
DottaandPaperclip 507033e48c feat(evals): enforce fixed Runner request allocations
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 10:08:58 -07:00
DottaandPaperclip d1fdfced9d feat(evals): route Runner sessions through a controller gateway (#17440)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Runner Evals test provider behavior against a mock control plane.
> - A campaign budget needs to observe and admit each provider request.
> - The existing eval CLI does not select the provider-home mode needed
for a controller-assigned route.
> - A proxy URL alone therefore cannot establish controlled Codex
routing.
> - This change adds an explicit route with an isolated home and
retained identity.
> - The controller can use that route as the basis for request
accounting and admission.

## Linked Issues or Issue Description

**What happened?**

The direct Runner eval CLI passes the ambient environment to the native
transport. The transport deliberately removes an environment-only
preconfigured-home marker unless its explicit option is also set. This
prevents a controller from assigning a Codex gateway through that marker
alone.

**Expected behavior**

A controller can explicitly route a supported eval attempt through its
local proxy. The provider receives the proxy token and isolated
configuration. The artifact identifies the assigned route on success and
failure. Ordinary direct runs keep their current path.

**Steps to reproduce**

Run the new CLI tests for `--gateway-config`. They validate the
descriptor, inspect the actual transport inputs and provider sanitizers,
and check retained artifacts and owned-home cleanup. The local probe
also ran built native Codex and ACPX Claude against a fake loopback API
using only synthetic credentials.

Related request-accounting work:
https://github.com/paperclipai/paperclip/pull/17432.

## What Changed

- Add an explicit gateway descriptor bound to the requested provider,
exact model, campaign, execution, and attempt.
- Accept only the controller's IPv4 loopback URL and a separate proxy
token.
- Create a fresh provider home for native Codex and ACPX Claude. Exclude
ambient provider credentials, subscription logins, proxies, and startup
hooks.
- Retain route identity and descriptor hash in successful and failed
artifacts. Remove the owned home in finalization.
- Document the CLI contract and its limits.

The Runner eval CLI owns this behavior. It uses the existing native
preconfigured-home contract. Production routing, permissions, and
provider protocols are unchanged.

## Verification

- Initial verification passed 56 focused gateway/CLI tests and 246 CLI
and adjacent provider-isolation tests. After the admission repair, all
247 CLI and adjacent provider-isolation tests passed, with TypeScript
checks and build passing again.
- Runner TypeScript checks and build passed.
- The native daemon rebuilt from the candidate base with locked, offline
dependencies.
- Local fake-API probe: six authenticated Responses requests from native
Codex and six authenticated Messages requests from ACPX Claude reached
the assigned endpoint. Claude also made an unauthenticated hello check.
The final commit retained both route artifacts. All 27 retained files
were scanned for the synthetic proxy token and contained none. Both
temporary homes were removed, and all four recorded provider/daemon PIDs
were absent after exit.
- The fake API intentionally rejected inference. Codex retained a failed
turn; Claude retained a timeout. These are route observations, not
passing semantic evals or live provider qualification. No paid provider
calls ran.
- The first focused command used the wrong directory and discovered no
tests. It was corrected before the successful runs above.
- Full Runner validation: 3,967 tests passed, 13 skipped, and five
failed in three unchanged installation and native-settlement files. The
failures were two installation timeouts and three retained-cleanup
settlement failures. One isolated run passed all 109 applicable tests
with six skips. This shows recovery, not a proven cause for the original
five failures; those remain retained. Hosted [CI
40111220754](https://github.com/paperclipai/paperclip/actions/runs/40111220754)
passed on final commit `a5c47b64e56c1228fca718b72a81ddc3b1d185a4`,
including repository typecheck, build, tests and Runner checks. All 57
checks passed or were neutral/skipped, and the legacy status passed.
Greptile and Paperclip reviews both scored 5/5 on that commit; no
findings remain unresolved.
- Rebased onto current master
`71c04c0bfb205cc9e7f7bc20c29fd76051b44cc4`. Its added change concerns
server cleanup; Runner source is unchanged.

- An actual ACPX Codex probe failed before reaching the fake API. Review
and source inspection confirmed that Runner deliberately rejects its
preconfigured home to protect Runner state. This route is now rejected
before service creation. Both explicit and default ACPX Codex admission
regressions failed before the repair. The unsupported lane is not
presented as qualified.

## Risks

This is an explicit new CLI route. Unsupported providers fail before
service creation. Upstream route is a controller declaration, not
independent billing evidence. The local probe saw six Codex requests
where legacy usage counted one, so the controller's request ledger must
remain the budget source. Campaign allocation, proxy lifecycle, hosted
workflow wiring, dedicated OTLP relay routing, and paid qualification
are separate remaining integration steps. This PR enables none of those
schedules.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use, and code
execution. 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, including the information
available in this session
- [x] I checked ROADMAP.md; this is eval infrastructure, not a competing
core feature
- [x] I searched for duplicate PRs for this exact route change
- [x] I described the issue in-PR using the bug template labels
- [x] I have not referenced internal or instance-local issues
- [x] My branch describes the change and contains no internal issue ID
- [x] Relevant focused tests pass
- [x] I added tests for the new contract and lifecycle
- [x] I updated the relevant documentation
- [x] I described the risks and unqualified behavior
- [x] All current-head Paperclip CI gates are green
- [x] Current-head Greptile is 5/5 with no unresolved findings
- [x] I will address review findings before merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-24 09:34:10 -07:00
DottaandPaperclip 87bf89756e fix: support Daytona runner upgrades and ignore obsolete Claude options (#17361)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Existing Claude and Codex agents can upgrade to Paperclip Runner.
> - A Daytona computer needs platform qualification before Runner
becomes its default.
> - The upgrade notice required that default, so it hid the action that
could start qualification.
> - Saved Claude turn limits also caused conversion to fail even though
Runner can run without them.
> - This pull request verifies the computer on click and ignores
obsolete Claude options with a warning.
> - Agents retain their model, account, environment, instructions, and
permission policy.

## Linked Issues or Issue Description

Related: #17122.

**What happened?**

A legacy Claude agent on a Daytona snapshot had no upgrade button until
its environment had platform qualification. A saved `maxTurnsPerRun:
1000` then caused conversion to return HTTP 422.

**Expected behavior**

The upgrade action verifies a supported Daytona computer and converts
the agent. Unsupported tuning options produce a visible warning and do
not block execution.

**Steps to reproduce**

1. Create a legacy Claude agent with `maxTurnsPerRun: 1000` on an active
Daytona snapshot.
2. Open Runtime before the environment has platform qualification.
3. Try to upgrade to Paperclip Runner.

**Paperclip version or commit**

After #17122, reproduced against `9c442cf48e`.

## What Changed

- Offer upgrade on probe-capable Daytona snapshots and immutable sandbox
images. Perform the public environment probe only after a click. Save
after fresh adapter discovery confirms support.
- Resolve the actual computer for explicit, instance-default, and hidden
managed environments. Preserve pending edits and reject stale probe
results.
- Ignore Claude `maxTurnsPerRun`, `chrome`, `warmHandleIdleMs`, and
`acpWarmHandleIdleMs`. Return structured warnings from create, hire, and
update. Show warnings in Runtime and after a save.
- Keep validation for commands, credentials, filesystem, network, and
permission settings.
- Retain the legacy turn limit in the existing live conversion fixture.
Update regression tests, upgrade stories, and capability maps.

## Verification

- Red: the old route returns HTTP 422 for the saved turn limit; the old
form hides the Daytona upgrade notice.
- Green: 417 focused server and form tests pass. A further 188 tests
pass after the review fixes. Qualification coverage includes failed
probes, unsupported targets, changed settings, managed defaults, unsaved
environment variables, and forced Kubernetes targets.
- `pnpm -r typecheck` and `pnpm build` pass. Token gates, map checks,
map tests, and Storybook catalog checks pass.
- Real local Claude Opus 5.5 conversion and task pass, with the
downloaded artifact checked against seeded source bytes, SHA-256, and
Git commit.
- Real browser upgrade and full Claude Opus 5.5 task on a disposable
named Daytona snapshot pass at `b81cec7d8c`. The saved agent retains its
model, high effort, credentials, timeout, and grace settings while
dropping the turn limit with a visible warning. Exactly one successful
run completed the task. The downloaded artifact matched independently
seeded source bytes, SHA-256, and Git commit; the source was unchanged,
no interactions remained pending, and the delivery link survived a
browser reload. The snapshot used current runtime assets built from
public source. The supplied Daytona account could not access the
original staging snapshot, so this verifies an equivalent local fixture
rather than that exact snapshot. Disposable test resources were cleaned
up.
- Earlier live attempts remain recorded: an image-based sandbox creation
failed, and the first named-snapshot probe returned failure with
unresolved provisional cleanup identity. Later named-snapshot probing
and the task succeeded; the initial creation failures are not attributed
to this PR or declared resolved. A separate attempt using the Oct 18
cached runtime failed the current provider-pack compatibility check;
rebuilding the runtime from matching public source removed that
mismatch. Owned resources were identified and cleaned up, with
diagnostic databases retained.
- 54 checks passed at `b81cec7d8c`, with no failed checks; Greptile and
Paperclip Review both scored 5/5, with both review threads resolved.
Master advanced during the live proof, so the branch was rebased with
both sides of additive import/test conflicts retained. On the rebased
head `2b74222b94`, 472 focused tests, UI typecheck, and UI build pass.
All required CI gates pass; Greptile and Paperclip Review both score 5/5
on this head, with no unresolved review threads. The PR is mergeable.
The serial local `pnpm test:run` was stopped after the CI test lanes
passed; that local run is not claimed as a pass.

## Risks

- An explicit upgrade can allocate a temporary probe sandbox and take
several minutes. Probe failures leave the agent on its existing runner.
- Ignored options no longer enforce a turn cap, Chrome integration, or
legacy idle-time tuning. The UI warns about each active ignored option.
- Unsafe or ambiguous execution settings still fail validation. This
change does not weaken environment qualification or runtime artifact
checks.

## Model Used

OpenAI Codex, GPT-6, with reasoning, code execution, and browser tools.
The exact deployment 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 <noreply@paperclip.ing>
2026-10-24 07:59:18 -07:00
Nicky Leach aa0037a137 Support Codex warm plugin task reuse and recovery (#17390) 2026-10-23 19:14:14 -07:00
Devin FoleyandPaperclip 724fa6144d fix(opencode-local): preserve the agent during sandbox tool OOMs (#17398)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Legacy OpenCode adapter runs an agent and its shell tools in the
same sandbox.
> - A large build can exceed the sandbox memory limit and trigger a
kernel OOM kill.
> - The kernel can kill OpenCode before its tool, which loses the active
turn.
> - OpenCode provides a shell environment hook that can change only the
tool subprocess.
> - This pull request uses that hook to prefer Bash tool processes as
OOM victims.
> - The benefit is that OpenCode can report a failed tool and continue
the task.

## Linked Issues or Issue Description

**What happened?**

An OpenCode sandbox run ended with exit 137 after a TypeScript process
had also been killed. Its cgroup limit was 4 GiB. It had completed
useful tool work and had not reached its execution deadline. The
original sandbox was deleted, so its historical OOM counters are
unavailable. Memory pressure is the leading explanation, not a proven
historical kernel diagnosis.

**Expected behavior**

When a shell tool exhausts sandbox memory, the kernel should prefer that
tool over OpenCode where possible. The adapter must preserve configured
shell startup, model, permissions and resource limits.

**Steps to reproduce**

Use a disposable 4 GiB Linux sandbox with OpenCode. Retain a 1,200 MiB
buffer in the agent and run a Bash tool with fourteen 250 MiB workers.
Compare the same workload before and after installing the generated
hook. Record the cgroup memory events, agent exit and final response.

Refs: #16844. That PR covers the same protection for Claude. This PR
adds the OpenCode-owned hook. Refs: #17254 for the separate shutdown
recovery path. This PR does not change retry policy.

## What Changed

- Generate a dependency-free OpenCode `shell.env` plugin and a Bash
startup file in the isolated runtime config.
- Stage those assets only for sandbox execution. Resolve the startup
file relative to the copied plugin.
- Set child `oom_score_adj` to `1000` on Linux. Keep the parent
environment unchanged and honor existing `BASH_ENV` values, including an
empty value.
- Keep command execution available if the optional plugin cannot be
installed or the process priority cannot be changed.
- Materialize linked plugin directories inside the isolated config
before writing. Keep the operator directory and its plugins unchanged.
- Cover relocation, shell output and exit status, operator settings,
local execution, remote staging and failed installation. Update the
adapter documentation and verification recipe.

## Verification

- Red before the fix: the new runtime regression failed because the
sandbox shell plugin was absent. The existing 15 runtime config tests
passed. The same regression passes with the fix.
- Apex first found a host-write risk through linked plugins directories
(4/5). A new regression reproduced the host directory modification, then
passed after the installer materialized the copied link. The finding is
resolved. Apex reviewed current head
`df82a8f51725a197c0ea3ebeb942488509f32b34` at 5/5 with no actionable
issues remaining.
- The full OpenCode adapter suite passes: 87 source tests. The adapter
typecheck and map checks pass.
- Real sandbox acceptance used the plugin generated from
`df82a8f51725a197c0ea3ebeb942488509f32b34` and OpenCode 1.18.34 in a
disposable 4 GiB Daytona sandbox. A controlled local model endpoint
issued an actual Bash tool call; no real model credentials were used.
The tool reported priority `1000`; the parent reported `0`.
- Controlled OOM comparison: without the guard, OpenCode was killed by
SIGKILL after 8.95 seconds and the cgroup recorded one OOM kill. With
the guard, five workers were killed, OpenCode exited `0` after 21.87
seconds, and its final response was `fixture-complete`. The cgroup
counter increased by five. Both attempts used the same allocation and
workload. The fixture started with a linked operator plugins directory.
The installer preserved that directory, staged a real plugins directory,
and passed both the process-priority probe and OOM comparison. The
fixture allocation was deleted afterward.
- The first live probe was not accepted because the harness could not
parse the provider response framing. After correcting the decoder, the
unchanged plugin passed the probe and the controlled OOM comparison.
Both initial disposable allocations were deleted. After the review fix,
a third allocation repeated the probe and OOM comparison with a linked
plugins directory. That allocation was also deleted. The initial
comparison had the same agent-killed/agent-survived result (8.42 seconds
without the guard; 19.42 seconds with it).
- Repository `pnpm -r typecheck` and `pnpm build` pass. The full CI
matrix passes on `df82a8f51725a197c0ea3ebeb942488509f32b34`, including
tests, browser shards, Runner checks, build, policy and Apex. [CI
run](https://github.com/paperclipai/paperclip/actions/runs/40043947375).
- The first full local test attempt had 15 embedded PostgreSQL startup
failures. Direct `initdb --version` and `postgres --version` probes
showed missing bundled library symlinks and SIGABRT. Running the bundled
symlink hydration script restored both executables. All 15 affected
cases pass after that setup repair. No product code changed for it.
- Two existing workspace tests assumed that new Git repositories have no
`main` branch. They failed under the host default branch setting of
`main`, then passed with a per-command `init.defaultBranch=master`
setting. The global setting was preserved. The full local test command
was restarted with hydrated libraries and that controlled fixture
default. It had no reported assertion failures when it was stopped after
the complete CI matrix passed. It did not complete locally; full-suite
verification comes from CI. The first failed invocation was also
stopped.
- The original tenant task has not been retried or recovered by this PR.
Its board API credential is expired. The original hosted workflow and
the separate shutdown recovery remain outside this acceptance result.

## Risks

- This is a preference, not a memory reservation. It cannot guarantee
that OpenCode survives every OOM condition, including memory growth
inside OpenCode itself.
- The guard applies to non-interactive Bash tools. Other shells ignore
`BASH_ENV`. Existing operator startup settings take precedence.
- Older OpenCode releases without the shell environment hook will not
apply the preference. No model, permission, deadline, memory quota or
retry limit changes.
- No schema change or migration.

## Model Used

OpenAI GPT-6 through Codex, with repository tools and code execution.
The exact deployment variant and context window 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
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-23 17:49:00 -07:00
cc127f5516 fix(runner): drop unusable musl builds and duplicate Copilot from provider packs (#17387)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The runner release builds a Linux "provider pack". The pack is a
self-contained runtime with Node, Claude Code, Codex, and OpenCode. The
Daytona runner image and SSH hosts use it.
> - The release artifacts for this pack are large: 1,182 MB for
linux-x64 and 543 MB for linux-arm64.
> - Most of that size is necessary. But about 20–30% of it is code that
the pack can never run.
> - pnpm 9 installs all libc variants of optional platform packages.
Thus the pack includes the musl (Alpine) builds of Claude Code and
OpenCode. The pack runs only its copied glibc Node, so these builds are
never used.
> - On linux-x64, the npm Copilot package is also a byte-identical copy
of the verified Copilot provider asset (162 MB).
> - This pull request removes these unused bytes from the pack at build
time.
> - The benefit is smaller release artifacts and Docker images, and
faster uploads and downloads.

## Linked Issues or Issue Description

**What happened?**
The runner release workflow uploads `runner-provider-packs-linux-x64`
(1,182 MB) and `runner-provider-packs-linux-arm64` (543 MB). The darwin
daemon artifacts are about 10 MB. The darwin artifacts are a different
artifact type, so the comparison is not direct. But inspection of the
extracted Linux packs found real waste:
- `@anthropic-ai/claude-agent-sdk-linux-*-musl` (about 225 MB
uncompressed)
- `opencode-linux-*-musl` and `opencode-linux-x64-baseline-musl` (about
180 MB each)
- on linux-x64,
`node_modules/.pnpm/@github+copilot-linux-x64@1.0.88/.../copilot`, which
has the same SHA-256 (`0059754c…`) as
`provider-assets/copilot/linux-x64/copilot`

**Expected behavior**
The pack contains only code that its copied Node and its launch shims
can run.

**Paperclip version or commit**
`master` at `e0d9033`. The inspected release run built commit `837add0`.

**Steps to reproduce**
1. Install `@agentclientprotocol/claude-agent-acp@0.73.0` and
`opencode-ai@1.18.34` with pnpm 9.15.4 on a glibc Linux host.
2. List `node_modules/.pnpm`.
3. See the `*-musl` entries. Their `package.json` declares `"libc":
["musl"]`.

## What Changed

- `provider-pack-runtime-tree.mjs`: add `providerPackLibc()`. It reports
the libc of the Node that the pack copies (`glibc` or `musl` on Linux,
none on other platforms).
- `provider-pack-runtime-tree.mjs`: add
`removeForeignLibcPackagesFromProviderPack()`. It removes each pnpm
virtual-store entry whose own `package.json` declares a `libc` list
without the pack libc. Packages without a `libc` field stay (for example
`@openai/codex-linux-*`, which holds Codex's static musl binary).
- `provider-pack-runtime-tree.mjs`: add `removePnpmStoreEntries()`. It
removes store entries and every symlink that resolves into them, so no
dangling link stays in the pack. It refuses names outside
`node_modules/.pnpm`.
- `provider-pack-executable-shims.mjs`: when
`provider-assets/copilot/<platform>/copilot` has the same bytes as the
npm Copilot executable, the `copilot-<platform>` command now points at
the asset and the npm copy is removed. If the bytes are different, the
old behavior stays.
- `build-provider-pack.mjs`: run the libc prune immediately after `pnpm
deploy`. Log the removed entries.
- No file bound by `provider-pack.json` changes. Thus the manifest
digest and the qualification and digest pins do not change.

## Verification

- `node --test
packages/paperclip-runner/scripts/provider-pack-runtime-tree.test.mjs
packages/paperclip-runner/scripts/provider-pack-executable-shims.test.mjs
packages/paperclip-runner/scripts/candidate-provider-pack.test.mjs
packages/paperclip-runner/scripts/release-provider-pack.test.mjs`: 40/40
pass.
- The new Copilot test fails with the old
`provider-pack-executable-shims.mjs` and passes with the new file.
- A real pnpm 9.15.4 tree with the pack's provider dependencies, on
linux-x64 (glibc):
- The prune removed `@anthropic-ai+claude-agent-sdk-linux-x64-musl`,
`opencode-linux-x64-musl`, and `opencode-linux-x64-baseline-musl`. The
Copilot dedupe removed the npm Copilot entry.
  - `node_modules` changed from 1.9 GB to 1.1 GB.
  - `find node_modules -xtype l` found no dangling links.
- The claude-agent-acp SDK still resolves
`@anthropic-ai/claude-agent-sdk-linux-x64/claude`.
  - `opencode --version` prints `1.18.34`.
- The rewritten `copilot-linux-x64` prints `GitHub Copilot CLI 1.0.88.`
- Earlier measurement: we pruned the same paths from the extracted pack
artifacts and compressed them again with `tar -czf`. linux-x64 went from
1131 MB to 876 MB. linux-arm64 went from 519 MB to 360 MB.
- Not run locally: a full `build-provider-pack.mjs` build. This sandbox
has 10 GB of disk, which is not sufficient for a full workspace install
and a 3 GB pack. The `release-verify.yml` provider-pack step and the
Daytona image `RUN claude/codex/acpx --version` checks run the full
build.
- `test/acpx-codex-package-contract.test.mjs` has 1 failure in this
sandbox, and the failure is the same on `master`. The cause is that this
checkout has no `node_modules`.

## Risks

- Low risk. All pack builds run on glibc hosts (Debian bookworm images
and Ubuntu CI). On a musl build host, the prune removes the glibc
packages, and the existing Claude executable resolution fails at build
time. Before this change, that build gave a pack with a Claude binary
that could not run.
- The `copilot-linux-x64` command now runs the binary from
`provider-assets/copilot/linux-x64/`. The bytes are identical. Runtime
admission already uses this asset, through the descriptor-pinned lease.
- The pack content changes but the manifest digest does not. The remote
verifier checks only the bound artifacts, `dist`, and provider asset
trees. None of these change.
- Not in this pull request: Pi's bundled Node is byte-identical to the
pack Node (121 MB, about 43 MB compressed). A fix for it changes the
qualified Pi closure digest. Also, the `release.yml` publish jobs
download the full packs, but they read only `provider-pack.json`. These
are possible follow-ups.

## Model Used

- Claude Opus 5.5 (Anthropic), model ID `claude-opus-5-5`, used through
Claude Code with tool use (shell, file edit) and code execution in a
sandbox.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes (no
doc change is necessary: build behavior and commands do not change)
- [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: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:41:14 -07:00
Devin FoleyandPaperclip b5bf86ea09 Clear process identity when settled native runs stop (#17374)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Each heartbeat run stores the process identity of its runner in
`heartbeat_runs.process_pid`, `process_group_id` and
`process_started_at`
> - Only cancellations (`release_run_resources`), restart recovery and
heartbeat recovery cleared these fields
> - Failed native runs take the `schedule_retry` path, so they kept a
PID after the run finished, for weeks
> - Liveness checks for old runs then depend on PID-reuse defenses, and
code that reads a set PID as "running" is misled
> - This pull request releases the process identity of settled native
runs when the process is confirmed gone, and backfills old sandbox rows
> - The benefit is that a finished run no longer looks like it owns a
live process, while live, warm and retrying processes keep their
identity

## Linked Issues or Issue Description

Refs #17358. Bug report described in-PR (bug report template).

**What happened?**
On one instance, 16 of 23 old native workspace sync folders belonged to
failed or cancelled runs that finished more than a week earlier and
still had `process_pid` set. The PID is written when the run's process
starts (for remote targets it is the sandbox PID). The status arbiter
clears it only through `release_run_resources`, which only cancellations
emit. Failed native runs take `schedule_retry`, and other terminal paths
(finalization reconciler, restart recovery terminal failures, watchdog
timeouts) do not clear it either.

**Expected behavior**
When a run is terminal and its process is confirmed gone (or no longer
owned), the process identity is cleared. A process that may still be
alive keeps its identity.

**Steps to reproduce**
1. Run a native task whose provider turn fails, so the arbiter decides
`run_failed_partial_evidence_preserved` with a `schedule_retry` effect.
2. Wait for the run to finish and the runner to exit.
3. Read the run row: `status = 'failed'` and `process_pid` /
`process_group_id` / `process_started_at` are still set.

**Paperclip version or commit:** `master` at 75fcb0b3fc.
**Deployment mode:** any.
**Agent adapter(s) involved:** native runtime (`paperclip_runner`);
local and sandbox targets.

## What Changed

- `releaseSettledNativeRunProcess` (in `native-local-process-stop.ts`)
clears `process_pid`, `process_group_id` and `process_started_at` of a
native run under a row lock, only when all of these are true:
- the status is `failed`, `cancelled`, `timed_out` or `interrupted`, and
`finished_at` is set;
- the finalization coordinator is absent, `committed` or
`terminal_failure` (a `retryable_failure` same-run retry keeps the
existing heartbeat recovery path; unfinished finalization keeps its
identity);
- the run has a session checkpoint or provider evidence, so native
session resume never mistakes it for an unused bootstrap;
- local target: every lease is released without failed cleanup, the
latest `native.process_identity_recorded` event names this host and the
same PID/group, and the PID and group probe absent. The release writes
the existing `native.local_process_stopped` receipt in the same
transaction, so `hasNativeLocalProcessStop`,
`readNativeLocalProcessStop`, explicit continuation and acknowledged
Stop keep their proof;
- sandbox target: every lease has a remote termination receipt (sandbox
PIDs are never probed on this host).
- Succeeded runs keep their identity: a warm runner can serve the next
turn, and chat retry matches warm predecessors by PID.
- The heartbeat executor calls the release after lease and scratch
cleanup at a terminal boundary (not when a same-run resume, workspace
finalization or held ownership is pending).
- Background work stays idle (see
`rules/sleep-aware-background-work.mdc`):
- Trigger: the executor settlement calls `settleNativeRunProcess`. If
the release is not ready yet (process still alive, local lease not
released, remote termination receipt missing), the run goes into an
in-memory pending set.
- Startup: the first `reapOrphanedRuns` pass after boot runs
`discoverSettledNativeRunProcesses` once, in pages of 50, over the
durable rows. This reconstructs work after a restart and covers terminal
transitions outside an executor (reconciler, restart recovery, watchdog,
timeouts).
- Retry: later passes call `retryPendingSettledNativeRunProcesses` only
for pending runs. It makes no database call when the set is empty. A run
leaves the set when it is released, becomes ineligible, or reaches 20
attempts.
  - Runs that this process still executes are skipped.
- Stop receipts now also record `processStartedAt`.
`withReleasedNativeRunProcess` restores a released identity from the
latest stop receipt. Retained session cleanup
(`reconcileRetainedNativeSessionCleanup`) and warm transition restart
checks use it. Quiescent runnerd state recovery reads the stop receipt
when the prior run's identity was released. Without a receipt, all three
still treat the identity as unproven.
- Migration `0377_clear_settled_native_run_process_identity` backfills
sandbox runs (without bumping `updated_at`) with the same rules: native,
settled status, `finished_at` older than 1 day, settled coordinator
phase, checkpoint or provider evidence, and at least one lease where
every lease has a valid remote termination receipt. The snapshot is
carried forward as `0377_snapshot.json`.
- Not changed: legacy (non-native) runs. Case handoff release, scratch
cleanup and conversation continuation read the terminal row's local
PID/group as process-tree absence evidence, so clearing them needs
separate work. `release_run_resources` cancellation behavior and
`native_run_finalizations.controller_*` fields (they track the server
controller, not the run process) are unchanged.

### Migration margin

The 1-day margin is defense in depth. The real proof is the remote
termination receipt: the provider confirmed that the sandbox stopped or
was destroyed. One day is far longer than lease release, sandbox stop,
restart recovery takeover and the finalization reconciler sweep, so no
in-flight teardown or same-run recovery can still read these rows. Local
rows are not backfilled in SQL: only the host that recorded the process
can verify that it stopped and write the stop receipt. The runtime sweep
releases them on that host.

## Verification

- `pnpm exec vitest run
src/__tests__/native-settled-process-release.test.ts` (new, 20 tests):
release with a stop receipt and restore of the identity from it;
`cancelled`/`timed_out`/`interrupted`; live process kept, then released
after exit; kept for succeeded, `retryable_failure`,
`workspace_finalizing`, unreleased local lease, another host, no
evidence, unconfirmed sandbox, mixed leases; sandbox release with
receipts; startup discovery paging and skip; pending retry; retry cap;
no database access while nothing is pending; `reapOrphanedRuns`
discovery, then retry after the process exits; executor release for a
failed native run and no release for a succeeded run.
- `native-provider-failure.integration.test.ts`: a real `schedule_retry`
decision (`run_failed_partial_evidence_preserved`) leaves the failed run
with its PID after finalization, and the release then clears it and the
stop receipt reads back.
- `native-session-executor.test.ts`: quiescent recovery and retained
session cleanup accept a released identity with a stop receipt and
reject one without a receipt.
- `packages/db` `client.test.ts` migration rule: the backfill does not
set `updated_at`.
- `native-finalization-migration.test.ts`: migration 0377 clears only
old settled native sandbox rows with confirmed termination (15 fixtures,
including recent, succeeded, running, legacy, unsettled phases, no
evidence, unconfirmed or local leases, no lease).
- Related suites, all passing: `pnpm exec vitest run` on
heartbeat-process-recovery, native-workspace-sync-retention(-config),
native-finalization-{migration,settled-candidates,recovery},
chat-failed-run-retry-boundary, acknowledged-native-stop,
explicit-native-continuation, conversation-continuation,
native-session-resume, native-run-finalizer-telemetry,
native-session-executor, native-safe-replacement,
native-finalization-reconciler-timing, native-restart-recovery,
native-finalization-recovery-state, run-lifecycle,
agent-instruction/directory working copies, heartbeat-case-handoff (1920
tests across 23 files).
- `pnpm run typecheck` in `server/`: passes. `pnpm run check:migrations`
and the snapshot drift test in `packages/db`: pass.
- Mutation checks (each reverted alone; a test failed every time):
executor call, startup discovery, pending retry in `reapOrphanedRuns`,
retry cap, pending-set cleanup on retry, start time in the stop receipt,
retained cleanup receipt fallback, coordinator phase guard, succeeded
exclusion, liveness probe, host check, remote receipt check,
provider-evidence guard, local lease check, quiescent recovery receipt
fallback, migration margin, migration NULL-safe receipt check, migration
phase guard. The NULL-safe check came from a real bug the migration test
found. The warm transition restart call site has no direct test: that
path has no existing fixture. It uses the same tested helper.
- Not run: the full server suite and e2e tests.

## Risks

- Behavior change: more native runs now have a null `process_pid` after
they settle. Every native reader was checked: explicit continuation,
acknowledged Stop and agent stop use the stop receipt for null
identities; cancelled-startup detection also requires no process events,
so a released run is not mistaken for a cancelled startup; native
session resume selection is protected by the evidence guard; chat retry
reads its warm-predecessor PIDs from succeeded runs, which are not
released.
- A recycled local PID keeps a row unreleased (conservative), and
sandbox runs from providers without termination receipts keep their
identity.
- Background cost: one paged discovery at startup. After that, only
pending runs are checked, at most 20 times each. With nothing pending
there is no recurring query.
- A transition outside an executor that happens after startup, with no
executor settlement, is released at the next startup discovery.
- Migration: a data-only `UPDATE` on `heartbeat_runs` with a selective
`WHERE`; it passes the migration safety check. It is safe to re-run.

## Model Used

Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through Claude Code
with tool use.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [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 <noreply@paperclip.ing>
2026-10-23 15:09:43 -07:00
Nicky LeachandPaperclip a3e8fa568a Reduce repeated work during plugin Runner admission (#17377)
## Thinking Path

> - Paperclip manages agents and their execution authority.
> - A plugin can host Runner tasks on a persistent execution host.
> - Admission checks must observe current lifecycle state and
credentials.
> - The current runtime reader also resolves files, skills, and
connections for each check.
> - Some providers also repeat the same project authentication check
during submit.
> - This pull request adds a compact fresh reader and an explicit
submit-time validation contract.
> - Providers can remove repeated work while Core keeps lease and scope
checks.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Plugin Runner startup before task submission.

**Current behavior**

One measured startup made 30 full runtime reads during bootstrap. These
reads took 5.759 seconds. Each guard can resolve instructions, skills,
and connection assignments even when it only needs current identity and
lifecycle state. Providers that already enforce project authentication
in submit must also perform a separate Core preflight.

**Proposed behavior**

Use a lifecycle-owned runtime identity reader for guards. Keep the full
runtime reader for synchronization inputs. A trusted provider can
declare `projectAuthCheckAtSubmit: true` in its prepared result. Core
pins the exact configuration, provider, and project set on the lease.
The provider must reject authentication conflicts before credential
installation or task start. Providers that omit the flag retain the
existing preflight.

**Reason and benefit**

Estimated savings with a paired consumer: 4–5 seconds from compact
runtime checks, plus roughly 0.5–0.9 seconds from removing the duplicate
authentication operation. These estimates overlap and are not a measured
deployed result. Refs #17367 for the separate run-log HEAD optimization;
this PR does not duplicate it.

**Breaking changes**

No migration. The SDK adds a host method and an optional prepared-result
field. Consumers that use them require this Core host version. Older
providers retain their existing flow.

## What Changed

- Add `agents.getRuntimeIdentity` across SDK, RPC, host service, and
test fixtures.
- Reuse the existing company, plugin ownership, readiness, and
capability checks. Read lifecycle state and protected instance secrets
fresh on every call.
- Skip instruction, organization, skill, and connection hydration in the
compact reader.
- Allow explicit submit-time authentication enforcement, with prepared
scope pinned and checked again at dispatch.
- Emit fixed preparation, project-auth, submit, and connection timing
phases.

## Verification

- 72 targeted SDK capability, plugin task-owner, and environment-task
mediator tests passed.
- 33 real embedded-Postgres lifecycle and secret tests passed, including
pause, content revision, company isolation, plugin disablement, and
secret rotation.
- SDK and server TypeScript checks passed. Local Runner/eval TypeScript
outputs were built to satisfy server type resolution.
- Local Postgres required missing library aliases in ignored
dependencies; no product source was changed for that repair.
- Full repository suites are delegated to GitHub CI. No live rollout or
speedup claim.

## Risks

The submit-time flag delegates enforcement to a trusted provider. It is
not proof that prepare already checked authentication. The provider must
check before VM credential installation or task admission. Core still
checks exact prepared scope, active lease, run identity, credential
type, and dispatch fencing. Existing providers keep the separate check.
Roll back a paired consumer before rolling back this host.

## Model Used

OpenAI GPT-6 (Codex), with code execution and repository tools. Exact
deployment build and context-window size are not exposed.

## 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
ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-23 14:54:25 -07:00
Nicky Leach 2e38649341 perf(run-logs): skip durable log restore for a proven-new run (#17367) 2026-10-23 14:30:06 -07:00
Devin FoleyandPaperclip f73ace9f1a Clear stale native recovery state so closed work expires (#17359)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Native runs keep a finalization row, and tasks can keep recovery
actions while work is stuck
> - Git cache expiration and native harness backup expiration read these
rows to decide if closed work is still live
> - Commit paths never cleared
`native_run_finalizations.recovery_state`, so committed rows kept
`resuming_session` forever
> - Recovery actions on done or cancelled tasks also stayed active when
the close did not go through the issue routes
> - So workspaces and backups of tasks that closed weeks ago stayed live
and used disk space
> - This pull request clears the stale state at commit, backfills old
rows, resolves stale recovery actions on closed tasks, and lets the two
expiration jobs ignore settled state on closed trees
> - The benefit is that closed work expires on schedule, while live
leases, unsettled phases, unverified owners, and in-flight system
recovery still keep it

## Linked Issues or Issue Description

Refs #17248

**What happened?**

On one instance, Git cache expiration and native harness backup
expiration kept the workspaces and backups of tasks that were done for
more than 7 days. Three kinds of stale state caused this:

1. 51 `native_run_finalizations` rows had `phase = 'committed'` and
`recovery_state = 'resuming_session'`. The commit writes (status
decision commit, attention replay, result persistence, and the finalizer
claim) never cleared `recovery_state`. No reader uses `recovery_state`
on a row that has a result, except the expiration predicates.
2. Rows with `phase = 'terminal_failure'` and `recovery_state =
'blocked'` (or NULL) are permanent states. Nothing clears them, and
continuations are refused for closed tasks.
3. `issue_recovery_actions` with status `active` or `escalated` stayed
open after the source task became done or cancelled. Only the issue
routes cancel them at close. The periodic sweep
`reconcileActiveRecoveryActions` skipped every action unless
`wakePolicy.type` was a bounded owner policy or the owner was the board.
Agent-owned `resume_native_run` actions (which use `wakePolicy.kind`)
and system-owned actions were never resolved.

**Expected behavior**

When a task tree is closed for the configured time, its Git cache and
native backup expire. Only live state keeps them: a held finalization
lease, an unsettled finalization phase, an unverified native workspace
owner, or system recovery that is scheduled, running, or awaiting
evidence.

**Steps to reproduce**

1. Run a native task. Let the runner restart so that restart recovery
sets `recovery_state = 'resuming_session'` on the finalization row.
2. Let the resumed run commit its result and close the task as done.
3. Wait past the expiration window (or run the sweep with a later
clock).
4. See that the finalization row is still `committed` +
`resuming_session`, and that Git cache expiration and native backup
expiration skip the closed workspace.

**Paperclip version or commit**

`master` at 837add0caa

**Deployment mode**

Self-hosted server with native runtime enabled

**Agent adapter(s) involved**

Not adapter-specific (core bug)

## What Changed

- Clear `recovery_state` and `recovery_request_id` in every write that
persists or commits a native result: `recordCoordinatorDecision` in the
status decision committer, the attention replay in
`routePersistedNativeResultAttention`, `completeRun` in both
`NativeRunCoordinatorStore` and `PaperclipControlPlanePort`, and
`claimCoordinator` in the finalizer.
- Add data migration `0376_clear_committed_native_recovery_state.sql`
with its unchanged schema snapshot (the oldest snapshot is pruned, as
the generator does). It clears `recovery_state` and
`recovery_request_id` on rows that are `committed` with no lease owner.
`recovery_history` keeps the audit trail.
- In `reconcileActiveRecoveryActions`, resolve active and escalated
actions on done or cancelled tasks (`source_terminal`) before the wake
policy and owner filter. The existing owner-unverified guard still runs
first. Actions with system recovery in `scheduled`, `running`, or
`awaiting_evidence` are skipped.
- In `isGitWorkspaceExpirationEligible` (closed-tree mode only), a
recovery action blocks only if its cause is
`native_workspace_finalization_owner_unverified` or its system recovery
is in flight. A finalization blocks only if it has a lease owner or its
phase is not `committed` or `terminal_failure`. Idle mode keeps the old
strict checks.
- In native harness backup expiration, a settled finalization
(`committed` or `terminal_failure`, no lease owner) no longer blocks
once the session's tasks are closed for the configured days.

## Verification

- `pnpm exec vitest run` in `server/` on the touched and related files:
`git-workspace-expiration`, `git-workspace-expiration-eligibility`,
`native-harness-backup-expiration`, `native-finalization-migration`,
`native-interaction-bridge`, `native-run-finalizer-telemetry`,
`paperclip-control-plane-port`, `issue-recovery-actions`,
`native-status-arbiter-corpus`: 9 files, 321 tests passed. After the
tests moved to `native-finalization-recovery-state.test.ts`, that file,
`git-workspace-expiration` and `native-run-finalizer-telemetry` passed
again (95 tests).
- `pnpm exec vitest run src/migration-snapshot-drift.test.ts` in
`packages/db`: passed.
- `pnpm exec vitest run
src/__tests__/heartbeat-process-recovery.test.ts`: 408 tests passed.
- `pnpm run typecheck` in `server/`: passed.
- `pnpm run check:migrations` in `packages/db`: passed.
- New tests:
- Result persistence (port and coordinator store), finalizer claim and
status decision commit (new focused file
`native-finalization-recovery-state.test.ts`), and attention replay each
clear `recovery_state` and `recovery_request_id`.
- The migration clears committed, unleased rows. It keeps leased
committed rows, `observed` rows, and `terminal_failure` rows unchanged,
and keeps `recovery_history`.
- The sweep resolves an agent-owned `resume_native_run` action and an
escalated system action on a done task, and keeps an action with
scheduled system recovery. The existing test "retains the exact native
owner hold without changing a terminal task's disposition" still covers
the owner-unverified hold.
- Git expiration eligibility: a closed tree expires with `committed` +
`resuming_session`, `terminal_failure` + `blocked`, an active agent
recovery action, or an escalated system action. It stays with a leased
finalization, an `observed` finalization, an owner-unverified hold,
in-flight system recovery, an open tree, and in idle mode.
- A full Git expiration of a closed tree with stale native state, then a
reopen to the recorded commit. The existing "preserves scheduled runs,
unresolved recovery, and live previews" test now uses an `observed` and
a leased finalization as the unresolved recovery.
- Native backup expiration expires `committed` + `resuming_session` and
`terminal_failure` + `blocked`, and keeps an `observed` or leased
finalization.
- Mutation checks. I reverted each fix on its own, saw the matching test
fail, and restored it:
- Status decision commit, finalizer claim, port `completeRun`, store
`completeRun`, attention replay: each new clearing test failed.
  - Migration (`WHERE false`): the backfill test failed.
- Sweep order (wake policy filter moved back in front) and the in-flight
system recovery guard: the sweep test failed.
- Git eligibility: the closed-tree action relaxation, the finalization
relaxation, the owner-unverified guard, and idle strictness each made
the matching eligibility tests fail.
- Backup expiration (`recovery_state IS NOT NULL` restored): both new
expiry cases failed.
- Not run: the full server suite and the e2e suites. CI runs them.

## Risks

- The migration is a data-only `UPDATE` on `native_run_finalizations`.
It touches only committed rows with no lease owner and a non-null
`recovery_state`. The table is not on the known-large list, and the
migration safety check passes. `recovery_history` is not changed.
- Clearing `recovery_state` at commit and claim: the readers of this
column need `phase = 'observed'` (continuation contract), `result_id IS
NULL` (recovery service, failed-run retry), or running runs (heartbeat
recovery). A row that has a result does not match any of them. The
restart recovery summary counts will drop because they no longer count
committed rows.
- The sweep now resolves more actions on closed tasks. A done or
cancelled task cannot continue, so these actions cannot act. Unverified
owner holds and in-flight system recovery are kept.
- Expiration now removes more closed workspaces and backups. Restore
points and Git cache restoration still allow a reopen to rebuild the
workspace.

> 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`), 1M context, through Claude Code
with tool use (shell, file edits, 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-23 14:06:17 -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 <noreply@paperclip.ing>
2026-10-23 14:05:34 -07:00
DottaandPaperclip 4d90634f74 feat(connections): add 1Password service-account connector (#17249)
## Thinking Path

> - Paperclip lets people manage AI agents and govern their access to
outside services.
> - Apps connections keep provider credentials in the instance vault and
mediate every action through grants, policies, and audit.
> - Agents currently have no Apps connection for reading or managing
1Password items with an operator-supplied service-account token.
> - 1Password supports this through its official SDK; its documentation
MCP server does not expose vault operations.
> - This pull request adds a one-field service-account connection and
registers eight reviewed SDK actions through the existing
native-provider catalog.
> - Operators can select vault permissions in 1Password and agent/action
access in Paperclip without installing a CLI or handing durable
credentials to agents.

## Linked Issues or Issue Description

Related: #16144 adds 1Password as Paperclip's secret-storage backend.
This PR adds the separate Apps/tool-access flow with a per-connection
credential; it does not change secret providers or use a server-wide
ambient token.

**Subsystem affected**
Apps catalog, native tool-provider registry, and shared connection
setup.

**Problem or motivation**
People need to paste their own `OP_SERVICE_ACCOUNT_TOKEN`, find official
service-account setup instructions, and authorize agents to use the
selected vaults through ordinary Paperclip governance.

**Proposed solution**
Add `1password/service-account`, with a password field labeled **Paste
your 1Password Service Account token** and a link to [1Password's
creation
instructions](https://www.1password.dev/service-accounts/get-started#create-a-service-account).
Use the pinned official `@1password/sdk` 0.5.0 for vault/item listing,
item and secret-reference reads, and item create/update/archive/delete.

**Alternatives considered**
A remote MCP card would not provide vault access. A local CLI or a
third-party MCP package adds installation and credential surfaces that
the official SDK avoids.

## What Changed

- Added the source and generated catalog definition, permission review,
official light/dark artwork, documentation, and capability-map
references.
- Added a provider-owned SDK bridge with strict schemas, account-domain
validation, contract hashes, fixed errors, bounded output, and
disposable workers with cancellation/deadlines.
- Registered the provider through master's native-provider catalog;
existing setup, health, discovery, gateway, policy, and audit owners
remain in place.
- Labeled the single password field **Paste your 1Password Service
Account token**. The shared formatter preserves explicit credential
prompts; ordinary key labels retain their existing presentation.
- Added credential-ownership, gateway read/write, policy/revocation,
worker, setup UI, and interactive Storybook coverage.

## Verification

- Final review follow-up: added two deterministic worker-entry tests for
nested exact-token redaction and oversized-output rejection. Removing
each guard caused exactly its regression test to fail. The focused
provider suite passed **15 tests**, and the official server typecheck
passed after rebuilding stale workspace SDK artifacts. Current head
`569cb7f9bd57db791fb13a6034ce69bf50878cb1` passed the full [CI
run](https://github.com/paperclipai/paperclip/actions/runs/40016166260),
including typecheck, build, unit/integration tests, and all eight
browser shards. The required `ci / verify` and `ci / e2e` gates passed.
[Greptile](https://github.com/paperclipai/paperclip/pull/17249#issuecomment-6353731805)
and [Paperclip
Review](https://github.com/paperclipai/paperclip/pull/17249#issuecomment-6372165895)
each rated this exact head **5/5**, with no open findings. Paperclip
Review completed with a neutral check because its scoped reviewer could
not inspect CI; the full CI result was verified separately. Other
neutral checks and intentionally skipped Storybook jobs contain no
actionable findings.
- CI recovery on the same commit: the first attempt lost six jobs. Five
logged simultaneous runner shutdowns at 18:49 UTC on 2026-10-23; the
sixth stopped logging before a test result and its root cause was not
established. No assertion failure appeared in the retrieved logs. All
six jobs and the aggregate gates passed one failed-job retry. [CI
run](https://github.com/paperclipai/paperclip/actions/runs/40005754936).
- Copy follow-up: the catalog, provider, and setup UI suites passed
**275 tests**. The governed gateway lifecycle passed **3 tests** (one
live-only test skipped), including create, update, archive, and delete.
UI token gates and map checks passed. The branch was rebased onto
`9c442cf48e` to resolve additive catalog conflicts while retaining the
Vercel and Supabase entries. The existing live write proof below remains
applicable; this follow-up changes presentation only.
- Five affected suites, including the existing Daytona consumer, passed
**290 tests** (one opt-in live test skipped in fixture mode). Full `pnpm
-r typecheck` and `pnpm build` passed. The local broad-suite attempt
could not complete because PostgreSQL semaphore exhaustion prevented a
competing integration suite from starting; the complete affected-file
retry passed after disposable-process cleanup. The prior commit
`f208a8aff6dc28859cc3f343497e18d85f124579` passed the complete CI
matrix. All CI build, typecheck, test, and browser gates also passed on
the earlier commit `9e2f3d3140368a08da43b004b2e8d3a0f4c0fa4d`. The
separate Paperclip Review reported a worker-result regression-test gap
on that head; the follow-up above addresses it. Its review thread is
resolved, and both fresh current-head reviews are clean as recorded
above.
- Token gates, module boundaries, brand validation, map checks, and
Storybook catalog checks passed.
- Direct SDK create/archive/delete and rejected-call coverage passed (13
provider tests); the Storybook Permissions/Test play completed
successfully in Chromium. Server and UI typechecks passed after those
fixes. Greptile passed prior commit
`9e2f3d3140368a08da43b004b2e8d3a0f4c0fa4d` at **5/5**. Both reviewers
also passed the worker-test follow-up at **5/5** as recorded above.
- Live 1Password proof on 2026-10-22: connect, eight-action discovery,
vault listing through an actual run-scoped gateway, refresh, reconnect
preserving identity, another read, removal, and denial through the
retained session passed. Credential absence was asserted in
responses/audit; an exact-token scan of inspected logs and changed files
passed.
- Real browser: Connectors → 1Password → token entry → personal access
limited to one disposable agent → completion → Permissions → Test
`onepassword_list_vaults` succeeded. The disposable connection was
removed afterward.
- Compiled-worker smoke initially exposed inherited Node launch flags.
Workers now use explicit empty `execArgv`; the compiled worker then
authenticated successfully and listed one vault.
- Live write proof on **2026-10-23**: the replacement Keychain token
passed all eight actions through the actual run-scoped gateway for both
personal and shared credentials. The test created, listed, read,
updated, resolved a secret from, archived, and deleted two synthetic
items. Both were absent from subsequent item lists. Refresh/reconnect,
identity preservation, removal, retained-session denial, and credential
absence passed. The live suite passed **3 tests** (one fixture-only test
skipped) in 116.65 seconds. The expanded fixture suite passed **3
tests** (one live-only test skipped); the 13 provider tests and server
typecheck passed. An exact-token scan passed over 64 source/log files.
- Failure history: the original token denied creation on 2026-10-22 and
created no item. The first 2026-10-23 attempt stopped before provider
calls because host PostgreSQL semaphore limits prevented test-database
startup. A temporary ten-connection test profile allowed the complete
retry to pass without changing shared services or host limits. A new
delete-result assertion also exposed an outdated fixture response
(`success` instead of `deleted`); the fixture was corrected and the
complete fixture suite passed.

Official contract sources: [service
accounts](https://www.1password.dev/service-accounts/get-started),
[SDKs](https://www.1password.dev/sdks), [item
management](https://www.1password.dev/sdks/manage-items), [official
JavaScript SDK](https://github.com/1Password/onepassword-sdk-js).

## Risks

- Authorized item/secret reads intentionally return secret values to the
agent and ordinary tool-result history. Select trusted agents and use
restricted service-account vaults; the connection token itself is
redacted.
- New connections retain existing Paperclip action defaults, including
Allowed writes/destructive actions for selected agents. 1Password still
enforces the token's permissions.
- Service-account access cannot be changed in place. Create a
replacement account and reconnect to change vault permissions; read-only
keys can connect but cannot write.
- SDK authentication happens per action in a disposable worker. Calls
have a 30-second maximum; cancellation stops local work, while a
mutation already accepted by 1Password can still finish. Writes are
never automatically retried.
- Additive dependency and catalog change; no database migration.
Disable/remove the connection to withdraw access, or remove the provider
from catalog visibility for rollback.

## Model Used

OpenAI Codex, based on GPT-6, with code editing, shell/browser tools,
and live provider verification. A more specific runtime model ID and
context-window limit 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 <noreply@paperclip.ing>
2026-10-23 13:27:33 -07:00
DottaandPaperclip 9c442cf48e feat(connections): deploy and configure Supabase and Vercel (#17217)
Add governed Supabase and Vercel deployment/configuration connectors with vaulted credentials, agent access policies, approvals, audit, and bounded provider operations.

Deliver reviewed REST actions to native agents and guard Vercel preview intent on empty projects. Preserve secret-safe request identity and durable retry behavior.

Verified by current-head CI, fresh Greptile 5/5 review, and real-agent deployment/configuration walkthroughs on disposable provider resources. Test resources and temporary provider tokens were removed after verification.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-23 11:37:29 -07:00
Devin FoleyandPaperclip 0001a4514e fix: keep stale pending restore points from blocking retries (#17344)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Git cache expiration saves restore points and tracks them in
`execution_workspace_restore_points`
> - A unique index allows one row per workspace, generation and checkout
unless the row is deleted
> - A pending row left by an interrupted attempt therefore blocked every
retry for an hour, and superseded rows blocked retries until retention
deleted their objects
> - This pull request narrows the index to live rows and supersedes
stale pending rows before inserting
> - The benefit is that an interrupted expiration no longer stalls a
workspace's cleanup

## Linked Issues or Issue Description

Refs #17257. Bug report described in-PR (bug report template).

**What happened?**
On one instance, a closed workspace with a nested checkout failed
expiration with `inspection_failed` on every sweep. An earlier attempt
had left a `pending` row for the nested checkout at the workspace's next
generation. Each retry stored the main worktree's restore point, then
failed to insert the nested row because the unique index on
`(execution_workspace_id, generation, checkout_name) WHERE status <>
'deleted'` still held the stale row. Running the expiration by hand
reproduced the failing insert.

**Expected behavior**
A retry after an interrupted attempt replaces the stale row and expires
the workspace.

**Steps to reproduce**
1. Leave a `pending` restore point row for a workspace's next generation
and a nested checkout, as an interrupted attempt does.
2. Run expiration with restore points: it fails with `inspection_failed`
until retention supersedes the row after the one-hour pending grace
period.

**Paperclip version or commit:** `master` at 0fb152ee94.
**Deployment mode:** any.
**Agent adapter(s) involved:** not adapter-specific (core).

## What Changed

- **Schema**
(`packages/db/src/schema/execution_workspace_restore_points.ts`,
migration `0375`): the unique index is renamed
`execution_workspace_restore_points_live_status_uniq` and covers only
`pending`, `active` and `expiring`. Superseded, delete-requested and
deleted rows no longer block a new row.
- **Storage** (`storeGitRestorePoint`): in the transaction that inserts
the new `pending` row, any `pending` row for the same workspace,
generation and checkout is first marked `superseded`. The expiration
holds the workspace lifecycle lock, so such a row can only come from an
attempt that already ended. Retention then deletes its object as usual.

No code comments were added.

## Verification

- `pnpm exec vitest run src/__tests__/git-workspace-expiration.test.ts
-t "stale pending restore point"` (in `server/`): 1 passed. A workspace
with a stale `pending` nested row and a `superseded` main row from an
earlier attempt expires with a nested restore point, and the stale row
is superseded.
- **Mutation checks:** removing the supersede step, or keeping the old
index scope in the migration, each reproduces the instance's
`inspection_failed`.
- `pnpm exec vitest run git-restore-point git-workspace-expiration
git-workspace-contents git-workspace-restoration
git-reproducible-managed-checkouts execution-workspaces
heartbeat-git-restore-point-resume` (in `server/`): 274 passed.
- `pnpm run check:migrations` (in `packages/db`): pass.
- `pnpm run typecheck` (in `server/`): pass.
- Not run: server build and boot smoke.

## Risks

- **Migration:** drops and recreates one partial unique index on a small
table. It cannot fail on existing data, because the old index was
stricter. The index is briefly absent while the migration runs.
- **Superseding a pending row:** only rows for the same workspace,
generation and checkout, while the current attempt holds the workspace
lifecycle lock.
- **Not addressed:** why the earlier attempt left the row behind. #17341
logs the underlying error of `inspection_failed` so the next occurrence
can be diagnosed.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through Claude Code
with tool use (shell, file edits, tests, read-only and dry-run
inspection of 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 <noreply@paperclip.ing>
2026-10-23 10:25:59 -07:00
Nicky LeachandPaperclip a708a13336 Separate plugin workspace completion from maintenance inspection (#17329)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Storage plugins own remote task workspaces.
> - Run completion currently requests the same inspection used for
storage cleanup.
> - That inspection can fetch remote refs and scan files after the agent
has finished.
> - Completion needs retained workspace identity and durable writes, not
permission to delete files.
> - This change adds an optional completion operation and keeps
maintenance inspection separate.

## Linked Issues or Issue Description

**What happened?**

A successful plugin-backed run can fail during completion because the
storage provider performs a slow remote-recoverability inspection. Dirty
or unpublished output does not need a deletion-safety proof to remain
valid task output.

**Expected behavior**

Completion verifies the assigned workspace and confirms durable writes
through the provider. Cleanup still requires the existing full
inspection.

**Steps to reproduce**

Run a task with plugin-owned storage whose inspection fetches remote
refs. Let the agent finish, then observe completion waiting for the
maintenance inspection.

Related context: #16428 added finalization observability. The duplicate
search found no existing PR for this provider contract.

## What Changed

- Add optional `workspaceProvider.finalizationVersion: 1` and strict
`finalize` / `finalized` schemas.
- Pass original run and agent context through live completion and
recovered completion.
- Give the new operation a 30-second deadline and keep ownership checks
and lifecycle serialization.
- Retain the inspection fallback for providers without the capability.
Keep maintenance inspection and removal unchanged.

## Verification

- SDK build passed.
- Targeted SDK and completion unit tests: 7 passed.
- Added integration coverage for capability selection, legacy fallback,
maintenance separation, and failure propagation.
- Local integration setup timed out starting PostgreSQL; the integration
suite passed in GitHub CI.
- Full build/typecheck and broader tests passed in GitHub CI (55
successful checks, 2 skipped). Local checks remained targeted as
requested.
- Diff reviewed for secrets and operational identifiers.

## Risks

The capability is opt-in. Providers must not advertise it until their
completion implementation is available. An unavailable provider or
failed durability barrier remains retryable. No schema or stored-binding
migration is required.

Test-file size exception: the provider integration suite grows from 973
to 1,002 lines. The 29-line case follows the same prepared binding
through legacy completion, opted-in completion, and maintenance
inspection using the existing single-connection database fixture.
Keeping it there avoids a second embedded-Postgres lifecycle for one
transition test. The completion helper unit tests are in a separate
focused file.

## Model Used

OpenAI GPT-6 Codex, with reasoning and code/test tools.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-23 09:07:35 -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 <noreply@paperclip.ing>
2026-10-23 07:18:37 -07:00
DottaandPaperclip 4173059081 fix(inbox): keep advisory GitHub review waits quiet (#17318)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Mine shows a person's participation and requests that need their
response.
> - Automatic GitHub reviews should remain quiet until the assigned work
needs human input.
> - Review bots used completion requests to keep tasks in review while
CI or a PR was still open.
> - Those requests correctly entered Mine, despite the automatic-work
filter from #16356.
> - This pull request gives published reviews an existing provider-event
wait in the in-review state and withdraws superseded completion
requests.
> - Real input requests and separate approval gates keep their current
behavior.

## Linked Issues or Issue Description

Refs #16356
Refs #13314

An advisory score below 5/5, pending or failed CI, or an unmerged PR
does not by itself require a person's approval in Paperclip. The bot can
track those conditions without adding a completion card to Mine.

## What Changed

- Explain the quiet review lifecycle in the GitHub review instructions.
Use a yielded provider-event wait, or an existing saved monitor, after
publication. Report done when the configured completion conditions are
met.
- Keep a confirmed, published review in `in_review` during that wait.
Reuse existing authorization and monitor delivery. Add no schema, timer,
or polling loop.
- Recognize the committed external wait in review attention and
recovery. Shared execution receives a verified publication flag; GitHub
receipt lookup stays with the GitHub module. Other chat waits keep their
current status.
- Withdraw an older system-created completion request only when the same
bot has published both reviews and its new report has no attention
request. Keep explicit cards, separate approval gates, and requests
without the prior review evidence. Finish validation stays read-only;
withdrawal commits atomically with the new status after checking current
governance, the admitted result, and both low-risk contracts.
- Update the product rule, capability maps, and regression coverage.

## Verification

- Final commit `7c502118eec36028d1b4537bb8797ef893940bf0`: 54 GitHub
checks passed, two intentionally skipped, none pending or failed.
Greptile is 5/5, the security scan passed, and all review threads are
resolved.

- Passed: `pnpm -r typecheck` and `pnpm build`. Rechecked server
typecheck and build after the final authorization guard.
- Passed: 195 tests across `external-chat-wait.integration.test.ts`,
`status-arbiter.test.ts`, `review-instructions.test.ts`, and
`issue-liveness.test.ts`. The database suite used an isolated PostgreSQL
18 container through `PAPERCLIP_TEST_DATABASE_URL`.
- Acceptance coverage: 3/5 stays in review outside Mine; 5/5 with
pending completion conditions stays in review; 5/5 with satisfied
conditions finishes quietly; a real repository-access request enters
Mine until resolved. Also covers obsolete-card withdrawal, transaction
rollback, rejected reports, separate approval gates, multiple reviewed
heads, saved monitors, and revoked endpoints, repositories, or
membership.
- Passed: `node scripts/check-maps.mjs`, `node
scripts/check-module-boundaries.mjs`, and `git diff --check`.
- The initial acceptance run exposed a missing authenticated user in the
test fixture. Added the user required by the existing addressee
authorization check. All 154 tests in that database suite then passed,
including the final rerun.
- Review found premature withdrawal during validation, stale repository
access, and a missed receipt when a run reviewed multiple heads. The
revision fixes all three and includes acceptance regressions. The
saved-monitor commit guard now accepts the verified published-review
state.
- `pnpm test:run` was attempted but could not complete on this Mac.
Embedded PostgreSQL failed to allocate semaphores (`semget: No space
left on device`); a direct initialization probe confirmed the host
resource limit. The isolated database run proves the affected suite, not
the full repository suite.
- These tests exercise persisted review receipts, finalization, and Mine
queries. They do not prove a live model followed the new instructions or
submit a live GitHub review.

## Risks

- The bot must follow the new lifecycle instructions. A real explicit
attention request still enters Mine.
- Existing obsolete cards are withdrawn when the same bot publishes a
later review and reports no human dependency. This change does not run a
bulk cleanup of historical inbox entries.
- Review attention now recognizes an external response path. It requires
an applied current status decision, a successful bound run, and current
conversation, repository, and principal authorization.
- No migration or UI change. The full local test suite is unverified
because of the host database limit; CI passes on the final commit.

## Model Used

OpenAI Codex, GPT-6, with reasoning, repository inspection, code
editing, and test execution. The exact deployment 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 (195 focused tests;
full-suite limitation 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 <noreply@paperclip.ing>
2026-10-23 07:14:40 -07:00