Commit Graph
956 Commits
Author SHA1 Message Date
scotttongandClaude Fable 5 c185e64b77 feat(ui): chat-style task view behind an experimental flag (#10606)
## Thinking Path

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

## Linked Issues or Issue Description

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

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

**Subsystem affected**

UI (issue detail page).

**Problem or motivation**

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

**Proposed solution**

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

**Alternatives considered**

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

**Roadmap alignment**

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

## What Changed

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

## Verification

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

## Risks

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

## Model Used

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

## Checklist

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

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 02:26:47 -07:00
Devin Foley 6401f4f78c fix(sandbox): bundle git copy-back against the merge-base so diverged/reset workspaces still import (#10601)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - An agent that runs in a sandbox has its workspace copied back to the
host when the run ends, so its work persists — the copy-back ships a git
bundle of the sandbox's commits
> - The bundle is created as a thin delta, `git bundle create HEAD --not
<baseSha>`, which records `baseSha` (the host workspace HEAD captured at
export) as a prerequisite the host must already hold
> - That assumption breaks when the sandbox HEAD has diverged from
`baseSha`, or when a shared host workspace no longer holds `baseSha` at
import time — then `git fetch` on the host hard-fails and the entire run
is lost even though the agent finished its work
> - This pull request bundles against the merge-base of `baseSha` and
the sandbox HEAD (with a full-bundle fallback), which the host can
satisfy in those cases
> - The benefit is that copy-back no longer discards a completed run's
work over a base the host can't reconcile

## Linked Issues or Issue Description

**What happened?**

A sandbox agent run completed its work, then failed during workspace
finalize:

```
git -C <host workspace> fetch --force <git-delta.bundle> refs/…/export:refs/…/imported
error: Could not read <baseSha>
fatal: revision walk setup failed
error: git-delta.bundle did not send all necessary objects
```

The run is reported as `adapter_failed` even though the agent produced
output. The copy-back bundle names the host workspace's recorded HEAD
(`baseSha`) as a prerequisite, but the host cannot satisfy it.

**Steps to reproduce**

Two independent triggers, both reproduced in tests:
1. The sandbox's HEAD has diverged from `baseSha` — e.g. the sandbox
carries a local-only branch that forked from an older commit than the
host's current HEAD.
2. The shared host workspace no longer holds `baseSha` at import time
(it was reset / re-realized between export and import).

In either case `git fetch` of the thin bundle fails with a missing
prerequisite.

**Expected behavior**

Copy-back imports the sandbox's work as long as the host holds any
common ancestor, instead of hard-failing and discarding the run.

**Paperclip version**

Current `master`.

**Deployment mode**

Any deployment running agents in sandbox environments with workspace
sync (notably shared-workspace clones and custom images that carry a
local-ahead branch).

## What Changed

- `buildRemoteGitDeltaBundleScript` now computes `bundle_base = git
merge-base <baseSha> HEAD` and bundles `HEAD --not <bundle_base>`. The
merge-base is an ancestor of `baseSha`, so any host that holds `baseSha`
(or an ancestor of it — e.g. after a reset) can satisfy the
prerequisite, and the bundle stays a delta rather than a full-history
transfer.
- When `baseSha` is absent from the sandbox, or no merge-base exists, it
falls back to a full, self-contained bundle (no prerequisites) so the
import can always complete.
- The existing empty-bundle no-op (no new commits) and the ordinary
fast-forward path are unchanged; the `cat-file` base check no longer
aborts the script under `set -e`.

## Verification

- `pnpm vitest run packages/adapter-utils/src/git-workspace-sync.test.ts
packages/adapter-utils/src/sandbox-managed-runtime.test.ts` — new cases:
a diverged sandbox HEAD imports when the host holds only the merge-base
(not `baseSha`), and the full-bundle fallback imports into a host that
shares no history; existing thin-delta and empty-bundle cases still
pass.
- `pnpm --filter @paperclipai/adapter-utils exec tsc --noEmit`.
- Standalone shell repro confirmed the old thin bundle fails with
"Repository lacks these prerequisite commits" in both trigger cases, and
the merge-base bundle imports successfully.

## Risks

- Low. For the common case (sandbox HEAD descends from `baseSha`) the
merge-base is `baseSha`, so the bundle is byte-for-byte the same delta
as before. The change only alters behavior when the old code would have
hard-failed.
- This makes the copy-back import succeed on a diverged base; the
subsequent reconciliation of divergent histories
(`integrateImportedGitHead`) is unchanged and still owns how the
imported head is merged into the host branch. Where a workspace's
history has genuinely diverged (e.g. a stale custom image carrying a
local-only branch), a clean re-clone/re-capture is still the right
operational fix — this change prevents work loss, it does not reconcile
intentional divergence.

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — extended
thinking, agentic tool use (file edits, shell repro, vitest/tsc runs).
No other models involved.

## Checklist

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

> - Paperclip is the open source app people use to manage AI agents for
work
> - A Codex agent runs `codex exec`, and the adapter assembles its
argument vector from the agent's config plus execution-context options
> - For sandbox execution the adapter injects `--skip-git-repo-check`,
because a headless remote workspace has no git trust prompt to answer
> - The adapter also appends the operator's `extraArgs` verbatim, so an
agent that already lists `--skip-git-repo-check` in its config gets the
flag twice on a sandbox run
> - `codex exec` rejects a repeated `--skip-git-repo-check` and exits
with code 2, which the adapter surfaces as `adapter_failed` before any
work runs
> - This pull request skips the sandbox injection when the operator's
args already carry the flag
> - The benefit is that a common, harmless-looking config no longer
crashes every sandbox run

## Linked Issues or Issue Description

**What happened?**

A `codex_local` agent configured with `extraArgs:
["--skip-git-repo-check"]` fails on every sandbox run:

```
error: the argument '--skip-git-repo-check' cannot be used multiple times

Usage: codex exec [OPTIONS] [PROMPT]
```

The adapter reports `stopReason: "adapter_failed"` (Codex exited with
code 2). The flag appears twice in the argv: once injected by the
adapter for sandbox execution, once from the operator's `extraArgs`.

**Steps to reproduce**

1. Configure a `codex_local` agent with `extraArgs:
["--skip-git-repo-check"]` (or the legacy `args` field).
2. Point it at a sandbox environment.
3. Start a run — `codex exec` aborts immediately on the duplicate flag.

**Expected behavior**

The run launches with a single `--skip-git-repo-check`. An operator
listing the flag the adapter already injects should be a no-op, not a
hard failure.

**Paperclip version**

Current `master`.

**Deployment mode**

Any deployment running Codex agents in sandbox environments.

## What Changed

- `buildCodexExecArgs` no longer pushes the sandbox
`--skip-git-repo-check` when the resolved args (`extraArgs`, or the
legacy `args` fallback) already contain it. The operator's copy stands;
the argv carries the flag exactly once. Non-sandbox runs and configs
without the flag are unchanged.

## Verification

- `cd packages/adapters/codex-local && pnpm vitest run
src/server/codex-args.test.ts` — new cases: `extraArgs` already carrying
the flag (single occurrence), the legacy `args` field carrying it
(single occurrence), and the operator's flag preserved when the sandbox
injection is not requested. Existing "adds --skip-git-repo-check when
requested" case unchanged.
- `cd packages/adapters/codex-local && pnpm vitest run` — full package
suite (218 tests).
- `pnpm run typecheck` in the package.

## Risks

- Low. The change only suppresses a duplicate of a single, idempotent
flag; it never removes an operator-supplied argument and never adds one
that was not already going to be present.

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — extended
thinking, agentic tool use (file edits, vitest/tsc runs). No other
models involved.

## Checklist

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

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents can currently perform many mutations directly, while humans
often need a durable review point before cross-issue or destructive
actions occur
> - Existing approvals and issue-thread interactions do not provide a
standalone, reusable object for presenting options, collecting typed
inputs, detecting stale targets, and auditing effect execution
> - The control plane therefore needs a first-class propose mode that
separates an agent's recommendation from the governed mutation it may
cause
> - This pull request adds Decisions v1 across the database, shared
contracts, server execution and telemetry, agent skill guidance, and
operator UI
> - The benefit is that agents can propose multi-option actions safely
while operators get explicit provenance, fail-closed execution,
per-effect results, and a focused attention workflow

## Linked Issues or Issue Description

### Subsystem affected

Cross-cutting: `packages/db`, `packages/shared`, `server`, and `ui`.

### Problem or motivation

Agents need a governed way to propose consequential work without
immediately mutating issues, especially when one choice can affect
several issue trees. Existing approvals and issue-thread interactions do
not provide a standalone object with typed options, target snapshots,
effect-level authorization, expiration, execution outcomes, and reusable
attention-feed presentation.

### Proposed solution

Add first-class Decisions that store options and typed inputs, surface
open proposals in the operator attention feed, validate target freshness
and the origin-agent/operator authorization intersection at decision
time, execute a bounded set of auditable effects, and retain terminal
outcomes. Decisions v1 supports comments, status and assignee changes,
follow-up issue creation, blocker resolution, and issue-tree
cancellation, plus bundle grouping, expiration/dismissal, rule-key
telemetry, and agent-facing API guidance.

### Alternatives considered

- Extend approvals with arbitrary effects: rejected because approvals
represent governed yes/no actions and would become an unsafe generic
mutation envelope.
- Model every proposal as an issue-thread interaction: rejected because
decisions can span several targets and need independent lifecycle,
telemetry, idempotency, and effect results.
- Let agents perform the mutation and ask for retrospective review:
rejected because it removes the pre-execution governance boundary this
feature is meant to provide.

### Roadmap alignment

Aligns with `ROADMAP.md` sections **Agent Reviews and Approvals**,
**Enforced Outcomes**, **MCP Tool Gateway & Apps (governed tool
access)**, and **Activity History** by making explicit decisions,
authorization gates, auditable execution, and terminal outcomes
first-class control-plane objects.

### Additional context

This does not replace existing approvals or issue-thread interactions,
and it does not add an unrestricted generic mutation effect.

## What Changed

- Added company-scoped decision, option, target, and effect-execution
schema plus migration and shared TypeScript/Zod contracts.
- Added decision routes and services for propose, list/get, decide,
dismiss, cancel, target freshness checks, authorization intersection,
idempotency, activity logging, and execution auditing.
- Added rule-key decision telemetry and attention-feed metadata so open
decisions are visible and measurable.
- Added agent skill documentation for proposing and resolving decisions
through the Paperclip API.
- Added the Decisions UI: API client, query keys, inline attention
resolver, bundle grouping, target-issue strip, terminal history,
destructive confirmation, and per-effect result rendering.
- Added server service coverage, DecisionCard state tests, and Storybook
stories for the supported visual states.

## Verification

- `pnpm -r typecheck` — passed.
- `pnpm test:run` — 2,876 passed, 1 skipped, with one unrelated
cross-suite cleanup-order failure in
`heartbeat-responsible-user-invariant.test.ts`; the failing file passes
in isolation (`6/6`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-responsible-user-invariant.test.ts` — passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/DecisionCard.test.tsx` — passed (`9/9`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/authz-existence-oracle-guard.test.ts
src/__tests__/openapi-routes.test.ts` — passed (`5/5`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/decisions-service.test.ts` — passed (`16/16`).
- `pnpm --filter paperclipai exec vitest run
src/__tests__/company-import-export-e2e.test.ts` — passed (`1/1`).
- `pnpm --filter @paperclipai/server typecheck` and `pnpm --filter
paperclipai typecheck` — passed.
- `pnpm build` — passed.
- Rebased-head focused suite — passed (`6` files, `88` tests): shared
decision contracts, Decisions service, OpenAPI routes, startup feedback
export, DecisionCard states, and attention helpers. The follow-up
stale-secondary-target regression passes in the DecisionCard suite
(`10/10`).
- Rebased-head scoped typechecks — passed for `@paperclipai/shared`,
`@paperclipai/db`, `@paperclipai/server`, and `@paperclipai/ui`.
- Rebased-head migration numbering and safety checks — passed after
renumbering the additive migration to `0193` and making it replay-safe
for environments that applied the earlier feature-branch number.
- `pnpm check:token-gates` — passed with all gates clean.
- GitHub PR workflow and Greptile review for
`1f9f7645882d05dfdd9c99377c03a1f53f20e8be` — running after the
stale-secondary-target fix and PR metadata refresh on July 27, 2026.
- `pnpm --filter @paperclipai/ui build-storybook` exposes an existing
Storybook version mismatch (`storybook` 10.4.6 vs
`@storybook/addon-docs` 10.5.0); Decisions stories were validated with
the docs addon temporarily disabled and the tracked config remains
unchanged.

## Risks

- **Migration:** Adds replay-safe migration `0193`; migration numbering
and safety checks pass. The new tables and indexes are additive.
- **Authorization:** Effect execution intersects the proposing agent's
permissions with the responsible user context and fails closed; mistakes
could reject a valid proposal rather than silently over-authorize it.
- **Concurrency:** Target snapshots and idempotency keys protect against
stale or duplicate execution, but reviewers should focus on mixed-effect
partial outcomes and retry behavior.
- **UI:** Decisions are integrated into the existing attention feed
rather than a separate navigation surface, reducing routing risk but
increasing the importance of attention-item metadata compatibility.

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

## Model Used

- OpenAI Codex CLI using `gpt-5.6-sol` for final PR preparation, review
fixes, and verification; repository tools and code execution were
enabled, and context-window size is not exposed in this runtime.
- Anthropic Claude Opus 4.8 with 1M context assisted with the Decisions
UI implementation, as recorded in the relevant commits.

## 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>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 19:17:02 -07:00
71231dfa38 feat(audit): agent audit UI — company page + per-agent tab (#9744)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators need an audit record of agent actions across tasks,
comments, documents, approvals, and runs
> - The permission-gated audit read API provides that record, but
operators cannot inspect it in the product
> - A readable UI must preserve company boundaries, server-side
permission decisions, and redaction
> - Audit exports must also be safe to open in spreadsheet software and
must record the export itself
> - This pull request adds company and per-agent audit views plus a
guarded CSV export
> - The benefit is a searchable, filterable, and reviewable agent action
history with direct links back to work

## Linked Issues or Issue Description

**Feature.** This change adds the frontend and CSV export for the agent
action audit log.

Refs #9731 and #9735.

- Problem: agent actions are recorded, but operators have no readable
product surface to inspect or export them.
- Solution: add a company audit page and a per-agent Audit tab that use
the permission-gated audit API.
- Alternative: build a separate plugin-only surface. This was rejected
because the existing permission model already supports a unified,
server-authoritative view.

This pull request targets the audit epic branch, which contains the
merged #9735 audit API.

## What Changed

- Added a company Audit page and sidebar entry.
- Added a per-agent Audit tab with a fixed agent filter.
- Added filters for agent, responsible user, action domain, entity type,
and date range.
- Added task and run links, responsible-user context, cursor pagination,
and readable action text.
- Added a permission-denied Enterprise card for callers without
`audit:view_agent_actions`.
- Added a CSV export that is permission-gated, capped, self-audited,
CSV-escaped, and protected against spreadsheet formula injection.
- Preserved the merged audit API cursor validation, redaction, and
sub-millisecond pagination behavior.

## Verification

- `pnpm exec vitest run ui/src/pages/audit/AuditFeed.test.tsx` — 6
passed.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/agent-action-audit-routes.test.ts` — 8 passed with
embedded PostgreSQL.
- `pnpm -r typecheck` — passed across all workspaces.
- `pnpm build` — passed across all workspaces.
- `pnpm test:run` — all completed shards passed except one
environment-sensitive CLI assertion caused by injected static AWS
credential variables; the exact test passes 8/8 with those variables
unset.
- Manual Chromium QA exercised the populated feed, active filters,
permission-denied card, per-agent tab, and CSV export.

## Screenshots and Manual QA

- [All audit states exercised in
Chromium](https://github.com/paperclipai/paperclip/pull/9744#issuecomment-4998997001)
- [Detailed browser report and per-agent tab root
cause](https://github.com/paperclipai/paperclip/pull/9744#issuecomment-4998771061)

The per-agent redirect defect found during QA is fixed in this branch.

## Risks

Low to moderate risk. The UI and export route are additive and use the
existing company-scoped permission gate. The main risks are large
exports and spreadsheet interpretation. The export is capped at 10,000
rows, records truncation accurately, and prefixes formula-like cells as
text. There are no schema changes or migrations.

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

## Model Used

- Anthropic Claude Opus 4.8, 1M context, extended thinking, tool use,
and code execution produced the original implementation.
- OpenAI Codex, GPT-5 (deployment ID and context window not exposed),
reasoning, tool use, code execution, browser-test orchestration, and
GitHub review tooling repaired and verified the pull request.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [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>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 19:09:13 -07:00
DottaandPaperclip 2e4774ac90 fix(adapter-utils): correct confirmation wake semantics (#10588)
<!-- Write all pull request text in Simplified Technical English
(ASD-STE100): short sentences, one instruction per sentence, simple
approved vocabulary, and the active voice. -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Local agent wakes include a default execution contract
> - That contract tells agents how issue-thread continuation policies
behave
> - The current text says `wake_assignee` resumes a confirmation only
after acceptance
> - The server actually wakes for every non-expired resolution and
reserves acceptance-only behavior for `wake_assignee_on_accept`
> - This pull request makes the default prompt match the server contract
and strengthens the recovery follow-up regression case
> - The benefit is that agents choose the correct continuation policy
and recovery tests cover normalized agent name keys

## Linked Issues or Issue Description

Related work: Refs #5473, Refs #5060, and Refs #10562.

**What happened?**

The default local-agent prompt described `wake_assignee` as
acceptance-only for `request_confirmation`. This conflicts with the
server. The server wakes on every non-expired resolution. A recovery
follow-up test also used an already-normalized execution agent name key,
so it did not exercise the normalization seam.

**Expected behavior**

The prompt must state that `wake_assignee` resumes after acceptance or
rejection. It must direct acceptance-only flows to
`wake_assignee_on_accept`. The recovery regression must use a
display-style agent name key and prove that the follow-up path still
works after normalization.

**Steps to reproduce**

1. Read the default local-agent prompt in
`packages/adapter-utils/src/server-utils.ts`.
2. Compare its confirmation continuation text with
`queueResolvedInteractionContinuationWakeup` in
`server/src/routes/issues.ts`.
3. Observe that the prompt gives acceptance-only semantics to
`wake_assignee`.
4. Inspect the recovery hand-back test and observe that its execution
name key is already normalized.

**Paperclip version or commit**

`7301fae942`

**Deployment mode**

Local dev. The prompt and test behavior are not deployment-specific.

## What Changed

- Corrected the default agent prompt for `wake_assignee` and
`wake_assignee_on_accept`.
- Added focused prompt assertions for both the required and obsolete
text.
- Changed the recovery follow-up fixture to use a display-style agent
name key.

## Verification

- `pnpm exec vitest run packages/adapter-utils/src/server-utils.test.ts
-t 'keeps the default local-agent prompt action-oriented'` passed: 1
test.
- `pnpm exec vitest run
server/src/__tests__/heartbeat-comment-wake-batching.test.ts -t 'defers
recovery hand-back wakes until the resolving run exits'` passed: 1 test.
- `pnpm --filter @paperclipai/adapter-utils typecheck` passed.
- `pnpm --filter @paperclipai/server typecheck` passed.
- `git diff --check origin/master...HEAD` passed.

## Risks

- Low risk. The production change updates prompt text only.
- Agents that followed the old text may now choose
`wake_assignee_on_accept` for acceptance-only flows.
- The server test change only broadens an existing regression fixture.

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

## Model Used

- OpenAI Codex with GPT-5. The exact serving model ID and context-window
size are not exposed to the agent. The model used reasoning, repository
tools, tests, Git, and GitHub CLI access.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-31 18:53:53 -07:00
DottaandPaperclip 627728bdde feat: add authoritative issue PATCH receipts (#10478)
<!-- Write all pull request text in Simplified Technical English
(ASD-STE100): short sentences, one instruction per sentence, simple
approved vocabulary, and the active voice. -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents update tasks through the issue API.
> - The update response did not state which values changed.
> - Blocker updates also did not echo the scalar blocker IDs.
> - Agents therefore used an extra GET request to confirm a successful
write.
> - This pull request adds an authoritative change receipt and an
optional small response.
> - The benefit is fewer API calls with a clear and compatible write
contract.

## Linked Issues or Issue Description

No public GitHub issue exists for this change.

### Subsystem affected

Cross-cutting: `server/`, `packages/shared`, and the UI issue cache.

### Problem or motivation

A successful issue PATCH returned the updated issue, but it did not
identify the effective changes. Blocker writes returned relation
summaries without the scalar IDs. Agents could not distinguish a
confirmed clear operation from missing data. The response must confirm
committed field and blocker changes while existing UI clients continue
to receive the full issue by default.

### Proposed solution

Add a `changes` receipt. Add a conditional `blockedByIssueIds` echo.
Support `Prefer: return=minimal`. Keep the full response as the default.

### Alternatives considered

Make the small response the default for agent tokens. This would create
different response contracts by actor type, so this pull request does
not use that design.

### Roadmap alignment

This is a focused control-plane reliability improvement. It does not
duplicate an open roadmap milestone.

## What Changed

- Compute committed issue row and relation changes in the issue service.
- Omit no-op fields and truncate changed long text values to 200
characters.
- Echo blocker ID arrays for blocker set and clear requests.
- Add the opt-in `Prefer: return=minimal` response and
`Preference-Applied` header.
- Keep receipt metadata out of React Query issue caches.
- Add route and embedded Postgres tests for the new contract.

## Verification

- `pnpm exec vitest run
server/src/__tests__/issue-activity-events-routes.test.ts`
- `pnpm exec vitest run server/src/__tests__/issues-service.test.ts -t
"returns authoritative update receipts for row fields and blocker
relations"`
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm check:token-gates`
- `git diff --check`

## Risks

- Low compatibility risk. The default response only adds receipt fields.
- Minimal mode is opt-in. Existing clients do not receive a smaller
body.
- The receipt excludes `updatedAt` because the response already returns
it as the freshness anchor.
- Prose API and agent workflow guidance will follow after the server
contract is available.

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

## Model Used

- OpenAI Codex based on GPT-5. The exact deployment ID, context window
size, and reasoning mode are not exposed to the agent. The agent used
repository tools, code execution, and test execution.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-31 18:52:59 -07:00
fc5a30805e feat(cli): add managed install, update, and service lifecycle (#10045)
## Thinking Path

> - Paperclip is the open source control plane people use to manage
AI-agent companies
> - Operators need a predictable installation path that survives beyond
an ephemeral `npx` process
> - A durable installation needs an owned per-user payload store, stable
command shim, safe shell integration, and supported service lifecycle
> - Updates must preserve recoverability by backing up data, installing
side-by-side, verifying the new payload, and retaining rollback state
> - Bootstrap scripts and privileged service operations must fail closed
across download, filesystem, ownership, and consent boundaries
> - This pull request integrates managed install, update, rollback,
service, uninstall, doctor, bootstrap-installer, and runtime-serving
support into one workflow
> - The benefit is a recoverable, inspectable, and documented
installation lifecycle with explicit safety boundaries across Linux,
macOS, containers, WSL, npm, npx, and source checkouts

## Linked Issues or Issue Description

### Problem

Paperclip lacks a first-class durable installation and lifecycle
workflow. Operators currently have to assemble npm/npx installation,
PATH setup, background-service management, updates, rollback,
diagnostics, and uninstall behavior themselves. That makes upgrades
harder to recover, creates inconsistent behavior across platforms, and
leaves shell/download/service trust boundaries without one documented
implementation.

### Proposed Solution

Add a managed per-user install store and stable shim, a verified shell
bootstrap installer, service lifecycle commands, install-mode-aware
update/rollback behavior, doctor checks, and documentation. Managed
updates back up the database, install and smoke-test a side-by-side
payload, atomically switch `current`, and retain prior payloads. The
shell installer pins registry/download trust boundaries and requires
explicit consent for non-interactive privileged actions.

### Alternatives Considered

- Keep recommending `npx`: simple for evaluation, but ephemeral and
unsuitable for stable services, atomic updates, or rollback.
- Require global npm installation only: familiar, but cannot provide the
owned side-by-side payload store and retained rollback semantics.
- Split the capability across multiple PRs: rejected because install,
update, service, uninstall, bootstrap, and serving behavior share
contracts and security boundaries that need review together.

### Related Pull Requests

- Supersedes #10042 and #10044 with one integrated final diff.
- Incorporates and replaces the closed preparatory work in #10032 and
#10034.

## What Changed

- Added `paperclipai install`, `update`/`upgrade`, rollback, uninstall,
service lifecycle, onboarding integration, and managed-install doctor
checks.
- Added a private managed payload store, verified manifest/marker
ownership, exclusive mutation locks, atomic manifest/current/shim
writes, retained previous payloads, and provenance validation.
- Added npm and GitHub-ref install sources with exact target resolution,
registry isolation, database backup, side-by-side verification, atomic
activation, service restart coordination, and failure rollback.
- Made managed-update backups report actionable service-start and
`--no-backup` recovery guidance for unreachable databases, while clean
never-onboarded instances skip an empty backup.
- Added systemd user and launchd service definitions, status/health/log
commands, single-instance coordination, stale-port recovery, and
explicit sudo/lingering consent handling.
- Added the `scripts/install.sh` bootstrap path with checked two-stage
downloads, pinned public npm registry usage, platform checks,
dry-run/non-interactive controls, and Docker fixtures.
- Added embedded Postgres/native bootstrap integration,
hot-restart/systemd-notify serving support, passive update notices,
configuration contracts, README/CLI/install documentation, and focused
regression tests.
- Security re-review should explicitly re-verify: (1)
`addManagedPathBlock`/`removeManagedPathBlock` reject symlinked or
non-regular rc files, assert current-user ownership, preserve
restrictive modes, and replace atomically; (2) managed shim replacement
rejects unsafe parents, foreign-owned or multiply linked files, and uses
checked atomic replacement; (3) the shell installer and sudo path
preserve explicit consent and checked downloads; and (4) installed
service/runtime serving remains bound to the validated managed shim and
instance configuration.

## Verification

- `bash -n scripts/install.sh scripts/clean-install-git.sh
scripts/clean-install-npm.sh scripts/test-install-sh-docker.sh`
- `pnpm exec vitest run cli/src/__tests__/install-store.test.ts
cli/src/__tests__/install-command.test.ts
cli/src/__tests__/managed-install-check.test.ts
cli/src/__tests__/onboard-service.test.ts
cli/src/__tests__/service-health-check.test.ts
cli/src/__tests__/service-manager.test.ts
cli/src/__tests__/update-command.test.ts
cli/src/__tests__/update-notice.test.ts
packages/db/src/embedded-postgres-native.test.ts` — 9 files, 66 tests
passed
- `pnpm --dir cli typecheck`
- `pnpm --dir cli build`
- Follow-up verification: `pnpm exec vitest run
cli/src/__tests__/update-command.test.ts` (14/14), `pnpm --dir cli
typecheck`, `pnpm --dir cli build`, and `pnpm --filter
@paperclipai/server typecheck`.
- `pnpm -r typecheck`
- `pnpm build`
- Full `pnpm test:run` exercised all suites; an injected static AWS
credential changed one unrelated doctor expectation, which passed when
those credentials were removed. A second run cleared that case and
exposed stale pre-existing adapter-utils `dist` output; rebuilding
`@paperclipai/adapter-utils` made the isolated test pass. The updated PR
CI is the authoritative clean-workspace full-suite run.

## Risks

- Installer/update code writes executable shims, symlinks, shell rc
blocks, service definitions, and managed payloads; ownership,
regular-file, symlink, hard-link, marker, and path-containment checks
fail closed before destructive changes.
- The bootstrap installer executes downloaded tooling; downloads are
staged and checked before execution, npm traffic is pinned to the public
registry, and non-interactive privileged behavior requires explicit
consent.
- Linux lingering may invoke `sudo`; the command is surfaced and
confirmed before execution, and unsupported service managers fall back
to foreground-run guidance.
- Database migrations remain forward-only; payload rollback does not
reverse migrations, so managed updates create a backup before activation
unless explicitly disabled.
- Service restart and runtime serving touch process/port ownership;
lifecycle locks, health/version checks, and stable-shim service
definitions reduce split-brain and stale-process risk.

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

## Model Used

- OpenAI Codex coding agents using GPT-5.5 and GPT-5.6-sol, with
reasoning, repository/API access, shell execution, and test tooling. The
runtime did not expose a reliable 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>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:52:23 -07:00
Devin Foley c0b875c46c fix(codex): let sandbox runs use the sandbox image's own Codex login (#10582)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Codex agents can run inside sandbox environments, and operators can
bake a Codex login into the sandbox image during interactive image setup
> - Two credential gates (the control plane's pre-dispatch
configuration-incomplete gate and the adapter's execute-time fail-fast)
required host-side Codex credentials — a usable `auth.json` in the
managed home or a configured `OPENAI_API_KEY` — regardless of where the
run executes
> - On managed cloud hosts a local Codex login never exists, so every
sandbox run of a Codex agent failed immediately with "configuration
incomplete: no Codex credentials available for managed home …", even
though the adapter's inbound auth merge already supports the image-login
case end to end
> - This pull request makes the execute-time gate probe the sandbox for
its own `~/.codex/auth.json` before failing, and exempts
sandbox-destined runs from the pre-dispatch host check
> - The benefit is that a sandbox image signed in to Codex is a
first-class credential source, matching what the auth-merge,
precedence-warning, and copy-back machinery were already built for

## Linked Issues or Issue Description

**What happened?**

Running a `codex_local` agent in a sandbox environment whose image
carries a Codex login failed instantly with `configuration incomplete:
no Codex credentials available for managed home "…/codex-home". Sign in
to Codex on the host with a ChatGPT subscription, or bind a per-agent
OPENAI_API_KEY secret for this agent.` The host has no Codex login and
never will on a managed cloud deployment; the sandbox's own login was
never consulted.

**Steps to reproduce**

1. Configure a sandbox environment and capture a custom image after
signing in to Codex inside the interactive image setup.
2. Create a `codex_local` agent that uses that environment, on a host
with no Codex login and no `OPENAI_API_KEY` bound.
3. Start a run: it fails pre-dispatch with the configuration-incomplete
blocker above.

**Expected behavior**

The run launches and Codex authenticates with the sandbox image's own
login, the same way the adapter's host↔sandbox auth merge already keeps
the sandbox credential when the host ships none. A run should only fail
fast when neither the host, a bound `OPENAI_API_KEY`, nor the sandbox
has credentials.

**Paperclip version**

Current `master` (cloud image deployments).

**Deployment mode**

Managed cloud stacks (any deployment where the server host has no local
Codex login).

## What Changed

- Extracted the adapter's execute-time gate into
`assertCodexCredentialsLaunchable`: when host readiness fails and the
target is a sandbox, it probes `~/.codex/auth.json` in the sandbox (same
command the auth-precedence warning uses) and proceeds with a log line
naming the credential source; when the sandbox has no login either, the
error now names all three remediation options (sandbox image sign-in,
per-agent `OPENAI_API_KEY`, host sign-in). Non-sandbox targets keep
today's strict behavior byte-for-byte.
- The control plane's pre-dispatch gate in
`resolveExecutionRunAdapterConfig` now takes the selected environment's
driver and skips the host-credential check for sandbox-destined runs —
only the adapter can probe the sandbox once it is up, so the
execute-time gate is the authority there. Non-sandbox runs keep the
early, well-attributed configuration-incomplete blocker.
- The codex Test flow needed no change: it already seeds host
credentials only when they exist and otherwise leaves the sandbox's
`CODEX_HOME` alone; this aligns the run path with it.

## Verification

- `cd packages/adapters/codex-local && pnpm vitest run` — 210 tests,
including new gate cases: sandbox login present (proceeds + logs
source), sandbox and host both credential-less (fails with the extended
message), non-sandbox target (strict host requirement kept, no sandbox
probe), per-agent API key (no probe at all).
- `cd server && pnpm vitest run
src/__tests__/heartbeat-project-env.test.ts
src/__tests__/codex-local-adapter-environment.test.ts` — includes the
new sandbox-exemption case next to the existing blocker tests.
- `pnpm run typecheck` in `server` and `packages/adapters/codex-local`.

## Risks

- Sandbox-destined misconfigurations (no credentials anywhere) now
surface at adapter execute time instead of pre-dispatch, so they read as
an adapter failure with a precise message rather than a
configuration-incomplete blocker. The trade-off is deliberate: the
sandbox must be up to know whether credentials exist, and the failure
message names the exact remediations.
- The sandbox probe adds one short (5s-capped) shell command to sandbox
runs whose host has no credentials; runs with host credentials or a
bound key are untouched.
- Self-hosted behavior is unchanged for local and SSH targets.

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — extended
thinking, agentic tool use (file edits, vitest/tsc runs). No other
models involved.

## Checklist

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

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

## Linked Issues or Issue Description

No matching public GitHub issue was found.

**What happened?**

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

**Expected behavior**

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

**Steps to reproduce**

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

**Reproduced on**

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

## What Changed

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

## Verification

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

## Risks

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

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

## Model Used

- OpenAI Codex used GPT-5.3-Codex for the earlier implementation.
- OpenAI Codex used GPT-5 for the lifecycle-hook revision and the
current fail-closed review fix. The runtime did not expose a more
specific snapshot ID or context-window size. Both runs used reasoning,
repository tools, and code execution.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-31 14:43:46 -07:00
Nicky LeachandPaperclip 53bcf3897f feat: sync @-mentioned projects into remote sandboxes (confined sandbox transport, flag ON) (#10564)
## Thinking Path

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

## Linked Issues or Issue Description

**Subsystem affected**

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

**Problem or motivation**

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

**Proposed solution**

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

**Alternatives considered**

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

**Roadmap alignment**

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

**Additional context**

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

## What Changed

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

## Verification

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

## Risks

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

## Model Used

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

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-31 13:31:17 -07:00
Nicky LeachandPaperclip c4f62644b0 docs(daytona): document operator enablement for the advisory bwrap wrapper (#10560)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Daytona sandbox provider now uses an advisory bwrap wrapper
> - Operators need clear host and image setup for bubblewrap, sudo, and
user namespaces
> - The repo should document that setup, but it should not own
provisioning
> - This pull request adds the operator guidance to the shared sandbox
requirements and the Daytona README
> - The benefit is that operators can enable the wrapper with the same
steps the code expects

## Linked Issues or Issue Description

Refs #10554 and #10541.

## What Changed

- Added an advisory bwrap prerequisites section to
`packages/plugins/sandbox-providers/SANDBOX-REQUIREMENTS.md`.
- Added an operator enablement section to
`packages/plugins/sandbox-providers/daytona/README.md`.
- Documented the install commands, the sudoers rule, the user namespace
setting, and the verification command.
- Kept provisioning out of the repo and left it to the image or snapshot
layer.

## Verification

- `git diff --check origin/master...HEAD`
- `gh pr checks 10560`

## Risks

- Low risk. This change updates documentation only.
- The docs can drift if the host setup changes later.
- Provisioning still lives outside the repo.

## Model Used

- OpenAI Codex, GPT-5, tool use enabled.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-31 11:50:11 -07:00
d295251550 feat(adapter-claude): add Claude Sonnet 5 to the static model fallback (#10280)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents pick their model from a dropdown in agent config, populated
per-adapter by `listAdapterModels()` → each adapter's live provider
catalog merged over a static fallback list
> - For `claude_local`, newer model ids only reach the dropdown via the
live Anthropic `/v1/models` fetch, which needs a server
`ANTHROPIC_API_KEY`, a <5s round-trip, non-Bedrock mode, and account
entitlement; on any miss it silently falls back to the static `models`
array
> - Claude Sonnet 5 (`claude-sonnet-5`) is a current flagship but was
absent from that static fallback, so it appeared only when live
discovery happened to succeed — i.e. "the newest model doesn't
consistently show up"
> - This pull request adds `claude-sonnet-5` to the `claude_local`
static model list so it is selectable regardless of the live-discovery
path
> - The benefit is a consistent, reliable dropdown that no longer
depends on a flaky live fetch to surface a shipped flagship model

## Linked Issues or Issue Description

No public GitHub issue. The bug is described inline following the
bug-report template:

**What happened**
The `claude_local` agent-config model dropdown intermittently omitted
Claude Sonnet 5. `claude-sonnet-5` was missing from the adapter's static
fallback `models` array (`packages/adapters/claude-local/src/index.ts`),
so it only surfaced when the live Anthropic `/v1/models` discovery
happened to succeed.

**Expected behavior**
Claude Sonnet 5 is a shipped flagship model and should always be
selectable in the dropdown, independent of whether live discovery
succeeds.

**Steps to reproduce**
1. Run the server without a working live Anthropic `/v1/models` path (no
`ANTHROPIC_API_KEY`, Bedrock mode, a discovery timeout, or a cache
miss).
2. Open agent config for a `claude_local` agent and inspect the model
dropdown.
3. Observe that `claude-sonnet-5` is absent because the static fallback
list omitted it.

**Deployment mode**
Self-hosted / local adapter (`claude_local`); the server process reads
`ANTHROPIC_API_KEY` from its environment.

## What Changed

- Added `{ id: "claude-sonnet-5", label: "Claude Sonnet 5" }` to the
`claude_local` static `models` fallback, immediately after
`claude-opus-4-8` (so Opus 4.8 stays the default first option).
- Added an explicit regression assertion in
`server/src/__tests__/adapter-models.test.ts` that `claude-sonnet-5` is
present in the `claude_local` fallback when live discovery is
unavailable.

## Verification

- `pnpm -C server exec vitest run src/__tests__/adapter-models.test.ts
-t "claude fallback"` — **passes** (the new `claude-sonnet-5` assertion
included).
- Reviewed the consuming tests: the fallback test also asserts
`models[0]?.id === "claude-opus-4-8"` (still index 0 — Sonnet 5 is index
1, unaffected); `adapter-registry.test.ts` reads `builtIn?.models`
dynamically, so no exact-array snapshot breaks.
- Change is a single static-data addition plus a test assertion; no
control-flow change.

## Risks

- Low risk. Pure additive change to a fallback list; no control-flow
change. Worst case is an id that a given account isn't entitled to,
which the existing "current"/manual-model UI paths already tolerate.

## Model Used

Claude (Anthropic), model id `claude-opus-4-8` (Opus 4.8), extended
thinking + tool use, run as the Paperclip CTO agent.

## Checklist

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-31 13:15:42 -05:00
Noah KellnerandNoah Kellner bd53b99686 feat(gemini-local): export detectGeminiQuotaExhausted for plugin adapter reuse (#3416)
## Thinking Path

> - Paperclip orchestrates AI agents via pluggable server-side adapters,
one per provider / CLI backend
> - Each adapter package exposes a server entry point
(`@paperclipai/adapter-<name>/server`) that downstream consumers —
including plugin adapters that wrap or extend the stock behavior —
re-use for helper functions
> - `gemini-local`'s server entry re-exports a curated set of parse
helpers from `./parse.js` (`parseGeminiJsonl`,
`isGeminiUnknownSessionError`, `describeGeminiFailure`,
`detectGeminiAuthRequired`, `isGeminiTurnLimitResult`) so consumers can
classify provider output without reaching into the package's internals
> - `detectGeminiQuotaExhausted` lives in the same `parse.ts` file next
to `detectGeminiAuthRequired`, is defined with `export function`, and is
the package's single source of truth for "is this output a Gemini quota
hit?" — but it is missing from the entry-point re-export block
> - As a result, any consumer that wants to classify quota exhaustion
either has to deep-import from `./server/parse.js` (brittle against
future `exports`-field changes) or reimplement the regex locally (drift
risk against the authoritative heuristic)
> - This pull request adds `detectGeminiQuotaExhausted` to the existing
re-export block, placed next to its thematic sibling
`detectGeminiAuthRequired`, with no other changes
> - The benefit is one extra supported public symbol on
`@paperclipai/adapter-gemini-local/server` — a purely additive ergonomic
improvement with no behavior change and no existing consumer impact

## What Changed

- `packages/adapters/gemini-local/src/server/index.ts`: added
`detectGeminiQuotaExhausted` to the `export { ... } from "./parse.js"`
block, inserted between `detectGeminiAuthRequired` and
`isGeminiTurnLimitResult` (thematic grouping — both `detect*` helpers)

## Verification

Local verification against the branch commit (base: `upstream/master` at
`b649bd45`):

```
$ pnpm --filter @paperclipai/adapter-gemini-local typecheck
> @paperclipai/adapter-gemini-local@0.3.1 typecheck
> tsc --noEmit
(exit 0)

$ pnpm --filter @paperclipai/adapter-gemini-local build
> @paperclipai/adapter-gemini-local@0.3.1 build
> tsc
(exit 0)

$ cd server && pnpm exec vitest run src/__tests__/gemini-local-execute.test.ts
 RUN  v3.2.4

 ✓ src/__tests__/gemini-local-execute.test.ts (3 tests) 1487ms
   ✓ gemini execute > passes prompt via --prompt and injects paperclip env vars
   ✓ gemini execute > always passes --approval-mode yolo
   ✓ gemini execute > uses a compact wake delta instead of the full heartbeat prompt when resuming a session

 Test Files  1 passed (1)
      Tests  3 passed (3)
```

Existing-consumer check — all references to `detectGeminiQuotaExhausted`
anywhere in the tree:

```
packages/adapters/gemini-local/src/server/index.ts:9    (this PR's new re-export)
packages/adapters/gemini-local/src/server/parse.ts:253  (the definition)
packages/adapters/gemini-local/src/server/test.ts:19    (intra-package import from "./parse.js")
packages/adapters/gemini-local/src/server/test.ts:174   (intra-package usage)
```

No cross-package consumer references the symbol today, so the new
re-export cannot break any existing import. It is strictly additive to
the public surface of `@paperclipai/adapter-gemini-local/server`.

## Risks

None. Purely additive re-export of a symbol that is already a named
export on `./parse.ts`. The clean-success path, failure paths, and all
other adapter behavior are untouched. No existing consumer is affected.

## Model Used

- **Provider**: Anthropic
- **Model**: Claude Opus 4.6 (1M context)
- **Interface**: Claude Code CLI
- **Role**: Upstream state verification (grep + diff against current
master), PR drafting against the `CONTRIBUTING.md` template, local
typecheck / build / test execution
- **Reasoning Mode**: Extended thinking enabled
- **Human oversight**: Noah Kellner reviewed the one-line re-export
addition and approved submission

## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Noah Kellner <noah.kellner@xenoscloud.com>
2026-07-31 13:00:26 -05:00
b4a7a12985 feat: make recovery updates quieter (#10542)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The recovery subsystem restores work after an agent run stops or
loses state
> - Recovery notices currently use the same visual weight as normal work
comments
> - Recovery agents can also post long narratives that obscure the
useful hand-off
> - The server must identify recovery output because agents cannot set
presentation controls
> - This pull request adds compact recovery notices, structured action
references, and brief recovery prompts
> - The benefit is a quieter issue thread that still keeps recovery
state inspectable

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting: `server/`, `packages/shared`, and
`packages/adapter-utils`.

**Problem or motivation**

Recovery notices and recovery-run comments can dominate an issue thread.
Operators must scan routine recovery narration before they find the work
hand-off.

**Proposed solution**

Give routine recovery output a compact system-notice presentation.
Derive the presentation on the server so agents cannot hide arbitrary
comments. Keep the successful missing-state summary fully visible
because that comment is the recovery deliverable.

**Alternatives considered**

The UI could detect recovery text. That approach is fragile and does not
provide structured action references. Agents could also set presentation
directly, but that would weaken the current board-only security
boundary.

**Roadmap alignment**

This change refines the completed “Self-healing runs & automatic
recovery” and “Enforced Outcomes” roadmap areas. It does not add a
competing roadmap capability.

**Additional context**

The scope covers shared comment validation, server recovery notices,
agent-comment derivation, and recovery prompt text. No database
migration is needed because presentation data already uses JSON.

## What Changed

- Add the `compact` issue-comment presentation density to shared
constants, types, and validation.
- Give recovery escalation, waiting, and in-place notices compact titles
and structured recovery-action metadata.
- Use recovery-action metadata for notice deduplication, with the legacy
text marker as a compatibility fallback.
- Derive compact presentation for comments from recovery-scoped runs
while preserving the board-only presentation boundary.
- Keep successful missing-state recovery summaries fully visible.
- Ask recovery participants to record outcomes in `resolutionNote` and
keep source-issue comments brief.
- Add shared, route, service, and prompt tests for the new behavior and
exceptions.

## Verification

- `pnpm -r typecheck`
- Focused Vitest coverage: 320 tests passed across shared validators,
adapter prompts, issue comments, recovery actions, and heartbeat
recovery.
- Full server phase: 292 files passed, 3,094 tests passed, and 2 tests
skipped.
- Full UI phase: 386 files passed and 3,182 tests passed.
- `pnpm build`
- Known master baseline: `cli/src/__tests__/secrets.test.ts` expects
`pass`, but the current implementation returns `warn` when strict secret
mode is disabled for Postgres. This branch does not change CLI secrets
code.

## Risks

- Low migration risk. The presentation column is JSON and needs no
database migration.
- Recovery-run detection depends on the persisted run context snapshot.
- Structured metadata becomes the primary deduplication key. The
existing body marker remains as a fallback for older comments.

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

## Model Used

- OpenAI Codex with `gpt-5.6-sol`. The runtime did not expose the
context-window size. The model used agentic reasoning, repository tools,
code execution, and test execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-31 09:55:01 -07:00
Nicky LeachandPaperclip 8c910b9a40 feat(daytona): activate the advisory bwrap wrapper at the execute seam (#10554)
## Thinking Path

> - Paperclip is the control plane for autonomous AI companies
> - Daytona runs code in a remote sandbox
> - The execute seam decides how agent commands run
> - The pure bwrap builder and the capability probes already merged in
#10541
> - This pull request activates the advisory bwrap wrapper at the
execute seam
> - The wrapper uses bwrap when the lease has the needed data and keeps
plain execution when wrap is not safe
> - The benefit is safer isolation without changing the current runtime
contract

## Linked Issues or Issue Description

- No public GitHub issue exists for this work.
- Related PR: #10541

Problem:
The Daytona execute seam can run commands without the advisory bwrap
wrapper when the lease does not yet provide the wrapper data.

Proposed solution:
Activate the advisory bwrap wrapper when the lease reports bwrap support
and a known uid or gid. Keep plain execution when wrap is not safe.

Alternatives:
- Always wrap every command. This can break current behavior and can
create root-owned files when the uid or gid is missing.
- Reject execution when bwrap is missing. This can fail a lease for a
best-effort wrapper and would change the current runtime contract.

Roadmap alignment:
This work fits the Cloud / Sandbox agents roadmap area.

## What Changed

- `executeOneShot` now accepts a bwrap execution plan.
- `onEnvironmentExecute` now reads the lease bwrap flags and the
writable sync paths.
- A helper resolves when the seam should wrap the command.
- The writable set now includes the workspace path and the collected
read-write sync destinations.
- The wrapper re-binds stdin after the fresh `/tmp` mount.
- The README explains the advisory wrapper behavior and the writable set
model.

## Verification

- `pnpm --filter @paperclipai/plugin-daytona exec vitest run
plugin.test.ts`
- `pnpm --filter @paperclipai/plugin-daytona exec tsc --noEmit`

## Risks

- A missing bwrap tool, a missing `sudo -n` rule, or a blocked user
namespace keeps plain execution.
- The change shifts the execute seam, so command setup needs careful
review.
- The wrapper is advisory, so it does not add a security boundary by
itself.

## Model Used

- OpenAI Codex, GPT-5, tool use, code execution.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-31 08:54:53 -07:00
Nicky LeachandPaperclip 70816c18e5 feat(daytona): add advisory bwrap command builder and capability probes (#10541)
## Thinking Path

> - Paperclip helps people manage AI agents for work
> - The Daytona sandbox provider needs a clear advisory wrapper path and
best-effort capability checks
> - The wrapper must not change the security model or block a lease when
the host lacks bubblewrap support
> - The lease metadata must carry the capability result so later steps
can make a stable choice
> - This pull request adds a pure command builder for the advisory
wrapper
> - This pull request adds non-throwing probes for bubblewrap and
sandbox uid or gid data
> - The benefit is a safer advisory path with no behavior change in the
execution seam

## Linked Issues or Issue Description

### Problem or motivation

The Daytona sandbox provider needs a clear advisory wrapper path and
best-effort capability checks.
The provider must not fail a lease when the host lacks bubblewrap
support.
The wrapper must stay advisory only.
It must not change the security model.

### Proposed solution

Add a pure command builder for the advisory wrapper and non-throwing
probes for bubblewrap and sandbox uid or gid data.
Store the probe result on the lease metadata so later steps can make a
stable choice.
Keep the execution seam unchanged.

### Alternatives considered

Do nothing and keep the current execution seam unchanged.
That path gives no signal when a file change is not durable.
This pull request adds the signal without changing runtime behavior.

### Roadmap alignment

This work fits the Daytona sandbox provider path and keeps the advisory
wrapper outside the execution seam.
It does not change the current security model.

### Additional context

The wrapper is advisory only.
It adds no security.
The read-only root is a feedback signal.

## What Changed

- Added `buildBwrapCommand` as a pure string builder for the advisory
wrapper command.
- Added `detectBwrapAvailable` and `detectSandboxUidGid` as best-effort
probes that never throw.
- Stored `bwrapAvailable`, `sandboxUid`, and `sandboxGid` on the lease
metadata in the acquire, resume, and probe hooks.
- Added a README section that describes the advisory wrapper model.
- Kept the execution seam unchanged.

## Verification

- `vitest run` for
`packages/plugins/sandbox-providers/daytona/src/plugin.test.ts`
- The run passed all 106 tests, including the new builder and probe
coverage.
- The standalone `tsc` run showed only the known baseline noise that
already exists on `master`.

## Risks

- Risk is low because the execution seam does not change.
- The new wrapper stays advisory and does not alter the sandbox security
model.
- The probe results only add metadata and do not fail the lease on
missing host support.

## Model Used

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-31 07:52:28 -07:00
Nicky LeachandPaperclip 4813ed3f0c fix(db): harden embedded Postgres test start with bounded retry (#10540)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The database layer uses embedded Postgres for isolated test runs
> - A port probe can fail when another process takes the same port
before Postgres binds it
> - That race can make a test fail even when the code under test is fine
> - This pull request adds bounded retry and clearer error text to the
embedded Postgres start path
> - The benefit is more stable tests and faster diagnosis when startup
still fails

## Linked Issues or Issue Description

No public GitHub issue exists for this change.

This PR fixes a flaky embedded Postgres test start path.
The helper can lose a port between probe and bind.
This PR retries the start with a fresh port and a fresh data directory.

Related public context:
- Refs #7259
- Refs #9769

## What Changed

- Add bounded retry around embedded Postgres initialization and start.
- Stop each failed attempt and remove its data directory before the next
attempt.
- Capture Postgres output in the thrown error so the failure is easier
to read.
- Add unit coverage for retry success, retry exhaustion, and the
improved error text.

## Verification

- `pnpm --filter @paperclipai/db exec vitest run
src/test-embedded-postgres.test.ts src/embedded-postgres-error.test.ts`
- `pnpm --filter @paperclipai/db exec vitest run`
- `pnpm --filter @paperclipai/db exec vitest run` passed in the worktree
after the change.
- `worktree.test.ts > quarantines copied live execution state in seeded
worktree databases` passed.
- A real cluster loop of 100 starts passed with 0 failures.

## Risks

- The retry can hide a real startup fault until the fifth try.
- The bound keeps the wait short, and the final error still shows the
captured Postgres log.
- This change only affects the embedded Postgres test start helper.

## Model Used

OpenAI Codex, GPT-5, tool-use enabled.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-30 22:22:31 -07:00
Nicky LeachandPaperclip 9f7565f4ce feat: activate OpenTelemetry spans on the sandbox start path (#10536)
## Thinking Path

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

## Linked Issues or Issue Description

No public GitHub issue exists for this change.

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

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

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

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

## What Changed

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

## Verification

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

## Risks

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

## Model Used

- OpenAI GPT-5, tool use enabled.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-30 21:20:58 -07:00
Nicky LeachandPaperclip 5cffd5c72e feat(sandbox): declare and collect the advisory read-write intent (#10521)
## Thinking Path

> - Paperclip is the control plane for AI agent companies
> - Sandbox providers move files between the host and the sandbox
> - The runtime needs advisory data for which sync paths are writable
> - The host already knows that intent when it prepares the sync map
> - Daytona can record that intent for later use
> - This pull request adds the advisory access field and collects
writable paths
> - The benefit is that later runtime work can use the data without
changing current flow

## Linked Issues or Issue Description

No public GitHub issue exists for this change. The description below
follows the feature-request template fields.

### Problem or motivation
An agent runs inside an ephemeral sandbox. Some sandbox paths keep agent
changes; other paths do not. Today the runtime has no declared signal
for which sync destinations the agent may change and keep. A later
feedback wrapper needs this signal to give the agent real-time feedback
when a write lands on a non-persistent path.

### Proposed solution
Add an optional advisory `access: "rw" | "ro"` field to the sync
file-mapping types. The host sets `rw` for the workspace, git-history,
and asset destinations, and `ro` for referenced-project trees. An absent
value defaults to `ro`. The Daytona provider collects the `rw` target
directories into a per-lease writable set for later use. This change
adds the metadata and the collection only. No execution path reads the
writable set yet, so runtime behavior does not change.

### Alternatives considered
Derive a static writable set in provider code. This alternative is
weaker: the layer that authors each sync destination already knows the
intent, so a per-operation declaration is more accurate and does not
hard-code a path list.

### Roadmap alignment
This is the first step toward an advisory sandbox feedback wrapper. The
wrapper is best-effort and adds no security. The ephemeral sandbox stays
the only boundary.

### Additional context
The field is advisory metadata. It does not change the file transfer and
adds no security.

## What Changed

- Added an optional `access` field to the sync file mapping types.
- Set the workspace, git history, and asset destinations to writable.
- Set referenced project destinations to read only.
- Added a writable set store in the Daytona provider.
- Recorded the parent directory of each writable sync mapping during
sync in.

## Verification

- The handoff reports these checks before push.
- `pnpm --filter @paperclipai/adapter-utils exec vitest run
sandbox-managed-runtime.test.ts`
- `pnpm --filter @paperclipai/plugin-sdk exec tsc --noEmit`
- `vitest run src/plugin.test.ts` in the Daytona package
- `tsc --noEmit` in the Daytona package

## Risks

- Low risk.
- The new field is advisory.
- The writable set store is best effort and in memory.
- A cold store falls back to the workspace baseline.

## Model Used

OpenAI Codex, GPT-5, tool use, code execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with version and capability
details
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either linked existing issues or described the issue in the
PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [ ] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes
- [ ] 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-07-30 21:20:13 -07:00
Devin Foley 5ec7ce76e5 Upload company import packages as compressed zip uploads (fix large-company imports through Cloud) (#10531)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Company Import (#10507, hardened in #10523) lets a user upload a
company package on the Import page
> - The page expanded the user's `.zip` into a files map and POSTed it
as ONE inline JSON body — ~40MB for a real company because attachment
blobs get base64-inflated
> - On Paperclip Cloud that body travels browser → harness proxy →
tenant, where it truncated in transit → body-parser 400 → the browser
saw "Failed to fetch", and nothing imported
> - Two compounding causes: the giant inline body itself, and the board
async opt-in riding an `x-paperclip-cloud-*` header that the Cloud
harness strips as anti-spoofing (so async never engaged and the import
held one fragile synchronous connection)
> - This pull request uploads the raw compressed `.zip` as a multipart
request (about a third the size, already compressed) parsed server-side
into the same bundle the importer consumes, and moves the async opt-in
to a proxy-safe `?async=1`
> - The benefit is that a large-company import actually completes
through Cloud: a small compressed upload, a real async job that survives
dropped connections

## Linked Issues or Issue Description

- Refs #10507 / #10523 (Import/Export and its hardening). No open issue;
problem described above (large-company browser import through a proxy:
inline JSON body truncates → 400 → "Failed to fetch"; async opt-in
header stripped by the front door → async never engages).

## What Changed

- **Multipart zip transport.** The Import page uploads the raw `File` as
`multipart/form-data` (field `package`, import options in a JSON `meta`
field); the server unzips it into `{ rootPath, files }` and runs the
exact existing preview/import logic. The `application/json` inline path
is byte-identical for CLI/programmatic callers. Bare `application/zip`
(meta via `?meta=`) is also accepted for programmatic use.
- **Shared node zip reader.** `packages/shared/src/portability-zip.ts`
(node-only subpath, not re-exported to the browser bundle — same pattern
as `portability-hash.ts`); the CLI's `zip.ts` becomes a thin re-export.
Identical codec (STORE + DEFLATE via `inflateRawSync`, rejects data
descriptors/zip64).
- **Proxy-safe async signal.** `wantsAsyncImport` = `?async=1` (board
browsers, survives the harness) OR the existing
`x-paperclip-cloud-async-import` header (cloud tenants, set
server-side). The UI async client now uses `?async=1`. Backward
compatible.
- **Size + preflight.** New `PORTABLE_ZIP_UPLOAD_LIMIT_BYTES = 128MB`;
the inline 56MB preflight no longer gates the zip path (it shows the
compressed size instead). Async submit/poll/resume, the duplicate-guard
fingerprint (now over the resolved bundle), pause-on-import,
progress/error panels, and activation all apply to the multipart path.
- OpenAPI documents json + multipart + zip bodies and the `async` query
param.

## Verification

- Full typecheck chain (shared, server, ui, cli) clean.
- 152 tests across 8 files: new `portability-zip.test.ts`
(STORE/DEFLATE/base64-blob byte-exact round-trip, truncation throws,
data-descriptor rejection); `company-portability-routes.test.ts` +7
(multipart import+preview equals the inline bundle; async multipart
202→poll→success; board async via `?async=1` with no cloud header;
cloud-tenant async via header; sync fallback with neither; truncated-zip
400, nothing imported); `CompanyImport.test.tsx` asserts the local zip
sends the raw File and the inline preflight no longer blocks;
`openapi-routes.test.ts` green.
- NOT yet measured: the end-to-end browser upload through the live Cloud
harness — verified on staging after deploy before closing out.

## Risks

- Import semantics unchanged — only transport changed; the JSON inline
path is byte-identical, the cloud-tenant header async path untouched.
Multipart parsing is server-side (memory-bound: a ~13MB zip → ~30MB
files map, fine on the server).
- The bare `application/zip` path is programmatic-only and covered by
content-type dispatch but not a dedicated route test (the multipart path
is).

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic), Claude Code CLI,
extended thinking + tool use; root-caused against live logs/DB and the
harness proxy source.

## Checklist

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

> - Paperclip helps people run and govern AI agent work
> - Sandbox startup needs a safe place to add telemetry spans without
forcing OpenTelemetry on every run
> - This change adds a no-op span seam, so the startup path can accept a
tracer later and still stay inert now
> - The server gets a lazy tracer accessor, and the adapter timing
helper gets an injected tracer hook
> - The change keeps the default path free of OpenTelemetry and keeps
the existing startup event path unchanged
> - The benefit is a future-safe seam with no runtime change today

## Linked Issues or Issue Description

This PR addresses a feature gap in the sandbox startup path.

### Problem

Sandbox startup has no safe span seam. A direct OpenTelemetry import
would load telemetry packages on every run.

### Proposed Solution

Add a lazy tracer accessor in the server. Add an injected no-op tracer
seam in startup timing.

### Alternatives

Import OpenTelemetry directly in the startup path. Reject that path
because the default startup flow must stay inert.

## What Changed

- Added a lazy startup tracer accessor in
`server/src/instrumentation.ts`.
- Added an injected startup tracer seam in
`packages/adapter-utils/src/acpx-engine/startup-timing.ts`.
- Kept the startup event path unchanged.
- Kept `adapter-utils` free of OpenTelemetry imports.

## Verification

- `pnpm exec vitest run
packages/adapter-utils/src/acpx-engine/startup-timing.test.ts`
- `pnpm exec vitest run server/src/__tests__/instrumentation.test.ts`
- `tsc --noEmit` for `@paperclipai/adapter-utils` and
`@paperclipai/server`

## Risks

Low risk. The default tracer is a no-op, so the runtime path stays inert
until a later change injects a real tracer.

## Model Used

OpenAI GPT-5. Tool use and code execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with version and capability
details
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and found none
- [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 or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-30 18:23:44 -07:00
Devin Foley 276ae3a75d Harden company import: durable UI, async jobs, integrity guard, batched inserts (#10523)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Company Import/Export (#10507) moves whole companies between
instances as portability bundles
> - Real-world use on a large company (1,418 issues, ~10.6k comments)
surfaced a cluster of related failures: the import took hours and the
browser connection died while the server kept running, a retry silently
produced a second partial import, the progress/error UI gave no durable
signal, and a cloud-tenant user couldn't even open the companies
afterward
> - Root cause of the slowness: importBundle inserted every issue,
comment, and document as a separate round-trip to a network Postgres —
an N+1-over-network pattern
> - This pull request hardens the whole import path: durable
progress/error UI, an async server-side job so imports survive dropped
connections (with a duplicate-submit guard), a fail-closed guard against
incomplete payloads, and batched inserts that cut a large import from
hours to minutes
> - The benefit is that migrating a real, large company actually
completes, is legible while it runs, and can't half-import twice

## Linked Issues or Issue Description

- Refs #10507 (the Import/Export feature this hardens). Supersedes
#10513 (the progress/error-UI piece, folded in here). No open issue;
problem described above (large-company import: slow, connection-fragile,
silently duplicable, opaque UI).

## What Changed

- **Batched inserts (perf):** importBundle pre-generates entity ids in
JS and inserts in chunked multi-row statements, so children no longer
wait on parents' generated ids. A 1,418-issue import drops from ~15,600
insert statements to **82** (190×); benchmark below. Import semantics —
collision handling, pause-on-import,
label/blocker/monitor/attachment/embedded-asset handling, blob sha
verification — are unchanged (full portability suite green).
- **Async import jobs for board sessions:** the existing cloud-tenant
async job path opens to board sessions with per-actor job keys; the
import page submits, polls, and resumes watching after a reload or
dropped connection instead of holding one fragile request. A
non-terminal job blocks a duplicate submit (409 returns the running
job), preventing the double-import.
- **Fail-closed completeness guard:** an optional `expectedFileCount` on
inline imports; the server rejects (422 `import_payload_incomplete`) a
body carrying fewer files than declared, so a re-framed/short payload
fails loudly instead of half-importing.
- **Durable progress/error UI (was #10513):** persistent progress panels
with size-aware copy, persistent error panels with retry guidance, and
inline explanation when the preview button is disabled;
request-lifecycle guards so stale previews/imports can't publish or
detach.

## Verification

- `pnpm -r` typechecks (shared, server, ui) clean.
- `company-portability.test.ts` (76) +
`company-portability-routes.test.ts` (30) green — the import correctness
net — plus new `CompanyImport.test.tsx` async/resume/409 coverage and a
new batching regression test (a 50-issue import issues <50 issue-insert
statements; rows land unchanged).
- **Batching benchmark (embedded Postgres):** at 1,418 issues × 7
comments × 1 doc — 82 insert statements vs ~15,598 one-per-row (190×),
~1s wall-clock; a row-verifying run at that scale imports all 1,418
issues / 9,926 comments / 1,418 documents with unique identifiers and no
warnings (no rows dropped by chunking). Over a network DB the round-trip
reduction is the hours→minutes lever.
- What is NOT directly measured here: wall-clock against a real network
Postgres (that happens on a staging deploy); the local timing is
network-free.

## Risks

- Batching is the load-bearing change: it rewrites the import write
path. Mitigated by the unchanged 106-test correctness suite, a new
scale/row-integrity test, and per-writer transactions (a failure rolls
back its table group; not a single outer transaction across writers —
noted, correctness preserved).
- Async jobs are in-memory (lost on server restart → pollers 404 and can
resubmit); matches the pre-existing cloud-tenant job semantics.
- `expectedFileCount` is optional (older callers unaffected); over-count
is allowed, only under-count fails closed.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic), Claude Code CLI,
extended thinking + tool use; implementation across Fable 5 subagents
with live diagnosis against a running 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 and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-07-30 16:52:58 -07:00
DottaandPaperclip fcf66f3a91 feat(skills): add managed skill rename API (#9688)
## Thinking Path

> - Paperclip is the open source control plane people use to manage
AI-agent companies.
> - Company skills are reusable capabilities that operators install,
edit, assign, and materialize for agents.
> - Managed local skills currently lack a safe backend operation for
changing their display name and canonical slug/key together.
> - Treating rename as an ordinary save can leave duplicate records,
stale runtime materializations, or agent assignments pointing at the old
key.
> - This pull request adds a company-scoped managed-skill rename
contract, service operation, and REST endpoint with focused
authorization and activity logging.
> - The benefit is an atomic-enough, recoverable rename path that keeps
disk state, database identity, and agent skill assignments synchronized.

## Linked Issues or Issue Description

- Refs #2121
- Problem: managed company skills need a dedicated rename operation
rather than save-time duplication behavior.
- Expected behavior: renaming a managed skill updates its name, slug,
key, source directory, frontmatter, runtime materialization, and
assigned-agent references while preserving version pins.

## What Changed

- Added shared request/result types and Zod validation for managed skill
rename requests.
- Added `POST /api/companies/:companyId/skills/:skillId/rename` with
`skills.edit` policy checks and `company.skill_renamed` activity
logging.
- Restricted renames to Paperclip-managed local skills and added slug,
key, and target-directory conflict handling.
- Moved the managed directory, rewrote only the `SKILL.md` frontmatter
name, updated the database row, and rolled filesystem changes back when
persistence fails.
- Rewrote assigned agents' desired-skill keys while preserving pinned
version IDs and removed stale runtime materialization.
- Added focused route and service coverage for success, no-op, name-only
changes, conflicts, unsupported sources, assignment rewrites,
rollback-sensitive behavior, and runtime cleanup.
- Rejected multiline rename names before they can inject extra
`SKILL.md` frontmatter fields.

## Verification

- `pnpm exec vitest run
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/company-skills-routes.test.ts` — 106 tests passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.

## Risks

- Filesystem and database updates cannot share one native transaction;
the service stages filesystem changes and explicitly restores the
original directory and markdown when the database transaction fails.
- Renames intentionally reject catalog, remote, project-scanned, and
unmanaged local skills to avoid changing identities owned by external
sources.
- No database migration is required; the endpoint updates existing
company-skill and agent configuration fields.

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

## Model Used

- OpenAI Codex coding agent (exact underlying model ID and
context-window size were not exposed to this runtime), with reasoning,
repository tool use, code execution, and test execution. The rescued
source commit also records assistance from Claude Opus 4.8.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-30 15:29:31 -07:00
DottaandPaperclip efe8b3b707 feat(skills-catalog): add optional /simplified-english skill (#10410)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents draw writing behavior from installable skills in the shipped
skills catalog (`packages/skills-catalog`)
> - Agent-authored comments, plans, and documents are often wordy or
ambiguous, which slows human readers
> - A small, opt-in skill can set a clear house style for user-facing
prose without touching agent code
> - This pull request adds an optional `simplified-english` catalog
skill that tells agents to write user-facing comments, plans, and
documents in ASD-STE100 Simplified Technical English
> - The benefit is shorter, unambiguous, one-meaning-per-sentence
writing that readers understand on the first pass

## Linked Issues or Issue Description

<!-- Feature request (no public GitHub issue). Described inline per
CONTRIBUTING.md → "Link Issues or Describe Them In-PR". -->

**Problem or motivation**

Agent-authored user-facing text (issue comments, plans, documents) is
frequently long-winded, uses inconsistent vocabulary, and packs multiple
instructions into one sentence. Readers have to re-read it. There is no
shared, installable house style for clear technical writing.

**Proposed solution**

Add an optional content skill, `simplified-english`, to the shipped
catalog. It instructs agents to write user-facing comments, plans, and
documents using only ASD-STE100 Simplified Technical English (short
sentences, one instruction each, approved single-meaning words, active
voice, present tense). Orgs opt in by installing it.

**Alternatives considered**

Baking the guidance into every agent's base instructions (too broad, not
opt-in) or a bundled skill (would apply everywhere by default). An
optional skill keeps it opt-in per org.

**Roadmap alignment**

Additive, opt-in catalog content only; no core behavior change.

## What Changed

- Add
`packages/skills-catalog/catalog/optional/content/simplified-english/SKILL.md`
— a short optional skill instructing agents to write user-facing
comments, plans, and documents in ASD-STE100 Simplified Technical
English. Includes an "Approved words" section that identifies the
controlled vocabulary (the ASD-STE100 Dictionary) and gives concrete
house-choice substitutions.
- Regenerate `packages/skills-catalog/generated/catalog.json` via
`build:manifest` so the manifest includes the new skill.
- Pin the new catalog key in
`packages/skills-catalog/src/shipped-catalog.test.ts`.

## Verification

- `pnpm --filter @paperclipai/skills-catalog build:manifest` → wrote
manifest with the new skill.
- `pnpm --filter @paperclipai/skills-catalog validate` → "Catalog
manifest is valid".
- `pnpm --filter @paperclipai/skills-catalog test` → the catalog
set/count/key pinning tests pass with the new skill included.
- Confirmed the generated entry: key
`paperclipai/optional/content/simplified-english`, trustLevel
`markdown_only`, description within the 300-char budget cap.

## Risks

Low risk. Additive, markdown-only optional skill plus a regenerated
manifest and a pinning-test update. No runtime code, migrations, or
workflow changes. Not installed by default (`defaultInstall: false`).

## Model Used

Claude Opus 4.8 (1M context), extended thinking, with tool use / code
execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
Paperclip 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-30 15:28:07 -07:00
Devin Foley 916c13501f Replace host-to-host Cloud Sync with full-fidelity company Import/Export (#10507)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A company accumulates real state — issues, labels, blockers,
documents, work products, monitors, attachments, agents, routines — and
people need to move that state between instances: self-hosted to cloud,
cloud back to self-hosted, or plain backups
> - The experimental, flag-gated Cloud Sync transport (#6548) tried to
solve this host-to-host: the source pushed into a receiver over HTTPS
with a cross-instance consent/token handshake, which required the
destination to be publicly reachable and broke for common self-hosted
topologies (plain-HTTP LAN/VPN origins); the receiver half never landed
upstream at all
> - Meanwhile the portability bundle and the existing export/import
pages already move companies offline with none of those networking
constraints — but silently dropped labels, blockers, issue documents,
work products, monitors, and every attachment
> - This pull request removes the host-to-host transport and makes
Import/Export the single data-movement path: the pages become
first-class company-settings destinations, exports declare exactly what
they do not carry, and bundle schemaVersion 6 now carries all of the
above, with attachments as content-addressed sha256 blobs verified
before a single row is written
> - The benefit is a migration and backup flow that works between any
two instances with no reachability requirements, no cross-instance auth,
and no silent data loss

## Linked Issues or Issue Description

- Refs #6548 — the original Cloud Sync sender this PR supersedes and
removes.
- Related, not duplicates: #1697 (goals in the portability manifest —
orthogonal field addition), #954 (an earlier import/export +
skill-visibility proposal predating the current portability bundle).
- No open issue describes this directly, so in brief (feature-request
shape): **Problem** — moving a company between instances silently lost
labels (imports with label references actually hard-failed), blocker
relations, issue documents, work products, monitor state, and all
attachments, and the alternative Cloud Sync transport required the
destination to be publicly reachable over HTTPS plus a consent
handshake, which failed for typical self-hosted setups. **Desired
behavior** — one Import/Export flow in company settings that produces a
portable bundle carrying all of that data, tells the operator up front
what it cannot carry, imports with automations paused, and offers real
one-click activation afterwards.

## What Changed

- New export fidelity report (`GET
/api/companies/:companyId/export/fidelity`) + an "Export fidelity" panel
on the Export page listing anything a bundle will not include (now only:
approvals, cost history, activity history)
- Imports accept `pauseAutomations`; imported agents and routines land
paused, the import result reports created routines, and the Import page
ends in an activation panel that actually resumes selected
agents/activates routines
- Export and Import pages promoted into the company-settings nav; the
Cloud Upstream wizard, ux-lab page, and API client removed; the old
settings route redirects to Export
- Host-to-host transport removed: upstream-sync/receiver-client routes
and services, CLI `cloud connect`/`cloud push` + keypair store, the
shared upstream transfer contract, and the `enableCloudSync` flag;
migration `0196` drops the two experimental `cloud_upstream_*` sender
tables
- Bundle schemaVersion 6: labels (definitions + per-task names, remapped
by name on import), blocker relations (`blockedBy` slugs,
cycle-tolerant), issue documents (`tasks/<slug>/documents/<key>.md`),
work products (system refs nulled), monitors (notes/scheduledBy
restored, imported un-armed)
- Attachments travel as content-addressed `blobs/<sha256>` entries
(deduped; comment-scoped attachments re-link via comment index); every
blob is hash-verified **before any write**, so a corrupted bundle cannot
leave a partially imported company; both zip codecs now round-trip
extensionless/binary entries byte-exactly; the Import page preflights
the inline body limit and offers continue-without-attachments
- v5 (and older) bundles still import, with an informational warning;
bundles newer than v6 are rejected cleanly
- Docs: board-operator import/export guide, CLI README, README/ROADMAP
updated

## Verification

- `pnpm -r` typechecks (shared, db incl. migration numbering/safety
checks, server, ui, cli) and `pnpm check:token-gates` — clean
- Vitest: full server + shared sweep 4,888 passed / 1 skipped, with the
only 3 failures being pre-existing on `master` (2×
heartbeat-workspace-branch-containment, 1× workspace-runtime auto-port;
reproduced identically with this change stashed); ui + cli suites green;
the embedded-Postgres export-fidelity suite applies the full migration
chain including the new `0196` against a fresh database
- Live end-to-end on a scratch instance: seeded a company with labels, a
blocker pair, an issue document, a work product, a monitor, an agent, a
routine, and two binary attachments (one comment-scoped) → export →
import into a fresh company → labels remapped to new ids, blocker edge
and document restored, monitor un-armed with notes intact, attachments
byte-identical (sha256-compared through the API), agents/routines paused
→ activation panel resumed them; a v5-shaped bundle imported with only
the info warning; flipping one byte in a blob made the import 422 with
**zero** rows created
- Reviewer repro: create a company with a labeled issue + attachment →
Settings → Export → download → Settings → Import on another
company/instance → watch the preview, apply with "start paused", then
activate

## Risks

- Migration `0196` drops
`cloud_upstream_connections`/`cloud_upstream_runs` — experimental tables
behind a default-off flag; their connection/run history is intentionally
discarded
- Breaking removals are all of experimental, flag-gated surface:
`/api/upstream-sync/*` + `/api/cloud-upstreams/*` routes, `paperclipai
cloud connect|push`, and the `enableCloudSync` flag (stale keys in
stored instance settings parse harmlessly)
- Import remains non-atomic on mid-apply errors generally (pre-existing
behavior); the new blob verification specifically moved ahead of all
writes so tampered bundles cannot create partial state
- GitHub-sourced imports do not fetch `blobs/*` and skip attachments
with a warning

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic), via Claude Code CLI with
extended thinking, tool use, and subagent orchestration; implementation
and review split across Fable 5 subagents, with live end-to-end
verification against a running 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
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-07-30 11:37:00 -07:00
Nicky LeachandPaperclip d5b9f6c8c9 perf(sandbox): coalesce git-workspace stage-sync into one confined syncIn (#10488)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Cloud and sandbox agents need less round-trip overhead at workspace
start
> - The workspace start path now uploads the git history and the working
tree overlay in two separate sync operations
> - That adds extra mkdir, guard, upload, and rename work for the same
workspace bytes
> - This pull request merges those two uploads into one guarded sync
operation
> - The benefit is one sync round trip for the workspace bytes with the
same guard and login-shell contract

## Linked Issues or Issue Description

No public GitHub issue exists for this change.

### Problem

A git-backed workspace start uploads the git-history clone and the
working-tree overlay as two separate host-to-sandbox sync operations.

### Proposed Solution

Merge the two uploads into one sync operation. Keep both tar mappings
under the same confinement guard. Run the git-history extract first,
then the overlay extract, then optional cleanup.

### Alternatives Considered

Keep two sync operations. That keeps the current round-trip cost and
duplicates the guard, upload, and rename work.

### Roadmap Alignment

This change supports the roadmap work on cloud and sandbox agents by
reducing workspace start overhead.

## What Changed

- Merge the git-history tar and the working-tree overlay tar into one
sync operation.
- Run the git-history extract first, then the overlay extract, then
optional remove-deleted-paths cleanup.
- Keep both temporary tar targets under the runtime directory so the
first extract cannot delete the second tar before use.
- Keep the symlink-escape guard, login-shell contract, and file-mapping
checks on the merged file set.

## Verification

- `tsc --noEmit` on `@paperclipai/adapter-utils`
- `@paperclipai/adapter-utils` full project test suite: 354 pass / 4
skipped
- Orchestrator proof: one merged operation with both tars and two
ordered extract commands
- Orchestrator proof: the confinement guard covers both tar mappings
- Daytona plugin `plugin.test.ts`: 94 pass

## Risks

- Any regression in the extract order could change workspace start
behavior.
- Any regression in the merged guard could block valid uploads or miss
an escape attempt.

## Model Used

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-29 23:31:57 -07:00
Nicky LeachandPaperclip 674d71548a perf(sandbox): fold dead sandbox start round trips (bridge dirs + handle-cache seed) (#10485)
## Thinking Path

> - Paperclip is the control plane for AI agents.
> - Sandbox startup uses bridge directories and a Daytona workspace
handle.
> - The cold path made repeat directory creation calls and one avoidable
handle fetch.
> - Those calls add delay but do not change state.
> - This pull request folds the bridge directory setup into one exec,
removes redundant process-session setup, and seeds the Daytona handle
cache at acquire.
> - The benefit is fewer deterministic host-to-sandbox round trips and
faster cold starts.

## Linked Issues or Issue Description

- Problem: Cold sandbox start does extra directory creation work and
re-fetches a handle it already has.
- Expected result: The startup path should create each directory once
and reuse the fresh handle.
- Related PRs I found on GitHub: #9280, #9293.

## What Changed

- Added `makeDirs` to the bridge queue client and used one `mkdir -p`
exec for the callback bridge directories.
- Removed the two upfront `mkdir` execs for the process-session bridge
stdin and events directories.
- Seeded the Daytona sandbox handle cache at acquire so realize can
reuse the fresh handle.
- Reset the process-scoped cache in the compatibility test so the second
sync run sees the expected exec count.

## Verification

- `pnpm --filter @paperclipai/adapter-utils exec vitest run` - 351
passed, 4 skipped.
- `pnpm --filter @paperclipai/sandbox-provider-daytona exec vitest run`
- 91 passed.
- `pnpm --filter @paperclipai/adapter-utils exec tsc --noEmit` - clean.
- I checked `ROADMAP.md` for sandbox round-trip work. I found no
duplicate planned core work for this change.

## Risks

- Low risk. The change removes redundant calls and adds cache seeding.
- A wrong cache scope would hide the handle. The seed now checks the
lease scope and fails loudly.
- The daytona package `tsc --noEmit` still depends on SDK types that are
not installed in this isolated workspace. CI covers that path.

## Model Used

- OpenAI GPT-5, tool-using, with code execution in the current
workspace.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with capability details
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either linked public issues or described the issue in-PR
- [x] I have not referenced internal or 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-07-29 22:23:45 -07:00
Nicky LeachandPaperclip 6a267e0328 feat(sandbox): stage referenced projects into the run sandbox (#10469)
## Thinking Path

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

## Linked Issues or Issue Description

No public issue exists.

Related PR: #10448.

## What Changed

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

## Verification

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

## Risks

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

## Model Used

OpenAI Codex, GPT-5, tool use enabled.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-29 16:09:57 -07:00
Nicky LeachandPaperclip 15ce70dc18 feat(server): thread plural referenced-project workspaces through run prep (#10448)
## Thinking Path

> - Paperclip coordinates work for autonomous companies.
> - A run needs a workspace view before execution starts.
> - That view now needs to cover one anchor project and more referenced
projects.
> - Those extra workspaces must stay separate and must not change the
anchor path when the feature stays off.
> - This PR threads plural workspace data through run prep behind a
default-off kill switch.
> - It also keeps extra project workspaces isolated and makes the
realization contract round-trip the new shape.
> - The benefit is safer run prep for referenced projects without
changing the current default path.

## Linked Issues or Issue Description

This PR does not link a public GitHub issue.
It follows the internal run-prep task for plural referenced-project
workspaces.

Problem:
- Run prep resolves the anchor project today, but it does not yet carry
each referenced project into the run workspace view.
- That gap blocks runs that need a second repo or sibling project during
preparation.

Proposed solution:
- Thread a plural workspace result through run prep.
- Keep the anchor path unchanged when the kill switch is off.
- Resolve each referenced project into its own managed checkout
directory when the flag is on.

Alternatives considered:
- Keep one shared workspace and layer the extra repos into it. Rejected
because it would blur isolation and make failures harder to bound.
- Upload the extra workspaces immediately. Rejected because this PR only
prepares the data path.

Roadmap alignment:
- This change sits in the workspace and sandbox path.
- It matches the roadmap work on workspace strategy and cloud or sandbox
agents.

## What Changed

- Added `additionalWorkspaces[]` to the run workspace result.
- Split workspace resolution into an anchor path and an optional
referenced-project path behind `PAPERCLIP_MULTI_PROJECT_WORKSPACE_SYNC`.
- Kept per-project failure isolation so one bad clone does not stop the
run.
- Keyed managed workspace directories by `projectId` so sibling
workspaces stay separate.
- Added `additionalSources[]` to the workspace realization request and
kept read and write paths backward compatible.
- Added tests for the anchor-only path, the new workspace shape, and the
per-project directory rule.

## Verification

- `pnpm --filter @paperclipai/server run typecheck`
- `pnpm --filter @paperclipai/shared run typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-project-env.test.ts` 21/21
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/workspace-runtime.test.ts` 98/98

## Risks

Low risk. The new path stays behind a default-off kill switch, so the
anchor flow does not change when the flag is off.
The main risk is a bad referenced project clone. That case now drops
only the affected project and keeps the run alive.
The shared type change also needs every consumer to use the new array
field where extra workspaces matter.

## Model Used

OpenAI Codex (GPT-5; exact internal model ID not exposed in this
environment; tool use enabled)

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-29 14:27:05 -07:00
Nicky LeachandPaperclip 7083c275c8 refactor(sandbox): retire the dead noProfile flag from the exec path (#10461)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The sandbox exec path starts agent commands and passes runtime
options to the server and plugin layers
> - This path kept a noProfile flag after the exec wrappers stopped
sourcing a login profile
> - The flag no longer changed behavior, so it left dead API surface in
the protocol and runtime helpers
> - This pull request removes that dead flag from the plugin protocol,
the server drivers, and the managed-runtime helpers
> - It also updates the tests and points the agent runtime README at the
sandbox requirements file
> - The benefit is a smaller and clearer exec-path contract with no
behavior change

## Linked Issues or Issue Description

- No public GitHub issue exists.

### What happened?

The sandbox exec path kept a `noProfile` field after the exec wrappers
stopped sourcing a login profile.

### Expected behavior

The plugin protocol, server drivers, and managed-runtime helpers should
not expose or forward a dead field.

### Steps to reproduce

1. Run a managed-runtime command through the sandbox exec path.
2. Inspect the protocol payload and runtime helper inputs.
3. Observe that `noProfile` is present even though it no longer changes
behavior.

### Paperclip version or commit

`60c7da86fc7a6c1dbf37bbcd86e25ecaaff01607`

### Deployment mode

Built from source (pnpm dev / pnpm build)

### Additional context

This pull request removes the dead field, updates the affected tests,
and updates the README note for the sandbox profile path.

## What Changed

- Removed noProfile from the plugin protocol and the server exec-path
call sites.
- Updated the managed-runtime helpers to use the narrower exec-path
contract.
- Updated the affected tests and added the README pointer to
SANDBOX-REQUIREMENTS.md.

## Verification

- `git grep -n "noProfile" -- packages/ server/` returns zero matches.
- `tsc --noEmit` passed for `@paperclipai/adapter-utils`,
`@paperclipai/plugin-sdk`, and `@paperclipai/server`.
- `command-managed-runtime.test.ts` passed: 22/22.
- `environment-runtime.test.ts` passed: 24/24.

## Risks

- Low risk. The flag was already a no-op.
- A hidden external caller may still send the removed field.

## Model Used

- OpenAI GPT-5, tool-enabled.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-29 14:21:57 -07:00
Nicky LeachandPaperclip d6e235cbcf perf(sandbox-providers): drop nvm sourcing from exec wrappers (#10443)
## Thinking Path

> - Paperclip keeps agent work on a controlled execution plane.
> - Sandbox exec wrappers run on the hot path for agent commands.
> - The current change removes the explicit `nvm.sh` load step from
those wrappers.
> - The sandbox image already restores PATH through profile startup.
> - This pull request keeps profile sourcing where the wrapper still
needs it and drops only the `nvm.sh` load step.
> - The result is a smaller command path with the same node and agent
CLI resolution.

## Linked Issues or Issue Description

No public GitHub issue exists for this change.

Problem:
The sandbox exec wrappers spent extra time sourcing `nvm.sh` before each
command.
The sandbox image already restores PATH in
`/etc/profile.d/00-restore-env.sh`, so that explicit `nvm.sh` work was
redundant.

Proposed solution:
Remove the `nvm.sh` source step from all six wrappers.
Keep the profile sourcing that the provider still needs for PATH setup.

Alternatives considered:
Keep the existing shell setup and accept the launch cost.
That keeps the current behavior, but it leaves the hot path slower than
needed.

Roadmap alignment:
This change keeps the sandbox command path small and predictable.
It does not change the adapter contract or the node resolution rules.

## What Changed

- Removed `nvm.sh` sourcing from all six sandbox exec wrappers.
- Kept profile sourcing where the provider still needs it for PATH
setup.
- Switched Modal to a non-login shell because the script now sources
profiles itself.
- Updated wrapper tests to assert that built commands do not source
`nvm.sh`.

## Verification

- Local TypeScript typecheck passed in each changed package.
- Focused provider tests passed for Daytona, E2B, Modal, exe-dev,
Cloudflare bridge, and adapter-utils.
- One Daytona test failure is pre-existing and unrelated to this change.

## Risks

- This change alters shell startup for sandbox exec paths.
- A provider that depends on implicit shell setup may need a follow-up.
- The current tests cover command shape, but they do not cover every
runtime shell path.

## Model Used

OpenAI Codex, GPT-5, 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 or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-29 12:40:16 -07:00
DottaandPaperclip 170c1e5adb fix(claude-local): trust Paperclip URLs in allowlist sandbox (#10438)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Local adapters can confine agent processes with filesystem and
network sandbox policies
> - Allowlist confinement must still let an agent reach Paperclip's own
control plane and managed MCP servers
> - The Codex local adapter already marks those runtime-owned endpoints
as trusted, but the Claude local adapter did not
> - As a result, allowlisted Claude runs could not call their own
Paperclip API unless operators duplicated runtime URLs manually
> - This pull request mirrors the trusted-URL wiring in the Claude local
adapter and adds a regression test at the process-execution boundary
> - The benefit is that confined Claude agents retain their required
control-plane access without broadening the operator-managed network
allowlist

## Linked Issues or Issue Description

Refs #447

No exact public issue was found. The underlying bug is:

- **Observed:** with `claude_local` configured for local-process
`networkScope: "allowlist"`, the sandbox options omitted the
runtime-owned Paperclip API and MCP server URLs. Requests to those
endpoints could therefore be denied by confinement.
- **Expected:** Paperclip's own API URL and managed MCP server URLs are
passed as trusted sandbox targets, matching `codex_local` behavior.
- **Steps to reproduce:** configure a local Claude agent with allowlist
network confinement, omit the runtime Paperclip API URL from the
operator allowlist, and have the agent call its injected
`PAPERCLIP_API_URL`.
- **Version:** `ca92f727c5` (`origin/master` at branch creation).
- **Deployment mode:** local-process sandbox confinement in a local
deployment.

## What Changed

- Added the injected Paperclip API URL and runtime MCP server URLs to
`claude_local` sandbox `networkTrustedUrls`, filtering empty values.
- Added a unit test proving allowlist sandbox construction trusts
`PAPERCLIP_API_URL`.

## Verification

- `pnpm exec vitest run
packages/adapters/claude-local/src/server/execute.acp-fallback.test.ts`
- `pnpm --filter @paperclipai/adapter-claude-local typecheck`

## Risks

- Low risk. The change only affects confined local Claude processes and
trusts runtime-owned endpoints already injected by Paperclip.
- Operator-configured `networkAllowlist` behavior is unchanged; the
added URLs use the sandbox's separate trusted-target path.

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

## Model Used

- OpenAI Codex based on GPT-5 (exact serving snapshot and context-window
size are not exposed), with reasoning, repository inspection, shell tool
use, code editing, and test execution.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-29 13:23:09 -05:00
dependabot[bot] 5b34d265a4 build(deps): bump @agentclientprotocol/claude-agent-acp from 0.59.0 to 0.63.0 (#10302)
Bumps
[@agentclientprotocol/claude-agent-acp](https://github.com/agentclientprotocol/claude-agent-acp)
from 0.59.0 to 0.63.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/agentclientprotocol/claude-agent-acp/releases">@​agentclientprotocol/claude-agent-acp's
releases</a>.</em></p>
<blockquote>
<h2>v0.63.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.62.0...v0.63.0">0.63.0</a>
(2026-07-27)</h2>
<h3>Features</h3>
<ul>
<li>Update to claude agent sdk v0.3.220 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/921">#921</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/4c7b89718306229254879e5045a183233b5ed073">4c7b897</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>Only resolve a denied tool call the client was told about (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/923">#923</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/8f67b6a92bec24ae43b3dfbd087fe35df0531857">8f67b6a</a>),
closes <a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/918">#918</a></li>
<li>Report tool_progress heartbeats against the tool call they describe
(<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/916">#916</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/5559ba890ca614cdaa189500aba65d81cc4cd51a">5559ba8</a>)</li>
<li><strong>tools:</strong> key Bash terminal metas off the announced
tool_use id (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/917">#917</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/d0604140f907adbf9747f26a070690926e1de82d">d060414</a>)</li>
</ul>
<h2>v0.62.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.61.0...v0.62.0">0.62.0</a>
(2026-07-24)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps:</strong> Bump <code>@​hono/node-server</code> from
1.19.14 to 1.19.15 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/908">#908</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/51936049e175274de8e0fd90bc1be2af988c3ca6">5193604</a>)</li>
<li><strong>deps:</strong> Bump media-typer from 1.1.0 to 1.1.1 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/909">#909</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/5d35001563ffd702a29c3bac3b7ca134e40c77f8">5d35001</a>)</li>
<li><strong>deps:</strong> Bump the minor group with 2 updates (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/900">#900</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/809d41c6b7c9e7ba3cb5b206d00793a70edba64a">809d41c</a>)</li>
<li><strong>deps:</strong> Bump the minor group with 2 updates (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/907">#907</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/14d06273c01ad4ae944912e36700f6b5599c4c4d">14d0627</a>)</li>
<li>Update to claude-agent-sdk 0.3.218 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/904">#904</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/8cbaf97254576089a3b5ee6ae222fb763003c01d">8cbaf97</a>)</li>
</ul>
<h2>v0.61.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.60.0...v0.61.0">0.61.0</a>
(2026-07-22)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps:</strong> Bump actions/setup-node from 6.4.0 to 7.0.0
(<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/897">#897</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/d9bd36d8b06764d63656d0387ad9430ec9fcb27d">d9bd36d</a>)</li>
<li><strong>deps:</strong> Update to
<code>@​anthropic-ai/claude-agent-sdk</code> 0.3.217 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/899">#899</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/edf3af043b6d00e5caca2cc81a2285c477c8b2ab">edf3af0</a>)</li>
</ul>
<h2>v0.60.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.59.0...v0.60.0">0.60.0</a>
(2026-07-20)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps:</strong> Update to claude-agent-sdk 0.3.215 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/890">#890</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/92548f043547b0ddac95921ece020e69f7c12c5f">92548f0</a>)</li>
<li>implement configurable LLM providers (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/853">#853</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/82cd692e500eedec182f817b18cebb005c4b98ce">82cd692</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>parse Agent/Task trailers without regex (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/879">#879</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/06c3d7bdbd8cc9415c8cabac060a50e0951c758b">06c3d7b</a>)</li>
<li>remove ~15s stall on session/new and model switch by seeding the
context window synchronously (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/894">#894</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/ff9b96d462831b1c3b96722ea20215ff6e529cb1">ff9b96d</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/agentclientprotocol/claude-agent-acp/blob/main/CHANGELOG.md">@​agentclientprotocol/claude-agent-acp's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.62.0...v0.63.0">0.63.0</a>
(2026-07-27)</h2>
<h3>Features</h3>
<ul>
<li>Update to claude agent sdk v0.3.220 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/921">#921</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/4c7b89718306229254879e5045a183233b5ed073">4c7b897</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>Only resolve a denied tool call the client was told about (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/923">#923</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/8f67b6a92bec24ae43b3dfbd087fe35df0531857">8f67b6a</a>),
closes <a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/918">#918</a></li>
<li>Report tool_progress heartbeats against the tool call they describe
(<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/916">#916</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/5559ba890ca614cdaa189500aba65d81cc4cd51a">5559ba8</a>)</li>
<li><strong>tools:</strong> key Bash terminal metas off the announced
tool_use id (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/917">#917</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/d0604140f907adbf9747f26a070690926e1de82d">d060414</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.61.0...v0.62.0">0.62.0</a>
(2026-07-24)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps:</strong> Bump <code>@​hono/node-server</code> from
1.19.14 to 1.19.15 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/908">#908</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/51936049e175274de8e0fd90bc1be2af988c3ca6">5193604</a>)</li>
<li><strong>deps:</strong> Bump media-typer from 1.1.0 to 1.1.1 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/909">#909</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/5d35001563ffd702a29c3bac3b7ca134e40c77f8">5d35001</a>)</li>
<li><strong>deps:</strong> Bump the minor group with 2 updates (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/900">#900</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/809d41c6b7c9e7ba3cb5b206d00793a70edba64a">809d41c</a>)</li>
<li><strong>deps:</strong> Bump the minor group with 2 updates (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/907">#907</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/14d06273c01ad4ae944912e36700f6b5599c4c4d">14d0627</a>)</li>
<li>Update to claude-agent-sdk 0.3.218 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/904">#904</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/8cbaf97254576089a3b5ee6ae222fb763003c01d">8cbaf97</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.60.0...v0.61.0">0.61.0</a>
(2026-07-22)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps:</strong> Bump actions/setup-node from 6.4.0 to 7.0.0
(<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/897">#897</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/d9bd36d8b06764d63656d0387ad9430ec9fcb27d">d9bd36d</a>)</li>
<li><strong>deps:</strong> Update to
<code>@​anthropic-ai/claude-agent-sdk</code> 0.3.217 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/899">#899</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/edf3af043b6d00e5caca2cc81a2285c477c8b2ab">edf3af0</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.59.0...v0.60.0">0.60.0</a>
(2026-07-20)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps:</strong> Update to claude-agent-sdk 0.3.215 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/890">#890</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/92548f043547b0ddac95921ece020e69f7c12c5f">92548f0</a>)</li>
<li>implement configurable LLM providers (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/853">#853</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/82cd692e500eedec182f817b18cebb005c4b98ce">82cd692</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>parse Agent/Task trailers without regex (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/879">#879</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/06c3d7bdbd8cc9415c8cabac060a50e0951c758b">06c3d7b</a>)</li>
<li>remove ~15s stall on session/new and model switch by seeding the
context window synchronously (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/894">#894</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/ff9b96d462831b1c3b96722ea20215ff6e529cb1">ff9b96d</a>)</li>
<li>Silence missing PostToolUse callbacks (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/895">#895</a>)
(<a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/1887ada215b27bb1025d9b7696a46ae7a4ac0f7a">1887ada</a>),
closes <a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/889">#889</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/15979bba7907484ee22111cdc33b79b0bdcd452d"><code>15979bb</code></a>
chore(main): release 0.63.0 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/922">#922</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/8f67b6a92bec24ae43b3dfbd087fe35df0531857"><code>8f67b6a</code></a>
fix: Only resolve a denied tool call the client was told about (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/923">#923</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/5559ba890ca614cdaa189500aba65d81cc4cd51a"><code>5559ba8</code></a>
fix: Report tool_progress heartbeats against the tool call they describe
(<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/916">#916</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/d0604140f907adbf9747f26a070690926e1de82d"><code>d060414</code></a>
fix(tools): key Bash terminal metas off the announced tool_use id (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/917">#917</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/4c7b89718306229254879e5045a183233b5ed073"><code>4c7b897</code></a>
feat: Update to claude agent sdk v0.3.220 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/921">#921</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/8663170f18c0b92a68972d8b56a91c064ab3df60"><code>8663170</code></a>
Add structured Bash titles and nested subagent transcripts (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/881">#881</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/53a0c36ce3b0b76929d11d8b9565e319da745608"><code>53a0c36</code></a>
chore(main): release 0.62.0 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/901">#901</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/c91f943f8663d847587d8f1b90b30630dde0c345"><code>c91f943</code></a>
<code>@​anthropic-ai/claude-agent-sdk</code> 0.3.218 -&gt; 0.3.219 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/912">#912</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/5d35001563ffd702a29c3bac3b7ca134e40c77f8"><code>5d35001</code></a>
feat(deps): Bump media-typer from 1.1.0 to 1.1.1 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/909">#909</a>)</li>
<li><a
href="https://github.com/agentclientprotocol/claude-agent-acp/commit/51936049e175274de8e0fd90bc1be2af988c3ca6"><code>5193604</code></a>
feat(deps): Bump <code>@​hono/node-server</code> from 1.19.14 to 1.19.15
(<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/908">#908</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.59.0...v0.63.0">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-29 09:33:08 -07:00
Michael NguyenandPaperclip 4eace88f6b feat(adapter-claude): add Claude Opus 5 to the static model fallback (#10327)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents pick their model from a dropdown in agent config, populated
per-adapter by `listAdapterModels()` → each adapter's live provider
catalog merged over a static fallback list
> - For `claude_local`, newer model ids only reach the dropdown via the
live Anthropic `/v1/models` fetch, which needs a server
`ANTHROPIC_API_KEY`, a <5s round-trip, non-Bedrock mode, and account
entitlement; on any miss it silently falls back to the static `models`
array
> - Claude Opus 5 (`claude-opus-5`) is generally available — Anthropic
lists it as the recommended model for complex agentic coding and
enterprise work — but it was absent from that static fallback, so it
appeared only when live discovery happened to succeed
> - This pull request adds `claude-opus-5` to the `claude_local` static
model list so it is selectable regardless of the live-discovery path
> - The benefit is a consistent, reliable dropdown that surfaces the
current GA Opus flagship without depending on a flaky live fetch

## Linked Issues or Issue Description

No public GitHub issue. The bug is described inline following the
bug-report template:

**What happened**
The `claude_local` agent-config model dropdown omitted Claude Opus 5.
`claude-opus-5` was missing from the adapter's static fallback `models`
array (`packages/adapters/claude-local/src/index.ts`), so it only
surfaced when the live Anthropic `/v1/models` discovery happened to
succeed.

**Expected behavior**
Claude Opus 5 is a shipped, generally-available flagship (Anthropic's
recommended model for agentic coding) and should always be selectable in
the dropdown, independent of whether live discovery succeeds.

**Steps to reproduce**
1. Run the server without a working live Anthropic `/v1/models` path (no
`ANTHROPIC_API_KEY`, Bedrock mode, a discovery timeout, or a cache
miss).
2. Open agent config for a `claude_local` agent and inspect the model
dropdown.
3. Observe that `claude-opus-5` is absent because the static fallback
list omitted it.

**Deployment mode**
Self-hosted / local adapter (`claude_local`); the server process reads
`ANTHROPIC_API_KEY` from its environment.

## What Changed

- Added `{ id: "claude-opus-5", label: "Claude Opus 5" }` to the
`claude_local` static `models` fallback. Placed after the current
5-family entries and above the legacy `claude-opus-4-7`, so
`claude-opus-4-8` stays the default (index 0) option.
- Added an explicit regression assertion in
`server/src/__tests__/adapter-models.test.ts` that `claude-opus-5` is
present in the `claude_local` fallback when live discovery is
unavailable.

## Verification

- `pnpm -C server exec vitest run src/__tests__/adapter-models.test.ts`
— **17/17 pass**, including the new `claude-opus-5` assertion and the
existing `models[0] === "claude-opus-4-8"` default invariant (unaffected
— Opus 5 is inserted lower in the list).
- Change is a single static-data addition plus a test assertion; no
control-flow change.

## Risks

- Low risk. Pure additive change to a fallback list; no control-flow
change. Worst case is an id a given account isn't entitled to, which the
existing "current"/manual-model UI paths already tolerate.
- Note for reviewers: a sibling PR adds `claude-sonnet-5` to the same
static array (near `claude-opus-4-8`). Both are complementary "refresh
the static list to current GA" changes; whichever merges second may need
a one-line merge resolution in
`packages/adapters/claude-local/src/index.ts` and the matching test
assertion block.

## Model Used

Claude (Anthropic), model id `claude-opus-4-8` (Opus 4.8), extended
thinking + tool use, run as the Paperclip CTO agent.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-28 16:25:25 -05:00
DottaandPaperclip 487e33b8b6 fix(codex-local): resolve GPT-5.6 model metadata at source (#9780)
## Thinking Path

> - Paperclip is the open source control plane people use to manage AI
agents for work
> - The `codex_local` adapter runs OpenAI's Codex CLI through direct CLI
and ACP execution lanes
> - The adapter defaulted to the bare `gpt-5.6` alias while the bundled
ACP Codex version lacked GPT-5.6-family metadata
> - Default and legacy-configured runs therefore emitted
fallback-metadata warnings and could use generic context limits
> - This pull request upgrades the bundled Codex ACP dependency, selects
the concrete `gpt-5.6-sol` model, and normalizes the legacy alias in
both execution lanes
> - The benefit is correct model metadata without hiding genuine stderr
or transcript warnings

## Linked Issues or Issue Description

Related public PRs: Refs #9342, Refs #9352, and Refs #9382. This PR is
narrower: it upgrades bundled Codex metadata and normalizes the legacy
bare alias in both execution lanes.

**Bug report**

### What happened

Default `codex_local` runs, and agents still configured with the bare
`gpt-5.6` model, print a model-metadata fallback warning and use generic
context-window limits.

Root cause: the ACP lane bundled a Codex release predating
GPT-5.6-family metadata, while Paperclip's default and advertised model
used the bare `gpt-5.6` alias for which Codex publishes no metadata.

### Expected behavior

A default Codex run resolves to a concrete model slug with published
metadata and does not emit a fallback-metadata warning.

### Deployment mode

Self-hosted/local `codex_local` adapter.

## What Changed

- Upgraded `@agentclientprotocol/codex-acp` from `^1.1.0` to `^1.1.4`
- Changed `DEFAULT_CODEX_LOCAL_MODEL` from `gpt-5.6` to `gpt-5.6-sol`
- Removed the bare alias from advertised models and listed concrete
GPT-5.6 Fast-mode variants
- Added `normalizeCodexModel()` and applied it in both CLI and ACP
execution lanes
- Updated adapter docs, Storybook fixtures, and regression tests
- Preserved warning visibility; no stderr, transcript, or log filtering
changed

## Verification

- `pnpm --filter @paperclipai/adapter-codex-local typecheck`
- `pnpm check:token-gates`
- `cd packages/adapters/codex-local && pnpm exec vitest run` — 205 tests
passed
- `cd server && pnpm exec vitest run
src/__tests__/adapter-models.test.ts` — 17 tests passed
- Confirmed the PR diff excludes `pnpm-lock.yaml` and
`.github/workflows/**` as required by repository policy
- Confirmed `.github/workflows/pr.yml` regenerates and uploads the PR
lockfile artifact before downstream `pnpm install --frozen-lockfile`
steps

## Risks

Low risk. The behavior change is scoped to `codex_local` model
selection. Existing concrete model IDs pass through unchanged; only the
legacy bare `gpt-5.6` alias is rewritten. Dependency resolution may
select a newer compatible `codex-acp` release within the declared range,
so CI remains the final compatibility gate.

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

## Model Used

- Original implementation: Anthropic Claude Opus 4.8 (`claude-opus-4-8`,
1M context, tool use and code execution)
- Conflict resolution and PR preparation: OpenAI GPT-5.5 (`gpt-5.5`,
Codex CLI coding agent, high-reasoning tool use and code execution;
host-managed context window)

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-28 16:15:08 -05:00
Devin Foley dc12197cce fix: prevent duplicate built-in agents and self-heal reconciliation (#10223)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Every company is auto-provisioned a set of built-in agents (e.g. the
Summarizer), and a startup reconciler keeps that set correct across
every company on boot.
> - Provisioning marks these agents with
`metadata.paperclipBuiltInAgent.key`, but nothing in the database
enforced one active agent per `(company, key)` —
`provision()`/`ensure()` did a check-then-insert with no guard.
> - Two concurrent server processes (e.g. a `tsx watch` double-boot)
could both read "no summarizer exists" and both insert, leaving a
company with duplicate built-in agents plus paired orphan pending
`hire_agent` approvals.
> - That data blemish then became a recurring outage: `findSingleAgent`
throws on >1 marked row, and because the throw escaped
`reconcileBuiltInAgentsOnStartup`'s sequential loop, **every company
after the affected one was silently skipped** on each boot — no
auto-provisioning, no default grants — until manual DB surgery.
> - This pull request closes the race at the database level and makes
reconciliation self-healing and fault-isolated.
> - The benefit is that concurrent provisioning can no longer create
duplicates, and even pre-existing duplicates are resolved automatically
instead of bricking startup reconciliation for unrelated companies.

## Linked Issues or Issue Description

- [x] I searched the GitHub PR list (open and recently closed) for
similar PRs and confirmed this is not a duplicate.

No public GitHub issue exists; describing the bug in-PR (bug-report
shape):

**What happened**

A dev instance booted with two concurrent server processes. Both ran
built-in agent provisioning for the same company at the same time, and
the check-then-insert in `provision()`/`ensure()`
(`server/src/services/built-in-agents.ts`) let both writers see "no
summarizer exists" and each create one — the company ended up with two
identical Summarizer agents (identical `paperclipBuiltInAgent` markers)
plus two paired pending `hire_agent` approvals.

From then on, **every** server boot logged:

```
ERROR: startup reconciliation of built-in agents failed
       Multiple built-in agents found for summarizer (built_in_agent_duplicate_instance)
```

because `findSingleAgent` throws on >1 marked row rather than resolving
the duplicate. Worse, `reconcileBuiltInAgentsOnStartup` loops companies
sequentially and the throw escaped the loop, so every company *after*
the affected one was silently skipped on every boot.

**Expected behavior**

1. Concurrent provisioning must not create duplicate built-in agents
(there was no DB uniqueness constraint on the marker key per company).
2. Reconciliation should be resilient: if duplicates exist anyway,
self-heal (keep the oldest row, terminate the newer dupe, cancel its
orphan pending `hire_agent` approval), and never let one bad company
abort reconciliation for the rest.

**Steps to reproduce**

- Race two `provision(companyId, "summarizer")` calls for a company with
board approval for new agents enabled (or simulate a double-boot); both
insert.
- Restart the server → startup reconciliation error fires, companies
later in the loop are never reconciled.

## What Changed

**Part 1 — stop creating duplicates**

- Migration `0192_built_in_agent_unique_marker` adds a **partial unique
index** on `(company_id, metadata->'paperclipBuiltInAgent'->>'key')`
where the marker exists and `status != 'terminated'`. It first resolves
any pre-existing duplicates (keep oldest by `created_at`, terminate
newer dupes, cancel their orphan pending `hire_agent` approvals, revoke
their API keys) so the index can be created on already-affected
instances.
- `provision()`/`ensure()` now catch the losing race's `23505` unique
violation (walking the driver's wrapped cause chain) and re-resolve to
the winning row instead of surfacing the error.

**Part 2 — resilient reconciliation**

- `findSingleAgent` self-heals: keeps the oldest marked row, terminates
the newer duplicates, and cancels each one's orphan pending `hire_agent`
approval (idempotent) instead of throwing.
- `reconcileBuiltInAgentsOnStartup` isolates per-company failures in
both loops so one bad company can't abort reconciliation for the rest;
it surfaces a `companyFailures` count in the startup log.
- Adds `approvalService.cancel()` for system-initiated cancellation of
an orphan approval.

## Verification

- `pnpm --filter @paperclipai/db run check:migrations` → numbering +
safety checks pass.
- `packages/db` migration test (real embedded Postgres) — seeds
pre-index duplicate state, runs the migration, asserts dupes resolved +
index enforced: **1 passed**.
- `server` `built-in-agents.test.ts` — self-heal, concurrent races
(plain and board-gated), and startup
self-heal-without-aborting-later-companies: **34 passed**.

```
pnpm --filter @paperclipai/db exec vitest run src/built-in-agent-unique-marker-migration.test.ts
pnpm --filter @paperclipai/server exec vitest run src/__tests__/built-in-agents.test.ts
```

## Risks

- **Migration safety**: the migration mutates data (terminates duplicate
rows, cancels their orphan pending approvals, revokes their API keys)
before creating the index. It keeps the oldest row per `(company, key)`
and only touches non-terminated marked rows; the destructive step is
covered by the migration test and the safety-check baseline. On a clean
instance it is a no-op cleanup followed by `CREATE UNIQUE INDEX IF NOT
EXISTS`.
- Otherwise low risk: the unique index is partial (excludes terminated
rows, so re-provisioning after a termination stays possible), and the
conflict handling degrades gracefully to re-resolving the existing
winner.

## Model Used

Claude Opus 4.8 (`claude-opus-4-8`), 1M context window, extended
thinking, with tool use.
2026-07-28 11:12:58 -07:00
dependabot[bot] f9034ab3ca build(deps-dev): bump @types/node from 22.19.21 to 22.20.1 (#10304)
Bumps
[@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)
from 22.19.21 to 22.20.1.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 10:48:45 -07:00
dependabot[bot] 4881f548be build(deps): bump @cursor/sdk from 1.0.19 to 1.0.24 (#10298)
Bumps [@cursor/sdk](https://github.com/cursor/cursor) from 1.0.19 to
1.0.24.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/cursor/cursor/commits">compare view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~luist18">luist18</a>, a new releaser for
<code>@​cursor/sdk</code> since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@cursor/sdk&package-manager=npm_and_yarn&previous-version=1.0.19&new-version=1.0.24)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 10:03:36 -07:00
9ff7e1caf3 perf(adapter-utils): content-hash-skip process-session remote script write + lock inbound round-trip count (#10377)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The sandbox runtime depends on efficiently staging files into remote
environments
> - The process-session bridge was rewriting a static remote script on
every start, even when the remote copy already matched
> - That caused unnecessary round trips and made the startup path
noisier than it needed to be
> - This pull request adds a hash-skip path for the process-session
script write and keeps the existing bridge-entrypoint hash gate on the
same helper
> - It also locks in the reduced inbound round-trip count so the
collapse from the prior PR cannot silently regress
> - The benefit is fewer remote execs on warm starts and a stronger
guard against performance regressions

## Linked Issues or Issue Description

- Refs: https://github.com/paperclipai/paperclip/pull/10354

### Feature request-style description

**Subsystem affected**
- Cross-cutting (packages/adapter-utils and sandbox staging helpers)

**Problem or motivation**
- The process-session bootstrap path was rewriting a static remote
script on every start even when the remote file already matched the host
content.
- That added avoidable remote execs and latency to warm starts.
- The inbound staging collapse from the previous PR also needed a
regression lock so it could not silently drift back to extra round
trips.

**Proposed solution**
- Route the process-session remote script upload through the existing
hash-skip helper used by the sandbox callback bridge entrypoint.
- Keep the bridge-entrypoint behavior unchanged by delegating it to the
same helper.
- Add a regression test that asserts the inbound staging path still
collapses to the expected round-trip count.

**Alternatives considered**
- Keep the existing unconditional write path and accept the extra execs
on warm starts. Rejected because it preserves avoidable overhead.
- Add a second specialized helper just for process-session scripts.
Rejected because the bridge-entrypoint logic already solved the same
problem and should stay aligned.

**Roadmap alignment**
- This fits the roadmap items around cloud/sandbox agents and enforced
outcomes by reducing bootstrap waste and preventing performance
regressions.
- It does not introduce new product surface area, telemetry, or
user-facing workflow changes.

**Additional context**
- The implementation preserves the existing upload/verify/rename
behavior when the remote content differs and only skips the write when
the hash already matches.
- PR #10354 collapsed the inbound staging path; this PR preserves that
win.

## What Changed

- Added a shared hash-skip helper for remote text-file synchronization
so unchanged content skips the write path after a single remote hash
check.
- Switched the process-session remote script upload to use that helper,
preserving the existing write/verify/rename behavior when the remote
content differs.
- Refactored the sandbox callback bridge entrypoint sync to use the same
helper without changing its observable behavior.
- Added a mocked-native-runner regression test that locks the inbound
staging path to one `client.syncIn` round-trip per step and zero direct
write/run execs.

## Verification

- `tsc --noEmit`
- `pnpm --filter @paperclipai/adapter-utils exec vitest run`
- Full adapter-utils vitest sweep: 338 passed / 4 skipped

## Risks

- Low risk: the helper changes when a write occurs, not the script
contents or the remote execution surface.
- A hash-check failure now fails loudly instead of silently rewriting,
which is safer but could surface provider-side issues earlier than
before.
- The regression test is intentionally specific to the current collapsed
staging path, so future architectural changes will need test updates.

## Model Used

OpenAI Codex (GPT-5, tool-using coding agent)

## Checklist

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

---------

Co-authored-by: Harold Kim <harold@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-28 08:33:12 -07:00
Nicky Leach 341993ebae feat(sandbox-runtime): route all inbound staging through client.syncIn (Codex home -> native uploadFiles; delete usesCustomProvision gate) (#10354) 2026-07-28 07:22:18 -07:00
Nicky LeachandPaperclip 7797995038 perf(plugin-daytona): opt-in no-profile fast path for default-PATH execs (#10352)
## Thinking Path

> - Paperclip is the control plane for autonomous AI companies
> - The Daytona adapter turns tasks into shell commands and manages
execution overhead
> - Many short-lived exec calls still pay for login-shell profile
sourcing even when the binary already resolves on the sandbox default
PATH
> - That extra startup work adds latency on the hot path for repeated
command execution
> - This pull request adds an opt-in fast path that skips profile
sourcing only when the caller explicitly requests it and the command
does not need shell initialization
> - The benefit is lower per-call latency for eligible commands without
changing the conservative default behavior for commands that need the
profile

## Linked Issues or Issue Description

This change does not reference a public GitHub issue. It follows the
same Daytona startup-speed work as merged PR #10335 and narrows the
execution path for eligible commands while keeping the default
login-shell behavior intact.

## What Changed

- Added an optional `noProfile` flag to
`PluginEnvironmentExecuteParams`.
- Refactored Daytona login-shell script assembly so the profile and nvm
sourcing block is omitted only on the explicit fast path.
- Preserved environment prefixing, `cd`, shell quoting,
`NONINTERACTIVE_GIT_ENV`, stdin handling, and `durationMs` behavior on
both paths.
- Added regression tests for the fast path omission, the preserved
execution parameters, and the default profile-sourcing path.

## Verification

- `pnpm --filter @paperclipai/sandbox-provider-daytona exec vitest run
src/plugin.test.ts`
- `pnpm --filter @paperclipai/plugin-sdk tsc --noEmit`
- Reverted the guard locally to confirm the two behavior tests fail
again, then restored the change.

## Risks

- If a caller opts into `noProfile` for a command that depends on shell
initialization, the command can fail to resolve its binary.
- The API comment and opt-in design keep that risk narrow; the default
path remains unchanged.

## Model Used

OpenAI GPT-5 (Codex tool-using coding agent)

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 21:47:05 -07:00
Nicky LeachandPaperclip 0ccba45e4d feat(sandbox-providers): native providers honor postUploadCommands (Daytona executes; Kubernetes executes) (#10347)
## Thinking Path

> - Paperclip is the control plane for autonomous AI companies, so
runtime handoffs have to preserve the exact behavior an agent asked for.
> - The sandbox-provider layer is where uploaded files and follow-up
commands become a real in-sandbox operation.
> - The provider-delegable sync-in seam already exists from the prior
PR; without this follow-up, native providers can still accept
command-bearing uploads and silently drop the commands.
> - That is a fail-open gap for native sandbox providers, because the
file transfer succeeds while the intended post-upload work never runs.
> - This pull request teaches Daytona and Kubernetes to execute
`postUploadCommands` in order, inside the sandbox, after the files land.
> - The benefit is consistent and safer sync semantics: native providers
either run the commands as requested or fail fast instead of pretending
the operation completed fully.

## Linked Issues or Issue Description

This PR builds on the previously merged provider-delegable sync-in seam
and closes the remaining gap for native providers that still dropped
`postUploadCommands`.

Problem:
- A sync operation could include ordered `postUploadCommands`, but a
native provider could finish the file upload and skip the commands
entirely.
- That creates fail-open behavior for command-bearing uploads,
especially when the caller relies on the provider to execute the
follow-up action in the sandbox.

Proposed fix:
- Execute `postUploadCommands` inside the sandbox after file placement.
- Preserve the provided command order.
- Fail fast on the first non-zero exit or timeout.
- Keep command execution verbatim and confine any provided `cwd` under
the workspace root.

Related public PR:
- Refs: #10340

## What Changed

- Daytona `performSyncIn` now executes ordered `postUploadCommands`
through the existing `executeCommand` seam.
- Kubernetes `performSyncIn` now executes ordered `postUploadCommands`
through its streaming pod exec path.
- Added workspace confinement for provided `cwd` values and defaulted
missing `cwd` to the remote root.
- Added tests covering the new post-upload command execution behavior in
both provider packages.

## Verification

- Latest validation recorded on the handoff branch:
`@paperclipai/plugin-sdk` and `@paperclipai/plugin-kubernetes`
typechecks passed.
- Daytona Vitest: `63/63` passing in `plugin.test.ts`.
- Kubernetes Vitest: `195/195` passing, including `file-sync.test.ts`.
- `git log --oneline origin/master..HEAD` showed a single expected
commit on the branch.

## Risks

- Command execution semantics are stricter now, so malformed commands or
a bad `cwd` will fail the sync instead of being ignored.
- The change makes provider behavior more explicit, which can surface
previously hidden failures in callers that assumed commands were
optional.
- Timeout behavior may differ slightly between providers, so the failure
mode is intentionally fail-fast.

## Model Used

OpenAI Codex, GPT-5-based coding agent with tool use enabled.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 21:17:16 -07:00
DottaandPaperclip 1cd09ed555 perf(heartbeat): reuse task sessions for issue-scoped timer wakes and bound control-plane write retries (#10350)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents make progress in heartbeats: the server wakes an agent
session, it does a slice of work on an issue, records a disposition, and
exits
> - Benchmarking identical coding tasks run as Paperclip-orchestrated
agent pairs vs invoking the same agent harness directly measured a
1.8–2.2× wall-clock slowdown for the Paperclip pairs, dominated by
per-heartbeat orchestration overhead rather than model time
> - Two contributors stood out: (1) since PF-4 (#4838) every
`heartbeat_timer` wake starts a brand-new task session, so continuation
work on a specific issue repays the full session-start and
re-orientation cost on every heartbeat; (2) in degraded environments
agents burn many tool calls retrying the same failing control-plane
write before giving up
> - This pull request reuses the task session for issue-scoped timer
wakes (keeping the PF-4 fresh-session rule only for unscoped exploratory
wakes, which were the original context-bloat case) and adds a
bounded-retry rule to the wake prompt and core skill: after 2
consecutive failures of the same control-plane write, stop retrying it
for the rest of the heartbeat and rely on the adapter/runtime status
channel
> - The benefit is materially less wall-clock and token overhead per
heartbeat while preserving the context-bloat protection PF-4 was added
for

## Linked Issues or Issue Description

Refs #4838 (merged PF-4 change whose reset rule this refines), Refs
#5287, Refs #1907 (related timer-heartbeat session work).

No public GitHub issue exists for the slowdown itself; bug-report
fields:

- **What happened:** Agent pairs orchestrated through Paperclip
heartbeats complete identical task sets 1.8–2.2× slower (wall-clock)
than the same harness invoked directly. Profiling attributed the gap to
per-heartbeat orchestration overhead: every timer wake discards the task
session (full session start + re-orientation), and in degraded
environments agents repeatedly retry the same failing control-plane
write.
- **Expected behavior:** Heartbeat orchestration should add minimal
wall-clock overhead on top of the underlying harness; issue-scoped
continuation work should not pay a fresh-session tax each interval.
- **Steps to reproduce:** Run a fixed benchmark task set once through
Paperclip issue heartbeats and once via direct harness invocation with
the same model/config; compare wall-clock totals.
- **Version/commit:** master @ 3d23c3b2c3, self-hosted deployment.

## What Changed

- `server/src/services/heartbeat.ts`: `shouldResetTaskSessionForWake`
now resets only for `heartbeat_timer` wakes with no derivable task key
(unscoped exploratory wakes). Issue-scoped timer wakes reuse the issue's
task session. `describeSessionResetReason` updated to stay in exact
agreement.
- `server/src/__tests__/heartbeat-timer-wake-session-reset-pf4.test.ts`:
new cases for scoped vs unscoped timer wakes, plus the scoped case added
to the reset/reason agreement invariant.
- `packages/adapter-utils/src/server-utils.ts`: wake prompt template and
execution contract gain a bounded-retry rule — after 2 consecutive
failures of the same control-plane write, stop retrying it for the rest
of the heartbeat, continue useful work, report the failure in the final
response, and use the adapter/runtime status channel as the sanctioned
fallback.
- `packages/adapter-utils/src/server-utils.test.ts`: asserts the new
prompt lines are present in both the template and the rendered wake
prompt.
- `skills/paperclip/SKILL.md`: documents the same bounded write-retry
rule in the core Paperclip skill.

## Verification

- `node_modules/.bin/vitest run
packages/adapter-utils/src/server-utils.test.ts` — 1 file, 83 tests
passed
- `cd server && node_modules/.bin/vitest run
src/__tests__/heartbeat-timer-wake-session-reset-pf4.test.ts` — 1 file,
14 tests passed
- Both run on this branch rebased onto current master (3d23c3b2c3)

## Risks

- Behavioral shift: issue-scoped timer wakes now reuse sessions, so a
long-lived issue session can grow across heartbeats. Mitigated by
keeping the PF-4 reset for unscoped wakes (the originally observed bloat
case) and by existing session compaction.
- Prompt/skill text changes alter agent guidance; the new rule is scoped
narrowly to repeated failures of the same control-plane write.
- No migrations, no API or schema changes, no dependency changes.

## Model Used

- Claude (Anthropic) — `claude-fable-5` (Fable 5), extended reasoning
with tool use, driven via Claude Code / Claude Agent SDK.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 22:01:29 -05:00
Nicky LeachandPaperclip 3d23c3b2c3 perf(plugin-daytona): cache the started sandbox handle per lease (#10335)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Daytona sandbox adapter spends time on repeated per-exec sandbox
lookups during a lease
> - That repeated lookup is pure overhead once the started sandbox
handle is already known and trusted for the lease
> - The adapter still needs strict isolation and fail-closed behavior
because the handle is an authenticated compute object, not an inert
value
> - This pull request memoizes the started sandbox handle per lease in a
process-scoped cache, and now advances freshness only after successful
reuse so failed commands cannot suppress stale-handle refreshes
> - The benefit is lower provider-get latency on the hot path while
keeping resume safety, teardown safety, and observability intact

## Linked Issues or Issue Description

This is a Daytona performance fix, not a standalone public GitHub issue.

### Problem / Motivation

- Repeated `client.get(sandboxId)` calls on the sandbox hot path
re-fetch a handle that is already started and trusted for the current
lease.
- The extra provider round-trip is pure overhead on repeated exec, sync,
resume, and interactive-cancel flows.
- Cache freshness also has to be tied to successful reuse, or a failed
command can make a stale sandbox look freshly used.

### Proposed Solution

- Cache the started `Sandbox` handle in process memory, keyed by a
non-secret composite lease scope.
- Enforce strict identity checks and eviction on release, destroy,
interactive cancel, and resume-sentinel mismatch.
- Block teardown cleanup until active lease operations finish so
delete/stop cannot race in-flight execute or sync work.
- Advance freshness only after successful execute, sync, or resume
reuse, so failed operations do not mask an auto-stopped sandbox.
- Preserve `getDurationMs` in exec metadata so provider-get latency
remains observable.

### Alternatives Considered

- Keep fetching the sandbox on every exec path.
- Cache only by bare lease id.

### Roadmap Alignment

- This change is part of the Daytona performance work and narrows
per-call overhead without changing the public adapter contract.

## What Changed

- Memoized the started Daytona `Sandbox` handle in a process-scoped
cache keyed by a composite lease scope.
- Added fail-closed identity checks so cache hits and single-flight
populate paths reject mismatched sandbox ids.
- Evicted cached handles on release, destroy, interactive cancel, and
resume sentinel mismatch.
- Blocked release, destroy, and interactive cancel teardown cleanup
until active lease operations drain.
- Advanced cache freshness only after successful execute, sync, and
resume reuse.
- Kept `getDurationMs` in exec metadata so provider-get latency remains
observable.
- Expanded the Daytona plugin tests to cover same-lease reuse,
cross-scope isolation, eviction paths, rejected populate handling,
concurrent single-flight behavior, cached-resume sentinel revalidation,
teardown cancellation safety, and failed-execute freshness handling.

## Verification

- `pnpm exec vitest run --config vitest.config.ts` in
`packages/plugins/sandbox-providers/daytona` — 81/81 passing, including
the teardown-cancel, snapshot-capture, syncIn-cancel, and failed-execute
freshness regressions.
- `git rev-parse origin/perf/daytona-sandbox-handle-cache` matched the
authorized submit SHA `528158f998fa88bb0748300f156b38d86d6589cd` before
the fixup commits.
- `git log --oneline
origin/master..origin/perf/daytona-sandbox-handle-cache` shows the
expected focused Daytona changes.
- GitHub duplicate/related PR search and ROADMAP review were completed
before opening the PR.
- Remote CI and Greptile completed successfully after this description
was updated; the PR is now ready for board handoff.

## Risks

- The cache is process-scoped, so correctness depends on the eviction
paths staying complete.
- A bug in the scope key or identity checks could leak reuse across the
wrong lease boundaries, but the implementation fails closed on id
mismatches.
- Teardown now waits for active operations to drain, so any missed
activity bookkeeping could delay cleanup instead of racing it.
- Freshness updates now happen after success, which is safer, but it
means any missed success-path call would trigger an extra refresh rather
than silently masking staleness.

## Model Used

OpenAI GPT-5 via Codex, tool-using coding agent; exact context window
not surfaced in the workspace.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 19:14:53 -07:00
Devin FoleyandPaperclip c274f10abc feat(server): computed owner instance-admin elevation for cloud-managed instances, behind platform floors (#10343)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Cloud-managed instances authenticate tenant users through a
trusted-header path (`resolveCloudTenantActor`) that deliberately never
grants `instance_admin`, so every tenant user is company-scoped
> - On a dedicated (single-owner) managed instance that leaves the
paying owner unable to administer their own instance: instance settings,
the environments admin surface, and the custom sandbox image flow are
all instance-admin gated (the environments UI can't even show the
provider/image of the platform sandbox because the restricted read view
blanks `config` entirely)
> - Re-granting the old blanket `instance_user_roles` row would repeat
the mistake the shared-pool hardening fixed: DB rows go stale, resurrect
via restores, and elevate through every auth path
> - This pull request elevates only the stack `owner`, computed per
request at the trusted-header boundary behind a new managed-tier feature
flag, and ships that elevation together with code floors on the
platform-owned surfaces an instance admin must not control on a managed
instance
> - The benefit is that dedicated-stack owners can administer their own
instance while platform credentials, execution policy, backups, and
runtime code-install stay platform-owned, and self-hosted behavior is
unchanged

## Linked Issues or Issue Description

No public GitHub issue exists for this change; the underlying issue is
described here following the feature-request template.

### Problem or motivation

- On cloud-managed instances, tenant users resolved from trusted headers
are always company-scoped. For dedicated instances with a single paying
owner, the owner cannot reach any instance-admin surface of their own
instance (instance settings, environments administration, custom image
setup), and the restricted environment read view hides even structural
fields like the sandbox provider and image.
- The previous hardening intentionally removed blanket elevation (and
purges stale `instance_user_roles` rows on every trusted-header
authentication). That protection must not regress for shared
multi-tenant pools.

### Proposed solution

- Owner-only, computed, flag-gated elevation plus code floors on
platform-owned surfaces, in one PR so the elevation can never ship
without the floors.

### Alternatives considered

- Re-inserting an `instance_user_roles` row for owners (the
pre-hardening model): rejected — DB rows go stale, survive restores, and
elevate through every auth path; #7525 removed exactly this.
- Widening only the environments read view without any elevation:
rejected — it fixes one screen but still leaves a dedicated-instance
owner unable to administer instance settings or custom images.
- Elevating additional stack roles (`member`/`admin`/`support`):
rejected — only the owner has an ownership claim over the whole
instance; other roles stay company-scoped.

### Roadmap alignment

- Extends the shipped "Cloud deployments" roadmap work (multi-tenant
isolation, company-scoped cloud tenants, managed-instance bootstrap)
without overlapping planned core items, and leaves self-hosted behavior
unchanged.

## What Changed

- **New feature key** `enableOwnerInstanceAdmin` (`packages/shared`):
boolean flag in `instanceExperimentalSettingsSchema`, catalog tier
`managed`, `cloudDefault: true`, `selfHostedDefault: false`. Inert on
self-hosted instances — the elevation path only exists behind the cloud
tenant trust token.
- **Computed elevation** (`server/src/middleware/auth.ts`):
`resolveCloudTenantActor` now returns `isInstanceAdmin: true` only when
the trusted-header stack role is `owner` **and** the flag is enabled.
The flag is resolved through the instance-settings service so the
managed-config overlay applies (the control plane can disable elevation
fleet-wide without touching tenant databases; a DB row edit or restore
cannot resurrect it). Resolution fails closed on settings read errors.
The `instance_user_roles` never-insert and the per-request stale-row
purge are byte-identical. `member`/`admin`/`support` stack roles stay
company-scoped.
- **Authorization guard split**
(`server/src/services/authorization.ts`): the blanket-allow now trusts
the actor's *computed* `isInstanceAdmin` flag (only the attested
resolver can set it for `cloud_tenant` actors) while keeping the
`instance_user_roles` DB lookup excluded for `cloud_tenant` — a stale or
hand-inserted role row still elevates nothing.
- **Floor F1 — platform environment credentials**
(`server/src/routes/environments.ts`): on cloud-managed instances,
platform-provisioned environment rows (`managedByPaperclip` marker, plus
the legacy managed-Kubernetes marker) use a single floored view for
every reader on all environment routes (list, get, create, update,
delete responses): `envVars` are never echoed and credential-shaped
`config` keys (reusing the managed-config
`SECRET_LIKE_CONFIG_KEY_PATTERN`) are dropped — for **all** actors
including instance admins — while structural config (provider, image,
template, region, …) and the managed markers stay visible. This also
fixes the environments UI for managed sandboxes, which previously lost
the provider/image entirely in the restricted view. The floor also
covers writes: `PATCH /environments/:id` and `DELETE /environments/:id`
on a platform-provisioned row are rejected (403,
`environment_platform_managed`) for every actor including instance
admins, and the guard binds to the persisted row's markers so a patch
cannot strip the managed marker to lift the floor. The one recovery path
is a metadata-only PATCH that solely clears the marker keys
(null/false), for rows stamped through the old unrestricted API before
the markers became reserved — and it never applies to a row whose slot
markers are live platform state: the single local row
(`environments_local_driver_idx`), which `ensureLocalEnvironment` adopts
and stamps on cloud-managed instances from every caller (company
creation, the heartbeat, run orchestration), and the single marked
sandbox row (`environments_managed_sandbox_idx`) while a managed-sandbox
bootstrap path is configured (managed-config `environments` section or
`PAPERCLIP_EXECUTION_MODE=kubernetes`) and the provisioner therefore
adopts and refreshes it on every boot. Clearing a live slot row's
markers would let the next write reclassify it as tenant-managed and
bypass the floor; conversely, when no sandbox provisioning path is
configured the platform holds no claim on any sandbox row, so a platform
marker there is stale by definition and the recovery patch applies.
Every marker outside a live slot is clearable, so no legacy row is ever
locked permanently. Custom-image setup and probes on the platform
sandbox stay available to instance admins — those are the owner-facing
flows this elevation exists for. The marker keys themselves are
reserved: client create/update payloads that set `managedByPaperclip` or
`managedKubernetesSandbox` are rejected (422,
`environment_platform_marker_reserved`) on cloud-managed instances, so a
tenant row can never be stamped platform-provisioned through the API and
self-locked behind the write floor (the provisioner writes markers at
the service layer, not through these routes). Tenant-created
environments are otherwise unaffected.
- **Floor F2 — executionMode**
(`server/src/routes/instance-settings.ts`): on cloud-managed instances,
`PATCH /instance/settings/general` rejects writes that would change
`executionMode` (403, `execution_mode_platform_managed`). Same-value
echoes pass so settings forms that submit the full general-settings
object keep working. The boot-time execution-policy bootstrap path is
untouched (it calls the service directly).
- **Floor F3 — manual database backups**
(`server/src/routes/instance-database-backups.ts`): floored off on
cloud-managed instances (403, `database_backups_platform_managed`);
backups are platform-owned there, and the result would also echo a
server-side filesystem path.
- **Floor F4 — adapter code install** (`server/src/routes/adapters.ts`):
`POST /adapters/install` and `POST /adapters/:type/reinstall` are
floored off on cloud-managed instances (403,
`adapter_install_platform_managed`). Adapter packages execute in the
server process, so a runtime install would let an instance admin read
the platform trust anchors out of the process environment. This mirrors
the existing bundled-only plugin install floor; adapter code on managed
instances comes bundled with the platform image.

## Instance-admin surface audit

Before widening who can hold `isInstanceAdmin`, every
instance-admin-gated surface in `server/src` was enumerated and reviewed
for whether its response or side effects could echo process environment
values or platform credentials (tenant trust token, JWT signing keys,
database connection strings, provider API keys): 29 distinct gate
definitions covering ~90+ call sites, in four groups — sole
instance-admin gates (12), instance-admin-or-company-permission gates
(10), response-shaping/scope-widening sites (6), and the central
`allow_instance_admin` short-circuit in the authorization service (58
`decide()` call sites).

Findings and dispositions:
- **Environment read/write responses** exposed platform sandbox
`envVars`/credential-shaped config to instance admins → closed by floor
F1.
- **Manual backup trigger** echoed a server filesystem path and triggers
a platform-owned operation → closed by floor F3.
- **Adapter install/reinstall** loads externally fetched code into the
server process (indirect, complete env exposure) → closed by floor F4.
The sibling plugin-install path already had a bundled-only floor on
managed instances and needed no change.
- **Token-minting surfaces** (gateway tokens, custom-image
terminal/connection tokens) mint credentials scoped to the instance's
own resources, not platform trust anchors → acceptable for an
owner-admin of a dedicated instance; unchanged.
- All remaining gated surfaces return ordinary instance-scoped business
data; none echo `process.env` or platform secrets directly. OAuth client
secrets are referenced by env-var *name* only; SSH private keys are
stored as secret refs before persistence and are not echoed.

Operational note for managed platforms: this model assumes the process
environment of a managed instance holds only that instance's own
credentials. Platform operators should keep provider credentials
per-instance (never fleet-shared) since an instance admin ultimately
controls in-process code on their own instance.

## Verification

- `pnpm vitest run server/src/middleware/cloud-tenant-actor.test.ts` —
resolver matrix: owner × flag on/off, flag via managed overlay
(on-over-DB-off and off-over-DB-on), member/admin/support × flag on,
no-token self-hosted, fail-closed settings read, purge still runs and no
role row is ever inserted (14 tests).
- `pnpm vitest run server/src/__tests__/authorization-service.test.ts` —
computed flag elevates a `cloud_tenant` actor; a stale
`instance_user_roles` row still never does; `session` actors unchanged
(full suite, embedded Postgres).
- `pnpm vitest run server/src/__tests__/environment-routes.test.ts` —
F1: no secret echo to admins on get/list, structural config visible to
restricted readers, platform-row PATCH/DELETE rejected for admins
(including a marker-stripping patch), marker-clear recovery allowed for
stale legacy rows and for a marked sandbox row when no provisioning path
is configured, but refused on the managed local row and on the sandbox
slot row under a managed-config `environments` entry or the forced
kubernetes execution mode, client marker-stamping creates/patches
rejected, tenant rows still readable and writable, self-hosted
read+write regression (60 tests).
- `pnpm vitest run server/src/__tests__/environment-service.test.ts` —
`ensureLocalEnvironment` adopts a pre-existing local row on
cloud-managed instances (marker stamped, other metadata preserved,
idempotent — no rewrite on re-ensure) and leaves self-hosted rows
untouched (22 tests, embedded Postgres).
- `pnpm vitest run server/src/__tests__/instance-settings-routes.test.ts
server/src/__tests__/instance-database-backups-routes.test.ts` — F2
change-vs-echo matrix incl. self-hosted regression; F3 floor for both
admin shapes (32 tests).
- `pnpm vitest run server/src/__tests__/adapter-routes-authz.test.ts` —
F4 floor; self-hosted install/reinstall behavior unchanged (existing
cases).
- `pnpm vitest run server/src/__tests__/first-admin-claim.test.ts
server/src/__tests__/bootstrap-claim-routes.test.ts
server/src/__tests__/managed-config.test.ts
server/src/__tests__/health.test.ts
server/src/__tests__/instance-settings-managed-overlay.test.ts
server/src/services/managed-environments.test.ts
server/src/services/execution-policy-bootstrap.test.ts` — first-admin
bootstrap gate and managed-config behavior unchanged (91 tests).
- `pnpm vitest run packages/shared/src/feature-catalog.test.ts` —
catalog/schema sync tests cover the new key (selfHostedDefault must
equal the schema default).
- `pnpm run typecheck` — all 31 workspace projects clean.

## Risks

- Self-hosted behavior is unchanged: every floor binds to
`isCloudManagedInstance()` (tenant trust token present), the new flag
defaults off with no elevation path, and regression tests pin the
self-hosted branches.
- The elevation is fail-closed and stateless: turning the flag off
(managed overlay or DB) de-elevates on the next request; there is no
role row to clean up and restores cannot resurrect elevation.
- On a cloud-managed instance a pre-existing unmarked local row is
adopted (stamped `managedByPaperclip`) by the next ensure and becomes
platform-owned — the intended managed-product semantic: the platform
owns the single local slot. Self-hosted instances are untouched.
- F1 widens restricted readers' view of platform-provisioned rows from
fully blanked `config`/`metadata` to structural-only `config` plus
markers. Platform-delivered config is guaranteed secret-free by the
managed-config contract (secret-shaped keys fail startup), and the floor
re-drops secret-shaped keys defensively.
- One extra instance-settings read per trusted-header request for
owner-role actors (the resolver already performs several queries per
request).

## Model Used

Claude Fable 5 (Anthropic) — model id `claude-fable-5`, extended
thinking enabled, agentic tool use via Claude Code; read-only explore
subagents on the same model were used for the surface audit sweep.

## 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
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 18:58:31 -07:00
DottaandPaperclip c3bd0c5d50 feat(skills): add beta releases for the core Paperclip skill (#10228)
## Thinking Path

> - Paperclip is the open source control plane people use to organize
and operate AI-agent companies.
> - Agent behavior depends partly on the bundled Paperclip core skill
synchronized into each runtime.
> - The existing database and runtime plumbing already supports
immutable skill-version snapshots and per-agent version selections, but
no product workflow exposed that capability.
> - Replacing the live bundled skill globally would make champion
adoption risky and difficult to compare across agents.
> - This pull request adds an experimental, instance-level beta-skills
gate plus a repository release registry, immutable seeded releases,
enforcement, and a per-agent release picker.
> - The benefit is controlled per-agent evaluation of frozen core-skill
releases while the default-off path remains behaviorally unchanged.

## Linked Issues or Issue Description

### Subsystem affected

Cross-cutting: `server/`, `ui/`, `packages/db`, and `packages/shared`.

### Problem or motivation

Paperclip needs a safe way to evaluate improved versions of its core
operating skill without globally replacing the live default. Today the
version-snapshot and per-agent pin plumbing exists, but operators cannot
use it. A global replacement would make regressions difficult to contain
and would prevent controlled comparisons across agents.

### Proposed solution

Add a default-off instance experiment that exposes immutable, named
core-skill releases. When enabled, operators can pin each agent to a
seeded release; when disabled, every agent resolves the live default
while saved pins remain intact. Validate pinned writes at the API
boundary, gate reads at runtime, and expose the selection in the agent
Skills tab.

### Alternatives considered

- **Replace the bundled core skill globally:** rejected because it
changes every agent at once and provides no rollback/isolation boundary.
- **Ship releases as separate skills:** rejected because releases are
versions of one core capability, not independently enabled skills.
- **Store release snapshots only outside the repository:** rejected
because repository provenance and hashes make builds reproducible and
reviewable.

### Roadmap alignment

This extends the Skills Manager / Skill Studio direction in `ROADMAP.md`
by making core-skill versions operable per agent. It does not duplicate
another open implementation PR; GitHub searches found no related
`enableBetaSkills` change.

### Additional context

The feature remains experimental and default off. The V7 champion was
selected through a multi-model evaluation process, and the frozen
release contents are verified by SHA-256 below.

## What Changed

- Added the default-off instance-level `enableBetaSkills` experimental
flag.
- Added `skills-releases/paperclip/` with the ordered release registry
and frozen `v0` plus `v7-roster` snapshots.
- Added release metadata to `company_skill_versions` and idempotent
release seeding. The migration was planned as `0191`, then renumbered to
`0192` because current `master` claimed `0191` before final rebase.
- Added read-time gating and write-time validation so disabled instances
always resolve the live default and reject pinned-version writes.
- Added the per-agent Release picker in the agent Skills tab, including
responsive layout and beta-pin state.
- Kept `EDITS.md` out of the release registry and PR diff.

### V7 Adoption Evidence

- Paid roster: 6 models, 94-case suite.
- Result: 553/564 pass-within-2, mean 92.17/94, versus the P2 baseline
of 544/564.
- Reference model improved 84→91; maximin improved 84→90.
- Final report:
https://pages.paperclip.ing/skills/optimization/paperclip/pap-14624-p3-final-20260721/

### Provenance

- `v7-roster` is the Phase 1 champion plus additions-only edits
E107–E112. Per-edit rationale remains in the evals repository at
`source/v7-roster/EDITS.md` and is deliberately excluded from this PR.
- `v0` is the `skills/paperclip` tree from commit `ea66ea81`.
- Champion selection was accepted on July 21, 2026 via board card
`9c304fc2` (PAP-14624 G3).
- This delivery mechanism was accepted on July 24, 2026 via plan
revision `2367abd2` (PAP-14858).

### QA Evidence

- P4 QA matrix comment `b7f40522-4e9b-4a3a-9821-28e86fe1a987`: all 6
acceptance criteria passed.
- Automated QA matrix: 166 tests passed with 0 failures, including real
filesystem materialization and full SHA-256 assertions.
- UI QA exercised the real agent Skills tab at desktop and mobile widths
with the experimental flag both on and off.

## Verification

- `pnpm check:token-gates`
- Focused beta-release matrix: 169 tests passed across shared
validators, server services/routes/heartbeat behavior, instance settings
UI, and release picker UI.
- `pnpm -r typecheck`
- `pnpm build`
- `pnpm test:run`: server and UI partitions passed; one CLI doctor test
inherited temporary AWS credentials from the agent heartbeat and
expected no static credentials. The isolated rerun with
`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`
unset passed 8/8.
- V7 SHA-256:
- `SKILL.md`:
`53ab290489684cbf116fdd1406a95f6b6f53c9c36358b1bf8bfeae481e253575`
- `references/cases.md`:
`3b821f59064a7761091020a14819a8d787131f24029748563d6c0e1be7e6eaec`
- `references/workflows.md`:
`69747bd6e05f7e3673d1e67b07ff295df1869c05e1fd029804d5fa9177db92cd`
- Confirmed 49 changed files, no `pnpm-lock.yaml`, no workflow changes,
and no `EDITS.md`.

## Risks

- **Migration:** low-to-moderate risk. Three nullable columns and one
partial unique index are added idempotently; existing rows remain valid.
- **Behavior:** low risk while the flag is off because read-time
resolution forces the live default and saved pins are preserved but
inactive.
- **Frozen content:** release snapshots intentionally diverge from
future live skill edits; provenance and hashes make that divergence
explicit and reproducible.
- **UI:** low risk. The picker only renders for the bundled core skill
when the experimental flag is enabled and seeded releases exist.

> This extends the existing Skills Manager / Skill Studio direction
described in `ROADMAP.md`; it does not duplicate another open
implementation PR. The GitHub PR search found no related
`enableBetaSkills` change.

## Model Used

- OpenAI Codex using `gpt-5.5` with reasoning and
terminal/code-execution tools; context-window size is not exposed by
this runtime. Earlier implementation commits also record Claude Opus 4.8
assistance where applicable.

## 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
- [ ] I have not referenced internal/instance-local Paperclip issues or
links (required governance identifiers are included above; no internal
URL is included)
- [ ] My branch name describes the change and contains no internal
Paperclip ticket id (the approved delivery plan mandated this shared
branch name)
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 19:45:59 -05:00
Nicky LeachandPaperclip 030dd9d15c feat(adapter-utils): provider-delegable syncIn seam with ordered post-upload commands (#10340)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The adapter/runtime layer has to move files into sandboxes safely
and efficiently
> - The current sync-in path needs a provider-delegable seam so
providers can use their native upload transport when available
> - The upload contract also needs ordered post-upload commands so
extracted content can be finalized fail-fast after transfer
> - The fallback path still has to preserve current behavior when the
provider does not expose native sync verbs
> - This pull request adds the contract and runtime seam for
provider-delegable sync-in, plus the single-stream collapse flag
> - The benefit is fewer round trips, a cleaner provider-owned upload
path, and a compatible fallback for existing runners

## Linked Issues or Issue Description

No public GitHub issue exists for this change. The underlying feature
request is described below in the repository's feature-request format.

### Problem or motivation

Paperclip needs a sync-in path that lets each provider choose the best
available upload transport instead of forcing the harness to orchestrate
uploads the same way every time. The runtime also needs a way to
describe ordered post-upload commands so providers can finalize
extracted content fail-fast after transfer.

### Proposed solution

Extend the sync contract with ordered post-upload commands, forward that
contract through the plugin and environment runtime layers, and make
client syncIn always available. When a provider advertises native sync
verbs, the client should delegate to that transport; otherwise it should
fall back to the existing tarball/write/extract behavior and then run
the post-upload commands in order.

### Alternatives considered

Keeping upload orchestration entirely host-side would avoid a contract
change, but it would block provider-specific transport optimizations and
keep the harness responsible for a path the provider can do more
efficiently. A separate post-upload API would add another surface
without improving the existing sync flow.

### Roadmap alignment

This work aligns with the broader runtime and adapter roadmap because it
improves provider integration without changing the external product
model. It is an additive contract change that preserves backward
compatibility for providers that do not expose native sync verbs.

### Additional context

The fallback path still needs to preserve existing observable behavior,
including command ordering, cwd confinement, and fail-fast execution.
The single-stream progress flag is part of the same transport
improvement so smaller writes can collapse to a single round trip when
the runner supports it.

## What Changed

- Added ordered `postUploadCommands` support to the sync operation
contract and SDK mirror.
- Plumbed the sync-in contract through the plugin and environment
runtime layers.
- Implemented a runtime client `syncIn` path that delegates to native
provider transport when available, otherwise uses the generic
tarball/write/extract fallback.
- Preserved fail-fast execution of ordered post-upload commands in the
fallback path.
- Flipped the sandbox runner's single-stream stdin progress flag to
collapse small `writeFile` operations to a single round trip.
- Added and updated tests for contract forwarding, fallback behavior,
cwd rejection, fail-fast behavior, and single-stream collapse.

## Verification

- `pnpm --filter @paperclipai/plugin-sdk exec vitest run
protocol.postupload.test.ts`
- `pnpm --filter @paperclipai/plugin-sdk exec vitest run
environment-sync-negotiation.test.ts`
- `pnpm --filter @paperclipai/adapter-utils exec vitest run
command-managed-runtime.test.ts`
- `pnpm --filter @paperclipai/server exec vitest run
environment-execution-target.test.ts`
- Local typecheck and targeted suite runs reported in the handoff passed
before PR creation.

## Risks

- The new fallback path could diverge from the previous inline upload
behavior if the tarball/extract contract changes.
- Provider-native sync handling may expose provider-specific edge cases
if a runner advertises sync verbs but does not fully honor the contract.
- The single-stream flag changes transport behavior for small uploads,
so regressions would likely show up as round-trip or upload failures.

## Model Used

OpenAI Codex (GPT-5, tool-using coding agent).

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 17:19:57 -07:00
DottaandPaperclip c111ee4cb3 feat(server): add per-user document stars (#9952)
## Thinking Path

> - Paperclip is the open source control plane people use to manage
AI-agent companies and their work.
> - Artifacts and documents are first-class outputs, but users need a
personal way to keep important documents easy to find.
> - Existing resource memberships already model per-user starred
projects and agents with company scoping and activity logging.
> - Documents lacked the equivalent membership model, route, and
artifact filtering behavior.
> - The shared membership contract also needs to remain safe for
existing UI project/agent mutation helpers when documents become a
recognized resource type.
> - This pull request extends the existing resource-membership system
with per-user document stars and a starred artifacts view.
> - The benefit is a company-scoped, idempotent server foundation for a
dedicated starred-documents experience without weakening authorization
or artifact filtering semantics.

## Linked Issues or Issue Description

### Problem / Motivation

Board users cannot star individual documents, and the company artifacts
API cannot return only the current user's starred documents.

### Proposed Solution

Add company/user-scoped document memberships, a board-only document star
route, document membership data in the shared contract, and a
`starred=true` artifacts filter.

### Alternatives Considered

A document column was rejected because stars are per-user; a separate
star API was rejected because projects and agents already use resource
memberships.

### Roadmap Alignment

This extends the existing Artifacts & Work Products roadmap area and
does not duplicate another open pull request found in the repository
search.

## What Changed

- Added the `document_memberships` schema and migration with
company/user/document uniqueness and starred ordering.
- Extended shared resource-membership and artifact-query contracts for
documents and `starred=true`.
- Added company-scoped document star/unstar service and board-only route
behavior with activity logging.
- Added starred document artifact filtering, including user-authored
documents, document kinds, cursor ordering, and incompatible-kind
handling.
- Preserved idempotency under concurrent star requests and synchronized
UI membership defaults/helpers with the expanded contract.
- Added focused shared, route, service, and UI regression coverage.

## Verification

- `pnpm exec vitest run packages/shared/src/resource-memberships.test.ts
server/src/__tests__/company-artifacts-service.test.ts
server/src/__tests__/resource-memberships-routes.test.ts`
- `pnpm exec vitest run ui/src/components/SidebarAgents.test.tsx
ui/src/components/SidebarProjects.test.tsx
ui/src/components/SidebarStarredProjects.test.tsx`
- `pnpm --filter @paperclipai/db check:migrations`
- `pnpm -r typecheck`
- `pnpm test:run`
- `pnpm build`

## Risks

- The migration adds a new membership table and non-concurrent indexes;
migration safety gates pass with the repository's established policy.
- The starred artifacts query intentionally returns only documents and
relaxes the normal agent-authored/system-kind predicates for documents
the current user explicitly starred.
- Document membership mutations remain board-user-only; agent callers
receive no document-star capability.

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

## Model Used

- OpenAI Codex CLI; runtime model ID and context-window size were not
exposed to this session. Reasoning, repository tool use, code execution,
and test execution were enabled.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 19:13:35 -05:00
DottaandPaperclip 1426494ab8 fix(agents): disable cheap model profiles by default (#10019)
## Thinking Path

> - Paperclip is the control plane people use to create and govern
AI-agent companies
> - Agent creation persists runtime configuration that controls which
model profiles future runs may select
> - Adapters can expose a `cheap` profile, and existing creation paths
implicitly left that profile available when operators made no choice
> - That made a newly created agent eligible for a lower-cost model
without an explicit operator opt-in
> - The UI also dropped an explicit opt-in when the operator selected
the adapter's default cheap model rather than a custom model ID
> - Codex additionally hardcoded `gpt-5.3-codex-spark` into its cheap
profile and static fallback model list, making Paperclip choose an
auth-dependent model rather than requiring an operator choice
> - This pull request makes new-agent creation disable an available
cheap profile by default while preserving explicit opt-in from the UI or
API
> - The Codex cheap profile now remains available for explicit
configuration but supplies no model default, so an unconfigured cheap
request stays on the primary model
> - The benefit is predictable model quality for new agents and an
intentional, auditable choice before lower-cost routing is enabled

## Linked Issues or Issue Description

**Problem**

New agents created with an adapter that exposes a `cheap` model profile
can inherit that profile without the operator explicitly enabling it. In
the UI, enabling the adapter-default cheap model is also omitted because
runtime configuration is only written when a custom model ID is present.

**Expected behavior**

- New agents default an available `cheap` model profile to `{ enabled:
false }` when the caller does not specify it.
- Explicit API configuration remains authoritative.
- UI opt-in persists even when the adapter default model is used.
- Codex does not advertise or automatically select
`gpt-5.3-codex-spark`; operators must explicitly configure any
lower-cost Codex model.

**Related public work**

- Refs #4881, which introduced cheap model profiles for local adapters.
- Supersedes the default-selection portions of #8032 and #10004 by
removing the Codex model default instead of replacing it with another
hardcoded model.

## What Changed

- Detect whether the selected adapter exposes a `cheap` model profile
during agent creation and hiring.
- Persist `runtimeConfig.modelProfiles.cheap.enabled = false` only when
the caller did not explicitly configure the profile.
- Preserve UI cheap-profile opt-in when using the adapter's default
model by writing an empty adapter config.
- Remove `gpt-5.3-codex-spark` from the Codex static model list.
- Keep the Codex `cheap` profile explicitly configurable while giving it
an empty adapter config, so Paperclip never chooses a cheap Codex model
automatically.
- Verify that a Codex cheap request without an explicit model leaves the
primary model unchanged.
- Extend server route and UI runtime-config tests for default-disable
and explicit-opt-in behavior.

## Verification

- `env -u PAPERCLIP_IN_WORKTREE -u PAPERCLIP_WORKTREE_NAME -u
PAPERCLIP_CONFIG -u PAPERCLIP_HOME -u PAPERCLIP_INSTANCE_ID -u
PAPERCLIP_CONTEXT pnpm exec vitest run
packages/adapters/codex-local/src/index.test.ts
packages/adapters/codex-local/src/server/codex-args.test.ts
server/src/__tests__/adapter-models.test.ts
server/src/__tests__/adapter-registry.test.ts
server/src/__tests__/heartbeat-model-profile.test.ts
server/src/__tests__/agent-permissions-routes.test.ts
ui/src/lib/new-agent-runtime-config.test.ts`
- Result: 7 test files passed, 105 tests passed.
- GitHub `Typecheck + Release Registry` check passed on the final head.
- `git diff --check public-gh/master...HEAD`

## Risks

- Low behavioral risk: only newly created or hired agents are
normalized; existing agents are unchanged.
- Explicit `cheap` profile settings remain untouched, including explicit
opt-in.
- Codex users who explicitly opt into the cheap lane must choose a
model; requests without a configured override intentionally continue on
the primary model.
- Adapter profile discovery is now awaited during creation, adding a
small amount of adapter metadata lookup work.
- The source branch name is automation-provided and retained as required
by the task, so it does not satisfy the preferred public branch naming
convention.

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

## Model Used

- OpenAI `gpt-5.4` via Codex CLI, with reasoning, repository editing,
terminal execution, and GitHub/Paperclip tool access. The runtime did
not expose a 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)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-07-27 19:11:51 -05:00