Commit Graph

4186 Commits

Author SHA1 Message Date
Dotta 373b675f94 fix(server): prevent review policy verdict downgrade bypass
Authorize verdicts and policy changes against the stored restrictive review policy, remove downgrade guidance, and cover both restrictive policies with route regressions.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-15 01:12:34 +00:00
Dotta 37fde84abd fix(server): enforce review policy on interaction verdicts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-15 01:12:24 +00:00
Dotta 8ee1fb21a6
feat(ui): badge the review policy when it constrains approval (#10938)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents move their work into review, and a reviewer must then give a
verdict on it
> - By default anyone with write access can give that verdict, including
the agent that did the work
> - The server can constrain that default per issue with a
`reviewPolicy` column, but no screen showed the value
> - A reviewer could therefore press Approve on a review that the server
refuses, and get a 403
> - This pull request shows the policy as a badge on the two surfaces
where a person gives a verdict
> - It also makes an agent verdict read as a verdict in the activity
timeline
> - The benefit is that a reviewer sees who can approve before they try

## Linked Issues or Issue Description

No public GitHub issue exists for this change. The description below
follows
`.github/ISSUE_TEMPLATE/enhancement.yml`.

**What existing behavior does this improve?**

The issue review flow. A reviewer cannot see the approval constraint on
an issue
before they give a verdict.

**Subsystem affected**

Web UI (`ui/`), with one supporting change in the server attention
service.

**Current behavior**

The server stores an optional approval constraint for each issue in a
`reviewPolicy` column. The column has three meaningful states: the
default
(`NULL` or `anyone`), `not_creator`, and `human_only`. The server
enforces the
constraint when it receives a verdict.

No screen shows the value. Two problems follow:

1. A reviewer presses Approve on a review that the server refuses. The
server
   answers 403, and the reason is not visible on the card.
2. An agent that accepts or rejects a review renders in the activity
timeline as
the raw action id, for example "issue thread interaction accepted". A
person
   who reads the timeline cannot tell that a verdict was given.

**Proposed behavior**

Show the constraint as a read-only badge on the two surfaces where a
person
gives a verdict. Show no pixels for the default state, because the
default is
what every issue already does. Make an agent verdict read as a verdict
in the
timeline.

Only agents set the column today, so this change adds no control to set
it.

**Reason and benefit**

A reviewer sees the constraint before they act. This prevents the 403,
and it
removes the need to explain the 403 afterwards. The timeline also
becomes
complete, because it now shows agent verdicts and human verdicts in the
same way.

**Breaking changes**

None. The change adds a badge and changes copy. It adds no column, no
endpoint,
and no request.

**Additional context**

The server-side column and the verdict enforcement landed earlier in
#10931.
This pull request is the user interface for that column. The default
state stays
unchanged on screen, so the badge appears on a small number of issues.

## What Changed

- **A read-only "Approvals" row** in the issue Execution properties. The
row
renders *only* for a constrained policy: "Anyone else" (`not_creator`)
or
"Human only" (`human_only`). A `NULL` or `anyone` column adds no row, so
the
  panel is untouched on the overwhelming majority of issues.
- **The same badge on the stalled-review card** in `/decisions`, above
the three
review verbs. A reviewer now sees the constraint before they press
Approve.
  The condition is the same, so the default card is unchanged.
- **Agent verdicts read as verdicts in the activity timeline.** An agent
that
accepted or rejected a review request previously rendered the raw action
id
("issue thread interaction accepted"). It now reads "approved the
request". A
  stalled-review decision names the verb that the actor chose.
- **A cleared policy reports as "anyone", not "none",** in the
field-change
receipt. The `reviewPolicy` column is nullable by default, so an absent
value
  is a real setting rather than a missing one.
- **All copy comes from `ui/src/lib/review-policy.ts`.** Its badge
lookup returns
`null` for the default. This makes "no pixels for the default" one
enforced
decision instead of a condition repeated at each call site. It also
keeps the
  badge, the activity line, and the receipt reading alike.
- **The server attention service carries the policy** on the review
attention
  subject, so the stalled-review card can read it.

## Verification

Automated tests:

- `ui/src/lib/review-policy.test.ts` — the default returns no badge,
however the
column spells it (`null`, `undefined`, `"anyone"`). An unrecognised
policy from
  the wire shows nothing rather than leaking an enum value.
- `ui/src/components/AttentionQueueRow.test.tsx` — no badge on the
default card,
and the verbs still render. Suppression of the badge must not suppress
the card.
- `ui/src/components/IssueProperties.test.tsx` — no Approvals row on the
default.
  The constrained row contains no `button`, so nothing there can PATCH.
- `server/src/__tests__/attention-service.test.ts` — the review
attention subject
carries the policy, and subjects built from narrower selects do not
claim one.

Run them with:

```sh
pnpm vitest run ui/src/lib/review-policy.test.ts \
  ui/src/components/AttentionQueueRow.test.tsx \
  ui/src/components/IssueProperties.test.tsx \
  server/src/__tests__/attention-service.test.ts
```

Manual steps:

1. Open an issue that has no `reviewPolicy`. Confirm that the Execution
   properties panel shows no Approvals row.
2. Set the column to `not_creator`. Reload the issue. Confirm that the
Approvals
   row reads "Anyone else", and that the row has no control.
3. Move that issue into review. Open `/decisions`. Confirm that the
stalled
   review card shows the same badge above the review verbs.
4. Let an agent approve the review. Confirm that the activity timeline
reads
   "approved the request" and not "issue thread interaction accepted".

Screenshots were captured at 1440x900 and 390x844, in light mode and
dark mode,
with the three policy states side by side. The leftmost column in each
capture is
the default. It carries no badge and no extra row.

## Risks

Low risk.

- The change is additive on screen. Every new surface is behind a
constrained
  policy, so the default path renders exactly as before.
- The badge is read-only. It has no control and sends no request, and a
test
  asserts that the row contains no `button`.
- An unknown policy value from the wire renders nothing. It does not
render the
  raw enum.
- No migration, no schema change, and no endpoint change.

## Model Used

Claude Opus 5 (Anthropic), model id `claude-opus-5[1m]`, 1M context
window,
with extended thinking and tool use enabled. Used through Claude Code
for the
implementation, the tests, and this description.

## Checklist

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 20:29:36 -04:00
Nicky Leach 69027cbaae
fix(workspaces): reopen archived git worktree for managed_checkout projects (#11395)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Execution workspaces give agent tasks isolated Git worktrees
> - Archived isolated workspaces must reopen against a live project
checkout
> - A managed_checkout project has no project workspace directory in its
row
> - The reopen path used the removed archived worktree as the Git
working directory
> - This pull request resolves the live managed checkout and reports a
clear error when it is unavailable
> - The benefit is reliable workspace reopen behavior after archive
cleanup

## Linked Issues or Issue Description

Related public pull request:
[#6164](https://github.com/paperclipai/paperclip/pull/6164) clears
archive state during un-archive. This pull request fixes the separate
reopen failure that occurs after archive cleanup.

**What happened?**

An archived isolated `git_worktree` workspace under a `managed_checkout`
project failed to reopen after cleanup. The route attempted to run Git
in the removed archived worktree and returned a generic service error.

**Expected behavior**

The reopen path should use the live managed checkout as the Git base
directory and should return a clear error when that directory is
unavailable.

**Steps to reproduce**

1. Create a project with `managed_checkout` source control.
2. Create and archive an isolated `git_worktree` execution workspace.
3. Let archive cleanup remove the worktree.
4. Reopen the workspace for an issue.

**Paperclip version or commit**

`cab0c31dc61310106caef42ca244e9f7b0f19460`

**Deployment mode**

Local dev with the default embedded database.

**Agent adapter(s) involved**

Not adapter-specific. This issue affects core workspace handling.

## What Changed

- Resolve the live managed checkout when a managed project reopens an
archived Git worktree.
- Keep local-folder projects on their project workspace directory.
- Validate the Git base directory before `git rev-parse` and return a
scrubbed error.
- Add nine regression tests for workspace reopen behavior.

## Verification

- `server` TypeScript check passes with `tsc --noEmit`.
- `server/src/__tests__/execution-workspace-reopen.test.ts` passes with
9 tests.
- GitHub Actions must pass all required PR checks.

## Risks

Low risk. The change affects only archived isolated workspace reopen
behavior. It reuses the existing managed checkout and Git authentication
helpers. It adds no new credential path, endpoint, or telemetry.

## Model Used

OpenAI GPT-5 assisted with review and GitHub operations. The
implementation author supplied the code and test results.

## 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-08-14 16:44:31 -07:00
Tonio 35a9b98733
fix(ui): theme the onboarding wizard decorative panel instead of hardcoding dark (#11379)
The onboarding wizard's decorative right-hand panel, which holds the ASCII
paperclip illustration, hardcoded a near-black surface. `ThemeContext`
supports light and dark and follows `prefers-color-scheme`, so in light mode
a new customer met the product as a pale form beside a solid black rectangle
— on the one screen meant to introduce it. The glyphs inside already used
`text-muted-foreground`, so the panel was the only part ignoring the theme.

It now uses the paired `bg-muted` surface, which is defined in both themes
(`oklch(0.97 0 0)` light, `oklch(0.269 0 0)` dark), so the illustration reads
as ink on a surface either way and follows any future theme without another
fix.

The guard for it asserts the complete set of `bg-` classes on the panel is
`["bg-muted"]`, anchored to the `<AsciiArtAnimation />` wrapper rather than
scanning the file. Forbidding specific spellings is what failed here
originally: the first version checked `bg-[#rrggbb]` and silently stopped
guarding anything once master migrated the class to `bg-(--hex-1d1d1d)`.
Naming what is allowed cannot decay that way, and it catches named colours
like `bg-black` that no spelling list covered.

Lands the work from #8982 by @stubbi, whose two commits are included
unchanged with their authorship. The rebase and the guard are mine.

Tested: ui typecheck clean; both theme cases fail against four spellings of
the regression — `bg-(--hex-1d1d1d)`, `bg-[#1d1d1d]`, `bg-black`,
`bg-zinc-900` — where the original caught only one and my first widening
caught two. Full ui suite 3958 pass, with one timezone-dependent
IssueProperties failure present on master. All CI gates green; Greptile 5/5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 12:06:20 -07:00
LeeJ a53cc8819b
fix(claude-local): pipe print prompt via stdin (#9500)
Fixes #2444.
Refs #4947.

The `claude_local` adapter launched Claude Code as
`claude --print - --output-format stream-json --verbose`. Paperclip writes
the rendered task prompt to Claude's stdin, but current Claude Code releases
can treat the stale `-` positional marker as the prompt itself, so Claude
received the literal string `"-"` instead of the issue body. The customer's
task ran against no content at all.

The fix keeps `--print` mode and stdin delivery, and removes the stale `-`.

Adds regression coverage on both sides of the delivery path: a `claude_local`
assertion that `--print` is present, `"-"` is absent and the prompt still
reaches stdin, and an adapter-utils case proving the sandbox run-log command
wrapper preserves stdin while streaming logs.

Authored by @elJayAdvisor, whose commit is included unchanged with their
authorship. The branch had gone stale and was showing CONFLICTING; the
conflict was in `execution-target-sandbox.test.ts`, where their new test was
added at the same point as master's `creates the process session directories
only in the launch exec` case and git interleaved the two into one hunk.
Resolved by taking master's file and re-inserting their test whole, after
checking every helper it needs still exists there.

Verified: the bug was still live on master at `execute.ts:838`; the
regression test genuinely catches it — restoring the stale `-` fails
`expect(captured.argv).not.toContain("-")`; `@paperclipai/adapter-claude-local`
and `@paperclipai/adapter-utils` typecheck clean; 67 pass across the two test
files. All CI gates green; Greptile 5/5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 11:34:15 -07:00
Dotta 8cb0ce0de5
fix(ui): restore queued message interrupt action (#11374)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The task thread lets an operator add guidance while an agent run is
active
> - A new message can wait behind that active run as a queued message
> - The classic task view lets the operator interrupt the target run
from that queued message
> - The redesigned task view did not expose the same action
> - This pull request restores the action and keeps it bound to the
exact target run
> - The benefit is that operators can apply urgent guidance without
switching task views

## Linked Issues or Issue Description

**What happened?**

The redesigned task view showed `Queued` for a queued operator message,
but it did not show the existing interrupt action.

**Expected behavior**

The queued message must show `Interrupt` next to `Queued`. The action
must stop the exact run that the message is waiting behind.

**Steps to reproduce**

1. Open a task in the redesigned task view while an agent run is active.
2. Send a new operator message so it enters the queued state.
3. Observe that the queued message has no interrupt action.

**Paperclip version or commit**

`bc0b5a1642`

**Deployment mode**

All deployment modes that use the redesigned task view.

## What Changed

- Preserve persisted queued state and the target run ID in the
redesigned thread model.
- Render a token-compliant `Interrupt` action beside the queued state.
- Reuse the existing exact-run interrupt callback and show a disabled
`Interrupting…` state during the request.
- Keep an assigned queue target immutable so an in-flight comment cannot
rebind its interrupt action to a replacement run.
- Add regression tests for persisted queued messages, replacement-run
races, and the in-progress action state.
- No documentation update was required because this restores existing
behavior.

## Verification

- `pnpm exec vitest run ui/src/components/TaskChatThread.test.tsx`
- `pnpm exec vitest run ui/src/components/TaskChatThread.test.tsx
ui/src/components/task-chat/task-chat-adapter.test.ts
ui/src/pages/IssueDetail.test.tsx -t 'queued message actions|queues
messages against a queued live run and interrupts that exact
run|commentsToTaskChatItems'`
- `pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx -t 'queued
message|queues messages'`
- `pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx` (52 passed)
- `pnpm -r typecheck`
- `pnpm test:run` (all server and UI groups passed; the CLI group passed
after inherited static AWS credential variables were omitted)
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest
run cli/src/__tests__/secrets.test.ts`
- `pnpm build`
- `pnpm check:token-gates`

## Risks

Low risk. The change only adds an action to queued messages that have a
target run and an interrupt callback. Messages without both values keep
the current rendering.

> 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 runtime does not expose a more specific
deployment ID or context-window size. The model used 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>
2026-08-14 14:26:18 -04:00
Tonio 66515582e4
fix(onboarding): do not restore wizard state for a company the user does not own (#11370)
Onboarding persists a draft to `localStorage`, including `createdCompanyId`.
That key is scoped per browser origin, not per account, so a browser that has
already run onboarding hands the stored company id to the next session on the
same origin — whoever is signed in. Every downstream call then targets a
company that account may not own: goals, agents and issues are created there,
and requests fail with authorization errors.

`restoreOnboardingState` now returns a saved draft only when the signed-in
account owns the company it names. Otherwise the draft is discarded and the
stale blob removed.

The wizard splits into a gate and an inner component because the inner one
has ~20 `useState(saved?.x ?? default)` initializers, and an initializer runs
only on the first render. Mounting before the restored draft is final locks
every field to its default with no way back, so the gate waits for the
company list while it is loading.

Ownership is judged only against a list that actually answered. Any company
query error makes it undecidable, whatever the list contains — the companies
cache is not account-scoped and survives sign-out, so a failed refetch after
an account switch can leave the previous account's companies in hand, and
trusting a non-empty list there would hand one account's draft to the next.
Nothing is restored and nothing is deleted in that state; the next successful
load decides.

Judging the draft and mounting the wizard are separate questions. The gate
withholds the wizard only while the list is *loading*, never on error: the
companies query sets `retry: false`, and with no companies the dashboard
offers a "Get Started" button wired to onboarding, so blocking there would
make that button do nothing at all. Mounting costs the draft nothing, because
the persist effect is itself gated on the wizard being open.

All four draft-storage call sites — read, write, cleanup, reset — go through
one guarded helper. Storage access throws outright where a browser denies it,
and each site sits in a render, an effect or a close handler, so an escaping
exception took down something the customer was using.

Lands the work from #9900 by @stubbi, whose two commits are included
unchanged with their authorship. The rebase, the error-path handling and the
storage guards are mine.

Follow-up filed separately: sign-out should remove account-scoped cached data
rather than invalidating two keys. This change is defensive and protects
onboarding only.

Tested: ui typecheck clean; 73 pass across the seven onboarding suites; full
ui suite 3952 pass, with one timezone-dependent IssueProperties failure
present on master in a file this does not touch. All CI gates green;
Greptile 5/5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 11:08:48 -07:00
Tonio bc0b5a1642
fix(ui): onboarding wizard keeps an invisible disabled adapter selected (#11371)
The wizard defaults `adapterType` to `claude_local`, and a saved draft can
name any adapter. The grid only renders adapters the server has enabled, so
on an instance where the held adapter is disabled nothing appears selected
while the wizard still holds it — and the first agent is hired on an adapter
the deployer turned off, which can never acquire a lease.

The selection now snaps to the first enabled, non-coming-soon adapter
whenever the held one is not visible, and adapter-specific model defaults
follow it.

The snap waits for the adapter registry to load. External adapter types are
registered into the UI registry only once the adapters query resolves, so
before that a saved external adapter is indistinguishable from a disabled
one — snapping on that transient list would replace the customer's choice
with a built-in and the persist effect would write it down. This gate fails
closed, unlike the fail-open gates in onboarding, because the directions of
harm are opposite: acting early silently rewrites a saved answer, while
waiting merely leaves the selection alone, which is the behaviour that
existed before the snap did.

The test file is named `OnboardingWizard.adapters.test.tsx` rather than
`OnboardingWizard.test.tsx`, which is the name #11370 uses for its restore
gate. Both merged cleanly onto master alone but collided with each other on
add/add, and nothing in either status showed it.

Lands the work from #9900's sibling, #9501, by @stubbi, whose commit is
included unchanged with their authorship. The rename and the registry gate
are mine.

Tested: ui typecheck clean; 51 pass across the adapter, hook, dialog,
config-form and wizard-step suites, including the other callers of the
adapter hook since that module changed. All CI gates green; Greptile 5/5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 10:27:59 -07:00
Dotta d2665ff6b4
fix(ui): align the mobile task chat composer with the thread (#11296)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The task thread is where people read work and guide agents.
> - The mobile composer should use the same content width as the thread.
> - The composer kept the desktop 80% width on mobile, so its edges did
not align with the thread.
> - Long assignee-aware placeholder text could also clip inside the
mobile editor.
> - Some extracted style tokens used legacy HSL wrappers around complete
semantic colors, which made those declarations invalid.
> - This pull request makes the composer full width on mobile, preserves
the narrower desktop layout, wraps the placeholder, and repairs the
invalid color compositions.
> - The benefit is a stable mobile composer that aligns with the task
thread and keeps its intended visual styles.

## Linked Issues or Issue Description

Related work: Refs #11263.

**What happened?**

At mobile widths, the task chat composer used the same 80% width as the
desktop composer. Its horizontal edges did not align with the full task
thread. A long assignee-aware placeholder could clip on one line. The
composer's extracted shadow also used a legacy `hsl(var(...))` wrapper
around complete semantic color values, so the browser could reject the
declaration.

**Expected behavior**

The composer must match the task thread width on mobile. It must stay
narrower on larger screens. Long placeholder text must wrap inside the
editor. Semantic color tokens must form valid shadows and gradients.

**Steps to reproduce**

1. Open a task with the chat-style thread on a mobile viewport.
2. Compare the composer edges with the task thread edges.
3. Select an assignee whose placeholder text wraps to two lines.
4. Inspect the computed composer shadow and the extracted semantic color
styles.

**Paperclip version or commit**

The change is based on `dc6fcd1ff1` from `master`.

**Deployment mode**

Local build from source. The behavior also applies to packaged web
builds.

## What Changed

- Made the task chat composer full width below the medium breakpoint and
kept the 80% desktop width.
- Matched the composer dock padding to the task thread padding.
- Allowed long composer placeholders to wrap and reserved enough mobile
editor height for two lines.
- Replaced invalid legacy HSL wrappers around full semantic colors in
extracted shadows, gradients, and approval styles.
- Added a token gate that prevents legacy `hsl(var(--token))` wrappers
from returning.
- Added focused regression tests for responsive width, padding,
placeholder wrapping, mobile height, and semantic shadow validity.

## Verification

- `pnpm check:token-gates` — all four gates pass.
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/TaskChatThread.test.tsx
src/components/task-chat/TaskChatComposer.test.tsx
src/components/task-chat/TaskChatComposerStyles.test.ts` — 37 tests
pass.
- `pnpm --filter @paperclipai/ui typecheck` — passes.
- `pnpm --filter @paperclipai/ui build` — passes. The build prints
existing CSS optimizer and bundle-size warnings.

## Risks

- Low risk. The width change is limited to the mobile breakpoint. The
desktop 80% layout remains in place.
- The semantic token fixes can affect shadows and gradients that were
previously invalid. The new gate prevents the invalid wrapper pattern
from returning.

> 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 context-window size is not
exposed in this environment. The model used reasoning, repository tools,
code execution, and GitHub tools.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-14 12:53:11 -04:00
Tonio d95340b0b8
feat(ui): send a company with no agent into onboarding, at the right step (#11352)
A company with no agent cannot do anything: no runs, no tasks, nothing to
show. The dashboard says so in a banner with a link, which asks the customer
to notice a problem the product can fix for them. It is worse for a company
created by Paperclip Cloud: Cloud creates the company before the tenant
boots, so the companyless redirect never runs, and the customer arrives on an
empty dashboard straight out of a signup flow that already asked for a
mission.

The dashboard now opens onboarding when the agent list has loaded and is
empty, and onboarding opens on the agent step when the company already has
its mission — read from the company-level goal the seed writes, under the
query key the launch path already uses, so it shares a cache entry rather
than adding a fetch.

The step is decided once. `initialStep` is derived from the company list and
the goal list, so it changes on any retry, refetch or cache invalidation. An
effect that took it as a dependency called `setStep` on every one of those
and moved a customer who was already mid-flow. Gating the input only narrowed
that window; it could not close it. The step now belongs to the request that
opened the wizard: the effect reads it through a ref and is keyed on the
wizard opening or the company changing. `createdCompanyIdRef` beside it
already used this pattern for the same reason.

That exposed a path nothing had ever taken. A company reached the mission
step only by creating itself on step 1, so opening an existing company there
found code that had never run: `companyName` is only typed on step 1, and
both ways forward require it, so the step could not be completed at all; and
confirming advanced without writing anything, so the mission the customer
typed was discarded. Both fixed, and the write now reconciles against the
goal list rather than adding a second company goal, since the mission lookup
fails open and can send a company that has one back to that step.

Company-scoped state now stays with its company. `clearCompanyScopedState`
runs when the route replaces a company and when it withdraws one — the same
event, and clearing half of it left a goal id that made the next company skip
a mission it had never given. `stillTheSameCompany` guards all five async
writes, after the server work rather than before it, so a company switch
mid-flight cannot hand the new company the old one's goal, project, issue or
agent, and cannot leave a hired agent without its instructions file. The
keyboard path honours `loading` like every button already did.
`claimOnboardingOffer` makes onboarding an offer that stays declined for the
visit.

Route ownership is now recorded whenever the route names a company, including
one the wizard already holds. This changes a documented rule deliberately:
without it a self-created company was never withdrawn, so `/onboarding` would
show "create a company" while still holding the previous one and write the
customer's new mission into it.

Tested at the seam, because every defect on this branch lived between a value
and its consumer and the predicate tests passed at every stage.
`OnboardingWizard.step.test.tsx` renders the real wizard against the real
resolver and the real mission hook across 18 cases, and each was
fault-injected against the code it replaces rather than trusted on a green
run. That caught a case that passed against the broken code, and a race in
one of the guards.

ui typecheck clean; full ui suite 3923 pass, with one timezone-dependent
IssueProperties failure present on this branch's base in a file this change
does not touch. All CI gates green; Greptile 5/5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 08:18:07 -07:00
Dotta dc6fcd1ff1
fix(ui): move agent secret access to searchable secrets tab (#11283)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The agent configuration UI controls each agent and its allowed
secrets.
> - The environment variable editor already has a secret selector with
search and folder navigation.
> - The secret access editor used a basic list and made large secret
stores hard to use.
> - The secret access controls also occupied the main Configuration tab.
> - This pull request reuses the rich selector and moves secret access
to a dedicated Secrets tab.
> - The benefit is one consistent secret selection workflow with clearer
agent configuration navigation.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The agent detail configuration view and its secret access editor.

**Subsystem affected**

`ui/` — React and Vite board UI.

**Current behavior**

The secret access editor uses a basic select control. It does not
provide the search and folder navigation available in the environment
variable editor. The editor also appears inside the Configuration tab.

**Proposed behavior**

The secret access editor uses the shared secret picker. Users can search
secrets and browse slash-delimited folders. Agent details provide a
dedicated Secrets tab for this editor.

**Reason and benefit**

Large secret stores are slow to scan in a flat list. Reusing one
selector reduces UI differences and makes scoped secret access easier to
manage.

**Breaking changes**

None. The API and saved secret access data do not change.

## What Changed

- Reused the environment variable secret picker in the agent secret
access editor.
- Preserved secret version selection and the create-secret action,
including nested-popover focus handling.
- Added a route-backed Secrets tab to agent details and removed secret
access controls from Configuration.
- Guarded unsaved configuration across tab, link, browser-history, and
action-triggered navigation.
- Rechecked dirty state when navigation-producing agent actions finish,
covering edits made while a request is pending.
- Added component, page, and Storybook coverage for the workflow.

## Verification

- `pnpm --filter @paperclipai/ui exec vitest run
src/components/AgentActionButtons.test.tsx
src/components/AgentConfigForm.render.test.tsx
src/components/AgentSecretAccessEditor.test.tsx
src/components/environment-variables-editor/EnvironmentVariablesEditor.test.tsx
src/pages/AgentDetail.progress.test.ts` — 82 tests passed.
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm check:token-gates`
- All GitHub PR checks passed on `5209c5b787`, including build, canary,
general and serialized tests, and all three e2e shards.
- Greptile completed at 5/5 with zero unresolved review threads.

## Risks

- Low risk. The API and persisted binding format are unchanged; this
changes agent configuration navigation and secret selection UI.
- Dirty-state guards now cover direct navigation, Back/Forward history,
and navigation-producing agent actions, including pending-request races.
- Tests cover tab separation, secret access updates, search, folder
navigation, focus restoration, and navigation rejection.
- No documentation change is required because commands, contracts, and
setup steps do not change.

> 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. This runtime did not expose a more specific
model ID or context window. The model used agentic 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-08-14 08:03:20 -04:00
Tonio aac6ce82e1
fix(ui): read the onboarding company prefix from the path, not the route match (#11351)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - New users meet the product through an onboarding wizard that creates
their company, their first agent, and a starter task
> - The wizard also serves an existing company, at
`/{PREFIX}/onboarding`, to add another agent to it
> - On that route the wizard ignores the company in the URL and opens at
"create a company" instead
> - It reads the prefix with `useParams()`, but it renders beside
`<Routes>` rather than inside it, so there is no route match to read
> - This pull request reads the prefix from the pathname, which is
available without a match
> - The benefit is that the URL a user follows decides what the wizard
asks them

## Linked Issues or Issue Description

No public issue exists for this. The problem follows.

**What happened?**

Open `/{PREFIX}/onboarding` for a company that already exists. The
wizard opens at step 1 and asks the user to create a company. The
company named in the URL is ignored.

**Expected behavior**

The wizard recognises the company in the URL and opens at step 2, so the
user adds an agent to that company instead of creating a second one.

**Steps to reproduce**

1. Create a company, so it has an issue prefix.
2. Go to `/{PREFIX}/onboarding`.
3. Read the first screen. It asks for a company name.

**Paperclip version or commit**

`master` at `5ca7b4c1f`.

**Deployment mode**

Any. This is client-side routing and does not depend on the server.

## What Changed

- `ui/src/lib/onboarding-route.ts` — adds
`companyPrefixFromOnboardingPath()`, which reads the prefix from the
pathname.
- `ui/src/components/OnboardingWizard.tsx` — uses that value when the
route match supplies none. One line, plus the import.
- `ui/src/lib/onboarding-route.test.ts` — six cases for the new
function.

`OnboardingWizard` renders beside `<Routes>` in `App.tsx`, so
`useParams()` returns nothing and `companyPrefix` was always
`undefined`. `resolveRouteOnboardingOptions` then took its no-prefix
branch every time. `useLocation()` needs only the router, not a match,
and the wizard already calls it.

The route match is still read first. If the wizard later moves inside
the route tree, this code does not need to change.

The new parser accepts the same shape as `isOnboardingPath()`: the
prefix is the first of exactly two segments. One test asserts the two
agree, because a disagreement would either open the wizard where no
company resolves, or resolve a company where onboarding is not served.

### Why the change is this small

Three pull requests are open against `OnboardingWizard.tsx` — #9900,
#9501 and #8982. A larger change there would collide with all three.
Almost all of this lands in `onboarding-route.ts`, a small file of pure
functions with existing tests.

## Verification

- `npx tsc --noEmit -p ui/tsconfig.json` — clean.
- `npx vitest run ui/src/lib/onboarding-route.test.ts` — 18 pass.
- `npx vitest run ui/src` — 3883 pass, 445 files.

One test shows the defect and the fix together. With `companyPrefix:
undefined`, which is what the wizard supplied before,
`resolveRouteOnboardingOptions` returns `{ initialStep: 1 }`. With the
parsed prefix it returns `{ initialStep: 2, companyId: "c1" }`.

**Pre-existing failures, unrelated:** `IssueProperties.test.tsx` and
`StatusCards/format.test.ts` fail on clean `origin/master` with these
changes stashed. Both look date-dependent.

**Not done:** no manual browser check. The behaviour is covered by unit
tests at the function boundary, and the wizard's own suite passes.

## Risks

Low. The route match is still preferred, so behaviour changes only where
`useParams()` gave nothing — which today is every render of this
component.

The parser returns a prefix only for a two-segment path ending in
`onboarding`, so no other route can start matching. An unknown prefix
already falls back to step 1 in `resolveRouteOnboardingOptions`, and
that path is unchanged.

To revert, remove the fallback in the wizard. The new function has no
other caller.

## Model Used

Claude Opus 5 (`claude-opus-5`), through Claude Code. Extended thinking
enabled. Tool use enabled: file read and edit, shell command execution
for typecheck and the test runs, and the GitHub CLI.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have 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: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-13 21:45:39 -07:00
Nicky Leach 5ca7b4c1fe
fix(security): standardize paperclipai CLI guidance on safe npx path (#11343)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip provides CLI guidance to agents and operators through
documentation and runtime messages.
> - Content-bearing `pnpm paperclipai` examples send arguments through a
shell.
> - Shell evaluation can execute command substitutions in untrusted
argument content.
> - Runtime hostname guidance can also place request-derived content
inside a shell command.
> - This pull request uses `npx paperclipai` for content-bearing
guidance and uses a static hostname placeholder.
> - The benefit is safer copy-paste guidance for agents and operators.

## Linked Issues or Issue Description

**Issue type**
Incorrect information

**Where is the issue?**
CLI guidance in `doc/CLI.md`, `skills/paperclip/SKILL.md`,
documentation, and runtime-generated hints.

**What's wrong?**
Content-bearing `pnpm paperclipai` commands can pass argument text
through `/bin/sh`. Shell command substitution in an argument can execute
before the CLI receives the value.

**Suggested fix**
Use `npx paperclipai` for content-bearing commands. Use a static
`<host>` placeholder when runtime guidance displays the allowed-hostname
command.

## What Changed

- Replace content-bearing `pnpm paperclipai` examples with `npx
paperclipai` across the documentation and agent-facing guidance.
- Update runtime-generated CLI hints to use a static `<host>`
placeholder.
- Add safety notes to `doc/CLI.md` and `skills/paperclip/SKILL.md`.
- Add scans and regression tests for unsafe invocation and hostile
hostname headers.
- Keep fixed lifecycle commands and `pnpm --filter @paperclipai/*` build
commands unchanged.

## Verification

- Run `tsc --noEmit` for the changed server files.
- Run `cli-invocation-safety.test.ts`.
- Run `private-hostname-guard.test.ts`.
- Confirm that hostile hostname headers do not enter shown shell
commands.
- Confirm that the three commits contain the required Paperclip
co-author trailer.

## Risks

- This change updates documentation and diagnostic text across many
surfaces.
- Fixed lifecycle and setup commands remain unchanged.
- The tests fail if content-bearing `pnpm paperclipai` guidance returns.
- The change does not alter the CLI argument parser.

## Model Used

OpenAI Codex, GPT-5, tool use, code execution, and repository review
assistance.

## 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-08-13 16:43:21 -07:00
Nicky Leach 05d58cd884
fix(tool-gateway): keep unsigned ask-first requests out of the review queue without cancelling them (#11338)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The tool gateway creates approval requests and the review queue
reads them
> - The gateway creates a request row before it adds the signature
> - A review-queue read can see the row during that short unsigned state
> - The old read path cancels the unsigned row, so approval returns `409
action_not_pending`
> - This pull request hides unsigned in-flight rows and keeps them
pending until signing finishes
> - The benefit is that approval succeeds while invalid signed requests
remain cancelled

## Linked Issues or Issue Description

**What happened?**

A review-queue read cancelled a pending tool action request when the
request had no signature yet. The next approval call returned `409
action_not_pending`.

**Expected behavior**

The review queue must hide an unsigned in-flight request and keep its
state as `pending`. A request with an invalid signature must remain
cancelled.

**Steps to reproduce**

1. Create a require-approval tool action request.
2. Read the review queue while the request signature is still null.
3. Approve the request after the creator adds the signature.
4. Observe that the old code cancels the request and the approval call
fails.

**Paperclip version or commit**

Commit `720aa0a494bbaa1711bc7a3d795f810765915bfe`.

**Deployment mode**

Local dev with the embedded PGlite database.

**Installation method**

Built from source with pnpm.

**Agent adapter(s) involved**

Not adapter-specific. This is a core tool access service bug.

**Database mode**

Embedded PGlite.

**Access context**

Board and agent tool approval flow.

## What Changed

- Keep a pending request with a null signature out of
`listActionRequests` results.
- Cancel a request when its non-null signature fails verification.
- Add a permanent regression test for the unsigned request transition.
- Update the contract test for unsigned and invalid-signature requests.

## Verification

- Run the tool access service, tool gateway service, tool gateway, and
tool access policy service tests.
- Confirm 227 tests pass.
- Run the `@mcp-runnable` Playwright end-to-end suite in CI.
- Run the US-9 loop 30 times in CI.

## Risks

The change alters review-queue filtering for unsigned requests. A null
signature now means that signing remains in progress. Invalid signed
requests keep the existing cancellation behavior. The change has no
database migration.

## Model Used

OpenAI Codex, GPT-5, with tool use and code execution. The model
reviewed the handoff, repository rules, and pull request state. The
implementation author supplied the code and tests.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-13 16:15:12 -07:00
Austin 0819cac4c6
feat(secrets): add agent-readable /secrets/catalog endpoint (#9530)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents can be configured with env bindings that reference company
secrets — they specify which secret by UUID in `adapterConfig.env`
> - But there is no API endpoint agents can call to look up a secret
UUID by name — `GET /companies/:companyId/secrets` is board-only, and
the internal `secrets.resolve` handler only accepts UUIDs
> - So when an agent needs to wire a new secret (e.g. an API key for a
new skill), it has no way to discover the UUID from a known name like
`HOMEBOX_API_KEY` — the user must find it by inspecting browser network
traffic
> - The fix is a read-only catalog endpoint that agents can call to get
the `id`/`name`/`key`/`status` mapping — no values, no provider config —
just enough to resolve a name to a UUID
> - This PR adds `GET /companies/:companyId/secrets/catalog`, guarded by
`assertBoardOrAgent` + `assertCompanyAccess`, so agents can discover the
UUID they need without board-level access and without any secret value
being exposed

## Linked Issues or Issue Description

No pre-existing public issue. Describing inline per the feature request
template:

**Subsystem affected:** `server/` — REST API & orchestration services

**Problem or motivation:**
Agents that configure env bindings must reference secrets by UUID
(`secretId`). There is no agent-accessible API to resolve a secret name
to its UUID. `GET /companies/:companyId/secrets` requires board access;
the internal `secrets.resolve` handler rejects anything that is not
already a UUID. Agents and their operators are forced to find UUIDs by
inspecting browser network requests, which is friction that should not
exist.

**Proposed solution:**
Add a read-only catalog endpoint — `GET
/companies/:companyId/secrets/catalog` — that agents can call. It
returns only non-sensitive metadata (`id`, `name`, `key`, `status`) for
each active company secret, stripped of values, provider configuration,
and version history. Board callers get the same response. The existing
full-detail list endpoint (`GET /companies/:companyId/secrets`) remains
board-only and is unchanged.

**Alternatives considered:**
- Allow agents to call the existing `/secrets` list — rejected because
it returns full rows including provider metadata; narrowing the response
is safer.
- Add a name-to-UUID lookup by query param — simpler but less useful; a
full catalog means the agent can do the resolution locally without a
second round-trip.

**Roadmap alignment:** Does not duplicate anything in `ROADMAP.md`.

## What Changed

- `server/src/routes/secrets.ts` — new `GET
/companies/:companyId/secrets/catalog` route registered before the
board-only `GET /companies/:companyId/secrets` route. Uses
`assertBoardOrAgent` + `assertCompanyAccess`. Calls `svc.list()` then
projects each row to `{ id, name, key, status }` before responding.
- `server/src/__tests__/secrets-routes.test.ts` — adds `list` to the
shared mock service object (it was missing); adds a `describe` block
with four test cases: board caller receives stripped metadata, agent
caller in the same company receives stripped metadata, unauthenticated
request gets 401, agent from a different company gets 403.

## Verification

**Automated:**
```bash
pnpm --filter @paperclipai/server test --run secrets-routes
```
All four new test cases (board access, agent access, unauthed rejection,
cross-company rejection) should pass.

**Manual:**
1. Start the Paperclip server locally.
2. Create a company and a secret via the UI.
3. Call the endpoint as a board user:
   ```bash
curl http://localhost:3100/api/companies/<companyId>/secrets/catalog \
     -H "Authorization: Bearer <board-session-token>"
   ```
Expect a JSON array with `id`, `name`, `key`, `status` fields — no
`provider`, no `referenceCount`, no version data.
4. Call the same endpoint with an agent API key:
   ```bash
curl http://localhost:3100/api/companies/<companyId>/secrets/catalog \
     -H "Authorization: Bearer <agent-api-key>"
   ```
   Expect the same response.
5. Call with an agent API key scoped to a *different* company — expect
403.

## Risks

Low risk. This is a purely additive, read-only endpoint. No existing
behavior changes. The only new capability is that agents can discover
the UUIDs of secrets in their own company — metadata they already need
to do their job. Secret values are never returned. Authorization reuses
the existing `assertBoardOrAgent` and `assertCompanyAccess` guards
already used throughout the codebase.

## Model Used

Claude Sonnet 4.6 (`claude-sonnet-4-6`) — Anthropic, extended context,
tool use enabled. The entire change (route, tests, PR description) was
produced by the model operating as a Paperclip CEO agent assigned to the
task.

## Checklist

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

---------

Co-authored-by: Austin Pilz <austinpilz@users.noreply.github.com>
Co-authored-by: root <root@paperclip.pilz.dev>
Co-authored-by: Internet Historian <agent@paperclip.internal>
Co-authored-by: Andrew Aymeloglu <aaymeloglu@gmail.com>
2026-08-13 17:43:43 -05:00
scotttong eabecc6f77
feat(annotations): include issue document annotations in agent review context (#11332)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Reviewers annotate plans and issue documents with inline comments,
and assigned agents act on that feedback
> - The server already builds a bounded review context from open plan
annotations and includes it in agent wake payloads
> - Non-plan issue documents did not get the same treatment: their open
annotation threads never reached the agent, and the properties pane did
not surface their annotations
> - This pull request extends the review-context path and the
properties-pane UI to issue documents, at parity with plans
> - The benefit is that agent feedback on any issue document reaches the
assigned agent, not only feedback on the plan

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The review-context pipeline that delivers inline annotation feedback to
assigned agents, and the properties pane that surfaces those annotations
to reviewers.

**Subsystem affected**

The server review-context path
(`server/src/services/plan-review-context.ts`, wake payload assembly in
`server/src/services/heartbeat.ts`, `server/src/routes/issues.ts`),
shared wake-payload types (`packages/shared`, `packages/adapter-utils`),
and the issue properties pane (`ui/src/components/issue-properties/`).

**Current behavior**

A reviewer can annotate any issue document, not only the plan. The agent
wake payload includes open annotation threads for the plan document
only. Feedback left on other issue documents is invisible to the
assigned agent. In the properties pane, the Artifacts tab also gives no
way to see or open a document's annotations.

**Proposed behavior**

Add `buildDocumentReviewContext` beside the existing plan builder. It
collects open annotation threads for all non-plan issue documents,
applies the same thread, comment, and character budgets across
documents, and reports truncation. Include the result as a new
`documentReviewContext` field in agent wake payloads and in the issue
wake-context route. Keep the plan context on its legacy builder and
field so plan-only wakes stay byte-for-byte compatible. Render the new
context in the adapter wake-payload text, and surface annotation counts
and the annotation panel for documents in the properties pane's Plans
and Artifacts tabs.

**Reason and benefit**

The floating annotation popover and persistent highlight UI landed
earlier; this change completes the loop so agent feedback on any issue
document reaches the assigned agent, not only feedback on the plan.

**Breaking changes**

None. The wake payload gains a new optional `documentReviewContext`
field; the existing plan context field and its legacy builder are
unchanged, so plan-only wakes stay byte-for-byte compatible.

## What Changed

- Add `buildDocumentReviewContext` in
`server/src/services/plan-review-context.ts`: bounded review context
(shared thread/comment/character budgets, per-document legacy limits)
over all non-plan issue documents
- Include `documentReviewContext` in agent wake payloads
(`server/src/services/heartbeat.ts`) and in the issue wake-context
response (`server/src/routes/issues.ts`)
- Add shared `DocumentReviewContext` / `DocumentReviewContextDocument`
types in `packages/shared`
- Normalize and render the new context in adapter wake-payload text
(`packages/adapter-utils/src/server-utils.ts`), with tests
- Show a `DocumentAnnotationsCountChip` and the annotation panel for
documents in the properties pane Plans and Artifacts tabs, with tests
- Extend server document-annotations service tests to cover the new
context builder

## Verification

- Run `npx vitest run packages/adapter-utils/src/server-utils.test.ts
server/src/__tests__/document-annotations-service.test.ts` from the repo
root — 104 tests pass
- Run `TZ=UTC npx vitest run
ui/src/components/issue-properties/IssuePropertiesDocumentAnnotations.test.tsx
ui/src/components/IssueProperties.test.tsx
ui/src/components/IssueDocumentAnnotations.test.tsx
ui/src/components/DocumentAnnotationPopover.test.tsx` from the repo root
— 75 tests pass (one pre-existing monitor-row case asserts UTC
timestamps, so use `TZ=UTC` locally; CI runs in UTC)
- `pnpm run typecheck` in `server/` passes
- Manual: annotate a non-plan issue document, then wake the assigned
agent with a comment — the wake payload lists the open document
annotation threads; the Artifacts tab shows the annotation count chip
and opens the panel

## Risks

- The wake payload gains a new optional `documentReviewContext` field;
consumers that ignore unknown fields are unaffected, and the plan
context field is unchanged
- The context is new input to agent wakes; shared budgets (same limits
as the plan context) bound token cost across all documents
- Low UI risk: the properties-pane changes reuse the existing annotation
components

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

## Model Used

- Claude (Anthropic), model ID `claude-fable-5` (Claude Fable 5), with
extended thinking and agentic tool use (Claude Code harness)

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-13 14:23:09 -07:00
Devin Foley 9b1fd42ac1
test(grok-local): isolate billing env in usage cost test (#11285)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Local adapters report run output, token use, and cost data.
> - The Grok local adapter now reports real token use and cost data.
> - Its new billing test must prove the no-key path and the API-key
path.
> - The no-key assertion used the caller environment without isolation.
> - This made the test fail when `XAI_API_KEY` was already set.
> - This pull request isolates that environment state in the test.
> - The benefit is stable coverage for the cost gate from #10433.

## Linked Issues or Issue Description

Refs #10433

**What happened?**

The Grok local usage and cost test asserted subscription billing while
it still used the ambient process environment. If `XAI_API_KEY` was set
before the test ran, the adapter selected API billing instead. The
subscription assertion could then fail on a developer machine or a CI
runner with provider credentials.

**Expected behavior**

The test should prove the subscription path with no `XAI_API_KEY`. It
should also prove the API billing path with a test key.

**Steps to reproduce**

1. Start from `master` after #10433.
2. Set `XAI_API_KEY` in the shell environment.
3. Run `vitest` for
`packages/adapters/grok-local/src/server/execute.test.ts`.
4. Observe that the subscription half can take the API billing branch
without test isolation.

**Paperclip version or commit**

`master` after #10433.

**Deployment mode**

Built from source.

## What Changed

- Isolated `XAI_API_KEY` with save, delete, set, and restore logic
around both billing assertions.
- Gave the subscription and API billing checks separate run ids and temp
roots.

## Verification

- `XAI_API_KEY=ambient-test-key corepack pnpm exec vitest run
packages/adapters/grok-local/src/server/execute.test.ts`
- `corepack pnpm --filter @paperclipai/adapter-grok-local typecheck`

## Risks

Low risk. This changes test setup only. It does not change Grok local
adapter runtime behavior.

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

## Model Used

OpenAI GPT-5 Codex local coding agent. The agent used shell tools,
GitHub CLI, and local test execution. The context window size was not
exposed 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: Claude <noreply@paperclip.ing>
2026-08-13 13:58:05 -07:00
github-actions[bot] 534c1a9630
chore(lockfile): refresh pnpm-lock.yaml (#11339)
Auto-generated lockfile refresh after dependencies changed on master.
This PR only updates pnpm-lock.yaml.

Co-authored-by: lockfile-bot <lockfile-bot@users.noreply.github.com>
2026-08-13 13:20:20 -07:00
dependabot[bot] 04432f82c6
build(deps): align Lexical packages at 0.49.0 (#10728)
Bumps
[@lexical/link](https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link)
from 0.46.0 to 0.49.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/facebook/lexical/releases">@​lexical/link's
releases</a>.</em></p>
<blockquote>
<p>v0.49.0 is a monthly release headlined by the completion of the
<code>$config()</code> protocol migration for Lexical's built-in node
classes (<a
href="https://redirect.github.com/facebook/lexical/pull/8640">#8640</a>),
a breaking change that replaces per-node
<code>getType()</code>/<code>clone()</code>/<code>importJSON()</code>/<code>importDOM()</code>/<code>transform()</code>
boilerplate with runtime-synthesized behavior. It ships with a cluster
of follow-up fixes hardening the synthesized <code>getType()</code> and
<code>clone()</code> (including a stack overflow in compiled builds), an
opt-in sticky horizontal scrollbar for overflowing tables, and a broad
batch of fixes across tables, Markdown, HTML, code highlighting, and
selection.</p>
<h2>Breaking Changes</h2>
<ul>
<li><a
href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a>
— Built-in node classes are ported to the <a
href="https://lexical.dev/docs/concepts/nodes"><code>$config()</code></a>
protocol. The static <code>importJSON()</code>,
<code>importDOM()</code>, <code>clone()</code>, and
<code>transform()</code> methods are no longer present on ported nodes;
use the higher-level equivalents instead
(<code>LexicalEditor.parseEditorState</code>/<code>$generateNodesFromDOM</code>,
the <code>$cloneWithProperties</code> helper, and extensions or
<code>$transform</code> in <code>$config</code>). <code>getType()</code>
is unchanged and safe to keep using, <code>__type</code> is now
<code>readonly</code>, and node constructors require zero-argument
defaults. Custom nodes may keep their static methods, but adopting
<code>$config()</code> is recommended (<a
href="https://redirect.github.com/facebook/lexical/pull/8640">#8640</a>)</li>
<li><a
href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a>
— <code>LexicalCommand&lt;T&gt;</code>'s payload type is now invariant
to prevent unsafe structural compatibility between commands. Previously
correct code (including explicit generics) needs no changes; only unsafe
<code>LexicalCommand&lt;unknown&gt;</code> usage must switch to the
newly exported <code>AnyLexicalCommand</code> alias.
<code>dispatchCommand</code>'s payload argument is now optional when a
command's payload type is <code>undefined</code> or <code>void</code>,
and redundant explicit type parameters were dropped from the
<code>registerCommand</code>/<code>registerNodeTransform</code> call
sites (<a
href="https://redirect.github.com/facebook/lexical/pull/8877">#8877</a>)</li>
</ul>
<h2>New APIs &amp; Features</h2>
<ul>
<li><a
href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a>
— Opt-in sticky horizontal scrollbar for tables wider than their
container, so the scrollbar stays anchored at the viewport bottom
instead of only being reachable at the end of the table. Enable it with
<code>hasStickyScrollbar: true</code> in the table config; style it via
the new <code>tableStickyScrollbar</code> theme key (<a
href="https://redirect.github.com/facebook/lexical/pull/8790">#8790</a>)</li>
<li><a
href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a>
— The <code>&lt;colgroup&gt;</code> element is now omitted from the
table DOM when column widths are undefined, producing cleaner markup and
letting the browser auto-size columns; it is created or removed
dynamically as widths change (<a
href="https://redirect.github.com/facebook/lexical/pull/8850">#8850</a>)</li>
<li><a
href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a>
— Added an editor operation benchmark suite (paragraph split, bold
formatting, range deletion, paste, select-all format) measured through
full DOM reconciliation, to help contributors track core editing
performance (<a
href="https://redirect.github.com/facebook/lexical/pull/8856">#8856</a>)</li>
</ul>
<h2>Deprecations</h2>
<ul>
<li><a
href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a>
— <code>$createTableSelection</code> is deprecated in favor of
<code>$createTableSelectionFrom</code>, which takes the table node plus
anchor/focus cells and validates them, instead of returning a blank
selection with placeholder <code>'root'</code> keys that must be
overwritten (<a
href="https://redirect.github.com/facebook/lexical/pull/8855">#8855</a>)</li>
<li><a
href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a>
— <code>getShape()</code> is deprecated because it computes incorrect
bounds for merged cells; use the now-exported
<code>$computeTableCellRectBoundary</code> (with
<code>$computeTableMap</code>) instead (<a
href="https://redirect.github.com/facebook/lexical/pull/8853">#8853</a>)</li>
</ul>
<h2>Notable Fixes</h2>
<p><strong><code>$config()</code> follow-ups (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>)</strong></p>
<ul>
<li>The synthesized <code>clone()</code> now applies
<code>afterCloneFrom()</code> when called directly (e.g.
<code>NodeClass.clone(node)</code>), fixing silent property loss across
25+ core nodes (<a
href="https://redirect.github.com/facebook/lexical/pull/8864">#8864</a>)</li>
<li>The synthesized <code>getType()</code> no longer returns the
superclass type when inherited, fixing node-type collisions during
editor registration (<a
href="https://redirect.github.com/facebook/lexical/pull/8867">#8867</a>)</li>
<li>The synthesized <code>getType()</code> no longer recurses infinitely
when a bundler copies it onto a subclass as an own static, fixing stack
overflows in compiled/minified builds (<a
href="https://redirect.github.com/facebook/lexical/pull/8869">#8869</a>)</li>
</ul>
<p><strong>Tables</strong></p>
<ul>
<li><code>DELETE_LINE_COMMAND</code> (Cmd/Ctrl-based delete-line
shortcuts) now works inside table cells instead of being silently
swallowed (<a
href="https://redirect.github.com/facebook/lexical/pull/8851">#8851</a>)</li>
<li>Table alignment now works when cells are selected in any direction,
not just top-left→bottom-right (<a
href="https://redirect.github.com/facebook/lexical/pull/8883">#8883</a>)</li>
</ul>
<p><strong>Selection &amp; editing</strong></p>
<ul>
<li>Editor updates dispatched from a read-only context (e.g. inside
<code>editor.read()</code>) now run in a fresh writable update instead
of being silently dropped, with a dev warning (<a
href="https://redirect.github.com/facebook/lexical/pull/8863">#8863</a>)</li>
<li>Firefox now creates a selection and shows the block cursor when
clicking in the gap between block decorators (<a
href="https://redirect.github.com/facebook/lexical/pull/8862">#8862</a>)</li>
<li>Pressing ArrowUp before a leading non-inline decorator no longer
moves the selection out of the editor (<a
href="https://redirect.github.com/facebook/lexical/pull/8887">#8887</a>)</li>
</ul>
<p><strong>Markdown, HTML &amp; code</strong></p>
<ul>
<li>Typing a list marker at the start of a heading no longer converts
the heading into a list (<a
href="https://redirect.github.com/facebook/lexical/pull/8879">#8879</a>)</li>
<li>HTML import now evaluates unrestricted CSS selector groups (e.g.
<code>p, .foo</code>) against all elements instead of only tag-matched
ones (<a
href="https://redirect.github.com/facebook/lexical/pull/8873">#8873</a>)</li>
<li>Concurrent async language/theme loads in the Shiki highlighter are
deduplicated and merged into a single history entry, avoiding spurious
undo states (<a
href="https://redirect.github.com/facebook/lexical/pull/8854">#8854</a>)</li>
<li><code>QuoteNode.updateDOM</code> now accepts the
<code>EditorConfig</code> argument the reconciler passes to every other
node, for API consistency (<a
href="https://redirect.github.com/facebook/lexical/pull/8882">#8882</a>)</li>
</ul>
<p><strong>Playground &amp; website</strong></p>
<ul>
<li>Playground scroll padding now accounts for the sticky toolbar so
selections scrolled into view from above aren't hidden behind it (<a
href="https://redirect.github.com/facebook/lexical/pull/8849">#8849</a>)</li>
<li>Fixed a homepage crash caused by a minified dev build, and isolated
each embedded example in its own error boundary so one failure no longer
takes down the page (<a
href="https://redirect.github.com/facebook/lexical/pull/8861">#8861</a>)</li>
</ul>
<h2>What's Changed</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/facebook/lexical/blob/main/CHANGELOG.md">@​lexical/link's
changelog</a>.</em></p>
<blockquote>
<h2>v0.49.0 (2026-07-29)</h2>
<ul>
<li>lexical-markdown Bug Fix Preserve headings when typing list
shortcuts (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8879">#8879</a>)
Steven Dang</li>
<li>lexical-rich-text Bug Fix Forward editorConfig to
QuoteNode.updateDOM (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8882">#8882</a>)
Sha Halimi</li>
<li>lexical-table Bug Fix Table alignment fails when selecting in
non-TL-BR direction (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8883">#8883</a>)
sahir</li>
<li>Breaking Changelexical Chore Remove redundant
registerCommandregisterNodeTransform generics (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8877">#8877</a>)
mayrang</li>
<li>lexical-html Bug Fix Dispatch unrestricted CSS selector groups (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8873">#8873</a>)
Madan kumar</li>
<li>lexical-playground Bug Fix Deterministic history coalescing for
flaky webkit undo tests (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8874">#8874</a>)
Bob Ippolito</li>
<li>lexical Performance Skip redundant selection restoration in
removeTextFromCaretRange (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8872">#8872</a>)
mayrang</li>
<li>lexical Refactor Simplify RangeSelection.insertText via removeText
decomposition (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8870">#8870</a>)
mayrang</li>
<li>lexical-list Chore Remove redundant decorator-adjacent backspace
handler (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8871">#8871</a>)
mayrang</li>
<li>lexical-table Feature Skip colgroup element in table DOM with
undefined col widths (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8850">#8850</a>)
Zachary Gallafent</li>
<li>lexical Bug Fix Create selection when clicking between block
decorators on Firefox (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8862">#8862</a>)
mayrang</li>
<li>fix config() synthesized getType() recurses infinitely when
inherited as own static (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8867">#8867</a>
follow-up) (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8869">#8869</a>)
Sherry</li>
<li>lexicallexical-website Bug Fix Homepage crash from optimized dev
build (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8861">#8861</a>)
Bob Ippolito</li>
<li>lexical Bug Fix run editor updates dispatched from a read-only
context in a fresh writable update (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8863">#8863</a>)
Sherry</li>
<li>lexical Feature Editor operation benchmarks (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8856">#8856</a>)
mayrang</li>
<li>lexical Chore Migrate LexicalSelection tests to
buildEditorFromExtensions (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8865">#8865</a>)
mayrang</li>
<li>fix config() synthesized getType() inherited by subclasses causes
node-type collision (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>)
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8867">#8867</a>)
Sherry</li>
<li>fix config() auto-synthesized clone() loses properties when called
directly (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>)
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8864">#8864</a>)
Sherry</li>
<li>lexical-tablelexical Feature Sticky scrollbar for overflowing tables
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8790">#8790</a>)
mayrang</li>
<li>lexical-code-shiki Bug Fix Deduplicate async loads and merge into
history (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8854">#8854</a>)
Alexis</li>
<li>lexical-table Chore Remove dead code in table command handlers (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8857">#8857</a>)
mayrang</li>
<li>lexical-tablelexical-playground Chore Deprecate getShape() and
migrate playground (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8853">#8853</a>)
mayrang</li>
<li>lexical-list Chore Rename isNestedListNode to isNestedListNode (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8843">#8843</a>)
Bob Ippolito</li>
<li>lexical-playground Bug Fix account for sticky toolbar in scroll
padding (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8849">#8849</a>)
Bob Ippolito</li>
<li>Breaking Changeslexical Refactor Port node classes to the config()
protocol (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>)
Bob Ippolito</li>
<li>lexical-table Chore Deprecate createTableSelection in favor of
createTableSelectionFrom (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8855">#8855</a>)
mayrang</li>
<li>lexical-table Bug Fix Enable DELETELINECOMMAND in table cells (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8851">#8851</a>)
mayrang</li>
<li>lexical Bug Fix Scope bench vitest projects to exclude regular test
files (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8852">#8852</a>)
mayrang</li>
<li>v0.48.0 (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8847">#8847</a>)
Bob Ippolito</li>
<li>v0.48.0 Lexical GitHub Actions Bot</li>
</ul>
<h2>v0.48.0 (2026-07-16)</h2>
<ul>
<li>lexical-reactlexical-table Bug Fix Enable table copy in read-only
mode (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8845">#8845</a>)
mayrang</li>
<li>lexical-extensionlexical-mdastdev-mdast-editor-example Feature Add
MdastHtmlExtension and Markdown custom-construct examples (collapsible,
kbd, alerts, footnotes) (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8826">#8826</a>)
Bob Ippolito</li>
<li>Fix fail closed in LinkNode.sanitizeUrl() on unparseable URLs (XSS)
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8846">#8846</a>)
xiezhenjia-meta</li>
<li>lexical Chore Fix serialize-javascript package dependency
vulnerability (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8803">#8803</a>)
vijay ojha</li>
<li>lexical-react Bug Fix Count block separators in character limit
overflow wrapping (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8840">#8840</a>)
mayrang</li>
<li>lexical-yjslexical-react Feature Customizable Yjs shared-type root
name (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8841">#8841</a>)
mayrang</li>
<li>lexical-list Bug Fix Backspace at start of list item outdents or
converts to paragraph (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8829">#8829</a>)
mayrang</li>
<li>lexical-table Feature Add moveTableRow function Add missing export
for unmergeCellNode (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8833">#8833</a>)</li>
<li>lexical-link Bug Fix disable link opening for disabled autolink in
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8839">#8839</a>)
Olivier Chevallier</li>
<li>lexical-rich-textlexical-plain-text Bug Fix dont cancel dragover for
text drags so native drops work again (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8842">#8842</a>)
Bob Ippolito</li>
<li>Open playground links in a new tab (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8837">#8837</a>)
Sherry</li>
<li>lexical-react Bug Fix Merge adjacent OverflowNodes in
useCharacterLimit (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8831">#8831</a>)
mayrang</li>
<li>lexical-code-shiki Bug Fix force re-tokenize after async language
load (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8830">#8830</a>)
Olivier Chevallier</li>
<li>lexical-tablelexical-playground Bug Fix Auto-scroll while
drag-selecting cells past the visible edge (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8822">#8822</a>)
Oleksandr Trukhnii</li>
<li>lexical-mdastlexical-markdown Bug Fix Roundtrip overlapping inline
formats (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8825">#8825</a>)
Bob Ippolito</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ffe90924bd"><code>ffe9092</code></a>
v0.49.0</li>
<li><a
href="5fc8eae3cd"><code>5fc8eae</code></a>
[Breaking Changes][lexical] Refactor: Port node classes to the $config()
prot...</li>
<li><a
href="ba4d6d0f9b"><code>ba4d6d0</code></a>
v0.48.0 (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8847">#8847</a>)</li>
<li><a
href="365516c5fc"><code>365516c</code></a>
Fix: fail closed in LinkNode.sanitizeUrl() on unparseable URLs (XSS) (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8846">#8846</a>)</li>
<li><a
href="71562324c7"><code>7156232</code></a>
[lexical-link] Bug Fix: disable link opening for disabled autolink in…
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8839">#8839</a>)</li>
<li><a
href="e4b7cc3f42"><code>e4b7cc3</code></a>
v0.47.0 (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8821">#8821</a>)</li>
<li><a
href="a7666ab11f"><code>a7666ab</code></a>
[*][lexical-devtools][lexical-playground] Chore: Update flow, hermes,
and bab...</li>
<li><a
href="e649ab28b7"><code>e649ab2</code></a>
[lexical][lexical-eslint-plugin] Feature: Add $getDocument() API and
Shadow D...</li>
<li><a
href="7b76175cc9"><code>7b76175</code></a>
[lexical-playground] Bug Fix: Support Unicode URLs in autolink matcher
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8787">#8787</a>)</li>
<li><a
href="62a4b30f38"><code>62a4b30</code></a>
[lexical][*] Feature: registerEventListener / registerEventListeners DOM
help...</li>
<li>Additional commits viewable in <a
href="https://github.com/facebook/lexical/commits/v0.49.0/packages/lexical-link">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>
Co-authored-by: Cody <noreply@paperclip.ing>
2026-08-13 13:17:29 -07:00
Nicky Leach 031003c5e1
fix(ui): search parent-issue picker on the server (#11334)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The board UI lets an operator choose a parent issue for an issue
> - The parent picker loads a priority-first page and filters that page
in the browser
> - A medium-priority or low-priority issue past the page limit never
enters the picker
> - This pull request sends typed parent-picker text to the server and
keeps the picker exclusions
> - The benefit is that the operator can select valid parent issues
beyond the default page

## Linked Issues or Issue Description

This pull request supersedes
[#6193](https://github.com/paperclipai/paperclip/pull/6193), whose old
file path no longer matches the current component tree.

**What happened?**

The parent picker fetched one default issue page and filtered it in the
browser. The default page sorts by priority and caps the result at 500
issues. Valid medium-priority and low-priority parent issues beyond that
page stayed hidden.

**Expected behavior**

The parent picker must search the server when the operator types text.
It must show matching issues beyond the default page while it keeps the
current issue and descendant exclusions.

**Steps to reproduce**

1. Open an issue in a company with more than 500 issues.
2. Open the parent picker and type the name of a medium-priority or
low-priority issue beyond the default page.
3. Observe that the picker does not show the matching issue.

**Paperclip version or commit**

Commit `c6965bd0237fd9536b41f1495e2a4bb252afcde7`.

**Deployment mode**

Local dev (`pnpm dev`).

## What Changed

- Send parent-picker searches to the issue list endpoint with `q` and a
bounded `limit` of 50.
- Keep the empty-search list, cycle exclusions, and current sort
behavior.
- Add a component test for a low-priority match hidden by the default
page.

## Verification

- Run `pnpm vitest run ui/src/components/IssueProperties.test.tsx`.
- Confirm that all 53 tests pass.
- Confirm that the new test checks `{ q, limit: 50 }` and the matching
issue.

## Risks

- Low risk. The change affects only parent-picker search requests.
- The server search uses the existing issue list query and does not
change stored data.

## Model Used

Codex, GPT-5, with tool use and code execution. The model assisted with
the change and test.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-13 12:18:49 -07:00
dependabot[bot] 403fcefb97
build(deps-dev): bump vite from 6.4.1 to 6.4.3 (#11317)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite)
from 6.4.1 to 6.4.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/releases">vite's
releases</a>.</em></p>
<blockquote>
<h2>v6.4.3</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v6.4.3/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v6.4.2</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/blob/v6.4.3/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted -->6.4.3 (2026-06-01)<!-- raw HTML omitted
--></h2>
<ul>
<li>fix: backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22572">#22572</a>,
reject windows alternate paths (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22576">#22576</a>)
(<a
href="96b0c10162">96b0c10</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/22572">#22572</a>
<a
href="https://redirect.github.com/vitejs/vite/issues/22576">#22576</a></li>
<li>fix(deps): backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22571">#22571</a>,
reject UNC paths for launch-editor-middleware (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22575">#22575</a>)
(<a
href="8fed5cf540">8fed5cf</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/22571">#22571</a>
<a
href="https://redirect.github.com/vitejs/vite/issues/22575">#22575</a></li>
</ul>
<h2><!-- raw HTML omitted -->6.4.2 (2026-04-06)<!-- raw HTML omitted
--></h2>
<ul>
<li>fix: apply server.fs check to env transport (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22159">#22159</a>)
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22163">#22163</a>)
(<a
href="fe28e47e94">fe28e47</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/22159">#22159</a>
<a
href="https://redirect.github.com/vitejs/vite/issues/22163">#22163</a></li>
<li>fix: avoid path traversal with optimize deps sourcemap handler (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22161">#22161</a>)
(<a
href="ca4da5d1fb">ca4da5d</a>),
closes <a
href="https://redirect.github.com/vitejs/vite/issues/22161">#22161</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6c2c881f15"><code>6c2c881</code></a>
release: v6.4.3</li>
<li><a
href="96b0c10162"><code>96b0c10</code></a>
fix: backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22572">#22572</a>,
reject windows alternate paths (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22576">#22576</a>)</li>
<li><a
href="8fed5cf540"><code>8fed5cf</code></a>
fix(deps): backport <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22571">#22571</a>,
reject UNC paths for launch-editor-middleware (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/2">#2</a>...</li>
<li><a
href="6b3fad02ab"><code>6b3fad0</code></a>
release: v6.4.2</li>
<li><a
href="ca4da5d1fb"><code>ca4da5d</code></a>
fix: avoid path traversal with optimize deps sourcemap handler (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22161">#22161</a>)</li>
<li><a
href="fe28e47e94"><code>fe28e47</code></a>
fix: apply server.fs check to env transport (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22159">#22159</a>)
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22163">#22163</a>)</li>
<li><a
href="5487f4f641"><code>5487f4f</code></a>
release: v6.4.1</li>
<li><a
href="1114b5d7ea"><code>1114b5d</code></a>
fix(dev): trim trailing slash before <code>server.fs.deny</code> check
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20968">#20968</a>)
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20969">#20969</a>)</li>
<li><a
href="f12697c0f6"><code>f12697c</code></a>
release: v6.4.0</li>
<li><a
href="ca6455ee9e"><code>ca6455e</code></a>
feat: allow passing down resolved config to vite's createServer (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20932">#20932</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite/commits/v6.4.3/packages/vite">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-08-13 12:14:30 -07:00
Nicky Leach 44694328a3
fix(issues): make DELETE /api/issues/:id succeed for issues with dependents (#11331)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server provides issue APIs and the database stores issue child
rows
> - The issue delete endpoint removes the parent issue before dependent
rows
> - Several issue foreign keys had no delete policy, so PostgreSQL
returned a foreign-key error
> - This pull request adds safe cascade and set-null policies and a
clear conflict response
> - The benefit is reliable issue deletion with a useful error when a
restricted audit row still blocks deletion

## Linked Issues or Issue Description

Fixes #7728
Fixes #4660
Fixes #7991
Fixes #4627
Fixes #5086

**What happened?**

`DELETE /api/issues/:id` returned HTTP 500 when dependent comments,
thread interactions, read states, inbox archives, feedback votes, or
ledger rows referenced the issue. The database raised SQLSTATE 23503
because several foreign keys had no delete policy.

**Expected behavior**

The endpoint must remove dependent rows that have no meaning without the
issue. It must keep ledger rows with a null issue reference. It must
return HTTP 409 when a restricted decision audit row still references
the issue.

**Steps to reproduce**

1. Create an issue.
2. Add a comment or thread interaction that references the issue.
3. Send `DELETE /api/issues/:id`.
4. Observe the HTTP 500 response.

**Paperclip version or commit**

Commit `1f8f456f8340823fe2bd891ae8933d942f190b7b`.

**Deployment mode**

Local dev with embedded PGlite or external PostgreSQL.

## What Changed

- Add `CASCADE` to five issue child foreign keys.
- Add `SET NULL` to the finance and cost event issue foreign keys.
- Keep decision audit references restricted.
- Map SQLSTATE 23503 from the issue delete service to HTTP 409.
- Add migration 0217 for the seven changed tables.
- Add regression tests for cascade deletion and restricted decision
references.

## Verification

- Run `pnpm --filter @paperclipai/db typecheck`.
- Run `pnpm --filter @paperclipai/server typecheck`.
- Run `npx vitest run src/__tests__/issue-remove-cascade.test.ts` from
`server/`.
- The regression test applies migration 0217 to a fresh embedded
PostgreSQL database.

## Risks

- Migration 0217 changes only seven foreign keys that reference
`issues.id`.
- Cascade deletion removes child rows that cannot exist without the
parent issue.
- Set-null preserves finance and cost ledger rows.
- Decision audit rows remain protected, so the endpoint can return HTTP
409.

## Model Used

Codex, based on GPT-5, with tool use and code-review support. The
implementation author used an AI coding agent. This PR handoff uses the
same model family to validate the commit and manage 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)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-13 12:02:15 -07:00
dependabot[bot] 062271e934
build(deps): bump mermaid from 11.16.0 to 11.16.1 (#11306)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.16.0 to
11.16.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mermaid-js/mermaid/releases">mermaid's
releases</a>.</em></p>
<blockquote>
<h2>mermaid@11.16.1</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a>
<a
href="12d472c9ed"><code>12d472c</code></a>
Thanks <a
href="https://github.com/aloisklink"><code>@​aloisklink</code></a>! -
fix: handle CSS sibling combinators in compileCSS</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a>
<a
href="2cd6dcf735"><code>2cd6dcf</code></a>
Thanks <a
href="https://github.com/aloisklink"><code>@​aloisklink</code></a>! -
fix: increase protections against prototype pollution</p>
<p>User-controlled input already has protections against prototype
pollution.</p>
<p>Fixes: GHSA-c4c3-pg64-4m4v</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a>
<a
href="99af3fc35e"><code>99af3fc</code></a>
Thanks <a
href="https://github.com/aloisklink"><code>@​aloisklink</code></a>! -
fix(architecture): use <code>Map</code>s and <code>Set</code>s to store
groups/services</p>
<p>Services are now rendered in the order they are defined and more
service IDs
are now supported.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a>
<a
href="2cd6dcf735"><code>2cd6dcf</code></a>
Thanks <a
href="https://github.com/aloisklink"><code>@​aloisklink</code></a>! -
deprecate: Deprecate the <code>mermaidAPI.setConfig()</code>
function</p>
<p>Calling this function has no observable effect, as the next time a
<code>render()</code> or <code>parse()</code> is called, the
<code>currentConfig</code> is cleared.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a>
<a
href="630aa7e5dd"><code>630aa7e</code></a>
Thanks <a
href="https://github.com/aloisklink"><code>@​aloisklink</code></a>! -
fix(xychart): support zero-width x-axis ranges</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a>
<a
href="59b22fad2b"><code>59b22fa</code></a>
Thanks <a
href="https://github.com/aloisklink"><code>@​aloisklink</code></a>! -
fix(radar): limit number of ticks to 32</p>
<p>Setting a ticks value higher than this would only show 32 ticks.</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7ecca0cd7f"><code>7ecca0c</code></a>
Version Packages (<a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8023">#8023</a>)</li>
<li><a
href="95b1b9cdba"><code>95b1b9c</code></a>
docs: change <code>mermaidAPI.setConfig()</code> changeset (<a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8024">#8024</a>)</li>
<li><a
href="acc69f172b"><code>acc69f1</code></a>
Merge pull request <a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8022">#8022</a>
from mermaid-js/release/11.16.1</li>
<li><a
href="eba72879e9"><code>eba7287</code></a>
docs: point changesets to correct commit hashes</li>
<li><a
href="12d472c9ed"><code>12d472c</code></a>
Merge commit from fork</li>
<li><a
href="2cd6dcf735"><code>2cd6dcf</code></a>
Merge commit from fork</li>
<li><a
href="630aa7e5dd"><code>630aa7e</code></a>
Merge commit from fork</li>
<li><a
href="59b22fad2b"><code>59b22fa</code></a>
Merge commit from fork</li>
<li><a
href="99af3fc35e"><code>99af3fc</code></a>
Merge commit from fork</li>
<li><a
href="2337f7ecd6"><code>2337f7e</code></a>
Merge branch 'test/improve-example.html' into release/11.16.1</li>
<li>Additional commits viewable in <a
href="https://github.com/mermaid-js/mermaid/compare/mermaid@11.16.0...mermaid@11.16.1">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-08-13 11:57:00 -07:00
Nicky Leach 68ea3fcf53
test(ui): stabilize annotation popover submit-shortcut test (#11330)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The board UI lets users add comments to document annotations
> - The annotation popover test submits a comment with a keyboard
shortcut
> - React can delay the controlled textarea update under load
> - The test can then send the shortcut before the handler sees the
typed value
> - This pull request waits for the value update before it sends the
shortcut
> - The benefit is a stable test that checks the real submit path

## Linked Issues or Issue Description

**What happened?**

The annotation popover test typed a comment and sent the submit shortcut
in one synchronous step. Under load, React sometimes had not committed
the typed value when the handler ran. The mutation then ran zero times.

**Expected behavior**

The test should wait for the controlled textarea value before it sends
the submit shortcut. The handler should read the comment and call the
create mutation.

**Steps to reproduce**

1. Run `npx vitest run
src/components/DocumentAnnotationPopover.test.tsx` from `ui/`.
2. Repeat the test under system load.
3. Observe intermittent failures where the create mutation runs zero
times.

**Paperclip version or commit**

The test runs against commit `9a08def5752bb13e4cbcb304c6295e175c92db3c`.

**Deployment mode**

This change affects the UI test suite only. It does not depend on a
deployment mode.

## What Changed

- Wait for the Comment button to enable after the controlled value
updates.
- Send the submit shortcut after React commits the typed value.
- Keep the test focused on the compose-mode submit path.

## Verification

- The author ran `npx vitest run
src/components/DocumentAnnotationPopover.test.tsx` from `ui/` with 3
tests passing.
- The current handoff worktree could not repeat the test because its
installed dependencies lack `react/jsx-runtime`.
- GitHub Actions will run the required project checks.

## Risks

Low risk. The change updates one UI test file and does not change
product code.

## Model Used

OpenAI Codex, GPT-5. The model used tool calls and code execution. The
context window size was not provided.

## 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-08-13 11:44:29 -07:00
github-actions[bot] 7ee2b5235d
chore(lockfile): refresh pnpm-lock.yaml (#11328)
Auto-generated lockfile refresh after dependencies changed on master.
This PR only updates pnpm-lock.yaml.

Co-authored-by: lockfile-bot <lockfile-bot@users.noreply.github.com>
2026-08-13 11:34:11 -07:00
dependabot[bot] eb6c6a8e9b
build(deps): bump lexical from 0.46.0 to 0.49.0 (#10724)
Bumps
[lexical](https://github.com/facebook/lexical/tree/HEAD/packages/lexical)
from 0.46.0 to 0.49.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/facebook/lexical/releases">lexical's
releases</a>.</em></p>
<blockquote>
<p>v0.49.0 is a monthly release headlined by the completion of the
<code>$config()</code> protocol migration for Lexical's built-in node
classes (<a
href="https://redirect.github.com/facebook/lexical/pull/8640">#8640</a>),
a breaking change that replaces per-node
<code>getType()</code>/<code>clone()</code>/<code>importJSON()</code>/<code>importDOM()</code>/<code>transform()</code>
boilerplate with runtime-synthesized behavior. It ships with a cluster
of follow-up fixes hardening the synthesized <code>getType()</code> and
<code>clone()</code> (including a stack overflow in compiled builds), an
opt-in sticky horizontal scrollbar for overflowing tables, and a broad
batch of fixes across tables, Markdown, HTML, code highlighting, and
selection.</p>
<h2>Breaking Changes</h2>
<ul>
<li><a
href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a>
— Built-in node classes are ported to the <a
href="https://lexical.dev/docs/concepts/nodes"><code>$config()</code></a>
protocol. The static <code>importJSON()</code>,
<code>importDOM()</code>, <code>clone()</code>, and
<code>transform()</code> methods are no longer present on ported nodes;
use the higher-level equivalents instead
(<code>LexicalEditor.parseEditorState</code>/<code>$generateNodesFromDOM</code>,
the <code>$cloneWithProperties</code> helper, and extensions or
<code>$transform</code> in <code>$config</code>). <code>getType()</code>
is unchanged and safe to keep using, <code>__type</code> is now
<code>readonly</code>, and node constructors require zero-argument
defaults. Custom nodes may keep their static methods, but adopting
<code>$config()</code> is recommended (<a
href="https://redirect.github.com/facebook/lexical/pull/8640">#8640</a>)</li>
<li><a
href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a>
— <code>LexicalCommand&lt;T&gt;</code>'s payload type is now invariant
to prevent unsafe structural compatibility between commands. Previously
correct code (including explicit generics) needs no changes; only unsafe
<code>LexicalCommand&lt;unknown&gt;</code> usage must switch to the
newly exported <code>AnyLexicalCommand</code> alias.
<code>dispatchCommand</code>'s payload argument is now optional when a
command's payload type is <code>undefined</code> or <code>void</code>,
and redundant explicit type parameters were dropped from the
<code>registerCommand</code>/<code>registerNodeTransform</code> call
sites (<a
href="https://redirect.github.com/facebook/lexical/pull/8877">#8877</a>)</li>
</ul>
<h2>New APIs &amp; Features</h2>
<ul>
<li><a
href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a>
— Opt-in sticky horizontal scrollbar for tables wider than their
container, so the scrollbar stays anchored at the viewport bottom
instead of only being reachable at the end of the table. Enable it with
<code>hasStickyScrollbar: true</code> in the table config; style it via
the new <code>tableStickyScrollbar</code> theme key (<a
href="https://redirect.github.com/facebook/lexical/pull/8790">#8790</a>)</li>
<li><a
href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a>
— The <code>&lt;colgroup&gt;</code> element is now omitted from the
table DOM when column widths are undefined, producing cleaner markup and
letting the browser auto-size columns; it is created or removed
dynamically as widths change (<a
href="https://redirect.github.com/facebook/lexical/pull/8850">#8850</a>)</li>
<li><a
href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a>
— Added an editor operation benchmark suite (paragraph split, bold
formatting, range deletion, paste, select-all format) measured through
full DOM reconciliation, to help contributors track core editing
performance (<a
href="https://redirect.github.com/facebook/lexical/pull/8856">#8856</a>)</li>
</ul>
<h2>Deprecations</h2>
<ul>
<li><a
href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a>
— <code>$createTableSelection</code> is deprecated in favor of
<code>$createTableSelectionFrom</code>, which takes the table node plus
anchor/focus cells and validates them, instead of returning a blank
selection with placeholder <code>'root'</code> keys that must be
overwritten (<a
href="https://redirect.github.com/facebook/lexical/pull/8855">#8855</a>)</li>
<li><a
href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a>
— <code>getShape()</code> is deprecated because it computes incorrect
bounds for merged cells; use the now-exported
<code>$computeTableCellRectBoundary</code> (with
<code>$computeTableMap</code>) instead (<a
href="https://redirect.github.com/facebook/lexical/pull/8853">#8853</a>)</li>
</ul>
<h2>Notable Fixes</h2>
<p><strong><code>$config()</code> follow-ups (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8640">#8640</a>)</strong></p>
<ul>
<li>The synthesized <code>clone()</code> now applies
<code>afterCloneFrom()</code> when called directly (e.g.
<code>NodeClass.clone(node)</code>), fixing silent property loss across
25+ core nodes (<a
href="https://redirect.github.com/facebook/lexical/pull/8864">#8864</a>)</li>
<li>The synthesized <code>getType()</code> no longer returns the
superclass type when inherited, fixing node-type collisions during
editor registration (<a
href="https://redirect.github.com/facebook/lexical/pull/8867">#8867</a>)</li>
<li>The synthesized <code>getType()</code> no longer recurses infinitely
when a bundler copies it onto a subclass as an own static, fixing stack
overflows in compiled/minified builds (<a
href="https://redirect.github.com/facebook/lexical/pull/8869">#8869</a>)</li>
</ul>
<p><strong>Tables</strong></p>
<ul>
<li><code>DELETE_LINE_COMMAND</code> (Cmd/Ctrl-based delete-line
shortcuts) now works inside table cells instead of being silently
swallowed (<a
href="https://redirect.github.com/facebook/lexical/pull/8851">#8851</a>)</li>
<li>Table alignment now works when cells are selected in any direction,
not just top-left→bottom-right (<a
href="https://redirect.github.com/facebook/lexical/pull/8883">#8883</a>)</li>
</ul>
<p><strong>Selection &amp; editing</strong></p>
<ul>
<li>Editor updates dispatched from a read-only context (e.g. inside
<code>editor.read()</code>) now run in a fresh writable update instead
of being silently dropped, with a dev warning (<a
href="https://redirect.github.com/facebook/lexical/pull/8863">#8863</a>)</li>
<li>Firefox now creates a selection and shows the block cursor when
clicking in the gap between block decorators (<a
href="https://redirect.github.com/facebook/lexical/pull/8862">#8862</a>)</li>
<li>Pressing ArrowUp before a leading non-inline decorator no longer
moves the selection out of the editor (<a
href="https://redirect.github.com/facebook/lexical/pull/8887">#8887</a>)</li>
</ul>
<p><strong>Markdown, HTML &amp; code</strong></p>
<ul>
<li>Typing a list marker at the start of a heading no longer converts
the heading into a list (<a
href="https://redirect.github.com/facebook/lexical/pull/8879">#8879</a>)</li>
<li>HTML import now evaluates unrestricted CSS selector groups (e.g.
<code>p, .foo</code>) against all elements instead of only tag-matched
ones (<a
href="https://redirect.github.com/facebook/lexical/pull/8873">#8873</a>)</li>
<li>Concurrent async language/theme loads in the Shiki highlighter are
deduplicated and merged into a single history entry, avoiding spurious
undo states (<a
href="https://redirect.github.com/facebook/lexical/pull/8854">#8854</a>)</li>
<li><code>QuoteNode.updateDOM</code> now accepts the
<code>EditorConfig</code> argument the reconciler passes to every other
node, for API consistency (<a
href="https://redirect.github.com/facebook/lexical/pull/8882">#8882</a>)</li>
</ul>
<p><strong>Playground &amp; website</strong></p>
<ul>
<li>Playground scroll padding now accounts for the sticky toolbar so
selections scrolled into view from above aren't hidden behind it (<a
href="https://redirect.github.com/facebook/lexical/pull/8849">#8849</a>)</li>
<li>Fixed a homepage crash caused by a minified dev build, and isolated
each embedded example in its own error boundary so one failure no longer
takes down the page (<a
href="https://redirect.github.com/facebook/lexical/pull/8861">#8861</a>)</li>
</ul>
<h2>What's Changed</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/facebook/lexical/blob/main/CHANGELOG.md">lexical's
changelog</a>.</em></p>
<blockquote>
<h2>v0.49.0 (2026-07-29)</h2>
<ul>
<li>lexical-markdown Bug Fix Preserve headings when typing list
shortcuts (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8879">#8879</a>)
Steven Dang</li>
<li>lexical-rich-text Bug Fix Forward editorConfig to
QuoteNode.updateDOM (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8882">#8882</a>)
Sha Halimi</li>
<li>lexical-table Bug Fix Table alignment fails when selecting in
non-TL-BR direction (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8883">#8883</a>)
sahir</li>
<li>Breaking Changelexical Chore Remove redundant
registerCommandregisterNodeTransform generics (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8877">#8877</a>)
mayrang</li>
<li>lexical-html Bug Fix Dispatch unrestricted CSS selector groups (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8873">#8873</a>)
Madan kumar</li>
<li>lexical-playground Bug Fix Deterministic history coalescing for
flaky webkit undo tests (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8874">#8874</a>)
Bob Ippolito</li>
<li>lexical Performance Skip redundant selection restoration in
removeTextFromCaretRange (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8872">#8872</a>)
mayrang</li>
<li>lexical Refactor Simplify RangeSelection.insertText via removeText
decomposition (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8870">#8870</a>)
mayrang</li>
<li>lexical-list Chore Remove redundant decorator-adjacent backspace
handler (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8871">#8871</a>)
mayrang</li>
<li>lexical-table Feature Skip colgroup element in table DOM with
undefined col widths (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8850">#8850</a>)
Zachary Gallafent</li>
<li>lexical Bug Fix Create selection when clicking between block
decorators on Firefox (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8862">#8862</a>)
mayrang</li>
<li>fix config() synthesized getType() recurses infinitely when
inherited as own static (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8867">#8867</a>
follow-up) (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8869">#8869</a>)
Sherry</li>
<li>lexicallexical-website Bug Fix Homepage crash from optimized dev
build (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8861">#8861</a>)
Bob Ippolito</li>
<li>lexical Bug Fix run editor updates dispatched from a read-only
context in a fresh writable update (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8863">#8863</a>)
Sherry</li>
<li>lexical Feature Editor operation benchmarks (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8856">#8856</a>)
mayrang</li>
<li>lexical Chore Migrate LexicalSelection tests to
buildEditorFromExtensions (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8865">#8865</a>)
mayrang</li>
<li>fix config() synthesized getType() inherited by subclasses causes
node-type collision (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8640">#8640</a>)
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8867">#8867</a>)
Sherry</li>
<li>fix config() auto-synthesized clone() loses properties when called
directly (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8640">#8640</a>)
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8864">#8864</a>)
Sherry</li>
<li>lexical-tablelexical Feature Sticky scrollbar for overflowing tables
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8790">#8790</a>)
mayrang</li>
<li>lexical-code-shiki Bug Fix Deduplicate async loads and merge into
history (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8854">#8854</a>)
Alexis</li>
<li>lexical-table Chore Remove dead code in table command handlers (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8857">#8857</a>)
mayrang</li>
<li>lexical-tablelexical-playground Chore Deprecate getShape() and
migrate playground (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8853">#8853</a>)
mayrang</li>
<li>lexical-list Chore Rename isNestedListNode to isNestedListNode (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8843">#8843</a>)
Bob Ippolito</li>
<li>lexical-playground Bug Fix account for sticky toolbar in scroll
padding (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8849">#8849</a>)
Bob Ippolito</li>
<li>Breaking Changeslexical Refactor Port node classes to the config()
protocol (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8640">#8640</a>)
Bob Ippolito</li>
<li>lexical-table Chore Deprecate createTableSelection in favor of
createTableSelectionFrom (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8855">#8855</a>)
mayrang</li>
<li>lexical-table Bug Fix Enable DELETELINECOMMAND in table cells (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8851">#8851</a>)
mayrang</li>
<li>lexical Bug Fix Scope bench vitest projects to exclude regular test
files (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8852">#8852</a>)
mayrang</li>
<li>v0.48.0 (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8847">#8847</a>)
Bob Ippolito</li>
<li>v0.48.0 Lexical GitHub Actions Bot</li>
</ul>
<h2>v0.48.0 (2026-07-16)</h2>
<ul>
<li>lexical-reactlexical-table Bug Fix Enable table copy in read-only
mode (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8845">#8845</a>)
mayrang</li>
<li>lexical-extensionlexical-mdastdev-mdast-editor-example Feature Add
MdastHtmlExtension and Markdown custom-construct examples (collapsible,
kbd, alerts, footnotes) (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8826">#8826</a>)
Bob Ippolito</li>
<li>Fix fail closed in LinkNode.sanitizeUrl() on unparseable URLs (XSS)
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8846">#8846</a>)
xiezhenjia-meta</li>
<li>lexical Chore Fix serialize-javascript package dependency
vulnerability (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8803">#8803</a>)
vijay ojha</li>
<li>lexical-react Bug Fix Count block separators in character limit
overflow wrapping (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8840">#8840</a>)
mayrang</li>
<li>lexical-yjslexical-react Feature Customizable Yjs shared-type root
name (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8841">#8841</a>)
mayrang</li>
<li>lexical-list Bug Fix Backspace at start of list item outdents or
converts to paragraph (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8829">#8829</a>)
mayrang</li>
<li>lexical-table Feature Add moveTableRow function Add missing export
for unmergeCellNode (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8833">#8833</a>)</li>
<li>lexical-link Bug Fix disable link opening for disabled autolink in
(<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8839">#8839</a>)
Olivier Chevallier</li>
<li>lexical-rich-textlexical-plain-text Bug Fix dont cancel dragover for
text drags so native drops work again (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8842">#8842</a>)
Bob Ippolito</li>
<li>Open playground links in a new tab (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8837">#8837</a>)
Sherry</li>
<li>lexical-react Bug Fix Merge adjacent OverflowNodes in
useCharacterLimit (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8831">#8831</a>)
mayrang</li>
<li>lexical-code-shiki Bug Fix force re-tokenize after async language
load (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8830">#8830</a>)
Olivier Chevallier</li>
<li>lexical-tablelexical-playground Bug Fix Auto-scroll while
drag-selecting cells past the visible edge (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8822">#8822</a>)
Oleksandr Trukhnii</li>
<li>lexical-mdastlexical-markdown Bug Fix Roundtrip overlapping inline
formats (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8825">#8825</a>)
Bob Ippolito</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ffe90924bd"><code>ffe9092</code></a>
v0.49.0</li>
<li><a
href="82cdae1d87"><code>82cdae1</code></a>
[Breaking Change][lexical] Chore: Remove redundant
registerCommand/registerNo...</li>
<li><a
href="0a84aef949"><code>0a84aef</code></a>
[lexical] Performance: Skip redundant selection restoration in
$removeTextFro...</li>
<li><a
href="97fef1f27b"><code>97fef1f</code></a>
[lexical] Refactor: Simplify RangeSelection.insertText via removeText
decompo...</li>
<li><a
href="443012cdbb"><code>443012c</code></a>
[lexical] Bug Fix: Create selection when clicking between block
decorators on...</li>
<li><a
href="fccc283e57"><code>fccc283</code></a>
fix: $config() synthesized getType() recurses infinitely when inherited
as ow...</li>
<li><a
href="3429b575bb"><code>3429b57</code></a>
[lexical][lexical-website] Bug Fix: Homepage crash from optimized dev
build (...</li>
<li><a
href="aabfaaad4b"><code>aabfaaa</code></a>
[lexical] Bug Fix: run editor updates dispatched from a read-only
context in ...</li>
<li><a
href="e5e345d7e1"><code>e5e345d</code></a>
[lexical] Feature: Editor operation benchmarks (<a
href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8856">#8856</a>)</li>
<li><a
href="385a93c009"><code>385a93c</code></a>
[lexical] Chore: Migrate LexicalSelection tests to
buildEditorFromExtensions ...</li>
<li>Additional commits viewable in <a
href="https://github.com/facebook/lexical/commits/v0.49.0/packages/lexical">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-08-13 10:26:17 -07:00
dependabot[bot] 3cd596725d
build(deps): bump @modelcontextprotocol/sdk from 1.29.0 to 1.30.0 (#10729)
Bumps
[@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk)
from 1.29.0 to 1.30.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/modelcontextprotocol/typescript-sdk/releases">@​modelcontextprotocol/sdk's
releases</a>.</em></p>
<blockquote>
<h2>1.30.0</h2>
<h2>What's Changed</h2>
<ul>
<li>fix(server): prioritize zod issues and format them by <a
href="https://github.com/mozmo15"><code>@​mozmo15</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/1503">modelcontextprotocol/typescript-sdk#1503</a></li>
<li>chore(ci): switch publish to OIDC trusted publishing by <a
href="https://github.com/felixweinberger"><code>@​felixweinberger</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/1839">modelcontextprotocol/typescript-sdk#1839</a></li>
<li>Add end-to-end test suite by <a
href="https://github.com/felixweinberger"><code>@​felixweinberger</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2167">modelcontextprotocol/typescript-sdk#2167</a></li>
<li>v1 stdio buffer limit by <a
href="https://github.com/KKonstantinov"><code>@​KKonstantinov</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2239">modelcontextprotocol/typescript-sdk#2239</a></li>
<li>fix: support Zod 3.25 method literals by <a
href="https://github.com/mattzcarey"><code>@​mattzcarey</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2368">modelcontextprotocol/typescript-sdk#2368</a></li>
<li>Validate Content-Type by parsed media type instead of substring
match (v1.x) by <a
href="https://github.com/felixweinberger"><code>@​felixweinberger</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2444">modelcontextprotocol/typescript-sdk#2444</a></li>
<li>fix: send SSE keep-alive comment frames from Streamable HTTP server
transport (v1.x) by <a
href="https://github.com/mattzcarey"><code>@​mattzcarey</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2538">modelcontextprotocol/typescript-sdk#2538</a></li>
<li>fix(deps): widen <code>@​hono/node-server</code> past
GHSA-frvp-7c67-39w9 by <a
href="https://github.com/arimu1"><code>@​arimu1</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2549">modelcontextprotocol/typescript-sdk#2549</a></li>
<li>Fix SSE keep-alive timer lifecycle in Streamable HTTP server
transport (v1.x) by <a
href="https://github.com/felixweinberger"><code>@​felixweinberger</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2547">modelcontextprotocol/typescript-sdk#2547</a></li>
<li>chore: bump version to 1.30.0 by <a
href="https://github.com/felixweinberger"><code>@​felixweinberger</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2563">modelcontextprotocol/typescript-sdk#2563</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/mozmo15"><code>@​mozmo15</code></a> made
their first contribution in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/1503">modelcontextprotocol/typescript-sdk#1503</a></li>
<li><a href="https://github.com/arimu1"><code>@​arimu1</code></a> made
their first contribution in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2549">modelcontextprotocol/typescript-sdk#2549</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/modelcontextprotocol/typescript-sdk/compare/v1.29.0...1.30.0">https://github.com/modelcontextprotocol/typescript-sdk/compare/v1.29.0...1.30.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2d889f2b32"><code>2d889f2</code></a>
chore: bump version to 1.30.0 (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2563">#2563</a>)</li>
<li><a
href="e3f3daa12c"><code>e3f3daa</code></a>
Fix SSE keep-alive timer lifecycle in Streamable HTTP server transport
(v1.x)...</li>
<li><a
href="bb5a718cbf"><code>bb5a718</code></a>
fix(deps): widen <code>@​hono/node-server</code> past
GHSA-frvp-7c67-39w9 (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2549">#2549</a>)</li>
<li><a
href="1dad2634ce"><code>1dad263</code></a>
fix: send SSE keep-alive comment frames from Streamable HTTP server
transport...</li>
<li><a
href="69749aa508"><code>69749aa</code></a>
Validate Content-Type by parsed media type instead of substring match
(v1.x) ...</li>
<li><a
href="369513df7b"><code>369513d</code></a>
fix: support Zod 3.25 method literals (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2368">#2368</a>)</li>
<li><a
href="e7ee57c2f3"><code>e7ee57c</code></a>
v1 stdio buffer limit (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2239">#2239</a>)</li>
<li><a
href="c36e1ef5bb"><code>c36e1ef</code></a>
Add end-to-end test suite (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2167">#2167</a>)</li>
<li><a
href="bf1e022bd2"><code>bf1e022</code></a>
chore(ci): switch publish to OIDC trusted publishing (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/1839">#1839</a>)</li>
<li><a
href="9edbab7a09"><code>9edbab7</code></a>
fix(server): prioritize zod issues and format them (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/1503">#1503</a>)</li>
<li>See full diff in <a
href="https://github.com/modelcontextprotocol/typescript-sdk/compare/v1.29.0...1.30.0">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/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for <code>@​modelcontextprotocol/sdk</code> since your current
version.</p>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-13 10:22:46 -07:00
dependabot[bot] 817225415c
build(deps-dev): bump rollup from 4.62.2 to 4.62.4 (#11319)
Bumps [rollup](https://github.com/rollup/rollup) from 4.62.2 to 4.62.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rollup/rollup/releases">rollup's
releases</a>.</em></p>
<blockquote>
<h2>v4.62.4</h2>
<h2>4.62.4</h2>
<p><em>2026-08-01</em></p>
<h3>Bug Fixes</h3>
<ul>
<li>Resolve a regression when using Rollup on older Linux distributions
(<a
href="https://redirect.github.com/rollup/rollup/issues/6467">#6467</a>)</li>
</ul>
<h3>Pull Requests</h3>
<ul>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6463">#6463</a>:
docs: add llms.txt documentation index for LLMs and agents (<a
href="https://github.com/abyworkings-coder"><code>@​abyworkings-coder</code></a>,
<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6464">#6464</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6465">#6465</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6466">#6466</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6467">#6467</a>:
ci: fix linux-gnu glibc regression and enforce glibc ≤ 2.28
compatibility (<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
</ul>
<h2>v4.62.3</h2>
<h2>4.62.3</h2>
<p><em>2026-07-26</em></p>
<h3>Bug Fixes</h3>
<ul>
<li>Sanitize illegal characters preserved modules input base (<a
href="https://redirect.github.com/rollup/rollup/issues/6439">#6439</a>)</li>
</ul>
<h3>Pull Requests</h3>
<ul>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6421">#6421</a>:
docs: update x_google_ignoreList link to canonical URL (<a
href="https://github.com/DucMinhNe"><code>@​DucMinhNe</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6422">#6422</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6423">#6423</a>:
chore(deps): update actions/checkout action to v7 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6424">#6424</a>:
chore(deps): update dependency eslint-plugin-unicorn to v68 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6425">#6425</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6426">#6426</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6432">#6432</a>:
fix: make isLegal idempotent by not using a global-flag regex (<a
href="https://github.com/spokodev"><code>@​spokodev</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6433">#6433</a>:
docs: clarify sideEffects and moduleSideEffects (<a
href="https://github.com/ishaanlabs-gg"><code>@​ishaanlabs-gg</code></a>,
<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6434">#6434</a>:
chore(deps): update dtolnay/rust-toolchain digest to 4be7066 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6435">#6435</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6436">#6436</a>:
chore(deps): update actions/cache action to v6 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6438">#6438</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6439">#6439</a>:
Sanitize input base before computing preserved module chunk names (<a
href="https://github.com/MahinAnowar"><code>@​MahinAnowar</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6443">#6443</a>:
chore(deps): update dependency eslint-plugin-unicorn to v71 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6444">#6444</a>:
fix(deps): update rust crate swc_compiler_base to v60 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6446">#6446</a>:
chore(deps): update dtolnay/rust-toolchain digest to 4cda84d (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6447">#6447</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6448">#6448</a>:
chore(deps): update actions/setup-node action to v7 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6449">#6449</a>:
chore(deps): update dependency eslint-plugin-unicorn to v72 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6450">#6450</a>:
chore(deps): update dependency pinia to v4 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6451">#6451</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6455">#6455</a>:
docs: fix broken commonjs namedExports link in troubleshooting (<a
href="https://github.com/Hashim1999164"><code>@​Hashim1999164</code></a>,
<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rollup/rollup/blob/master/CHANGELOG.md">rollup's
changelog</a>.</em></p>
<blockquote>
<h2>4.62.4</h2>
<p><em>2026-08-01</em></p>
<h3>Bug Fixes</h3>
<ul>
<li>Resolve a regression when using Rollup on older Linux distributions
(<a
href="https://redirect.github.com/rollup/rollup/issues/6467">#6467</a>)</li>
</ul>
<h3>Pull Requests</h3>
<ul>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6463">#6463</a>:
docs: add llms.txt documentation index for LLMs and agents (<a
href="https://github.com/abyworkings-coder"><code>@​abyworkings-coder</code></a>,
<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6464">#6464</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6465">#6465</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6466">#6466</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6467">#6467</a>:
ci: fix linux-gnu glibc regression and enforce glibc ≤ 2.28
compatibility (<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
</ul>
<h2>4.62.3</h2>
<p><em>2026-07-26</em></p>
<h3>Bug Fixes</h3>
<ul>
<li>Sanitize illegal characters preserved modules input base (<a
href="https://redirect.github.com/rollup/rollup/issues/6439">#6439</a>)</li>
</ul>
<h3>Pull Requests</h3>
<ul>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6421">#6421</a>:
docs: update x_google_ignoreList link to canonical URL (<a
href="https://github.com/DucMinhNe"><code>@​DucMinhNe</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6422">#6422</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6423">#6423</a>:
chore(deps): update actions/checkout action to v7 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6424">#6424</a>:
chore(deps): update dependency eslint-plugin-unicorn to v68 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6425">#6425</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6426">#6426</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6432">#6432</a>:
fix: make isLegal idempotent by not using a global-flag regex (<a
href="https://github.com/spokodev"><code>@​spokodev</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6433">#6433</a>:
docs: clarify sideEffects and moduleSideEffects (<a
href="https://github.com/ishaanlabs-gg"><code>@​ishaanlabs-gg</code></a>,
<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6434">#6434</a>:
chore(deps): update dtolnay/rust-toolchain digest to 4be7066 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6435">#6435</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6436">#6436</a>:
chore(deps): update actions/cache action to v6 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6438">#6438</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6439">#6439</a>:
Sanitize input base before computing preserved module chunk names (<a
href="https://github.com/MahinAnowar"><code>@​MahinAnowar</code></a>, <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6443">#6443</a>:
chore(deps): update dependency eslint-plugin-unicorn to v71 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6444">#6444</a>:
fix(deps): update rust crate swc_compiler_base to v60 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6446">#6446</a>:
chore(deps): update dtolnay/rust-toolchain digest to 4cda84d (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6447">#6447</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6448">#6448</a>:
chore(deps): update actions/setup-node action to v7 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6449">#6449</a>:
chore(deps): update dependency eslint-plugin-unicorn to v72 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6450">#6450</a>:
chore(deps): update dependency pinia to v4 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6451">#6451</a>:
chore(deps): lock file maintenance (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6455">#6455</a>:
docs: fix broken commonjs namedExports link in troubleshooting (<a
href="https://github.com/Hashim1999164"><code>@​Hashim1999164</code></a>,
<a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6456">#6456</a>:
fix(deps): update minor/patch updates (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot])</li>
<li><a
href="https://redirect.github.com/rollup/rollup/pull/6457">#6457</a>:
chore(deps): update dependency magic-string to v1 (<a
href="https://github.com/renovate"><code>@​renovate</code></a>[bot], <a
href="https://github.com/lukastaegert"><code>@​lukastaegert</code></a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ddc4ffab62"><code>ddc4ffa</code></a>
4.62.4</li>
<li><a
href="86d171076b"><code>86d1710</code></a>
Update audit resolve</li>
<li><a
href="7beedfa949"><code>7beedfa</code></a>
ci: fix linux-gnu glibc regression and enforce glibc ≤ 2.28
compatibility (<a
href="https://redirect.github.com/rollup/rollup/issues/6">#6</a>...</li>
<li><a
href="9c2c58d556"><code>9c2c58d</code></a>
docs: add llms.txt documentation index for LLMs and agents (<a
href="https://redirect.github.com/rollup/rollup/issues/6463">#6463</a>)</li>
<li><a
href="dc692883d8"><code>dc69288</code></a>
chore(deps): lock file maintenance (<a
href="https://redirect.github.com/rollup/rollup/issues/6466">#6466</a>)</li>
<li><a
href="5ee08215ea"><code>5ee0821</code></a>
chore(deps): lock file maintenance (<a
href="https://redirect.github.com/rollup/rollup/issues/6465">#6465</a>)</li>
<li><a
href="4501389a63"><code>4501389</code></a>
fix(deps): update minor/patch updates (<a
href="https://redirect.github.com/rollup/rollup/issues/6464">#6464</a>)</li>
<li><a
href="a80a1974c5"><code>a80a197</code></a>
4.62.3</li>
<li><a
href="e87e19b31e"><code>e87e19b</code></a>
Update audit resolve</li>
<li><a
href="72f98e9922"><code>72f98e9</code></a>
Fix build:docs after rollup update (<a
href="https://redirect.github.com/rollup/rollup/issues/6460">#6460</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/rollup/rollup/compare/v4.62.2...v4.62.4">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-08-13 10:16:26 -07:00
Apolinario Ratio 7787106e5c
fix board key issue writes across assignees (#9025)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Board users and board API keys coordinate agents by commenting on
and updating issues.
> - `issue:comment` and `issue:mutate` are intentionally null-mapped
authorization actions, so they need explicit same-company fallback
handling.
> - Same-company board-key writes worked for unassigned or same-actor
issues but failed for issues assigned to another agent.
> - That blocked cross-agent coordination because a board key could not
comment on or patch another agent's issue even inside the same company.
> - This pull request adds the missing board-member issue-write fallback
while keeping viewers denied and sparse service calls fail-closed.
> - The benefit is that non-viewer board members can coordinate agent
work across assignees without restoring broad instance-admin elevation.

## Linked Issues or Issue Description

No public GitHub issue exists. Duplicate search performed:

- `gh search prs --repo paperclipai/paperclip "board key issue mutate"`
returned only this PR.
- `gh search issues --repo paperclipai/paperclip "board key
authorization boundary"` returned no issues.

Bug description:

### What happened

Same-company board-key actors received `403 "Issue is outside this
actor's authorization boundary"` when posting comments or patching
issues assigned to another agent.

### Expected behavior

Active same-company non-viewer board members can comment on and mutate
issues in their company, regardless of agent assignee; viewer members
remain denied.

### Steps to reproduce

Authenticate as a board API key for an active non-viewer company member,
then `POST /api/issues/{id}/comments` or `PATCH /api/issues/{id}`
against an issue assigned to a different agent in the same company.

### Paperclip version or commit

Observed against the current published 2026.626.0 package line and fixed
against current `master`.

### Deployment mode

Authenticated/tailnet board-key access.

## What Changed

- Added a board-actor fallback for `issue:comment` and `issue:mutate` in
`server/src/services/authorization.ts`.
- Restricted that fallback to fully contextualized issue resources with
issue id, status, and explicit assignee fields so sparse service calls
still fail closed.
- Allowed active same-company non-viewer board memberships and denied
viewer memberships for these issue-write actions.
- Added regression coverage for non-viewer board-key comment/mutate on
an issue assigned to another agent.
- Added regression coverage for viewer denial on both `issue:comment`
and `issue:mutate`.

## Verification

- `pnpm exec vitest run
server/src/__tests__/authorization-service.test.ts` passed: 35/35.
- `pnpm --filter @paperclipai/server typecheck` passed.
- `git diff --check` passed.

## Risks

Low-to-moderate authorization risk because this changes issue-write
access. The scope is constrained to active same-company board
memberships, excludes viewers, and requires route-shaped issue context
before granting access. Cross-company access and sparse/null-mapped
calls continue to fail closed.

> 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 using GPT-5-class reasoning with local shell,
GitHub CLI, and test execution tools in an OpenClaw/Codex environment.

## Checklist

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

Co-authored-by: ApolinarioRatio <ApolinarioRatio@users.noreply.github.com>
2026-08-13 10:14:05 -07:00
dependabot[bot] 88e1ccb424
build(deps): bump @aws-sdk/client-s3 from 3.1075.0 to 3.1106.0 (#11315)
Bumps
[@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3)
from 3.1075.0 to 3.1106.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/aws/aws-sdk-js-v3/releases">@​aws-sdk/client-s3's
releases</a>.</em></p>
<blockquote>
<h2>v3.1106.0</h2>
<h4>3.1106.0(2026-08-07)</h4>
<h5>New Features</h5>
<ul>
<li><strong>clients:</strong> update client endpoints as of 2026-08-07
(<a
href="c5d05426d8">c5d05426</a>)</li>
<li><strong>client-amplify:</strong> Increased the maximum allowed
length of the oauthToken parameter in the CreateApp and UpdateApp APIs
to support longer OAuth tokens issued by third-party Git providers. (<a
href="b239e29295">b239e292</a>)</li>
<li><strong>client-healthlake:</strong> Adds provenanceEnabled to
StartFHIRImportJob (<a
href="18ac6efeb9">18ac6efe</a>)</li>
<li><strong>client-securityagent:</strong> Added enableEmailMfa input
field on Actor to enable email-based MFA during penetration tests. When
enabled, a server-generated mfaForwardingAddress is returned. Set up a
forwarding rule in your email provider to forward MFA emails to this
address so the agent can complete email-based MFA login flows (<a
href="e21d39190e">e21d3919</a>)</li>
<li><strong>client-mediapackagev2:</strong> StreamNameOutputMode - a new
optional field on MediaPackageV2 OriginEndpoints that lets customers
choose whether egress manifests use numeric stream indices (default) or
encoder-assigned stream names from the input (<a
href="7f49cb0607">7f49cb06</a>)</li>
<li><strong>client-sagemaker:</strong> Amazon SageMaker adds maintenance
lifecycle statuses for Notebook Instances (<a
href="6ce0f8843a">6ce0f884</a>)</li>
<li><strong>client-ec2:</strong> This release adds support for BGP route
protection in Amazon VPC IP Address Manager (IPAM), including route
discovery, RPKI route protection findings, and delegated RPKI (Internet
Registry Associations, routing policy registrations, and ROA management)
for BYOIP prefixes. (<a
href="62f281df5a">62f281df</a>)</li>
<li><strong>client-mediatailor:</strong> Added support for inserting ads
via the VAST Ad Buffet standard. You can now configure MediaTailor to
insert ads in sequence order using the AdSequencingMode setting in your
playback configuration. Standalone ads are used as fallbacks when a
sequenced ad is unavailable. (<a
href="7bebb1e56d">7bebb1e5</a>)</li>
<li><strong>client-connect:</strong> Supports updating the task template
associated with in-progress task contacts using the new
UpdateContactTaskTemplate API. This enables supervisors and developers
to dynamically reassign task templates without creating a new task. (<a
href="24f4041681">24f40416</a>)</li>
</ul>
<hr />
<p>For list of updated packages, view
<strong>updated-packages.md</strong> in
<strong>assets-3.1106.0.zip</strong></p>
<h2>v3.1105.0</h2>
<h4>3.1105.0(2026-08-06)</h4>
<h5>Chores</h5>
<ul>
<li><strong>lib-dynamodb:</strong> add error msg and fallback when
incompatible client is supplied (<a
href="https://redirect.github.com/aws/aws-sdk-js-v3/pull/8231">#8231</a>)
(<a
href="e663d41f0c">e663d41f</a>)</li>
</ul>
<h5>New Features</h5>
<ul>
<li><strong>clients:</strong> update client endpoints as of 2026-08-06
(<a
href="e4f7b32fca">e4f7b32f</a>)</li>
<li><strong>client-cloudwatch-logs:</strong> This release adds index
category support to the CloudWatch Logs DescribeFieldIndexes API.
Customers can filter and identify DEFAULT, CUSTOM, AUTO, and INACTIVE
field indexes. (<a
href="e17fff6fee">e17fff6f</a>)</li>
<li><strong>client-socialmessaging:</strong> Add support for WhatsApp
Conversions APIs. (<a
href="5c29a86986">5c29a869</a>)</li>
<li><strong>client-gamelift:</strong> Adds support for C8a, C8i, C9g,
M8a, M8i, and M9g EC2 instance type families for managed EC2 and
container fleets. Also adds explicit anchors on most string regexes. (<a
href="30dfd63ab8">30dfd63a</a>)</li>
<li><strong>client-securityhub:</strong> Security Hub is adding a new
public API, ListFreeTrialStatusesV2 to describe the free trial statuses
of the Security Hub service and its opt-in features. (<a
href="e44b3582d5">e44b3582</a>)</li>
<li><strong>client-bedrock-agentcore-control:</strong> Add support for
Gateway rate limits and Runtime instances in Amazon Bedrock AgentCore.
Customers can now configure rate limits scoped to control request rates,
token consumption rates, and active connection rates. Customers can now
create capacity providers to launch runtimes on their EC2 instances. (<a
href="865d21efa6">865d21ef</a>)</li>
<li><strong>client-device-farm:</strong> Adds support for service
generated insights across runs, jobs, and tests. (<a
href="6c601b7101">6c601b71</a>)</li>
<li><strong>client-sagemaker:</strong> Releases new Model Customization
SequenceLength parameter for Training and g7 instance types for Training
and Processing. (<a
href="14bd2ac7dc">14bd2ac7</a>)</li>
<li><strong>client-agent-registry-control:</strong> Agent Registry's
Public Preview release (<a
href="a137863d85">a137863d</a>)</li>
<li><strong>client-backup:</strong> AWS Backup now lets you create
read-only access points for Amazon S3 recovery points, enabling you to
access backup data using S3 APIs without initiating a restore. (<a
href="636228a953">636228a9</a>)</li>
<li><strong>client-mediatailor:</strong> AWS Elemental MediaTailor now
supports concurrent function execution. The new Concurrent Executor
function type runs multiple independent child functions in parallel
within a single lifecycle hook, reducing pipeline latency to the
duration of the slowest call instead of the sum of all calls. (<a
href="1cf61475d4">1cf61475</a>)</li>
<li><strong>client-marketplace-agreement:</strong> GetAgreementTerms now
returns a new term variant in AcceptedTerm, netPaymentTerm, with a
paymentDuePeriod field (example &quot;P30D&quot;). (<a
href="50b0d6d565">50b0d6d5</a>)</li>
<li><strong>client-agent-registry:</strong> Agent Registry's Public
Preview release (<a
href="632ae47917">632ae479</a>)</li>
<li><strong>client-kafka:</strong> MSK Clusters can now deliver
authorizer logs alongside broker logs to the destinations defined by you
(<a
href="b7e3193783">b7e31937</a>)</li>
<li><strong>client-bedrock-agentcore:</strong> Add support for capacity
provider sessions in Amazon Bedrock AgentCore. Customers can now delete
an active session running on a runtime instance launched through their
capacity provider. (<a
href="bd301533b8">bd301533</a>)</li>
<li><strong>client-auto-scaling:</strong> EC2 Auto Scaling now supports
being managed by other AWS services via the operator field. (<a
href="f5d54fce5f">f5d54fce</a>)</li>
<li><strong>client-ec2:</strong> Adds a new optional IncludeLocalZones
parameter to the Spot Placement Score API that defaults to false. When
set to true, the Spot Placement Score API will consider the relevant
Local Zones with Spot capacity when computing the Spot Placement Score.
(<a
href="43673842a0">43673842</a>)</li>
<li><strong>client-marketplace-discovery:</strong> GetOfferTerms now
returns netPaymentTerm in offerTerms, specifying payment due period
after invoice date. The paymentDuePeriod field uses ISO 8601 duration
format (e.g., &quot;P30D&quot; for net 30 days). This is a
backward-compatible addition. See API documentation for full structure
and examples. (<a
href="f4fd7ae7b8">f4fd7ae7</a>)</li>
<li><strong>client-s3:</strong> AWS Backup now lets you create read-only
access points for Amazon S3 recovery points, enabling you to access
backup data using S3 APIs without initiating a restore. (<a
href="faf6560269">faf65602</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md">@​aws-sdk/client-s3's
changelog</a>.</em></p>
<blockquote>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1105.0...v3.1106.0">3.1106.0</a>
(2026-08-07)</h1>
<p><strong>Note:</strong> Version bump only for package
<code>@​aws-sdk/client-s3</code></p>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1104.0...v3.1105.0">3.1105.0</a>
(2026-08-06)</h1>
<h3>Features</h3>
<ul>
<li><strong>client-s3:</strong> AWS Backup now lets you create read-only
access points for Amazon S3 recovery points, enabling you to access
backup data using S3 APIs without initiating a restore. (<a
href="faf6560269">faf6560</a>)</li>
</ul>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1103.0...v3.1104.0">3.1104.0</a>
(2026-08-05)</h1>
<p><strong>Note:</strong> Version bump only for package
<code>@​aws-sdk/client-s3</code></p>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1102.0...v3.1103.0">3.1103.0</a>
(2026-08-04)</h1>
<p><strong>Note:</strong> Version bump only for package
<code>@​aws-sdk/client-s3</code></p>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1101.0...v3.1102.0">3.1102.0</a>
(2026-08-03)</h1>
<p><strong>Note:</strong> Version bump only for package
<code>@​aws-sdk/client-s3</code></p>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1100.0...v3.1101.0">3.1101.0</a>
(2026-07-31)</h1>
<p><strong>Note:</strong> Version bump only for package
<code>@​aws-sdk/client-s3</code></p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="655d311ea0"><code>655d311</code></a>
Publish v3.1106.0</li>
<li><a
href="d6c0ea3622"><code>d6c0ea3</code></a>
Publish v3.1105.0</li>
<li><a
href="faf6560269"><code>faf6560</code></a>
feat(client-s3): AWS Backup now lets you create read-only access points
for A...</li>
<li><a
href="b3929bd0a7"><code>b3929bd</code></a>
Publish v3.1104.0</li>
<li><a
href="672c90ddc7"><code>672c90d</code></a>
Publish v3.1103.0</li>
<li><a
href="c5285315f7"><code>c528531</code></a>
Publish v3.1102.0</li>
<li><a
href="272a6ebbae"><code>272a6eb</code></a>
Publish v3.1101.0</li>
<li><a
href="6969cf9ed5"><code>6969cf9</code></a>
Publish v3.1100.0</li>
<li><a
href="5b15ca73a3"><code>5b15ca7</code></a>
Publish v3.1099.0</li>
<li><a
href="ee76673ea9"><code>ee76673</code></a>
Publish v3.1098.0</li>
<li>Additional commits viewable in <a
href="https://github.com/aws/aws-sdk-js-v3/commits/v3.1106.0/clients/client-s3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@aws-sdk/client-s3&package-manager=npm_and_yarn&previous-version=3.1075.0&new-version=3.1106.0)](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-08-13 10:09:00 -07:00
dependabot[bot] 49b80e36f7
build(deps): bump dompurify from 3.4.12 to 3.4.13 (#11305)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.12 to
3.4.13.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/cure53/DOMPurify/releases">dompurify's
releases</a>.</em></p>
<blockquote>
<h2>DOMPurify 3.4.13</h2>
<ul>
<li>Fixed an issue with hook removal during <code>IN_PLACE</code>
sanitization, thanks <a
href="https://github.com/koyokr"><code>@​koyokr</code></a></li>
<li>Fixed an issue with hooks potentially bypassing the clone guard,
thanks <a
href="https://github.com/AkshayjainG"><code>@​AkshayjainG</code></a></li>
<li>Fixed an issue with DOM clobbering via <code>ownerDocument</code>
during <code>IN_PLACE</code>, thanks <a
href="https://github.com/AkshayjainG"><code>@​AkshayjainG</code></a></li>
<li>Bumped several dependencies where possible</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3067f77467"><code>3067f77</code></a>
release: 3.4.13 (<a
href="https://redirect.github.com/cure53/DOMPurify/issues/1562">#1562</a>)</li>
<li>See full diff in <a
href="https://github.com/cure53/DOMPurify/compare/3.4.12...3.4.13">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dompurify&package-manager=npm_and_yarn&previous-version=3.4.12&new-version=3.4.13)](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)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/paperclipai/paperclip/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-13 10:06:53 -07:00
dependabot[bot] 70e6c80f5f
build(deps-dev): bump @types/express-serve-static-core from 5.1.1 to 5.1.3 (#11320)
Bumps
[@types/express-serve-static-core](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/express-serve-static-core)
from 5.1.1 to 5.1.3.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/express-serve-static-core">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@types/express-serve-static-core&package-manager=npm_and_yarn&previous-version=5.1.1&new-version=5.1.3)](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-08-13 10:06:25 -07:00
dependabot[bot] 3040db3343
build(deps): bump @agentclientprotocol/claude-agent-acp from 0.63.0 to 0.66.0 (#11314)
Bumps
[@agentclientprotocol/claude-agent-acp](https://github.com/agentclientprotocol/claude-agent-acp)
from 0.63.0 to 0.66.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.66.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.65.0...v0.66.0">0.66.0</a>
(2026-08-07)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps-dev:</strong> Bump globals from 17.8.0 to 17.9.0 in the
minor group (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/960">#960</a>)
(<a
href="7f27c47c5c">7f27c47</a>)</li>
<li>expose provider-neutral ACP goal extension (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/964">#964</a>)
(<a
href="8b31dea11b">8b31dea</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>publish and replace Claude goals reliably (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/967">#967</a>)
(<a
href="f8fd3ab822">f8fd3ab</a>)</li>
</ul>
<h2>v0.65.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.64.2...v0.65.0">0.65.0</a>
(2026-08-05)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps-dev:</strong> Bump nanoid from 3.3.16 to 3.3.17 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/951">#951</a>)
(<a
href="b965dd2191">b965dd2</a>)</li>
<li><strong>deps-dev:</strong> Bump tinyexec from 1.2.4 to 1.3.0 in the
minor group (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/959">#959</a>)
(<a
href="15b4eb46f3">15b4eb4</a>)</li>
<li><strong>deps:</strong> Bump <code>@​hono/node-server</code> from
1.19.17 to 2.1.0 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/956">#956</a>)
(<a
href="f9123f3e18">f9123f3</a>)</li>
<li><strong>deps:</strong> Bump fast-uri from 3.1.4 to 3.1.5 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/952">#952</a>)
(<a
href="0988438428">0988438</a>)</li>
<li><strong>steering:</strong> settle a steered turn at idle, not at the
interrupt (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/958">#958</a>)
(<a
href="a84b81080a">a84b810</a>)</li>
</ul>
<h2>v0.64.2</h2>
<h2>Bug Fixes</h2>
<ul>
<li>restore the single-tool representation for ExitPlanMode (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/942">#942</a>)
(4302a4b)</li>
</ul>
<h2>v0.64.1</h2>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.64.0...v0.64.1">0.64.1</a>
(2026-08-02)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>release 0.65.0 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/939">#939</a>)
(<a
href="0936ec281e">0936ec2</a>)</li>
</ul>
<h2>v0.64.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.63.0...v0.64.0">0.64.0</a>
(2026-07-30)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps:</strong> Bump actions/checkout from 7.0.0 to 7.0.1 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/925">#925</a>)
(<a
href="8e099e8442">8e099e8</a>)</li>
<li><strong>deps:</strong> Bump the minor group with 7 updates (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/928">#928</a>)
(<a
href="3f60921959">3f60921</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<!-- 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.65.0...v0.66.0">0.66.0</a>
(2026-08-07)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps-dev:</strong> Bump globals from 17.8.0 to 17.9.0 in the
minor group (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/960">#960</a>)
(<a
href="7f27c47c5c">7f27c47</a>)</li>
<li>expose provider-neutral ACP goal extension (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/964">#964</a>)
(<a
href="8b31dea11b">8b31dea</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>publish and replace Claude goals reliably (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/967">#967</a>)
(<a
href="f8fd3ab822">f8fd3ab</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.64.2...v0.65.0">0.65.0</a>
(2026-08-05)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps-dev:</strong> Bump nanoid from 3.3.16 to 3.3.17 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/951">#951</a>)
(<a
href="b965dd2191">b965dd2</a>)</li>
<li><strong>deps-dev:</strong> Bump tinyexec from 1.2.4 to 1.3.0 in the
minor group (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/959">#959</a>)
(<a
href="15b4eb46f3">15b4eb4</a>)</li>
<li><strong>deps:</strong> Bump <code>@​hono/node-server</code> from
1.19.17 to 2.1.0 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/956">#956</a>)
(<a
href="f9123f3e18">f9123f3</a>)</li>
<li><strong>deps:</strong> Bump fast-uri from 3.1.4 to 3.1.5 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/952">#952</a>)
(<a
href="0988438428">0988438</a>)</li>
<li><strong>steering:</strong> settle a steered turn at idle, not at the
interrupt (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/958">#958</a>)
(<a
href="a84b81080a">a84b810</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.64.1...v0.64.2">0.64.2</a>
(2026-08-02)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>restore the single-tool representation for ExitPlanMode (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/942">#942</a>)
(<a
href="4302a4b0b6">4302a4b</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.64.0...v0.64.1">0.64.1</a>
(2026-08-02)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>release 0.65.0 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/939">#939</a>)
(<a
href="0936ec281e">0936ec2</a>)</li>
</ul>
<h2><a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.63.0...v0.64.0">0.64.0</a>
(2026-07-30)</h2>
<h3>Features</h3>
<ul>
<li><strong>deps:</strong> Bump actions/checkout from 7.0.0 to 7.0.1 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/925">#925</a>)
(<a
href="8e099e8442">8e099e8</a>)</li>
<li><strong>deps:</strong> Bump the minor group with 7 updates (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/928">#928</a>)
(<a
href="3f60921959">3f60921</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>steering:</strong> add opt-in host-owned fallback (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/919">#919</a>)
(<a
href="43af4ec29e">43af4ec</a>),
closes <a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/903">#903</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6b405138fc"><code>6b40513</code></a>
chore(main): release 0.66.0 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/961">#961</a>)</li>
<li><a
href="8aaf608b4e"><code>8aaf608</code></a>
ci: fix release flow (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/971">#971</a>)</li>
<li><a
href="f8fd3ab822"><code>f8fd3ab</code></a>
fix: publish and replace Claude goals reliably (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/967">#967</a>)</li>
<li><a
href="133337ffe5"><code>133337f</code></a>
ci: simplify the release flow and make it agent-friendly (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/965">#965</a>)</li>
<li><a
href="8b31dea11b"><code>8b31dea</code></a>
feat: expose provider-neutral ACP goal extension (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/964">#964</a>)</li>
<li><a
href="bba912728f"><code>bba9127</code></a>
ci: validate PR titles against release-please conventions (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/962">#962</a>)</li>
<li><a
href="7f27c47c5c"><code>7f27c47</code></a>
feat(deps-dev): Bump globals from 17.8.0 to 17.9.0 in the minor group
(<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/960">#960</a>)</li>
<li><a
href="6d608cb399"><code>6d608cb</code></a>
chore(main): release 0.65.0 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/957">#957</a>)</li>
<li><a
href="a84b81080a"><code>a84b810</code></a>
feat(steering): settle a steered turn at idle, not at the interrupt (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/958">#958</a>)</li>
<li><a
href="b965dd2191"><code>b965dd2</code></a>
feat(deps-dev): Bump nanoid from 3.3.16 to 3.3.17 (<a
href="https://redirect.github.com/agentclientprotocol/claude-agent-acp/issues/951">#951</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.63.0...v0.66.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@agentclientprotocol/claude-agent-acp&package-manager=npm_and_yarn&previous-version=0.63.0&new-version=0.66.0)](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-08-13 10:06:19 -07:00
dependabot[bot] d68cf32ae9
build(deps): bump @codemirror/view from 6.43.1 to 6.43.8 (#11321)
Bumps [@codemirror/view](https://github.com/codemirror/view) from 6.43.1
to 6.43.8.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/codemirror/view/commits">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@codemirror/view&package-manager=npm_and_yarn&previous-version=6.43.1&new-version=6.43.8)](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-08-13 10:06:17 -07:00
Ravi b5bb236bc1
Fix stale closure-comment wakeups on done issue updates (#8656)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue update route is part of the workflow layer that records
board state changes and emits follow-up wakes for agents.
> - A single `PATCH /api/issues/:id` request can both close an issue and
add the closure comment that explains the final disposition.
> - The bug was that the comment-wakeup decision used the issue's
pre-update status, so a request that changed `in_progress` to `done`
could still enqueue an `issue_commented` wake as if the issue remained
open.
> - That stale wake could cause already-completed Sentry-family
follow-up issues to drift back into active work even though the closure
comment was the only new activity.
> - This pull request makes the wake suppression decision use the
post-update issue status and covers the closure-comment path with a
focused regression test.
> - The benefit is that terminal issue updates stay terminal unless a
separate explicit reopen or resume path is used.

## Linked Issues or Issue Description

No public GitHub issue exists for this instance-specific workflow bug,
so the issue is described inline.

Bug report:
- What happened: when an issue was marked `done` with a closure comment
in the same `PATCH /api/issues/:id` request, the route could still
enqueue an `issue_commented` wake because it checked the pre-update
status.
- Expected behavior: a closure comment written as part of the terminal
update should not wake the assignee again or clear the terminal
disposition.
- Steps to reproduce: start with an assigned issue in `in_progress`,
patch it to `done` while including a comment, then inspect whether an
`issue_commented` wake is emitted for the assignee.
- Deployment mode: local Paperclip workflow/API behavior.
- Related public PRs found during duplicate search: #6657 appears to
address a broader stale closeout-comment reopen path; this PR is
narrower and targets the same-request post-update status decision in
`PATCH /api/issues/:id`.

## What Changed

- Use the post-update issue status when deciding whether a PATCH comment
should enqueue an `issue_commented` wake.
- Add a regression test covering `in_progress` to `done` with a closure
comment so the assignee is not woken again after the issue is already
closed.

## Verification

- `bin/ci`: absent in this repo, so I used the repo's targeted
test-equivalent commands for the touched API route.
- `pnpm install --frozen-lockfile --ignore-scripts`: passed, with
non-fatal warnings about missing `paperclip-plugin-dev-server` bins
because `packages/plugins/sdk/dist/dev-cli.js` is not built under
`--ignore-scripts`.
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/issue-update-comment-wakeup-routes.test.ts`: passed
(`Test Files 1 passed`, `Tests 8 passed`).
- GitHub PR workflow checks for build, typecheck, server tests,
workspace tests, serialized suites, e2e, canary dry run, security scans,
and policy are green on commit
`5a8bd799edd606731fd5e215ea97417a655338ea`.
- A normal `pnpm install --frozen-lockfile` is blocked on this host
before tests because `sharp` attempts a native build under Node `26.1.0`
/ Python `3.14.5` and fails on missing Python `distutils`; the
route-level verification above used `--ignore-scripts` to avoid that
local toolchain issue.

## Risks

Low risk. The behavior change is limited to comment-wakeup suppression
during issue update handling and only narrows wake emission when the
post-update status is terminal. The main edge case is that a
same-request terminal update with a comment will no longer wake the
assignee; explicit reopen or resume flows should remain the correct way
to restart completed work.

> 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 via the managed local Codex adapter, model `gpt-5.5` with
repository tool use and shell execution. The implementation and PR
update were produced with AI assistance under the TechWright CTO
Architect role.

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

Checklist notes:
- The branch was already opened as `worker/TEC-1440-reopen-drift`; I am
leaving the box unchecked rather than hiding that the live PR branch
includes an internal coordination id.
- The only non-green automated check before this body update was the
automated review/template gate. Greptile was 4/5 because of this
PR-description issue, with no code change requested.

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-13 09:51:22 -07:00
Eric Brookfield 166f381d3f
fix(runtime): only rewrite base-URL port for loopback hosts (#10258)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server derives each spawned agent's `PAPERCLIP_API_URL` from
`authPublicBaseUrl` via `choosePrimaryRuntimeApiUrl` →
`buildPaperclipEnv`
> - At startup, `rewriteLocalUrlPort` rewrote the port of the configured
`auth.publicBaseUrl` to the internal listen port
> - The rewrite was applied to *any* explicit-port URL, not just
loopback ones — so an external base URL on a non-default port (e.g. a
Tailscale Serve listener on `:8443`) got clobbered to the internal HTTP
port `:3101`
> - `https://host:3101` (HTTPS scheme against the plaintext HTTP port)
is unreachable, and that dead value propagated to every spawned agent's
`PAPERCLIP_API_URL`
> - This pull request preserves explicit external base URLs at startup
while keeping the worktree path's intended per-worktree port rewrite
> - The benefit is that agents following the documented `curl
"$PAPERCLIP_API_URL/..."` pattern no longer hit a dead endpoint

## Linked Issues or Issue Description

No public GitHub issue; describing inline (bug report).

**Summary:** at server startup, `rewriteLocalUrlPort` corrupts an
explicit external `auth.publicBaseUrl`, leaking a dead
`PAPERCLIP_API_URL` to spawned agents.

**Steps to reproduce:**
1. Configure `auth.publicBaseUrl = https://<host>:8443` (an external
listener on a non-default port, e.g. Tailscale Serve).
2. Start the server (internal listen port `3101`).
3. Inspect a spawned agent run's env:
`PAPERCLIP_API_URL=https://<host>:3101`.

**Expected:** the agent-facing URL points at a reachable origin.
**Actual:** `curl "$PAPERCLIP_API_URL/..."` → `http_code=000` (HTTPS
against the plaintext HTTP port; TLS handshake fails). The fleet stays
healthy only because the runtime falls through its candidate list, but
any agent following the documented curl pattern silently hits a dead
endpoint first.

Related open PRs in the same area (dedup — none merged; this is a
smaller, targeted fix with regression tests):
- Refs #9916 (PAPERCLIP_RUNTIME_API_URL precedence + authPublicBaseUrl
port preservation)
- Refs #7342 (preserve explicit authPublicBaseUrl during startup,
GH#7341)
- Refs #9228 (prefer reachable runtime API URLs for local adapters)

## What Changed

- New `server/src/url-utils.ts` with two intent-revealing helpers
(single source of truth):
  - `rewriteUrlPort` — rewrite any explicit-port URL to a new port.
- `rewriteLoopbackUrlPort` — rewrite **only** loopback hosts; explicit
external URLs survive untouched.
- `isLoopbackHost` — bracket-tolerant so a URL hostname form `[::1]`
matches.
- `server/src/index.ts` (startup, the bug): `authPublicBaseUrl` now uses
`rewriteLoopbackUrlPort`, so an external Serve URL keeps its port.
Nested helper copies removed in favor of the shared module.
- `server/src/worktree-config.ts` (worktree path): uses `rewriteUrlPort`
— **behavior unchanged**; a worktree still advertises its own server
port even on a non-loopback host (this is intended and asserted by the
existing worktree suite).
- `server/src/url-utils.test.ts`: regression coverage for both helpers.
- Updated one stale assertion in
`server-startup-feedback-export.test.ts` that had encoded the old
(buggy) external-host rewrite at startup.

## Verification

- `vitest run src/url-utils.test.ts
src/__tests__/worktree-config.test.ts
src/__tests__/server-startup-feedback-export.test.ts` → **33 passed**;
the only local failure is a pre-existing, environment-coupled test
(`derives trusted origins…`) that leaks the dev machine's real Tailscale
identity into an origins list and passes in CI (it is unrelated to this
change — its `authPublicBaseUrl` is loopback and rewrites identically
before/after).
- `npm run typecheck` (`tsc --noEmit`) → **clean, exit 0**.
- PR CI: Build, Typecheck + Release Registry, serialized server suites,
and `review` gate green.

## Risks

Low risk. The only behavioral change is at startup: an explicit
*external* base URL on a non-default port is no longer rewritten to the
internal listen port (the bug). Loopback/worktree behavior is unchanged.
No schema/migration changes.

## Model Used

Claude Opus 4.8, 1M context (`claude-opus-4-8[1m]`), extended thinking,
with tool use / code execution (Claude Code).

## Checklist

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-13 09:47:13 -07:00
Frank Gonnello 0a1f9fda65
fix(adapters): wrap modulePath in pathToFileURL() before dynamic import (Windows) (#4287)
## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies
> - One of its pluggability surfaces is external adapter packages,
loaded at startup by `server/src/adapters/plugin-loader.ts` and routed
through the adapter registry so third parties can override built-in
adapters like `claude_local`
> - `loadExternalAdapterPackage` calls `await import(modulePath)` where
`modulePath` is an absolute filesystem path
> - On Windows that path begins with a drive letter (`C:\…`), which
Node's ESM loader parses as a URL scheme and rejects with
`ERR_UNSUPPORTED_ESM_URL_SCHEME`; the defensive `try/catch` around the
call masks the failure and the builtin adapter silently keeps serving
traffic, so the override never activates
> - `reloadExternalAdapter` in the same file already tries to build a
`file://` URL, but does it via template-string concatenation
(`file://${modulePath}`) which produces a malformed URL on Windows
(`file://C:\…` instead of `file:///C:/…`) — so dev hot-reload of
adapters is broken on Windows even after initial load works on POSIX
> - This pull request swaps both paths to `pathToFileURL()` from
`node:url`, the idiomatic cross-platform conversion
> - The benefit is external adapter packages load reliably on Windows
with no changes required to existing adapters, and the two sibling paths
in the same file stop diverging in their URL-handling discipline

Closes #4286.

## What Changed

- `server/src/adapters/plugin-loader.ts`:
  - Import `pathToFileURL` from `node:url`.
- `loadExternalAdapterPackage`: wrap `modulePath` in
`pathToFileURL(modulePath).href` before passing to `import()`.
- `reloadExternalAdapter`: replace `` `file://${modulePath}` `` string
concatenation with `pathToFileURL(modulePath).href` so the cache-bust
URL is well-formed on Windows too (drive letter, UNC, percent-encoding).

Three lines changed + one import. No behavior change on POSIX:
`pathToFileURL("/foo/bar.js").href === "file:///foo/bar.js"`, which
Node's ESM loader accepts identically to the bare path.

## Verification

**Runtime, Windows 11, Node v24, `@paperclipai/server@2026.416.0`:**

Before (installed dist, vanilla):
```
INFO: Loading external adapter package {packageName: "@reforged/adapter-claude-local", modulePath: "C:\\Users\\…\\index.js"}
WARN: Failed to dynamically load external adapter; skipping
err: ERR_UNSUPPORTED_ESM_URL_SCHEME … Received protocol 'c:'
```

After (same dist with the equivalent two-line patch applied):
```
INFO: Loading external adapter package {packageName: "@reforged/adapter-claude-local"}
INFO: Loaded external adapters from plugin store {count: 1, adapters: ["claude_local"]}
```

End-to-end: the override actually services execute calls and its
telemetry fields (e.g. `errorCode: "rate_limited"` on 429) surface into
heartbeat-run records — I've been running this heartbeat through the
override on a vendor-patched copy while drafting this PR.

**Static / logic review:**

- `pathToFileURL` is part of Node's stdlib since v10.12.0, no new dep.
- On POSIX, `path.resolve("/a", "b") → "/a/b"` and
`pathToFileURL("/a/b").href → "file:///a/b"`. `await
import("file:///a/b")` and `await import("/a/b")` both resolve to the
same ESM module — no double-load risk.
- Reload path: the existing cache-bust query (`?t=${Date.now()}`) still
appends cleanly because `pathToFileURL(...).href` returns a normalized
`file:///…` URL with no pre-existing query string.

**Local test suite:** I did not run the full `pnpm test` suite in this
fork — the monorepo test infrastructure (embedded Postgres, pnpm
workspace install) is a significant local-setup cost and this change is
surgical enough that CI should be the source of truth. Happy to iterate
based on CI signal. No existing test directly exercises
`plugin-loader.ts`'s initial-load path.

## Risks

**Low.** This aligns the initial-load path with the already-existing
intent of the reload path (which tried, but imperfectly, to use a
`file://` URL). POSIX behavior is unchanged. The only runtime difference
is that Windows stops throwing and starts loading the adapter — which is
exactly the bug being fixed.

Edge cases worth naming:
- **UNC paths** (`\\server\share\…`): previously broken the same way on
the load path, still broken with `file://` string concat on the reload
path. `pathToFileURL` handles UNC correctly (→
`file:////server/share/…`), so this change also quietly fixes UNC-path
adapter installs on Windows.
- **Bun**: the reload path has a Bun cache-eviction block that keys off
`modulePath` and the old `fileUrl`. Bun accepts both `file://` URLs and
bare paths in its module cache keys, so changing the URL form is
consistent with the existing evict-both pattern (we still evict both
`fileUrl` and `modulePath` after the change).

## Model Used

Claude Opus 4.7 (`claude-opus-4-7`, provider: Anthropic) via Claude
Code, running as the CTO agent in a Paperclip-orchestrated company. 200k
context, tool use. No extended thinking mode. Model authored the patch,
the issue body, and this PR description; human review by the company's
principal (fronc) is pending.

## 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
- [ ] I have run tests locally and they pass — *deferred to CI, see
Verification note*
- [ ] I have added or updated tests where applicable — *no existing
tests for this file; adding one would require stubbing
`adapter-plugin-store` + filesystem, which seemed out of scope for a
3-line fix. Happy to add one on request.*
- [x] If this change affects the UI, I have included before/after
screenshots — *not UI, N/A*
- [x] I have updated relevant documentation to reflect my changes — *no
user-facing docs affected; behavior unchanged on POSIX and now-working
on Windows*
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-13 10:57:39 -05:00
Nicky Leach d0d242e843
feat(server): reopen an archived isolated execution workspace in place (#11322)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Issue execution uses isolated workspaces that hold the issue
worktree
> - A terminal issue can leave its isolated workspace archived and
unable to resume
> - The existing closed-workspace guards returned a conflict and gave
the user no self-serve recovery
> - This pull request reopens the same isolated workspace row and
rebuilds its worktree
> - The benefit is that resume, checkout, and comment actions can
continue without a new workspace row

## Linked Issues or Issue Description

**Problem or motivation**

A terminal issue can point to an archived isolated execution workspace.
Resume, checkout, and comment actions then stop with a conflict.

**What happened?**

A terminal issue kept its issue-to-workspace link after the isolated
workspace reached a closed status. The guarded actions returned HTTP 409
instead of restoring access.

**Expected behavior**

The next authorized resume, checkout, or comment action reopens the same
isolated workspace row. The action rebuilds the worktree and then
continues.

**Steps to reproduce**

1. Create an issue that uses an isolated execution workspace.
2. Move the issue to a terminal state and let the workspace archive.
3. Try to resume the issue or add a comment.
4. Observe the closed-workspace conflict.

**Paperclip version or commit**

e6e79f458e

**Deployment mode**

Built from source with pnpm.

**Proposed solution**

Reopen the closed isolated workspace in place. Rebuild the worktree
before the route reports success.

**Alternatives considered**

Create a new workspace row. This would require repointing the issue link
and would not preserve access for issues that share the original row.

**Roadmap alignment**

ROADMAP.md has no matching reopen item.

## What Changed

- Reopen closed isolated workspace rows in place and rebuild their
worktrees.
- Use the reopen path from resume, checkout, and comment guards.
- Return a clear error when the rebuild fails and keep the workspace
closed.
- Fence terminal reaping and archive cleanup while a reopen is in
flight.
- Update the comment composer to allow the next action to reopen the
workspace.
- Add service and route tests for reopen, scope, failure, and lifecycle
races.

## Verification

- Server TypeScript check passes with the tsc --noEmit command.
- UI TypeScript check passes with the tsc --noEmit command.
- Seventy-two server tests pass across the affected test files.
- The isolated worktree UI test suite has a dependency mismatch and
fails before tests run with the error TypeError: act is not a function.
- CI confirms the UI test job after a clean dependency install.

## Risks

The reopen path changes behavior for closed isolated workspaces. A
rebuild failure returns an error and keeps the row closed. Lifecycle
locks and generation checks protect the worktree from stale cleanup. The
UI test mismatch needs CI confirmation.

## Model Used

OpenAI Codex, GPT-5, with tool use and code review assistance. The
runtime did not provide the context window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-13 07:44:19 -07:00
Tonio f0e6c0f549
feat(server): receive and apply the Paperclip Cloud onboarding seed (#11098)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip Cloud provisions a dedicated tenant stack for each
customer. During signup it asks for a mission, a name and role for the
first agent, and a first task.
> - Cloud pushes those answers into the new stack at activation, as
`POST /api/companies/:companyId/onboarding-seed`.
> - No route served that path. The tenant answered 404, so Cloud
recorded the push as unacknowledged and retried on every portfolio
fetch.
> - The failure was soft. The answers stayed durable in Cloud and the
stack still activated. But the stack opened on the empty first-run
wizard, and it asked the customer again for what they had already given.
> - This pull request adds the receiving endpoint. It validates the
seed, applies it, and acknowledges it.
> - The benefit is that a seeded stack opens with the mission, the agent
and the first task already in place.

## Linked Issues or Issue Description

No public GitHub issue covers this. The problem is described in-PR,
following the feature template.

**Subsystem affected**

server/ — Express REST API and orchestration services. Also
`packages/db` (one new table) and `packages/shared` (one new validator).

**Problem or motivation**

Paperclip Cloud collects onboarding answers at signup and pushes them to
the tenant stack at activation. The tenant had no route for that
request. It answered 404. Cloud treats a non-2xx as "not yet applied",
so it kept the answers and retried, but the stack itself stayed
unseeded. A customer who had already named their mission, their first
agent and their first task arrived at an empty first-run wizard that
asked for all three again.

**Proposed solution**

Serve `POST /api/companies/:companyId/onboarding-seed`. Validate the
body, apply it to the company, then acknowledge it.

The seed is customer free text, so it is bounded and validated in
`packages/shared` and read from the JSON body only. It is never read
from an `x-paperclip-cloud-*` header. That header set is the trusted
identity envelope: every member is derived server-side from the host
plus verified domain records, and that is exactly what makes it
trustworthy. Mixing user content into it would remove the property. A
test plants a mission on a cloud header and asserts that the body value
wins.

Application reuses the shapes the first-run wizard already produces, so
a seeded stack and a manually onboarded one look the same afterwards:

- The mission becomes the company-level goal. A multi-line mission
splits into a title and a description, as the wizard does.
- The agent becomes the company's first hire. Its free-text role ("Chief
of Staff") lands on `title`. The structural `role` stays `ceo`, which is
what the org chart and the default-instructions lookup read.
- The first task becomes an issue in the Onboarding project, assigned to
that agent.

Cloud retries until it gets a 2xx, and it reads any 2xx as "the tenant
holds this content". So the endpoint is idempotent per `revision`. A new
`company_onboarding_seeds` table records the applied revision together
with the goal, the agent and the issue it produced. A replay of a
revision that already matches is a successful no-op. A later revision —
the customer edited their answers — updates those three rows in place
instead of creating a second agent and a second task. The record is
written last, after every other write has landed, so a partial
application cannot present itself as acknowledged.

Everything is applied before the 200 is sent. This is an ordering
guarantee, not eventual consistency. The tests read the database
immediately after the response, with no waiting and no polling, so a
lazy receiver fails them on a fast machine as well as a slow one. That
matters because the redirect into the tenant dashboard is gated on this
acknowledgement.

**Alternatives considered**

Store the seed and let the tenant UI apply it on first load. Rejected:
the dashboard redirect is gated on the acknowledgement, so a background
apply would let the dashboard open before the agent and the task exist.
The whole point is that it must not.

Reuse `POST /companies/:companyId/agents` and `POST
/companies/:companyId/issues` over HTTP from Cloud. Rejected: it needs
three round trips with no shared idempotency key, and it moves the "did
all of it land?" decision to the caller.

**Roadmap alignment**

This completes an existing Cloud-to-tenant contract. It does not add a
new user-facing surface.

## What Changed

- Add `POST /api/companies/:companyId/onboarding-seed` in
`server/src/routes/onboarding-seed.ts`. It authenticates exactly as
`POST /api/companies/:companyId/logo` does, through
`assertCompanyAccess`.
- Add `server/src/services/onboarding-seed.ts`. It applies the mission,
the agent and the first task, and records the applied revision last.
- Add the `company_onboarding_seeds` table: schema, migration `0216`,
and journal entry. It holds the applied revision and the ids of the
goal, agent and issue the seed produced.
- Add `applyOnboardingSeedSchema` in `packages/shared`. It bounds
mission to 2000, agent name to 80, agent role to 120, task title to 200,
and task details to 2000 — the same limits Cloud enforces before it
sends.
- Mount the router in `server/src/app.ts` and register the path in the
OpenAPI document.
- Add `server/src/__tests__/onboarding-seed-route.test.ts` with 13
tests.
- The seeded agent is created on `claude_local`. This mirrors the
teams-catalog default for agents created server-side, where no human
runs an environment test first. `PAPERCLIP_ONBOARDING_SEED_ADAPTER_TYPE`
overrides it.

## Verification

```sh
pnpm typecheck                      # whole workspace, passes
npx vitest run \
  server/src/__tests__/onboarding-seed-route.test.ts \
  server/src/__tests__/openapi-routes.test.ts        # 15 passed
```

The suite runs against embedded Postgres with migrations applied, so
migration `0216` is exercised by every test.

The route tests cover:

- the happy path — mission, agent and task all applied, read immediately
after the 200
- replay of the same revision — no second agent, no second task, no
second goal, no second project
- a later revision — the goal, agent and task are updated in place
- a multi-line mission splitting into a goal title and description
- a revision-only seed
- the activity log entry written once, and not again on a replay
- a caller without access to the company — 403, and nothing written
- a body with no revision — 400
- each field bound past its limit — 400
- a mission planted on an `x-paperclip-cloud-*` header — ignored, body
wins
- an existing Onboarding project — reused, not duplicated

Not verified here: the full Cloud-to-tenant walk against a live stack.
That needs a deployed Cloud and a provisioned tenant together, which is
separate staging work.

## Risks

Migration `0216` creates one new table. It adds no column to an existing
table, rewrites nothing, and backfills nothing, so it is safe to apply
online. The migration safety check passes.

The endpoint writes to a company. Access is enforced by
`assertCompanyAccess`, the same gate the company logo write uses, and a
test covers the denial.

Behavioral note for stacks that already hold data. If a company already
has a non-built-in `ceo` agent, a first seed updates that agent's name
and title rather than creating a second lead. Likewise a seed adopts an
existing company-level goal rather than adding a parallel one. This is
deliberate: the seed is the customer's own stated answer from signup,
and two competing missions or two leads would be worse than one updated
in place. In the intended case — a stack that Cloud has just activated —
none of these exist yet.

The seeded agent is created on `claude_local` with an empty adapter
config. It is idle and needs the usual credential setup before it runs.
Seeding it does not start it.

## Update — rebased onto master + review hardening

Master moved on after this PR was cut, so it was **rebased onto
`master`** and
the seed migration was **renumbered from `0212` to `0216`** (the merged
#11101
took `0212_onboarding_first_task_unique`); the drizzle journal was
re-stitched
and `check:migrations` passes.

Two things landed on top of the original receiver:

- **Mission-only walk contract (PAP-67 r17.4).** The tenant now owns the
first
agent and the first task via #11101's server-owned onboarding path,
which
stamps `ONBOARDING_FIRST_TASK_ORIGIN_KIND` and races safely on the
partial
unique index `issues_onboarding_first_task_uq`. A comment in the apply
path
documents why this receiver leaves the first task to that path on the
cloud
walk, and a paperclip-cloud `node:test`
(`src/onboarding/walk-seed.test.ts`)
asserts the walk's seed carries no `agent`/`firstTask`. The receiver
retains
the agent/first-task code for its documented body contract, kept inert
on the
  cloud path by the mission-only seed.
- **Three Greptile P1 fixes** (`95622fa37`): concurrent application is
now
  serialized under a per-company `pg_advisory_xact_lock` (no duplicate
goal/agent/project/task on overlapping pushes); a revised first task
carries
its resolved `assigneeAgentId`/`goalId`; and the
`company.onboarding_seed_applied`
  audit write is best-effort so a logging failure can't leave the entry
  permanently absent. Two new regression tests cover the first two.

## Model Used

Claude Opus 5 (`claude-opus-5`), 1M context window, extended thinking,
with tool use and code execution. Used for the original codebase
investigation, the implementation, and the tests. The rebase, migration
renumber, mission-only contract, and the three P1 fixes were done with
Claude Opus 4.8 (`claude-opus-4-8`), extended thinking, with 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 linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 22:54:07 -07:00
Tonio 1e07d5b9aa
fix(db): give the last two embedded-Postgres migration tests a timeout (#11313)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The `@paperclipai/db` package owns the database schema and its
migrations
> - Some migration tests start an embedded Postgres server and replay a
migration against it
> - An embedded Postgres server needs 7 to 12 seconds to start on a CI
runner
> - Vitest stops a test after 5 seconds unless the test sets its own
timeout
> - Two of these tests do not set a timeout, so they fail on CI before
they assert anything
> - This pull request gives both tests a 30 second timeout
> - The benefit is that unrelated pull requests stop failing on a test
they did not change

## Linked Issues or Issue Description

No public issue exists for this. The problem follows.

**What happened?**

The test `packages/db/src/company-secret-proposals-migration.test.ts`
fails on CI. The error is `Test timed out in 5000ms`. The test never
reaches its assertions. The suite reports `1 failed | 104 passed`.

The failure is not caused by the branch under test. It appeared on three
different branches in a few hours:

| Run | Head | Failing jobs |
| --- | --- | --- |
| 31630781317 | `95622fa3` | `General tests (workspaces-b)`, `verify`,
`e2e shard (2/3)`, `e2e` |
| 31652020976 | `feba90c9` | `General tests (workspaces-b)`, `verify`,
`e2e shard (3/3)`, `e2e` |
| 31651467721 | `f2115207` | `General tests (workspaces-a (1/2))`,
`verify` |

The `verify` job reads the result of the general tests. One timeout
therefore turns into two red checks. A reviewer sees two failures and
reads them as a regression.

**Expected behavior**

The test starts an embedded Postgres server, replays the migration, and
asserts the schema. It must pass on a normal CI runner.

**Steps to reproduce**

1. Open any pull request against `master`.
2. Wait for the job `General tests (workspaces-b)`.
3. Read the failure. The test times out after 5000 ms.

The failure needs a slow runner. A fast development machine starts
embedded Postgres in less than 5 seconds, so the test passes there.

**Paperclip version or commit**

`master` at `a09d7dcc0`.

**Deployment mode**

CI only. GitHub Actions, `ubuntu24` runner image.

## What Changed

- `packages/db/src/company-secret-proposals-migration.test.ts` — the
test now uses a 30 second timeout. The migration suites in this package
already use 20 to 60 seconds. 30 seconds is the most common value.
- `packages/db/src/status-card-migrations.test.ts` — the same change.
This test has the same defect. It does not fail yet because it replays
fewer statements. A fix to only one test moves the problem instead of
removing it.
- Both tests get a comment. The comment tells the next author why the 5
second default is too short.

These two tests were the only embedded-Postgres migration tests in the
package without a timeout.

## Verification

- Run `pnpm vitest run src/company-secret-proposals-migration.test.ts
src/status-card-migrations.test.ts` in `packages/db`. Both tests pass.
- These suites skip themselves when the Postgres binaries are absent. A
pass alone therefore proves nothing. Run the command with
`--reporter=verbose`. The output contains Postgres `NOTICE` messages,
for example `relation "status_cards" already exists, skipping`. These
messages prove the tests ran real SQL.
- Run the same command with `--testTimeout=1`. Both tests still pass.
This proves the per-test timeout overrides the global timeout. Before
this change, the same command fails immediately.
- All CI jobs on this pull request pass. The job `General tests
(workspaces-b)` passes. This job failed on the three runs listed above.

Not done: no attempt to reproduce the timeout on a development machine.
A fast machine starts embedded Postgres in less than 5 seconds, so the
failure does not occur there.

## Risks

Low risk. The change adds two timeout arguments to tests. It changes no
source code, no schema, and no dependency.

A longer timeout cannot hide a regression here. The tests assert the
same conditions as before. A migration that truly hangs now fails after
30 seconds. Before, it failed after 5 seconds with a message that
pointed at the wrong cause.

The `e2e` failures on the runs above have a different cause. The spec
`mcp-user-stories.spec.ts › US-9` fails with `502 — fetch failed` and
`fetch failed: bad port`. These errors come from MCP tool-connection
health checks. The failures hit different shards on different runs. This
pull request does not change that behavior. `e2e shard (2/3)` passes
here, which supports the view that those failures are unstable
infrastructure.

To revert, remove the two timeout arguments.

## Model Used

Claude Opus 5 (`claude-opus-5`), through Claude Code. Extended thinking
enabled. Tool use enabled: file read and edit, shell command execution
for the local test runs, and the GitHub CLI to read the failing CI logs.

## 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: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-12 21:30:50 -07:00
Devin Foley a09d7dcc06
feat(ui): bounce cold arrivals off archived company URLs, add Unarchive (#11302)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Archiving a company hides it from the sidebar switcher, but
remembered last-visited paths, browser history, bookmarks, and restored
tabs keep depositing users onto its URLs long after archiving
> - Since the selection ping-pong fix (#11300) those arrivals render,
but the user is stranded inside a workspace the sidebar refuses to show
— and unarchiving had no UI anywhere, so the only way back was a
hand-typed settings URL
> - This pull request bounces cold arrivals at archived company URLs to
an active company (with a toast naming why), lets deliberate visits
stick, and adds an Unarchive action to the companies list
> - The benefit is that stale URLs stop stranding users in retired
workspaces, and archived companies become restorable from the one page
that still lists them

## Linked Issues or Issue Description

Follow-up to #11300. No existing issue for the remaining gap;
description follows the enhancement template:

**What happened?**

After #11300, opening an archived company's URL (stale tab, history,
bookmark, remembered path) renders that company's pages — but the
sidebar switcher does not list it, so the user is stranded in a
workspace they retired, and every stale URL pulls them back in.
Separately, unarchiving a company has no UI: the archive button lives in
company settings, which becomes unreachable through normal navigation
once the company is archived.

**Expected behavior**

Arriving cold at an archived company's URL lands the user in an active
workspace, with a toast explaining the redirect. Explicitly choosing the
archived company (from the companies list) still works, so its pages
remain reachable. Archived companies can be restored from the companies
list.

**Steps to reproduce**

1. Create two companies; archive one.
2. Open `/{archivedPrefix}/dashboard` directly — before: renders the
archived workspace with no sidebar presence; after: bounces to the
active company's dashboard with a toast.
3. On the companies list, open the archived company's row menu — before:
no restore action anywhere; after: Unarchive.

## What Changed

- `ui/src/lib/company-selection.ts`: `resolveArchivedCompanyBounce` —
pure policy: bounce when the URL names an archived company that is not
the current selection and an active company exists; prefer the currently
selected active company as the destination.
- `ui/src/components/Layout.tsx`: the route-sync effect applies the
bounce (toast + selection + `replace` navigation) before syncing
selection from the route.
- `ui/src/pages/Companies.tsx`: Unarchive action (`PATCH status:
"active"`) in the row menu for archived companies.
- Tests: unit cases for the bounce policy; the e2e now drives all three
behaviors (direct-load bounce with toast, re-arrival bounce, deliberate
visit sticks) on top of the existing crash regression.

## Verification

- `pnpm vitest run src/lib/company-selection.test.ts
src/context/CompanyContext.test.tsx src/pages/Companies.test.tsx` in
`ui/` — 20 tests pass.
- `npx playwright test --config tests/e2e/playwright.config.ts
archived-company-url` — passes, covering bounce, toast, and
deliberate-visit paths.
- `pnpm typecheck` in `ui/` — clean.

## Risks

Low risk. The bounce only fires for archived-company URLs when the
archived company is not already selected and an active company exists;
all-archived instances render as before. Deliberate selection from the
companies list is unaffected (selection equals the matched company, so
no bounce). Unarchive reuses the existing `PATCH /api/companies/:id`
status transition the server already supports.

## Model Used

- Claude (Anthropic), Claude Fable 5 (`claude-fable-5`), via Claude Code
CLI with extended thinking and tool use (code search, edit, test
execution, Playwright e2e).

## 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-08-12 21:02:12 -07:00
Devin Foley d24a79f741
ci(dependabot): surface major npm updates as one grouped weekly PR (#11307)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Dependabot keeps the npm dependency tree and the GitHub Actions
workflows current with weekly update PRs
> - The npm config ignores every major version bump with a wildcard
`ignore` rule, and no other process reports pending majors
> - Major-version debt grows silently, and ignore rules also suppress
Dependabot security updates when the fix ships only in a newer major
> - Individual major PRs are not a good replacement: the board decided
in #7560 to keep the PR list mergeable, and a flood of breaking bumps
works against that
> - This pull request removes the blanket ignore and groups all pending
majors into one weekly PR, while minors and patches keep one PR per bump
> - The benefit is a standing, visible signal of pending major updates,
at a cost of at most one extra PR per week

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The Dependabot npm update flow configured in `.github/dependabot.yml`.

**Current behavior**

Dependabot opens weekly PRs for minor and patch npm updates. A wildcard
`ignore` rule suppresses every major version update. No report or
reminder replaces the suppressed PRs — the comment says "review those
manually", but nothing triggers that review. Ignore rules also apply to
Dependabot security updates, so a security fix that ships only in a
newer major is suppressed as well.

**Proposed behavior**

Dependabot opens one grouped weekly PR that contains every pending major
npm update. Minor and patch updates keep their current one-PR-per-bump
flow. A deliberate hold on a specific major can use a targeted
per-dependency `ignore` entry instead of the wildcard.

**Reason and benefit**

Silent major-version drift compounds: each skipped major makes the
eventual upgrade jump larger and riskier, especially across
peer-dependency families. A single grouped PR makes the backlog visible
in the PR list without flooding it. When the grouped PR is green, it
merges cheaply. When it is red, it is a visible standing task instead of
invisible debt.

**Breaking changes**

None. This changes repository automation only. Runtime behavior,
response shapes, and outputs are unchanged.

**Additional context**

Related history: #7483 grouped patch/minor updates by dependency type,
and #7560 reverted that grouping because the resulting 26-package PR was
hard to merge. This PR does not touch the patch/minor flow. It only
groups majors, which currently produce no PRs at all — it adds a signal
that does not exist today rather than replacing individually mergeable
PRs.

## What Changed

- Removed the wildcard `ignore` rule for `version-update:semver-major`
from the npm ecosystem in `.github/dependabot.yml`.
- Added a `major-updates` group (`applies-to: version-updates`,
`update-types: ["major"]`, `patterns: ["*"]`) so all pending majors land
in one weekly grouped PR.
- Left the schedule, labels, PR limits, and the github-actions ecosystem
unchanged.

## Verification

- `npx js-yaml .github/dependabot.yml` parses cleanly and the `groups`
stanza follows the Dependabot v2 schema (`applies-to`, `update-types`,
`patterns`).
- After merge: check Insights → Dependency graph → Dependabot for config
errors. The next weekly run (Monday 06:00) opens a single
`major-updates` grouped PR that lists the pending majors.
- No code changed, so the test suite is unaffected.

## Risks

- Low risk. This is CI/automation configuration only.
- The first grouped PR may be large, and red if several majors break the
build. That is the intended visibility mechanism, and it does not block
other work. A noisy or deliberately held-back dependency can be excluded
from the group with `exclude-patterns` or a targeted per-dependency
`ignore` entry.
- This does not regroup minors or patches, so it does not reintroduce
what #7560 reverted.

## Model Used

- Claude Fable 5 (Anthropic), model ID `claude-fable-5`, via Claude Code
CLI, extended thinking and 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
- [ ] I have run tests locally and they pass — N/A, YAML-only CI config
change; validated with `js-yaml`
- [ ] I have added or updated tests where applicable — N/A, no code
changed
- [x] I have updated relevant documentation to reflect my changes — none
reference the Dependabot config
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green — one e2e shard flaked on an
unrelated MCP UI spec and passed on re-run with identical code
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-12 21:00:41 -07:00
dmndbrp-oss a8d118a779
Prefer public base URL for generated invite links (#7619)
Fixes #7623

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Company invites are part of the access subsystem and must produce
URLs that recipients can open from outside the host machine.
> - Paperclip already has public/auth base URL configuration for
deployments behind a public hostname, Tailscale, or a reverse proxy.
> - Invite URL composition was still deriving its origin from the
incoming request host, so loopback-bound servers emitted
`http://127.0.0.1:3100/invite/...`.
> - A loopback invite URL is not shareable with a remote human or agent,
even when the token itself is valid.
> - This pull request makes invite URL builders prefer the configured
public base URL and keep the existing request-host fallback when it is
unset.
> - The benefit is that copied invite links use the reachable deployment
origin without changing local-only behavior.

## Linked Issues or Issue Description

Fixes #7623

No duplicate or related PRs/issues were found in a GitHub search for
invite URL, loopback, public base URL, and `authPublicBaseUrl` terms.

## What Changed

- Added base URL resolution in `server/src/routes/access.ts` that strips
trailing slashes and prefers configured `authPublicBaseUrl` over the
request-derived host.
- Threaded `authPublicBaseUrl` through invite summary, invite onboarding
manifest, onboarding text, access routes, `createApp`, and server
startup wiring.
- Added `server/src/__tests__/invite-url-public-base-url.test.ts`
covering configured public-base precedence, unset fallback behavior, and
trailing-slash normalization.
- Registered the invite public-base URL test in the serialized Vitest
server runner.

## Verification

```bash
pnpm install --frozen-lockfile
pnpm exec vitest run server/src/__tests__/invite-url-public-base-url.test.ts
pnpm run test:run:serialized
```

Local results from the rebased PR branch:

- `pnpm install --frozen-lockfile` exited 0.
- Targeted invite URL test exited 0: 1 file, 3 tests passed.
- Serialized server suite exited 0: 106 serialized suites completed; the
new invite URL test passed inside that runner.

Manual check after deployment: set `PAPERCLIP_AUTH_PUBLIC_BASE_URL` or
equivalent public base URL config, create a company invite, and confirm
the returned/copied invite URL uses that public origin instead of
`127.0.0.1`.

## Risks

Low risk. The new public base URL parameter is optional and falls back
to existing request-derived behavior when unset. The main operational
risk is misconfigured public base URL input; the implementation only
trims trailing slashes and otherwise trusts the configured origin.

## Model Used

- Original implementation: Anthropic `claude-sonnet-4-6`, 200k context,
tool use and test execution.
- Conflict repair and verification: OpenAI Codex GPT-5.5, coding agent
with shell, git, GitHub CLI, and local 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)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Coder (Claude) <coder-claude@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Paperclip Coder (Claude) <lad-agent@paperclip.ing>
2026-08-12 16:44:28 -07:00
Dylan Roy 6a546e8a9a
fix(server): align agent run JWT default TTL with documented 48h default (#10176)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Local adapters (claude_local, codex_local) run agent heartbeats as
child processes, with a short-lived run JWT injected as
`PAPERCLIP_API_KEY` at spawn time
> - That JWT is minted exactly once, when the adapter spawns the process
— its TTL must therefore cover the entire wall-clock life of the run,
not just a prompt startup
> - On laptops the gap between spawn and first real execution can be
huge: a timer heartbeat scheduled while the lid is closed fires during a
~2s macOS dark wake, the machine re-sleeps immediately, and the frozen
child only executes during a later, longer wake — over an hour of
wall-clock delay in observed runs
> - The server's default TTL was 1h, so those sessions started with an
already-expired `PAPERCLIP_API_KEY` and every control-plane call 401'd;
the agent had to recover by manually minting a fresh key
> - The 1h default was also a spec drift: the CLI `env` command
(`DEFAULT_AGENT_JWT_TTL_SECONDS`) and the agent-authentication design
doc both document 172800s (48h)
> - This pull request realigns the server default to 48h and documents
the host-suspension constraint at the mint site and in the regression
test
> - The benefit is that lid-closed/suspended-host heartbeat runs come up
with a valid credential, and the three places that state the default now
agree

## Linked Issues or Issue Description

No public GitHub issue exists for this; per the bug-report template:

- **What happened:** A timer-driven heartbeat run on a MacBook (lid
closed, on battery) was invoked during a ~2s dark wake. The adapter
spawned the CLI and logged init within 2s, then the host re-slept and
the session sat frozen for ~64 minutes until a longer dark wake let it
execute. By then the injected run JWT (1h TTL, minted at spawn) had
expired, so every API call from the agent returned 401 and the run could
only recover via a manually minted key. A second agent's run the same
night showed the identical signature (output timestamps exactly matching
`pmset -g log` dark-wake windows).
- **Expected behavior:** A run that starts late because the host was
suspended should still have a valid `PAPERCLIP_API_KEY` when it finally
executes.
- **Steps to reproduce:** Run Paperclip on a laptop with a
`claude_local` agent on a timer heartbeat; close the lid on battery
overnight; observe a run invoked during a dark wake whose session
executes >1h later with an expired token (compare run-log timestamps to
`pmset -g log` sleep/wake entries).
- **Version/commit:** current `master` (14f20be9); local trusted
deployment mode.

Related context: #5864 introduced per-company signing keys in this same
module (no TTL changes).

## What Changed

- `server/src/agent-auth-jwt.ts`: default `ttlSeconds` for local agent
run JWTs raised from `60 * 60` (1h) to `60 * 60 * 48` (48h), matching
`DEFAULT_AGENT_JWT_TTL_SECONDS` in `cli/src/commands/env.ts` and
`doc/plans/2026-02-18-agent-authentication-implementation.md`; comment
documents why the TTL must cover host-suspension gaps
- `server/src/agent-auth-jwt.ts`: stale "~1h by default" reference in
the legacy-fallback guidance updated to 48h
- `server/src/__tests__/agent-auth-jwt.test.ts`: default-TTL regression
test updated to assert 48h and explain the constraint
- `PAPERCLIP_AGENT_JWT_TTL_SECONDS` remains the explicit override knob;
operators who set it see no behavior change

## Verification

- `cd server && pnpm vitest run src/__tests__/agent-auth-jwt.test.ts
src/__tests__/agent-auth-middleware.test.ts` — 24/24 pass locally
- Review that the three default sources now agree:
`server/src/agent-auth-jwt.ts` (`60 * 60 * 48`),
`cli/src/commands/env.ts` (`DEFAULT_AGENT_JWT_TTL_SECONDS = "172800"`),
design doc (`default: 172800`)
- Manual: on a laptop, set no TTL env, trigger a heartbeat, `echo
$PAPERCLIP_API_KEY` inside the run and decode the JWT — `exp - iat` is
172800

## Risks

- Longer-lived bearer tokens widen the leak window if a run token is
exfiltrated. Mitigations already in place: tokens are
per-company/per-instance signed (#5864), bound to a `run_id`, and never
persisted server-side. Operators wanting shorter tokens keep the
`PAPERCLIP_AGENT_JWT_TTL_SECONDS` override.
- The legacy master-secret fallback window guidance ("disable ~one TTL
after deploy") lengthens accordingly; the comment now states 48h
explicitly.
- Follow-up ideas intentionally out of scope: rejecting run JWTs whose
run has terminated (server-side revocation check), and holding a power
assertion (`caffeinate`-style) for the duration of local adapter runs so
dark-wake-spawned runs keep the host awake.

## Model Used

- Claude (Anthropic) — Fable 5, model ID `claude-fable-5`, via Claude
Code 2.1.x under Paperclip's `claude_local` adapter; extended thinking
and full tool use (shell, file edits, test execution) 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
- [ ] 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-08-12 16:44:20 -07:00
Sergio-LPA b7b8fbf688
fix(adapter-utils): let explicit PAPERCLIP_API_URL override the derived runtime URL in run env (#10339)
## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies
> - Every agent run gets a run-scoped bridge into the Paperclip API
through the injected `PAPERCLIP_API_URL` / `PAPERCLIP_API_KEY` env vars,
built by `buildPaperclipEnv` in
`packages/adapter-utils/src/server-utils.ts`
> - `buildPaperclipEnv` resolves that URL as `PAPERCLIP_RUNTIME_API_URL
?? PAPERCLIP_API_URL ?? http://<listen-host>:<port>`, and the server
always exports `PAPERCLIP_RUNTIME_API_URL` derived from
`authPublicBaseUrl` at boot
> - When `authPublicBaseUrl` points at an address that is not reachable
from inside the runtime container (e.g. a VPN/tailnet-only address used
to keep the web UI off the public internet), every local run receives a
dead API URL (`curl` exit 7) and agents only survive by hand-rolling a
localhost fallback
> - An operator-set `PAPERCLIP_API_URL` is the documented escape hatch —
`docs/deploy/environment-variables.md` states the server "preserves the
value" when set externally and that the run-level var "inherits the
server-level value" — but the run env builder inverts the precedence, so
the override never actually reaches runs
> - This pull request swaps the precedence in `buildPaperclipEnv` so an
explicit `PAPERCLIP_API_URL` wins over the derived runtime URL, aligning
the behavior with the documented contract
> - The benefit is that operators with split-horizon topologies (public
auth URL != container-reachable URL) can point agent runs at a reachable
endpoint with one env var, with zero behavior change for deployments
that do not set it

## Underlying Issue

No pre-existing public issue covers this, so per CONTRIBUTING ("Link
Issues or Describe Them In-PR") here are the `bug_report.yml` fields
inline:

- **What happened:** with `PAPERCLIP_AUTH_PUBLIC_BASE_URL` on a
tailnet-only address and `PAPERCLIP_API_URL=http://localhost:3100`
explicitly set in the server environment, every agent run still received
`PAPERCLIP_API_URL=http://100.x.y.z:3100` (the derived,
container-unreachable URL); `curl` from inside the run exits 7 and
agents can only reach the API by hand-rolling a localhost fallback
- **Expected behavior:** the run env inherits the operator-configured
`PAPERCLIP_API_URL`, as documented in
`docs/deploy/environment-variables.md` ("preserves the value", run-level
var "inherits the server-level value")
- **Steps to reproduce:** (1) set `PAPERCLIP_AUTH_PUBLIC_BASE_URL` to an
address not reachable from inside the server container, (2) set
`PAPERCLIP_API_URL=http://localhost:3100` in the server env, (3) trigger
any agent run and inspect the spawned process env: it carries the
derived URL, not the override
- **Version/commit:** reproduced on the `91e58acb` image (2026-07-19);
the precedence is unchanged on current `master` (`a3b293e`)
- **Deployment mode:** single-host Docker Compose, local adapters
(`claude_local`/`codex_local`), web UI exposed via VPN/tailnet only

## Related PRs (dedup search)

Several in-flight PRs touch the same pain point (runs receiving an
unreachable injected API URL) — linked for reviewer context; none of
them honors the documented explicit override, and the older ones appear
stale:

- #9916 — reworks `PAPERCLIP_RUNTIME_API_URL` derivation and port
preservation (server side); complementary, does not change run-env
precedence
- #8130 — honors a pre-set `PAPERCLIP_RUNTIME_API_URL` (server side); a
complementary escape hatch via the runtime var instead of the documented
`PAPERCLIP_API_URL` override
- #8025 — heuristic: prefer loopback when the runtime bind is loopback
(no activity since Jun 12)
- #5692 — heuristic loopback-safe URL inside `buildPaperclipEnv` (no
activity since May 14)
- #4877 — broader same-host injection rework across 10 files (no
activity since May 2)
- #4794 — always forces loopback for spawned agents (no activity since
Apr 30; would break split-horizon setups where a reachable non-loopback
URL is intended)

This PR intentionally takes the Path-1 route from CONTRIBUTING: the
smallest possible change (swap two lines so the documented operator
override wins) plus regression tests, rather than a new heuristic.

## What Changed

- `packages/adapter-utils/src/server-utils.ts`: `buildPaperclipEnv` now
resolves the injected URL as `PAPERCLIP_API_URL ??
PAPERCLIP_RUNTIME_API_URL ?? http://<listen-host>:<port>` (explicit
override first), with a short comment explaining why
- `packages/adapter-utils/src/server-utils.test.ts`: three new tests
covering the override precedence, the derived-URL fallback, and the
listen-host default (including the `0.0.0.0` to `localhost` mapping)
- `server/src/__tests__/paperclip-env.test.ts`: updated the expectation
that encoded the old runtime-URL-first precedence and added the
symmetric fallback case (runtime URL used when no explicit override is
set)
- No docs changes needed: `docs/deploy/environment-variables.md` already
describes the fixed behavior

## Verification

- `vitest run` on the new `buildPaperclipEnv` tests in
`packages/adapter-utils`: 3/3 pass
- `vitest run` on `server/src/__tests__/paperclip-env.test.ts` after the
expectation update: 5/5 pass (the first CI run correctly flagged the one
test that encoded the old precedence)
- Reproduced and verified on a production deployment (single-host
Docker, `PAPERCLIP_AUTH_PUBLIC_BASE_URL` on a tailnet-only address):
- Before: freshly spawned runs received
`PAPERCLIP_API_URL=http://100.x.y.z:3100` (verified in the spawned
process `/proc/<pid>/environ`); `curl` to it from inside the container
exits 7
- After (with `PAPERCLIP_API_URL=http://localhost:3100` in the compose
environment): a fresh run received `http://localhost:3100`, and `curl
$PAPERCLIP_API_URL/api/agents/me` with the run-scoped key returned HTTP
200; the run finished `succeeded` with usage telemetry recorded

## Risks

- Low. Behavior changes only for deployments that explicitly set
`PAPERCLIP_API_URL`; when unset (the default),
`PAPERCLIP_RUNTIME_API_URL` is used exactly as before
- The sandbox callback bridge (`execution-target.ts`) is intentionally
untouched: remote sandboxes genuinely need the publicly reachable URL,
and its `input.hostApiUrl || PAPERCLIP_RUNTIME_API_URL || ...` chain
still provides it

## Model Used

- Claude Fable 5 (Anthropic, model ID `claude-fable-5`), extended
thinking + agentic tool use via Claude Code, operating over SSH against
the affected deployment

## Checklist

- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [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 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

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

---------

Co-authored-by: Sergio-LPA <204395363+Sergio-LPA@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 16:44:11 -07:00
Philip D'Souza 4660562fde
fix(opencode-local): make the model-availability probe non-fatal (#10294)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents run through adapters; the `opencode-local` adapter shells out
to the OpenCode CLI and, before each run, does a pre-flight `opencode
models` **availability probe** to fail fast on a misconfigured
`provider/model`.
> - That probe was written to **throw on any probe failure** — a
timeout, a non-zero exit, or a transient `Unexpected error` from the CLI
— which aborts the whole heartbeat run.
> - In practice the CLI probe fails transiently (provider hiccup, cold
cache, momentary CLI error). When that happens *after* the agent has
already done its work, the run dies before its terminal disposition is
written, so the platform reopens the issue and re-runs it — a spurious
crash/re-run loop that affects every agent on the OpenCode adapter.
> - This PR makes the probe **non-fatal when it cannot run**: it warns
and proceeds with the configured model, letting the real invocation be
authoritative.
> - It deliberately **keeps** the genuine guard: when the probe
*succeeds* and the configured model is absent from a non-empty list, it
still throws (this is what catches misconfigured slugs).
> - The benefit is that a best-effort pre-flight check can no longer
take down an otherwise-healthy run, while the useful misconfiguration
guard is retained.

## Linked Issues or Issue Description

No public GitHub issue exists; describing inline (bug).

**What happened:** an OpenCode-adapter agent run terminated at the
adapter level with `` `opencode models` failed: Unexpected error ``. The
failure landed after the agent had produced its work, so the
terminal-status update never applied and the run was reopened and
re-executed.

**Expected:** a transient failure of the `opencode models` availability
*probe* should not abort the run — the probe is a best-effort pre-flight
guard, not a gate.

**Actual:** the probe threw on timeout / non-zero exit / empty output,
aborting the run and discarding the completed work + disposition.

**Scope:** both the local (`models.ts`) and remote/SSH (`execute.ts`)
probe paths; affects any agent on the `opencode_local` adapter.

Related PRs (context / prior art):
- Refs #5119 — added the remote execution-target model-probe validation
this PR softens.
- Refs #3291 — closed prior attempt to make the `opencode_local` model
probe non-blocking (at agent-create time; different entry point).
- Refs #8014 — related open work raising the probe timeout (20s → 60s);
complementary, not overlapping.

## What Changed

- `models.ts` (`ensureOpenCodeModelConfiguredAndAvailable`): if
discovery throws (probe can't run) or returns an empty list, **warn and
proceed** with the configured model instead of throwing. The "model
present in a non-empty list" check is unchanged and still throws when
the configured model is genuinely absent.
- `execute.ts` (`ensureRemoteOpenCodeModelConfiguredAndAvailable`):
remote probe **timeout / non-zero exit / empty output** now warn and
return (proceed) instead of throwing. The remote model-absent guard
still throws.
- `models.test.ts`: the local "discovery cannot run" case now asserts
the probe **proceeds** with the configured model (was: asserts it
rejects).
- `execute.test.ts`: added remote regression tests — non-zero exit,
timeout, and empty output all proceed; a successful probe missing the
configured model still rejects.

## Verification

```bash
pnpm --filter @paperclipai/adapter-opencode-local typecheck   # clean
# opencode-local server suite (default 5s per-test timeout is too tight for the
# heavy SSH tests on some machines; use a realistic timeout):
node node_modules/.pnpm/vitest@*/node_modules/vitest/vitest.mjs run \
  packages/adapters/opencode-local/src/server/models.test.ts \
  packages/adapters/opencode-local/src/server/execute.test.ts \
  packages/adapters/opencode-local/src/server/execute.remote.test.ts \
  --testTimeout=45000
```

Result: typecheck clean; all opencode-local server tests pass, including
the new remote fail-open tests and the retained "model unavailable on
the remote target" guard test.

## Risks

- **Fail-open behavior (intentional).** When the probe can't run, a
genuinely misconfigured model is no longer caught at pre-flight — it
surfaces at the real invocation instead. This is the accepted tradeoff:
the probe is best-effort, and the real invocation is authoritative. The
high-value guard (probe succeeds + model absent from a non-empty list)
is retained, so the common misconfiguration — a bad `provider/model`
slug — is still caught.
- No API, schema, or migration changes. Behavior change is confined to
the two probe helpers. Low risk overall.

## Model Used

Anthropic **Claude Opus 4.8** (`claude-opus-4-8`), used via Claude Code
with agentic tool use (repo search, file editing, shell/code execution)
and extended reasoning. Used to diagnose the crash, implement the fix,
and write the tests; the change was reviewed before 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 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
(`fix/opencode-model-probe-non-fatal`) and contains no internal ticket
id
- [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 — N/A
(internal adapter behavior; no user-facing docs affected)
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green (functional gates:
tests/build/e2e/typecheck/security). Review/Greptile gate re-running
after this update.
- [ ] Greptile is 5/5 with no open P2s — re-triggered after addressing
both P2s (remote test coverage + this template-complete description)
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-12 16:44:03 -07:00
Eric Brookfield c6727e7b20
fix(server): don't implicitly reopen a blocked issue when the same PATCH wires blockers (#10269)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Issues coordinate that work, and first-class blockers
(`blockedByIssueIds`) are how dependent work auto-resumes when its
prerequisites finish
> - A human commenting on a blocked issue implicitly reopens it to
`todo` — a deliberate heuristic so "please continue" comments revive
parked work
> - But that heuristic evaluates the issue's *pre-update* blocker set,
ignoring blockers being wired in by the very same PATCH
> - So the natural repair action for a bare-blocked issue — one PATCH
adding `blockedByIssueIds` plus an explanatory comment — silently flips
the issue to `todo`, contradicting the dependency edit it just made
> - This pull request suppresses the implicit reopen when the request
itself declares a non-empty blocker list
> - The benefit is that structured dependency edits always win over the
conversational-comment heuristic, so blocked issues keep their intended
waiting posture and auto-resume via `issue_blockers_resolved` as
designed

## Linked Issues or Issue Description

No existing issue describes this exact behavior; per the bug-report
template:

- **What happened:** On a `blocked` issue with an empty blocker set, a
board user sent one `PATCH /api/issues/:id` containing
`blockedByIssueIds: ["<unresolved-issue-id>"]` and a `comment`. The
response showed `status: "todo"` — the implicit comment-reopen fired
even though the same request wired an unresolved blocker. A follow-up
`PATCH { status: "blocked" }` was then needed to restore the waiting
posture (and because the blocker array replaces on every update, the two
fields had to be re-sent together).
- **Expected behavior:** A request that explicitly declares dependencies
is stating that the issue is waiting on other work. The implicit reopen
exists for plain conversational comments; it should not override a
structured dependency edit made in the same request.
- **Steps to reproduce:** (1) Create issue A with `status: "blocked"`
and no blockers; (2) as a board user, `PATCH /api/issues/A` with `{
"blockedByIssueIds": ["<id of an open issue>"], "comment": "wiring the
dependency" }`; (3) observe the response/issue status is `todo` instead
of remaining `blocked`.
- **Version/commit:** reproduced on `master` @ `d1b9448b5`.
- **Deployment mode:** `authenticated`, single-host (macOS launchd),
embedded Postgres.

Related (not fixed here): the family of "blocked with empty
`blockedByIssueIds` zombie" reports — Refs #9201, Refs #6523 — this bug
is one way an issue's status and blocker list end up contradicting each
other; and Refs #8062, which proposes a different auto-transition at the
status/blocker boundary.

## What Changed

- `shouldImplicitlyMoveCommentedIssueToTodo`
(server/src/routes/issues.ts) accepts an optional
`requestAddsExplicitBlockers` input and returns `false` when set,
alongside the existing suppression guards, with a comment documenting
the rationale.
- The `PATCH /api/issues/:id` call site passes
`requestAddsExplicitBlockers: Array.isArray(req.body.blockedByIssueIds)
&& req.body.blockedByIssueIds.length > 0`.
- Two route tests in `issue-comment-reopen-routes.test.ts`: a regression
test (comment + non-empty blocker list on a blocked issue must not flip
status) and a boundary test (comment + `blockedByIssueIds: []` still
implicitly reopens, preserving the existing clear-blockers behavior).

Deliberately unchanged: explicit `reopen`/`resume` flags still behave as
before, and the `POST /comments` route is untouched (its body cannot
carry `blockedByIssueIds`).

## Verification

- `cd server && pnpm vitest run
src/__tests__/issue-comment-reopen-routes.test.ts` → 74/74 pass.
- Reverting the `issues.ts` change makes the new regression test fail
with `expected 'todo' to be undefined` — it bites.
- `cd server && pnpm tsc --noEmit` → clean.

## Risks

- Low. The change is a single additional suppression guard on the
*implicit* reopen path, scoped to requests that carry a non-empty
`blockedByIssueIds` array; all other reopen behavior is untouched.
- Edge case considered: a request wiring only already-resolved blockers
plus a comment now stays `blocked` instead of implicitly reopening. This
is the conservative reading of caller intent (an explicit dependency
edit), and an explicit `status`/`reopen` in the same request still wins.

## Model Used

- Anthropic Claude — Fable 5 (`claude-fable-5`), extended thinking
enabled, agentic tool use via Claude Code (CLI). Production repro,
diagnosis, fix, and tests all model-authored under human direction.

## 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 (none
applicable — behavior comment added inline)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green (pending first CI run on this PR)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(pending first review pass)
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 16:43:56 -07:00
Jannes Stubbemann 6d2eab742f
fix(server): retry runs that hit a sandbox provider worker restart window instead of failing setup (#10212)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent runs execute in sandbox environments acquired through provider
plugins (e.g. the Kubernetes sandbox provider)
> - Lease acquisition happens during run setup, before the adapter
executes
> - When a provider plugin's worker is momentarily unavailable (a server
or plugin restart window), lease acquisition throws "Sandbox provider
... is installed via plugin ..., but its worker is not running."
> - The heartbeat setup path records that as a terminal `setup_failed`:
no retry classifier matches the message, so the run dies instantly even
though the worker returns seconds later
> - This PR classifies that transient condition as retryable
infrastructure so the run is retried instead of being lost to a restart
blip
> - The benefit is that routine restarts no longer produce spurious
instant run failures

## Linked Issues or Issue Description

No public GitHub issue exists; describing inline following the bug
report template.

**What happened**

During a brief sandbox-provider-worker restart window, several runs
failed instantly with `setup_failed` ("... but its worker is not
running."), while runs on the same agent moments earlier and later
succeeded.

**Expected behavior**

A transient, self-healing worker-unavailable condition should schedule a
bounded retry, not terminally fail the run.

**Steps to reproduce**

Trigger a run while the sandbox provider plugin worker is momentarily
unavailable (a server or plugin restart). Lease acquisition throws the
worker-not-running error and the run is finalized as `setup_failed` with
no retry. The recovery test added here reproduces the classification
path.

**Deployment mode**

Cloud multi-tenant execution (Kubernetes sandbox provider plugin).

## What Changed

- Added a dedicated, readable predicate that recognizes the transient
sandbox-provider-worker-unavailable lease failure and treats it as
retryable infrastructure, so the heartbeat schedules a bounded
continuation retry instead of finalizing terminally
- The predicate is anchored to the full lease-failure phrasing (`is
installed via plugin ... but its worker is not running`) so it cannot
match the permanent "provider not installed" message emitted by config
validation
- Added tests proving the readiness poll already waits the full deadline
while the worker handle is absent or `starting` (registered-late
coverage); no poll behavior change was needed

## Verification

- `cd server && npx vitest run
src/__tests__/environment-runtime.test.ts` — poll exhaustion +
registered-late cases
- `npx vitest run src/__tests__/heartbeat-process-recovery.test.ts` —
worker-unavailable message schedules a retry; a non-matching permanent
provider failure still escalates terminally (negative case)

## Risks

Low risk. The retry is bounded by the existing
infrastructure-continuation attempt cap (max 3), the message match is
narrow enough to exclude the permanent provider-not-installed failure
(covered by a negative test), and no readiness-poll or lease-acquisition
behavior changed.
## Model Used

Claude (Anthropic) via Claude Code. Implementation and tests authored by
a Claude Sonnet-class model (`claude-sonnet-5`) dispatched as isolated
per-task implementer agents under a multi-agent orchestration workflow;
root-cause investigation, planning, and two-stage adversarial code
review performed by additional Claude agents. Extended thinking and tool
use enabled throughout.

## 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
- [ ] 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-08-12 16:43:48 -07:00
Jannes Stubbemann a0bdf388af
fix(agents): refuse to hire onto an adapter this instance cannot run (#10256)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Hiring an agent means choosing a harness (adapter) for it, and an
instance can declare which harnesses it actually runs through
`PAPERCLIP_ADAPTERS`, which `reconcileAdapterAvailability` turns into a
disabled set at boot
> - The hire and create routes validate the adapter type with
`assertKnownAdapterType`, which only asks whether the adapter is
REGISTERED — a disabled adapter passes
> - So an agent can be created on a harness the instance cannot run, and
the failure only appears later, per run, at lease time: `Adapter "..."
is not in the configured adapter registry`
> - By then the error is in a run log, minutes after the choice, with
nothing tying it back to the harness the user picked; the agent also
keeps accepting work it can never do
> - This pull request validates the hire and create paths against the
ENABLED set and refuses with a message that names the adapters that are
available
> - The benefit is that an impossible choice fails at the moment it is
made, in the words of the choice itself, instead of as a run failure the
user cannot act on

## Linked Issues or Issue Description

No existing issue; describing it here per the bug report template.

**What happened**

On an instance with a curated registry, a company's Chief of Staff was
hired on `cursor_cloud`, which that instance had disabled. The API
accepted the hire. Its first assignment run then failed:

```
Failed to acquire lease for environment "Kubernetes Sandbox" (sandbox): Adapter "cursor_cloud" is not in the configured adapter registry
```

and its automation run sat in `queued` for hours afterwards. Nothing in
the hire response, the agent detail view, or the agent's status
explained that this harness could never run.

**Expected behavior**

hiring on an adapter the instance has disabled is refused at hire time,
with a message naming the adapters that can be chosen.

**Steps to reproduce**

1. Start the server with a registry that omits an otherwise-registered
adapter, e.g. `PAPERCLIP_ADAPTERS` listing `claude_local` but not
`cursor_cloud`.
2. `POST /api/companies/:companyId/agents` with
`{"name":"CoS","adapterType":"cursor_cloud"}`.
3. The agent is created (201). Every run it attempts fails at lease time
with the message above.

**Paperclip version or commit**

master (`4c55f0d8d`).

## What Changed

- `server/src/routes/agents.ts`: adds `assertSelectableAdapterType`,
which extends `assertKnownAdapterType` with an enabled-set check and
throws `422 Adapter "<type>" is not available on this instance.
Available adapters: <list>`. The hire (`POST .../agent-hires`) and
create (`POST .../agents`) paths now use it.
- Routes that operate on an EXISTING agent keep
`assertKnownAdapterType`, so an agent already running on a
since-disabled adapter is unaffected — the same rule
`listEnabledServerAdapters` already documents ("hidden from selection,
still functional for agents that already use them").
- `server/src/__tests__/agent-adapter-validation-routes.test.ts`: mocks
the adapter-plugin store's disabled set (so the test never writes to a
real `~/.paperclip/adapter-settings.json`), and covers
refuse-when-disabled (including that the message names the alternatives
and that no agent is created) plus create-still-works-when-enabled.

## Verification

```
pnpm vitest run server/src/__tests__/agent-adapter-validation-routes.test.ts
```
13 tests pass, including the two new cases and the existing
unknown-adapter-type test.

Manual: disable an adapter (`PATCH /api/adapters/:type {"disabled":
true}` as an instance admin, or omit it from `PAPERCLIP_ADAPTERS` and
restart), then POST an agent with that `adapterType` — 422 naming the
available adapters, and no agent row is created.

## Risks

Low, and scoped to new selections:

- Automation that creates agents on a disabled adapter now gets a 422
where it previously got a 201 followed by runs that always failed. That
is the intended behavior change, and the message names the valid
choices.
- Existing agents, and every route that acts on an existing agent, are
untouched.
- The enabled set comes from the same store `GET /api/adapters` already
reports, so the API and the picker cannot disagree.

## Model Used

Claude Opus 5 (Anthropic), model id `claude-opus-5`, 1M context window,
extended thinking, with tool use and code execution via Claude Code.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change
(`upstream/adapter-selection-guard`) 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 (the
new helper documents the selection-vs-existing-agent rule)
- [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

Related: #10254 makes the adapter inventory readable during onboarding,
which is what lets the picker hide these adapters in the first place.
This PR is the server-side backstop for the same failure.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 16:43:40 -07:00