Commit Graph

1841 Commits

Author SHA1 Message Date
Dotta 25cf079ec5
feat(runner): add Codex-native application integration (#12591)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The runner package is useful only when the application can start,
observe, and recover a native Codex run safely.
> - Existing direct adapters must keep their current execution and
finalization paths.
> - The application boundary therefore needs additive persistence,
authorization, coordination, and recovery behind an explicit
experimental adapter.
> - This pull request adds that Codex-only boundary without activating
generalized providers, remote environments, or the later task/SDK
surfaces.

## Linked Issues or Issue Description

**Subsystem affected**

Shared contracts, database persistence, adapter utilities, server
native-runtime services, and the experimental Paperclip Runner adapter.

**Problem or motivation**

The already-landed runner package has a qualified Codex path, but the
application needs durable native-run state, guarded runtime selection,
authenticated coordination, tool security, finalization, and recovery
before the experimental adapter can be exercised safely.

**Proposed solution**

Add a Codex-only `paperclip_runner` application path behind the existing
default-off native-runner setting. Bind native state and coordination to
company/run identity, preserve persisted-run recovery, and leave every
direct adapter on its existing legacy execution path.

**Alternatives considered**

The earlier stack boundary introduced a generalized executor and
remote-environment lifecycle here. That made this PR depend on
implementations in higher PRs and changed reusable sandbox behavior
globally. Those pieces are now deferred together to #12592.

**Roadmap alignment**

ROADMAP.md does not list a conflicting native-runner integration
project. This change adds the application boundary for the existing
Runner architecture.

## What Changed

- Added native run/result/finalization/provider-trace persistence,
shared validators, and idempotent migration/replay coverage.
- Added guarded Codex-only runtime selection, authenticated PRP
coordination, recovery, finalization, and interaction services.
- Added run/company-bound tool-gateway authorization, credential
redaction, SSRF protections, and replay-safe behavior.
- Added the explicit `paperclip_runner` adapter behind the default-off
rollout setting.
- Preserved legacy answered-question wake projection and direct-adapter
execution/finalization paths.
- Hardened cancellation so only owned in-memory child processes are
signaled; persisted recycled PIDs/process groups are never trusted.
- Retained the narrow Claude ACPX isolated-context security follow-up
discovered after #12590.
- Deferred the generalized executor, provider ingress, remote lifecycle,
SDK/lab/eval work, release-process changes, and lockfile.

## Verification

- Changed-file delta against `master`: 133 files.
- GitHub Actions is the authoritative verification environment for this
PR.
- Full CI, security, and Greptile review will run on this lowest
unmerged stack PR.
- Local tests/build/typecheck were not run because this checkout is
resource constrained.
- Static diff/reference checks pass, and `pnpm-lock.yaml` is unchanged.

## Risks

- This touches central heartbeat and agent-route code, so legacy
compatibility is the primary risk.
- Runtime selection remains Codex-only and explicit; direct Codex,
Claude, OpenCode, process, HTTP, and plugin adapters remain on their
existing paths.
- Fresh native starts fail closed while the rollout flag is off;
persisted native records remain readable and recoverable.
- Cancellation, company/run binding, tool calls, status decisions, and
completion writes are guarded or replay-safe.

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

## Model Used

OpenAI Codex, GPT-5.6, with repository tools, code execution, and
parallel agent review.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either linked existing issues or described the issue in-PR
following the relevant issue template
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [ ] I have run tests locally and they pass — GitHub Actions is
authoritative for this resource-constrained checkout
- [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 risks above
- [ ] All Paperclip CI and security gates are green
- [ ] Greptile is 5/5 with no open actionable findings
- [x] I will address all Greptile and reviewer comments before merge

## Stack

- Position: 3 of 5 overall; lowest of 3 currently unmerged
- Base: `master`
- Previous:
[#12590](https://github.com/paperclipai/paperclip/pull/12590), qualified
Claude ACPX runtime — merged
- Next: [#12592](https://github.com/paperclipai/paperclip/pull/12592),
generalized Codex executor, task experience, and developer SDKs

---------

Co-authored-by: Dev Agent <dev@paperclip.ing>
2026-08-31 14:38:38 -05:00
Dotta a7e6b818e9
feat(apps): add Paperclip Cloud managed OAuth connector (#12600)
## Thinking Path

> - Paperclip lets operators give governed tools to AI agents.
> - Connected Apps already support provider OAuth and personal
connection grants.
> - Some providers require one stable callback and do not support
dynamic client registration.
> - Self-hosted Paperclip instances can run at private or changeable
origins.
> - Paperclip Cloud can provide the stable callback while each instance
keeps its durable provider credentials.
> - This pull request adds the instance side of that managed OAuth
protocol and keeps customer-created clients available.
> - The benefit is a safe path to one-click Workspace connections for
hosted and enrolled self-hosted instances.

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting. This change updates the server, Apps UI, shared app
definitions, and connection documentation.

**Problem or motivation**

Some OAuth providers require a pre-registered callback and
provider-owned client. An arbitrary self-hosted Paperclip origin cannot
use that client callback directly. Paperclip ID must also stay limited
to product identity instead of resource authorization.

**Proposed solution**

Use the existing Paperclip Cloud application as the fixed callback
broker. Enroll each instance to an exact origin and separate Ed25519 and
X25519 keys. Bind every request and sealed envelope to the instance,
environment, user, company, provider, profile, and exact scope set.
Store durable provider credentials only in the originating instance
vault.

**Alternatives considered**

Customer-created OAuth clients remain available as the independent
fallback. A generic redirect relay was rejected because it would allow
caller-selected destinations and scopes. Paperclip ID was rejected as
the broker because it is the identity boundary. A new service was
rejected because the existing Cloud application already owns customer
login and the public callback origin.

**Roadmap alignment**

This work extends the shipped MCP Tool Gateway and Apps milestone. It
also supports the Connected Apps and Cloud deployments roadmap items.

Companion Cloud implementation:
https://github.com/paperclipai/paperclip-cloud/pull/312

The duplicate search found no related open Paperclip PR or issue.

## What Changed

- Add a `paperclip_cloud_connector` client with signed requests, exact
profile and scope bindings, and X25519-sealed credential handling.
- Add explicit self-hosted enrollment with owner-only instance key
storage and exact HTTPS origins.
- Route managed Google Workspace setup through Paperclip Cloud and
preserve customer-created OAuth clients.
- Keep broker claims retryable until the local vault transaction
commits.
- Keep managed Google per-profile removal local-only to avoid
client-wide provider revocation.
- Add setup status to the Connections page and retain the Paperclip ID
names as compatibility aliases.
- Document the trust boundaries, enrollment, callback, refresh, removal,
and rollout flows.

## Verification

- `pnpm -r typecheck`
- `pnpm --filter @paperclipai/shared exec vitest run
src/app-definitions.test.ts`
- `pnpm --filter @paperclipai/server exec vitest run
src/services/paperclip-cloud-connector.test.ts
src/services/paperclip-cloud-connector-enrollment.test.ts`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/tool-access-service.test.ts -t 'brokered Gmail
OAuth|brokered OAuth state'`
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/apps/Connections.test.tsx`
- `pnpm check:token-gates`
- `pnpm build`
- The full stable test runner also reproduced existing macOS workspace,
skill-discovery, and listener fixture failures outside the changed
paths. GitHub Linux CI is the authoritative full-suite result.

## Risks

- The managed flow depends on
https://github.com/paperclipai/paperclip-cloud/pull/312. Real provider
profiles stay disabled until Cloud deploys that protocol and the
provider approves the managed client.
- A Cloud outage blocks new authorization and refresh. Existing access
tokens continue to work until expiry.
- Managed Google profile removal only deletes the local grant. This
avoids invalidating the user's other profiles that share the managed
Google client.
- Legacy `paperclip_id_connector` records require a reconnect after
their current access tokens expire. Old Paperclip ID keys and refresh
tokens are not sent to Paperclip Cloud.

> 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.6 (Codex). Agentic coding, tool use, code execution, and
subagents were enabled. The context-window size is not exposed in this
session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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-31 14:34:46 -05:00
Dotta 39eafad47d
test(e2e): shorten and split Smoke Lab coverage (#12506)
## Thinking Path

> - Paperclip uses browser tests to protect critical operator flows.
> - The trusted pull request workflow runs the E2E catalog on three
existing runners.
> - Smoke Lab was one 168-second spec, so the shard scheduler could not
divide it.
> - The spec also repeated service-start calls, page loads, and
full-page screenshots.
> - This pull request removes that repeated work and divides the
scenario catalog into two independent specs.
> - The benefit is a shorter Smoke Lab run and a balanced E2E lane
without more AWS capacity.

## Linked Issues or Issue Description

Refs: #10629

**What existing behavior does this improve?**

This improves the trusted pull request E2E lane and its Smoke Lab
Playwright coverage.

**Current behavior**

Smoke Lab is one indivisible 168-second CI spec. It starts services for
every scenario, loads the same evidence page twice, and captures a
full-page success screenshot for all 56 lifecycle steps.

**Proposed behavior**

Start Smoke Lab services once per spec. Keep the per-scenario fixture
reset. Capture one representative success screenshot per scenario and
keep every failure screenshot. Run P1–P4 and P5–P7 as separate specs so
the existing duration-aware scheduler can put them on different runners.

**Reason and benefit**

The optimized lifecycle reduced local Smoke Lab wall time from 57.68
seconds to 37.04 seconds. This is a 35.8% reduction. The two halves also
let the existing three runners target about 125, 124, and 124 seconds of
recorded spec work instead of about 168, 125, and 124 seconds.

**Breaking changes**

None. The same seven scenarios and eight lifecycle steps still run. The
result API still records every step. Successful non-connect steps no
longer attach redundant screenshots.

## What Changed

- Reused one Smoke Lab service start within each spec while retaining
isolated fixture installation for every scenario.
- Removed the duplicate catalog evidence navigation.
- Reduced success screenshots from 56 to 7 while retaining screenshots
for every failed step.
- Split the shared lifecycle runner into P1–P4 and P5–P7 specs.
- Mark each successful split result as partial and keep dashboard health
amber until one run covers the full catalog.
- Updated the duration manifest and contributor docs for the split.

## Verification

- `pnpm -r typecheck` passed on Node.js 24.20.0.
- `pnpm build` passed on Node.js 24.20.0.
- `node --test scripts/__tests__/e2e-shard.test.mjs` passed 9 tests.
- `pnpm exec vitest run ui/src/pages/tools/smoke-lab-matrix.test.ts`
passed 8 tests.
- Both split specs passed together on Node.js 24.20.0 after the review
fixes: 2 passed in 35.7 seconds; shell wall time was 36.86 seconds.
- The pre-change Smoke Lab baseline passed with a 57.68-second shell
wall time. The optimized unsplit A/B run passed with a 37.04-second
shell wall time.
- The full local E2E catalog passed 44 tests and skipped 2 tests. One
existing `pipelines-tutorial-flow.spec.ts` assertion failed again when
run alone.
- The broad local unit run reproduced failures in untouched
workspace-runtime suites. Typecheck, build, shard tests, and all changed
browser coverage pass. CI remains the authoritative full-suite result.

## Risks

- The split duration weights use the measured local reduction and the
previous 168-second CI weight. They should be refreshed after two real
pull request runs.
- Service state is shared within each half. Fixture installation still
runs before every scenario to reset connection, policy, and catalog
state.
- Each half records passed execution with partial coverage. Dashboard
health recognizes the partial flag and stays amber because no single
runner covers the full catalog. A failed half still records failed/red.
- Fewer success screenshots reduce redundant artifacts. Every scenario
keeps its connect screenshot, and every failure still captures evidence.

> 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, GPT-5. The exact deployment ID and context window are
not exposed in this session. The model used agentic reasoning, code
editing, shell execution, browser testing, 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-31 10:15:39 -05:00
Dotta 9e4be0e60c
fix(ui): prevent false agent instruction saves (#12502)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators edit each agent instruction bundle in the agent detail
page
> - The rich Markdown editor can normalize content and emit an onChange
event while it mounts
> - Paperclip treated that editor event as a user edit and retained the
dirty state after the tab unmounted
> - This pull request accepts rich-editor changes only after real user
interaction and clears shared edit state when the instructions tab
closes
> - The benefit is that opening instructions or moving between agent
tabs no longer shows false save controls or navigation warnings

## Linked Issues or Issue Description

**What happened?**

Opening an agent Instructions page could mark the page as edited without
user input. The page showed Save and Cancel controls and warned about
unsaved changes during unrelated tab navigation. The shared edit
callbacks could remain active after the Instructions tab unmounted.

**Expected behavior**

Opening an instruction file must not create a draft. Save controls and
navigation warnings must appear only after a user changes content.
Leaving the Instructions tab must clear its shared dirty, saving, save,
and cancel state.

**Steps to reproduce**

1. Open an agent Instructions tab with a Markdown entry file.
2. Do not edit the file.
3. Move to another agent tab or navigate away.
4. Observe false save controls or an unsaved-changes prompt.

**Paperclip version or commit**

Current `master` before this change.

**Deployment mode**

Self-hosted server and local development.

## What Changed

- Ignore rich Markdown editor normalization events until keyboard,
pointer, paste, input, drop, or before-input interaction occurs.
- Reset the interaction guard when the selected file, agent, or
persisted content changes.
- Clear the parent dirty, saving, save, and cancel state when the
Instructions tab unmounts.
- Add regression tests for mount normalization and cross-tab state
cleanup.

## Verification

- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/AgentDetail.instructions.test.tsx
src/pages/AgentDetail.liveRun.test.ts
src/pages/AgentDetail.progress.test.ts
src/components/MarkdownEditor.test.tsx` — 76 tests passed.
- `pnpm -r typecheck` — passed.
- `pnpm build` — passed.
- `pnpm test:run` — the relevant UI tests passed. The local full runner
reproduced unrelated workspace-runtime failures present on `master`;
GitHub CI is the authoritative isolated full-suite gate.
- `pnpm check:token-gates` — the changed files are clean. The command
reports nine existing color literals in
`ui/src/components/onboarding/PillGuy.tsx` from `master`.

## Risks

- Low risk. The change affects only local instruction-editor dirty-state
tracking.
- The interaction guard covers keyboard, pointer, paste, input, drop,
and before-input events.
- There are no API, database, migration, or visual design changes.

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

## Model Used

- OpenAI Codex, GPT-5 family. The deployment model identifier and
context-window size are not exposed in this session. The agent used
reasoning, repository tools, shell 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-30 08:23:35 -05:00
Dotta bc9ba7cd26
feat(runner): project native runs into task threads (#12321)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The experimental Paperclip Runner can execute a guarded Codex run
and persist provider-neutral events.
> - The task page still reads direct-adapter transcripts and cannot
present those native events.
> - Structured runner questions must also use the existing task
interaction experience.
> - Runtime selection must use the persisted run mode, not an adapter
name or a current feature flag.
> - This pull request projects native events and questions into the
existing task thread.
> - Direct adapters keep their existing transcript, composer,
interaction, and finalization paths.
> - The benefit is a complete native Codex task thread without a
behavior change for existing adapters.

## Linked Issues or Issue Description

Refs #12202. This pull request replaces that stale implementation on
current `master`.

**What happened?**

The server persists native runner events and structured input requests.
The task page only consumes direct-adapter transcripts. A native run
therefore cannot present a complete transcript, usage, or question flow
through the normal task experience.

**Expected behavior**

Native runs project persisted provider-neutral events into the existing
task thread. Native structured questions use the existing interaction
card. Direct adapters retain their current behavior.

**Steps to reproduce**

1. Enable the experimental runner.
2. Start a native Codex run that emits progress, usage, a structured
question, and a final reply.
3. Open the task page.
4. Observe that the direct-adapter transcript path cannot project the
native event records.

**Paperclip version or commit**

`master` at `67f9867bc`.

## What Changed

- Add the canonical structured-question validator and shared contract
exports.
- Materialize native input requests as existing task interactions.
- Validate native answers and deliver them through the durable
question-response receipt.
- Resume the original PRP request with an idempotent `request.resolve`
command.
- Project native messages, tool activity, cumulative usage, and final
replies into the existing transcript model.
- Propagate persisted `runtimeMode` to the task page and select native
handling only for `runtimeMode: "native"`.
- Expire pending interactions through the shared issue service on every
terminal transition, including decisions, stalled reviews, tree control,
and pipeline retry cleanup.
- Queue native run cancellation while a transaction is open and execute
it only after the owning transaction commits.
- Keep nonterminal and non-runner issue paths on their existing service
call shapes and behavior.

## Verification

- `pnpm --filter @paperclipai/server typecheck` — passed, including the
Rust runner release build and protocol/catalog drift gates.
- Focused native-thread and lifecycle suites — 18 files and 481 tests
passed during review.
- `issue-execution-policy-routes.test.ts` — 19/19 passed after the final
transactional-queue expectation update.
- `issue-agent-mutation-ownership-routes.test.ts` — 87/87 passed in the
final isolated compatibility rerun.
- GitHub Actions — policy, build, canary, typecheck/release registry, 5
serialized server shards, 8 general-test shards, 3 browser shards, and
both aggregate gates passed on `7793f3193`.
- Security — Snyk, Socket Project Report, Socket PR Alerts, and
Superagent passed.
- Greptile — 5/5 on `7793f3193`; all actionable review threads resolved.
- `git diff --check` — passed.
- Diff against `master`: 44 files.

## Compatibility Boundary

- Native transcript polling only runs when the persisted run reports
`runtimeMode: "native"`.
- Missing or legacy runtime modes continue through
`useLiveRunTranscripts`.
- Legacy questions keep the existing optional free-text choice.
- Native closed select sets can suppress that legacy fallback.
- Terminal cleanup uses the same issue service for native and legacy
interactions; only a bound native question schedules a native run
cancellation.
- Native cancellation happens after transaction commit, so failed or
rolled-back writes do not cancel a still-valid run.
- The durable delivery service checks the original native request before
it considers a continuation run.
- This pull request adds no migration, dependency, workflow, manifest,
or lockfile change.

## Risks

The main risk is routing a direct-adapter task through native handling
or changing terminal issue behavior. The implementation selects the
native path only from persisted runtime facts, retains the existing
nonterminal call shape, and schedules native cancellation only for a
validated bound native question after commit. Focused and
repository-wide tests cover both paths. Native requests remain bound to
the company, issue, run, and agent; answers are validated, durable, and
idempotent across reconnects.

## Model Used

OpenAI Codex, GPT-5 family. The client does not expose the exact
deployment ID or context window. Agentic reasoning, tool use, and code
execution were enabled.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run the affected local tests and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated the compatibility notes for this change
- [x] I have considered and documented risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I addressed all Greptile and reviewer comments before requesting
merge
2026-08-29 19:26:20 -05:00
Dotta a560b48d6d
feat(apps): refine Postman and Shopify setup (#12357)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Apps give those agents governed access to external tools.
> - Provider catalogs must match each provider's current protocol and
credential contract.
> - Postman method labels and API-key placement were outdated.
> - Shopify now offers a UCP commerce endpoint that needs a managed
agent-profile argument.
> - This pull request updates both providers and documents the complete
connection-authoring workflow.
> - The benefit is accurate setup, safer runtime defaults, and a
repeatable provider review process.

## Linked Issues or Issue Description

Refs #11965

This is stack 10 of 11. It depends on stack 9 and preserves the final
catalog work recovered from #11965.

Related: #5904 covers Shopify skill routing. This pull request covers
the Apps connection contract instead.

## What Changed

- Update Postman hosted MCP methods, capability choices, default
selection, and bearer-token placement.
- Add Shopify UCP commerce and Storefront compatibility methods with
public-store prerequisites.
- Inject the reviewed Shopify UCP agent profile at runtime and remove
that managed field from user input schemas.
- Classify Shopify checkout completion and cancellation as destructive
actions.
- Expand the connection authoring runbook from provider research through
verification and pull request handoff.
- Add focused shared, server, and UI coverage.
- Make the approved-execution waiter phase-aware so slow preparation
cannot consume the provider execution timeout and grace period.
- Settle legacy pre-execute-on-approve requests and invocations as
failed, clear their stale idempotency key, and allow a fresh governed
approval instead of leaving work stuck in `executing`.

## Verification

- `pnpm exec vitest run packages/shared/src/app-definitions.test.ts
server/src/__tests__/tool-access-service.test.ts
ui/src/pages/apps/AppsConnect.test.tsx -t
"Postman|Shopify|normalizeConnectionMethodConfig|classifyRisk"` (16
passed)
- `pnpm exec vitest run
server/src/services/approved-execution-wait.test.ts` (4 passed)
- `pnpm exec vitest run server/src/__tests__/tool-gateway.test.ts -t
"enforces policy, approvals, retries, rate limits, and company
boundaries for connected remote MCP calls"` (1 passed)
- `pnpm exec vitest run
server/src/__tests__/tool-gateway-service.test.ts` (21 passed; includes
legacy approval settlement and fresh-approval recovery)
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm check:token-gates`
- `pnpm -r typecheck`
- `pnpm build`

## Risks

- Shopify UCP calls now include a Paperclip-managed agent profile that
overrides caller input at the same path.
- Postman EU credentials now use the hosted MCP server's bearer-token
contract instead of the general REST API header.
- The catalog generator and checked-in definitions change together to
prevent regeneration drift.
- Approved execution preparation has an explicit two-minute bound;
provider execution retains its own 65-second timeout and persistence
grace starting from durable provider start.
- Legacy approvals created before execute-on-approve are intentionally
terminalized and must be requested again under the current signed
contract.

> I checked `ROADMAP.md`. This provider update does not duplicate
planned core work. The related open Shopify PR addresses skill routing,
not Apps connections.

## Model Used

OpenAI Codex, GPT-5. The runtime exact model ID and context window were
not exposed. The model used reasoning, 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 and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-29 12:08:35 -05:00
Dotta c90d904779
feat(apps): add connection intent setup experience (#12347)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The backend now turns agent requests into durable connection
intents.
> - Operators need a clear path to inspect, configure, and finish those
requests.
> - The experience must preserve identity, agent access, and interrupted
setup state.
> - This pull request adds the connection intent setup experience across
the app UI.
> - The benefit is one guided flow from agent request to governed
connection.

## Linked Issues or Issue Description

Refs #11965

This is stack 9 of 11. It depends on stack 8 and replaces another
reviewable part of #11965.

## What Changed

- Add connection intent cards and setup flow integration.
- Add browse, connection, app detail, and sidebar experience updates.
- Preserve exact draft identity and access choices across resume and
OAuth recovery.
- Add focused UI, architecture, policy, and end-to-end coverage.
- Keep transient retained-connection lookup failures retryable instead
of misclassifying them as missing targets.
- Align the dark-mode E2E contract with the intentionally hidden
Gateways and Profiles sidebar tabs.

## Verification

- `pnpm -r typecheck`
- Focused UI result: 372 tests passed across 20 files.
- AppsConnect regression suite: 80/80 passed, including failed
connection and application lookups during retained reconnect.
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/AppsSidebar.test.tsx` (1 passed)
- `pnpm check:token-gates`
- `pnpm --filter @paperclipai/db check:migrations`
- `pnpm build`

## Risks

- An interrupted OAuth flow can leave a durable draft that needs resume.
- The UI resumes the exact draft and keeps its identity and agent access
settings.
- Retained reconnect retries refetch connections and applications
together to avoid mixing partial snapshots.
- Gateways and Profiles remain route-accessible but intentionally absent
from the sidebar until their existing ship gate is lifted.
- The change does not add a database migration.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, 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 linked the public source pull request with `Refs #`
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-29 12:08:35 -05:00
Dotta d387cc0ff0
feat(connections): add managed external MCP connectors (#12346)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Connection intents need secure provider implementations to complete
setup.
> - Some providers use managed OAuth or external credential brokers.
> - Those tokens must stay out of durable Paperclip state and fail
closed when refresh fails.
> - This pull request adds managed connector backends and the required
storage contract.
> - The benefit is safer provider setup with governed credential
lifecycles.

## Linked Issues or Issue Description

Refs #11965

This is stack 8 of 11. It depends on stack 7 and replaces another
reviewable part of #11965.

## What Changed

- Add managed Google Workspace and external connector backends.
- Add Vercel Connect support without storing provider bearer tokens.
- Add replay-safe migration 0232 and its generated snapshot.
- Fail closed and clear stale token bindings when organization OAuth
refresh needs reauthorization.

## Verification

- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/tool-access-service.test.ts`
- Result: 194 tests passed.
- `pnpm --filter @paperclipai/db check:migrations`
- `pnpm build`
- `pnpm exec vitest run --project @paperclipai/server
server/src/services/remote-url-credentials.test.ts` (5 passed, including
URL userinfo vault extraction)

## Risks

- Broker metadata errors can block provider setup.
- OAuth refresh failure disables the shared organization connection
until reauthorization.
- Migration 0232 is generated, ordered after 0231, and safe to replay.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, 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 linked the public source pull request with `Refs #`
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-29 12:08:34 -05:00
Dotta b3343dbd64
feat(connections): add self-serve intent runtime (#12345)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents need a governed way to request app connections during issue
work.
> - The catalog now describes the available providers and setup methods.
> - A request must become a durable, company-scoped intent before an
operator acts on it.
> - This pull request adds that intent runtime across server, agent,
CLI, and shared contracts.
> - The benefit is a safe bridge from agent need to operator-approved
setup.

## Linked Issues or Issue Description

Refs #11965

This is stack 7 of 11. It depends on stack 6 and replaces another
reviewable part of #11965.

## What Changed

- Add connection intent types, validation, service logic, and routes.
- Add agent runtime tools and CLI support for connection requests.
- Add issue-thread interaction support for connection intents.
- Add runtime, route, adapter, and contract tests.
- Hold the final resolved-continuation row lock through asynchronous
adapter preparation until an actual process spawn, so parking or
reassignment cannot cross that boundary.
- Report Hermes Gateway's first remote run request through the shared
dispatch hook so the resolved-intent lock is released at the true
dispatch boundary.
- Revalidate the addressed user's live non-viewer membership and
connection-management authority for every intent mutation, including
OAuth completion.

## Verification

- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/tool-access-service.test.ts`
- Result: 176 tests passed.
- `pnpm build`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-stale-queue-invalidation.test.ts` (32 passed;
includes non-process dispatch lock-release coverage)
- `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/connection-intents-service.test.ts -t
"addressed-user mutation"` (1 passed)
- `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/tool-access-service.test.ts -t "binds OAuth
callback completion to the initiating board session"` (1 passed)
- `pnpm --filter @paperclipai/hermes-paperclip-adapter test --
src/gateway/server/execute.test.ts` (23 passed; includes dispatch-hook
ordering and exactly-once coverage)
- `pnpm --filter @paperclipai/hermes-paperclip-adapter typecheck`

## Risks

- A malformed intent could create an unusable operator request.
- Validators and company checks reject invalid or cross-company
requests.
- The final continuation gate holds the issue row lock through adapter
preparation until process or remote dispatch; later operator changes use
the normal active-run interruption path.
- The change does not add a database migration.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, 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 linked the public source pull request with `Refs #`
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-29 12:08:34 -05:00
Dotta fcb2e99e8f
feat(apps): expand the self-serve connection catalog (#12344)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A useful app store needs accurate and selectable provider
definitions.
> - Local brand assets now cover the expanded provider set.
> - Provider methods differ in transport, authentication, ownership, and
required scope.
> - This pull request expands the catalog and encodes those provider
contracts.
> - The benefit is a larger self-serve store with explicit setup
choices.

## Linked Issues or Issue Description

Refs #11965

This is stack 6 of 11. It depends on stack 5 and replaces another
reviewable part of #11965.

## What Changed

- Add and update provider definitions for the self-serve catalog.
- Add Google Workspace connection methods and capability profiles.
- Add catalog generation, ingestion, URL matching, and contract tests.
- Update legacy key tests to use a provider that still uses header
credentials.

## Verification

- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/tool-access-service.test.ts`
- Result: 164 tests passed.
- `pnpm build`

## Risks

- An incorrect provider definition can offer the wrong setup method.
- Contract tests verify transport, authentication, and provider URL
behavior.
- The change does not add a database migration.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, 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 `Refs #` or (b)
described the issue in this pull request
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-29 12:08:34 -05:00
Dotta 10c4902ffc
feat(apps): add local connection brand assets (#12343)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The app store needs stable provider marks that do not depend on
remote image hosts.
> - The connector layer adds many recognizable services.
> - Local assets must work in light and dark themes and follow the
design token gate.
> - This pull request adds the provider asset set and its rendering
support.
> - The benefit is fast and consistent app identification across the UI.

## Linked Issues or Issue Description

Refs #11965

This is stack 5 of 11. It depends on stack 4 and replaces another
reviewable part of #11965.

## What Changed

- Add local provider logos for the connection catalog.
- Add light and dark asset selection where providers need it.
- Add deterministic fallback behavior and UI tests.
- Document the fixed brand-color allowlist in the token gate.

## Verification

- `pnpm -r typecheck`
- `pnpm check:token-gates`
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/apps/AppLogo.test.tsx`
- `pnpm build`

## Risks

- A missing asset path can show the deterministic letter fallback.
- Brand SVG colors use the documented asset allowlist because provider
marks cannot use product theme tokens.
- The change does not add a database migration.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, 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 `Refs #` or (b)
described the issue in this pull request
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-29 12:08:33 -05:00
Dotta 6244e4cf32
feat(apps): add Composio and Gmail connectors (#12342)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - App connections need both direct providers and managed provider
hubs.
> - The grant layer now defines safe credential ownership.
> - Composio needs parent and child connection lifecycle rules, and
Gmail needs governed setup.
> - This pull request adds both connector families on the grant
foundation.
> - The benefit is broader app access without weakening credential
isolation.

## Linked Issues or Issue Description

Refs #11965

This is stack 4 of 11. It depends on stack 3 and replaces another
reviewable part of #11965.

## What Changed

- Add Composio parent and child connection support.
- Add Gmail connection setup and governance.
- Preserve credential paths and remove duplicate binding declarations.
- Cascade Composio pause and restore actions to child connections.

## Verification

- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/tool-access-service.test.ts`
- Result: 164 tests passed.
- `pnpm build`

## Risks

- Parent lifecycle changes can affect every Composio child.
- The service restores only children whose provider accounts remain
active.
- Credential binding paths are normalized before secret resolution.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, 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
- [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-29 12:08:33 -05:00
Dotta 20ccf3f476
feat(apps): add connection grants and delegated identities (#12341)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - External tools need explicit identity and access boundaries.
> - Shared connection credentials cannot represent every user-scoped use
case.
> - Grants must stay company-scoped and support safe delegation.
> - This pull request adds connection grants, identity rules, and their
database contract.
> - The benefit is durable control over which identity an agent may use.

## Linked Issues or Issue Description

Refs #11965

This is stack 3 of 11. It depends on stack 2 and replaces another
reviewable part of #11965.

## What Changed

- Add company and user connection grants.
- Add delegated identity and membership rules.
- Synchronize database, shared, server, and UI contracts.
- Register the grant-member replacement route in the OpenAPI surface in
the same layer that mounts it.
- Add migration 0231 with replay-safe guards and coverage.

## Verification

- `pnpm -r typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/tool-access-service.test.ts`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/openapi-routes.test.ts` (5 passed)
- `pnpm --filter @paperclipai/db check:migrations`
- `pnpm build`

## Risks

- Incorrect grant selection could expose the wrong credential scope.
- The service enforces company and subject boundaries before credential
use.
- Migration 0231 is generated, ordered after 0230, and safe to replay.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, 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 linked a public issue or pull request with `Refs #`
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-29 12:08:33 -05:00
Dotta b51112798f
feat(apps): improve gateway and workspace connection UX (#12340)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - App connections must work in both the operator UI and agent tool
gateway.
> - The first stack layer adds secure remote connections.
> - Operators still need clear setup, test, and recovery states.
> - This pull request adds the gateway behavior and the workspace
connection experience.
> - The benefit is a connection flow that is easier to understand and
recover.

## Linked Issues or Issue Description

Refs #11965

This is stack 2 of 11. It depends on stack 1 and replaces another
reviewable part of #11965.

## What Changed

- Improve remote tool gateway connection behavior.
- Add clearer app setup, test, and recovery states.
- Add focused server and UI tests for the new paths.
- Keep the diff isolated from later identity and catalog work.
- Stabilize DNS-pinned remote HTTP protocol fixtures and the
managed-runtime public-origin fixture for this independently tested
layer.

## Verification

- `pnpm -r typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/tool-access-service.test.ts` (150 passed)
- `pnpm test:run`
- `pnpm check:token-gates`
- `pnpm build`

## Risks

- Gateway errors now surface through new user-facing states.
- A stale connection can require a new setup attempt.
- The change does not add a database migration.
- The injected HTTP transport and public URL are test-only fixtures;
production DNS pinning and runtime behavior are unchanged.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, 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
- [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-29 12:08:32 -05:00
Dotta cabc9146d0
feat(apps): add secure remote MCP and PostHog setup (#12339)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Apps give those agents governed access to external tools.
> - Remote MCP setup needs secure endpoint validation and durable
credentials.
> - PostHog needs both browser sign-in and personal API key setup paths.
> - This pull request adds the shared remote MCP foundation and the
PostHog definition.
> - The benefit is a secure and reusable base for later app connection
work.

## Linked Issues or Issue Description

Refs #11965

This is stack 1 of 11. It replaces the first reviewable part of #11965.

## What Changed

- Add guarded remote MCP setup and credential handling.
- Add PostHog OAuth and API key connection methods.
- Add focused server, shared contract, and UI coverage.
- Keep the migration replay-safe and idempotent.
- Give the late-close security regression the same 10-second CI headroom
as the adjacent real-timer handshake test.
- Synchronize fake-timer handshake tests at the exact ensure-session
boundary so real filesystem setup cannot race the fake deadline.
- Drive PTY overflow coverage only after listener registration so
scheduling cannot reorder the test fixture.

## Verification

- pnpm exec vitest run
packages/adapter-utils/src/acpx-engine/execute.test.ts
server/src/__tests__/plugin-worker-manager.test.ts (220 passed; affected
cases also passed five focused stress repetitions)
- `pnpm exec vitest run
packages/adapter-utils/src/acpx-engine/execute.test.ts -t "never leaks a
sandbox-provided value from a late close rejection into logs or the
result"` (1 passed)
- `pnpm exec vitest run
packages/adapter-utils/src/acpx-engine/execute.test.ts -t "never
promotes a late ensureSession resolution|closes a late-resolving real
handle exactly once"` (2 passed)
- `pnpm -r typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/tool-access-service.test.ts`
- `pnpm --filter @paperclipai/db check:migrations`
- `pnpm build`

## Risks

- Remote endpoint validation can reject configurations that previously
passed without checks.
- OAuth configuration errors can block setup until the operator corrects
the provider settings.
- The migration uses guarded statements so repeated execution is safe.
- The test-only synchronization changes do not affect runtime behavior;
they remove filesystem/fake-clock and listener-registration races
observed under parallel CI load.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, 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 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-29 12:08:32 -05:00
Nicky Leach a20a4944ec
feat: add Grok device login to the sandbox login panel (#12469)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip uses adapters to connect agents and model providers to its
control plane
> - The sandbox login panel supports displayed-code login for selected
adapters
> - Grok users need the same login path and a private credential home
for later runs
> - This pull request adds Grok support to the shared device-login path
and preserves the existing Codex path
> - The benefit is one secure login flow for both adapters with
company-scoped credential storage

## Linked Issues or Issue Description

**Agent or provider**

Grok Local needs displayed-code login support in the sandbox login
panel.

**Why this adapter is useful**

This change lets users sign in to Grok from the sandbox login panel. It
also gives later Grok runs access to the stored credential.

**How the agent is invoked**

The Grok local adapter uses its login command through the shared
displayed-code login flow. Later runs receive the managed home through
`GROK_HOME`.

**Additional context**

The change uses adapter-scoped login lifecycle handling. It stores the
credential in a company-scoped directory with mode `0700`, and it stores
the credential file with mode `0600`.

## What Changed

- Rename the shared device-login modules to adapter-neutral names.
- Scope the shared login lifecycle to a closed adapter set.
- Return the device-login URL that the provider prints.
- Add the Grok prompt parser, login command, capability, and login panel
entry.
- Store the Grok credential in a private, company-scoped home directory.
- Pass `GROK_HOME` to later Grok runs.
- Add tests for the Grok adapter, the Daytona sandbox provider, the
server login path, and the user interface.

## Verification

- Run `pnpm vitest run
packages/adapters/grok-local/src/server/adapter-auth-promotion.test.ts`.
- Run the Grok adapter package suite.
- Run the Daytona sandbox provider suite.
- Run the server device-login suites.
- Run the user interface suite.
- Confirm the full CI suite passes.

## Risks

The change extends shared login lifecycle code to another adapter. A
regression could affect Codex login. The credential path uses explicit
`chmod` calls to keep the directory at mode `0700` and the file at mode
`0600`.

## Model Used

OpenAI Codex, GPT-5. The runtime used tool calls and code review
support. The runtime did not provide a context-window value.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-28 21:48:34 -07:00
Nicky Leach d9449e636e
feat(onboarding): sign in to an agent provider during onboarding (#12440)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - New organizations create their first agent through the onboarding
wizard
> - The wizard does not show provider sign-in when a host credential is
absent or unknown
> - The create step also gives unclear feedback when the provider needs
authentication
> - This pull request adds a safe auth signal and a provider sign-in
step for sandbox drivers
> - The benefit is a clearer onboarding path with no token or account
data in the signal

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting (server API, shared types, and UI)

**Problem or motivation**

The onboarding wizard can fail when the selected provider needs
authentication. It does not tell the person how to complete sign-in.

**Proposed solution**

Add a status-only provider auth signal. Show the sign-in panel for
sandbox drivers when the signal says `absent` or `unknown`. Apply a
stored Claude login to the new agent and block creation when the adapter
test reports missing authentication.

**Alternatives considered**

The wizard could hide the sign-in panel when the signal read fails. This
would hide a needed action, so this pull request shows the panel when
the signal is unknown.

**Roadmap alignment**

The change supports the roadmap goal for scoped and audited credential
bindings.

**Additional context**

The auth signal returns only `present`, `absent`, or `unknown`. It never
returns a token, identifier, or account name.

## What Changed

- Add `GET /api/companies/:companyId/adapters/:type/auth-signal` with
company and permission checks.
- Add shared auth-signal types and the UI query path.
- Apply a stored Claude login by reference without reading its token.
- Show the provider sign-in panel only for sandbox drivers with
interactive terminal support.
- Block agent creation when the provider test reports missing
authentication.
- Add route, wizard, and end-to-end test coverage.

## Verification

- `pnpm --filter @paperclipai/server test adapter-auth-signal-routes`
passes 50 tests.
- `pnpm --filter @paperclipai/ui test OnboardingWizard` passes 69 tests.
- `pnpm --filter @paperclipai/ui exec tsc --noEmit` exits with code 0.
- The `e2e_shards` lane runs `tests/e2e/onboarding.spec.ts`.

## Risks

The route reads a host-local readiness signal. It returns `unknown` on
read errors and never exposes credential data. The UI may add a sign-in
step when the signal is unavailable.

## Model Used

OpenAI Codex, GPT-5, extended reasoning, tool use, and code execution.
The exact context window 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-28 10:20:37 -07:00
Tonio 4436cf00a2
Render the onboarding agent arc's real steps in Storybook (#12369)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - New tenants meet it through an onboarding wizard: create your agent,
connect a model, review
> - Those three screens only render for a signed-in account that owns a
provisioned stack, so the only way to look at one was to walk a real
signup
> - Round 4 redesigned all three and shipped them without anyone seeing
them render; when the connect step then failed on a live stack, the
review step behind it could not be reached at all
> - Storybook already mounts the app's provider stack and stubs `/api`,
and already has an `Onboarding/Agent arc` story — but that story
previews `AgentCapsule`, which the wizard stopped using in round 4
> - This pull request mounts the real wizard in Storybook at each step,
and replaces the stale capsule stories with the component the arc
actually renders
> - The benefit is that these screens can be reviewed, and regressions
seen, without provisioning anything

## Linked Issues or Issue Description

No existing issue. Describing it inline, following
`.github/ISSUE_TEMPLATE/enhancement.yml`.

**What existing behavior does this improve?**

Reviewing the tenant onboarding wizard. Today it can only be seen by
signing up for a real account and provisioning a real stack.

**Subsystem affected**

`ui` — the onboarding wizard and its Storybook coverage.

**Current behavior**

`OnboardingWizard` renders only for a signed-in account that owns a
company. There is no route, harness, or story that mounts it, so no
screen in the agent arc can be looked at in isolation. The existing
`Onboarding/Agent arc` story previews `AgentCapsule` in
`slot`/`configured`/`online` and describes it as what "the wizard holds
in one tree slot" — round 4 replaced that with `PillGuy` and
`dormant`/`alive`, so the story documents a component the arc no longer
renders.

**Proposed behavior**

Stories that mount the real `OnboardingWizard` at each of the three
steps against the existing Storybook API fixtures, plus stories for
`PillGuy` and its transition.

**Reason and benefit**

Round 4 shipped three redesigned screens that nobody could see. The
connect step then failed on staging, which made the review step
unreachable even with an account — reviewing it required hand-editing
`localStorage`. Stories remove that whole class of problem.

**Breaking changes**

None. Storybook-only; no product code is touched.

## What Changed

- `CreateYourAgent`, `ConnectAModel`, `Review` — the real wizard, per
step.
- `PillStates` and `PillMorph` — the two states, and the transition on a
loop with a toggle. The morph is the arc's payoff and the hardest thing
to judge from a still.
- Replaces the `AgentCapsule` stories in this file with `PillGuy`.
`AgentCapsule` is still used by `DesignGuide` and keeps its coverage
there.
- Four routes added to the Storybook fetch fixtures:
`/api/instance/settings`, `…/environments`, `…/adapters/:type/models`.
The empty environment list is the cloud-tenant shape, and also the state
that produces the "no managed sandbox environment is available" notice —
worth being able to look at rather than only meeting it on a live stack.

## Three properties of the wizard the stories had to respect

Each of these cost a debugging cycle, so they are documented at the call
site:

1. **The draft is seeded during render, not in an effect.** Roughly
twenty `useState(saved?.x ?? default)` initializers read the restored
blob exactly once, so a draft written after mount arrives too late.
2. **Nothing mounts until the companies list settles.** The wizard's own
mount gate waits on `isFetching`, but that query is *disabled* until the
account settles, and a disabled query is not fetching. Mounting straight
away gets an inner wizard that reads a null draft, falls back to
`initialStep`, and then persists that back over the seed. A real session
never hits this because the dashboard has already loaded the list.
3. **The review step opens with no `initialStep`.** An explicit option
takes precedence over saved state by design, so passing one clamps 5 to
4 and lands on Connect.

## Verification

- `npx vitest run src/components/OnboardingWizard.test.tsx
src/components/OnboardingWizard.step.test.tsx` — 44 tests, all passing.
- `npx tsc -p tsconfig.json --noEmit` — no new errors.
(`src/lib/sentry.*` reports pre-existing missing-type errors for
`@sentry/browser` on master.)
- Each of the three step stories loaded in a running Storybook and read
back:
- `create-your-agent` → "STEP 1 OF 3 / Create your first agent / Name /
Next"
- `connect-a-model` → "STEP 2 OF 3 / Connect a model / Paperclip works
with your existing subscription or API keys. / Claude Code / Codex /
Advanced settings / Connect"
- `review` → "STEP 3 OF 3 / Let's get started... / Darnold is ready to
work! / Get started"

One difference from a real walk, worth knowing before treating a story
as ground truth: the review story has no Back button, because
`entryStep` is 5 there and back-navigation is bounded by where the run
entered.

## Risks

Low. Storybook-only — no product code, routes, or bundles change. The
four added fetch fixtures are inside the Storybook mock and cannot
affect the app.

The one thing to watch: these stories mount the real wizard, so a future
change to how it restores drafts or decides its initial step can break
them. That is arguably the point — the stories would be the first place
it shows — but it does mean they are coupled to internals rather than to
a prop surface, and the three notes above are what a maintainer needs.

## Model Used

Claude Fable 5 (`claude-fable-5`), extended thinking, with tool use:
file editing, shell, and browser automation for loading and reading back
each story.

## 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: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 16:53:12 -07:00
Devin Foley bc1a21564f
Remove the company brand color and per-company attachment limit (#12291)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A company is the top-level container, and the company General page
holds its settings
> - Two of those settings did almost nothing: the brand color only
tinted the generated company icon, and the attachment size limit sat
under the deployment-level `PAPERCLIP_ATTACHMENT_MAX_BYTES` cap that
already bounded every upload
> - A setting that changes one icon hue, and a setting that can only
lower a limit the operator already set, are not worth the page space or
the code that carries them
> - This pull request deletes both settings from the UI, the validators,
the API contract, the server, and the database
> - With the deployment cap as the only limit left, the message a person
sees when an upload is rejected has to name that limit in terms they can
act on, so the raw byte count becomes a human-readable size
> - The benefit is a shorter company General page for every deployment,
one attachment limit instead of two, and less code between an upload and
its ceiling

## Linked Issues or Issue Description

No existing issue. The description below follows
`.github/ISSUE_TEMPLATE/enhancement.yml`.

**What existing behavior does this improve?**

The company General page (`/company/settings`), the `PATCH
/api/companies/{companyId}` and `PATCH
/api/companies/{companyId}/branding` request contracts, and the
attachment upload limit on task, case, and company-import uploads.

**Subsystem affected**

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

**Current behavior**

The company General page shows an "Appearance" section with three
controls: Logo, Brand color, and Attachment size limit. The brand color
is a hex value that feeds one thing — the hue of the generated company
pattern icon. Companies that never set one already get a hue derived
from the company name. The attachment size limit is a per-company byte
count stored on `companies.attachment_max_bytes`. Every upload path
clamps it against the deployment-level `PAPERCLIP_ATTACHMENT_MAX_BYTES`
cap, so the per-company value can only lower a limit the operator
already chose.

**Proposed behavior**

The Appearance section keeps the Logo control only. The company pattern
icon always derives its hue from the company name. Every attachment path
reads the deployment cap directly, so `PAPERCLIP_ATTACHMENT_MAX_BYTES`
is the single limit. An upload rejected by that limit says so in human
units — "File is larger than the 10 MB limit" rather than a raw byte
count. The `companies.brand_color` and `companies.attachment_max_bytes`
columns are dropped, and both fields leave the company API contract.

**Reason and benefit**

Both settings ask an operator to make a decision that changes almost
nothing. The brand color moves one icon hue on a page that also lets you
upload a real logo, which overrides the icon entirely. The attachment
limit reads as a real control but cannot raise anything, so it is a
second place to look when an upload is rejected. Removing both shortens
the page every deployment sees, removes a company-scoped read from the
task attachment upload path, and leaves one attachment limit to reason
about instead of two.

**Breaking changes**

The company API responses no longer include `brandColor` or
`attachmentMaxBytes`, and `GET /api/invites/{token}` no longer includes
`companyBrandColor`. `PATCH /api/companies/{companyId}/branding` is
strict, so a request that sends `brandColor` now returns 400; the
non-strict `PATCH /api/companies/{companyId}` schema strips it. Company
packages exported by older versions still import: the portability
company manifest schema is non-strict, so the retired keys are stripped
and ignored rather than rejected. Companies that stored a brand color
lose it — their icon reverts to the name-derived hue that every company
without a color already used.

## What Changed

- Removed the "Brand color" and "Attachment size limit" fields from the
company General page, along with their state, dirty checks, save
payload, and Save-button gating.
- Removed `brandColor` and `attachmentMaxBytes` from
`createCompanySchema`, `updateCompanySchema`, and
`updateCompanyBrandingSchema`, and deleted the now-orphaned
`DEFAULT_COMPANY_ATTACHMENT_MAX_BYTES` and
`MAX_COMPANY_ATTACHMENT_MAX_BYTES` constants.
- Removed both fields from the `Company` type, the portability manifest
type and schema, and the `companiesApi.update` payload allowlist.
- Dropped `brandColor` from `CompanyPatternIcon` and its callers, so the
icon hue always comes from the company name. Deleted the now-unused
`hexToHue` helper and the now-unused `pickTextColorForSolidBg` export.
- Stopped emitting `brandColor` from the company service selection and
from the invite-summary and invite-branding payloads in
`server/src/routes/access.ts`.
- Replaced `normalizeIssueAttachmentMaxBytes` with the deployment cap:
task attachments, case attachments, and company import now use
`MAX_ATTACHMENT_BYTES` directly. The helper is deleted.
- Added `formatAttachmentSize()` next to `MAX_ATTACHMENT_BYTES` and
routed every over-limit message through it, so a rejected upload names
the limit in human units instead of raw bytes: `Image exceeds 10485760
bytes` becomes `Image is larger than the 10 MB limit`. Enforcement is
unchanged — the same single cap, the same multer limits, the same status
codes and response shapes.
- Added migration
`0229_drop_company_brand_color_and_attachment_max_bytes.sql` and removed
both columns from the Drizzle `companies` schema.
- Kept legacy imports working: the portability company manifest schema
is non-strict, so older packages carrying the retired keys still import
with the keys ignored.
- Updated the skill API reference and the implementation spec, and
pruned the token-extraction allowlist entries that the removed code made
stale.

## Verification

Commands run from the repository root:

- `pnpm --filter @paperclipai/shared typecheck` — pass
- `pnpm --filter @paperclipai/db typecheck` — pass (includes
`check:migrations`, which validates the new migration number and journal
entry)
- `pnpm --filter @paperclipai/ui typecheck` — pass
- server typecheck via `node_modules/.bin/tsc --noEmit` in `server/` —
pass. `pnpm --filter @paperclipai/server typecheck` could not run
locally because it builds the Rust runner first and `cargo` is not
installed on this machine; the TypeScript step it wraps is the command
above.
- `npx vitest run packages/shared/src/validators/company.test.ts` — 6
passed
- `npx vitest run server/src/__tests__/company-portability.test.ts` — 90
passed
- `npx vitest run server/src/__tests__/attachment-types.test.ts
server/src/__tests__/assets.test.ts
server/src/__tests__/issue-attachment-routes.test.ts
server/src/__tests__/company-portability.test.ts
server/src/__tests__/cases-routes.test.ts` — 165 passed (the
human-readable limit messages)
- `npx vitest run server/src/__tests__/company-branding-route.test.ts
server/src/__tests__/issue-attachment-routes.test.ts
server/src/__tests__/invite-summary-route.test.ts
server/src/__tests__/openclaw-invite-prompt-route.test.ts
server/src/__tests__/companies-route-cross-company-authz.test.ts` — all
passed
- `npx vitest run cli/src/__tests__/company.test.ts
cli/src/__tests__/company-delete.test.ts` — 27 passed
- `npx vitest run` in `ui/` — 4425 passed, 1 pre-existing failure
unrelated to this change (`OnboardingWizard.test.tsx` "renders instead
of throwing when the browser denies storage access", which also fails on
`master`)
- `npx vitest run` in `server/` — see the note below
- `node scripts/check-token-gates.mjs` — no new violations; the only
reported violations are the pre-existing `PillGuy.tsx` ones present on
`master`

New tests added:

- `packages/shared/src/validators/company.test.ts` — the create and
update schemas strip the retired keys, the strict branding schema
rejects `brandColor`, and the portability manifest schema accepts a
legacy entry carrying both keys and drops them.
- `server/src/__tests__/company-branding-route.test.ts` — `PATCH
/api/companies/{companyId}/branding` returns 400 for `brandColor` and
does not call the company service.
- `server/src/__tests__/company-portability.test.ts` — a legacy package
that declares `brandColor` and `attachmentMaxBytes` imports
successfully, and neither key reaches `companies.create`.
- `server/src/__tests__/issue-attachment-routes.test.ts` — the effective
task attachment limit is the deployment cap, and the route no longer
loads the company to size an upload.
- `server/src/__tests__/attachment-types.test.ts` —
`formatAttachmentSize()` renders the default cap as `10 MB`, keeps one
decimal place for fractional sizes and drops a trailing `.0`, falls back
to KB and bytes for small caps, steps up to GB, and never emits `NaN`
for a degenerate input.
- `server/src/__tests__/assets.test.ts` — the asset-image and
company-logo routes both return the human-readable limit message on an
over-cap upload.

## Merge with master

`master` moved while this was open, and the merge needed two
resolutions:

- **`ui/src/pages/CompanySettings.tsx`.** #12243 reworded the
user-facing
copy from "company" to "organization", and that rewording landed inside
  the "Brand color" and "Attachment size limit" hints — the two fields
this change deletes. Both fields are removed, so the conflicted block is
dropped whole. The Logo field and every other copy change from #12243
are
  kept.
- **Migration renumbered 0228 -> 0229.** #12307 landed
  `0228_nasty_grim_reaper`, so this migration is now
`0229_drop_company_brand_color_and_attachment_max_bytes`. Its snapshot
is
rebuilt from master's `0228_snapshot.json` with only the two `companies`
  columns removed, and `meta/_journal.json` is master's journal plus a
single `idx: 229` entry. `pnpm --filter @paperclipai/db
check:migrations`
  passes.

The snapshot was rebuilt by hand rather than taken from `drizzle-kit
generate`, because master's `0228_snapshot.json` has drifted from
master's
own schema: `issue_question_response_deliveries.error_count` is created
by
master's 0228 SQL but missing from its snapshot, and the snapshot still
carries `decision_archive_notification_outbox.error_count`. Regenerating
folds both into this migration, and the resulting `ADD COLUMN
error_count`
would fail on a fresh database where master's 0228 already created that
column. Rebuilding from master's snapshot leaves that drift exactly
where
it is and keeps this migration to the two column drops. The drift is
pre-existing on master and is not addressed here.

## Risks

- **The migration is a destructive column drop.**
`0229_drop_company_brand_color_and_attachment_max_bytes.sql` removes
`companies.brand_color` and `companies.attachment_max_bytes`. It is safe
because both features are removed in the same change and nothing reads
either column after it. The statements use `DROP COLUMN IF EXISTS`,
matching the convention of the recent drop migrations in this
repository. The drop is not reversible: a downgrade after this migration
loses any stored values.
- **Stored brand colors are lost.** A company that had set a color now
renders the name-derived icon hue that every company without a color
already used. No other surface changes, and an uploaded logo still
overrides the icon.
- **API response shape narrows.** `brandColor` and `attachmentMaxBytes`
leave the company payloads, and `companyBrandColor` leaves the invite
summary payload. A client reading those fields now sees `undefined`. The
bundled UI and CLI are updated in this change.
- **Legacy imports are covered.** Packages exported by older versions
still carry both keys. The manifest schema is non-strict, so the keys
are stripped rather than rejected, and a test locks that in.
- **The over-limit message strings changed.** Anything matching on the
old `... exceeds N bytes` text — a test, a script, or a client that
string-matches `body.error` — needs updating. The status codes (422) and
response shapes are unchanged, so structured clients are unaffected.
- **Attachment limits can only widen.** A deployment that had lowered a
company below the deployment cap now allows uploads up to the cap for
that company. Lower `PAPERCLIP_ATTACHMENT_MAX_BYTES` if a smaller
ceiling is needed.
- **Storybook visual baselines shift** for the `CompanyPatternIcon`
matrix story, because those fixtures had brand colors. That workflow
runs only on a PR labeled `storybook-visual`, so it does not gate this
PR; regenerate the baselines if the label is added.

## Model Used

Claude (Anthropic), Claude Opus, agentic tool use 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 (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-27 12:11:05 -07:00
Devin Foley 7b91fe9ea7
Hide host-path and execution-engine surfaces in managed-sandbox-only mode (#12293)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - An instance can turn on the `enableManagedSandboxOnly` feature,
which hides the local environment and runs every agent in the
platform-managed environment
> - That feature already gated the environment pickers, the onboarding
wizard, and the server-side run selection, but many other screens still
showed absolute paths on the execution host and still let the user pick
an execution engine
> - On such an instance those controls name a filesystem the user cannot
reach; a path written there is stored and then ignored, which reads as a
broken control
> - This pull request hides the remaining host-path and execution-engine
surfaces behind the same feature, adds a server rule that refuses a
project-workspace path write while the feature is on, and closes a
related route gap in the isolated-workspace pages
> - The benefit is that a managed instance shows no host path and no
folder picker anywhere, and a write that carries a path now fails with a
clear message instead of being silently discarded

## Linked Issues or Issue Description

No public issue exists. The description below follows
`.github/ISSUE_TEMPLATE/enhancement.yml`.

**What existing behavior does this improve?**

The `enableManagedSandboxOnly` instance feature, and the UI surfaces
that show
a host filesystem path: project properties, the new-project dialog, the
project
workspace and execution workspace detail pages, the workspace and task
cards,
plugin local folders, and the agent configuration form with its
per-adapter
fields. It also improves route gating for `enableIsolatedWorkspaces`.

**Subsystem affected**

Cross-cutting (`ui/` and `server/`).

**Current behavior**

When `enableManagedSandboxOnly` is on, the local environment disappears
from the
environment pickers and the server refuses to run an agent on the local
host.
Everything else stays visible. A user still sees:

- the project "Local folder" row, its absolute path, and the
Set/Change/Clear buttons
- the "Local folder" field and its "Choose" folder picker in the
new-project dialog
- the "Local path" field and fact row on a project workspace
- the "Paths" and "Lifecycle commands" groups on an execution workspace
- the working directory on workspace cards, task properties, and runtime
service rows
- the plugin "Local folders" section
- "Working directory (deprecated)", "Command", "Execution engine",
"ACP server command", "ACP state directory", and "Agent instructions
file" in
  the agent configuration form

A path typed into any of these names a filesystem no agent on the
instance uses.
The project workspace API also accepts a `cwd` write and stores it.

Separately, `/workspaces`, `/execution-workspaces/*`, and
`/projects/:projectId/workspaces/:workspaceId` render for anyone who
types or
bookmarks the URL, even with `enableIsolatedWorkspaces` off. Only the
sidebar
entry reads that flag.

**Proposed behavior**

With `enableManagedSandboxOnly` on, none of those surfaces render. A
project
whose codebase came from a managed checkout keeps its one-line
"Paperclip-managed folder." label and shows no path. The non-path
controls stay:
repo URL, branch, service URL, port, command output, ACP session mode,
ACP
non-interactive permissions, Codex fast mode, and the sandbox toggles.

The project-workspace create and patch routes, and the nested workspace
on
project create, answer `422` with
"This instance runs agents only in the platform-managed environment;
local
folders are not configurable." when the payload carries a non-null
`cwd`.
A `cwd: null` write still passes, so an instance that just turned the
feature on
can clear a stale path.

With `enableIsolatedWorkspaces` off, the three workspace route groups
redirect to
the dashboard.

**Reason and benefit**

A control that cannot do anything is worse than a missing control: the
user fills
it in, saves, and gets no error and no effect. The server rule turns
that silent
no-op into a clear refusal. The route gate stops a feature that an
instance has
turned off from staying reachable by URL, which is the same standard the
Cases,
Pipelines, and hidden-settings pages already meet.

**Breaking changes**

None for a default instance: both flags are off by default for
self-hosted and
managed instances, so nothing changes unless an operator turns them on.
Stored
`adapterConfig` values are never cleared, so turning the feature off
restores
every previous value.

## What Changed

- Add `ui/src/hooks/useManagedSandboxOnly.ts`, modelled on
`useAppsEnabled`, for
components that do not already read the experimental settings. It
exposes
`hideHostPaths`, which fails closed while the settings query is in
flight, so
a cold cache never flashes a host path before the policy resolves.
Components
that keep their own settings read compute the same gate from
`isFetched`.
- Add `managedSandboxOnly` to `AdapterConfigFieldsProps` and populate it
where
`AgentConfigForm` builds the adapter field props. Resolve the effective
instructions-file gate once as `hideInstructionsFile || hideHostPaths`,
so
  every adapter hides that path field with no per-adapter edit.
- Hide under the flag: the project "Local folder" block and its
absolute-path
  edit panel (a managed checkout keeps its label, without the path); the
new-project "Local folder" field; the project-workspace "Local path"
field and
fact row; the execution-workspace "Paths" and "Lifecycle commands"
groups; the
working directory on the workspace summary card, the task workspace
card, the
task properties "Folder" row, and the runtime service rows; the plugin
"Local
folders" section; "Working directory (deprecated)" and "Command" in the
agent
form; and the per-adapter "Execution engine", "ACP server command", and
"ACP state directory" for `claude_local`, `codex_local`, and
`gemini_local`.
- Drop two working-directory fallbacks that had no gate to read: the
close-workspace
dialog now falls back to "No additional details", and the reuse-existing
  workspace label and picker subtitle fall back to a neutral phrase.
- Refuse a non-null `cwd` with `422` on `POST /projects/:id/workspaces`,
`PATCH /projects/:id/workspaces/:workspaceId`, and the nested workspace
on
  `POST /companies/:companyId/projects`, following the
  `assertNoAgentHostWorkspaceCommandMutation` precedent on those routes.
- Add `IsolatedWorkspacesRouteGate` and wrap the `/workspaces`,
`/execution-workspaces/*`, and
`/projects/:projectId/workspaces/:workspaceId`
  routes with it.
- Leave the SSH "Remote workspace path" and the workspace file browser
alone,
  with a comment explaining why.

## Verification

Automated:

- `pnpm --filter @paperclipai/ui exec vitest run` — 479 of 480 files
pass
(4455 of 4456 tests). The one failure is `OnboardingWizard.test.tsx >
renders
instead of throwing when the browser denies storage access`, which also
fails
  on `origin/master` and is unrelated to this change.
- `pnpm --filter @paperclipai/server exec vitest run project workspace
instance-settings`
— 45 of 50 files pass. Four files fail on macOS for reasons unrelated to
this
  change: `workspace-instance-cleanup`, `workspace-runtime`,
  `execution-workspace-runtime-control-conflict`, and
  `workspace-runtime-exposure` compare `/var/...` against the resolved
`/private/var/...` or bind real ports. The same files fail on a clean
`master`
  checkout on the same machine.
- `pnpm --filter @paperclipai/ui typecheck`
- `tsc --noEmit` in `server/` (after
`pnpm --filter @paperclipai/plugin-sdk ensure-build-deps`). The package
`typecheck` script also builds the Rust runner, which needs `cargo`; it
is not
  installed on the machine that ran this.

New and extended tests:

- `ui/src/adapters/managed-sandbox-only-config-fields.test.tsx` — the
three
adapters drop the execution engine, the ACP paths, the instructions-file
path,
and every "Choose" button when the flag is on, and keep the non-path
controls.
- `ui/src/components/AgentConfigForm.render.test.tsx` — flag-on and
flag-off
renders for the working directory, the command, the engine, the ACP
paths, and
  the resolved adapter field props.
- `ui/src/components/ProjectProperties.managed-sandbox.test.tsx`,
  `ui/src/components/NewProjectDialog.managed-sandbox.test.tsx`,
  `ui/src/pages/ProjectWorkspaceDetail.test.tsx`,
  `ui/src/components/ProjectWorkspaceSummaryCard.test.tsx`,
  `ui/src/components/WorkspaceRuntimeControls.test.tsx`.
- `ui/src/components/IsolatedWorkspacesRouteGate.test.tsx` — redirect
when off,
  render when on, and render nothing while the flag query is in flight.
- "Still loading" cases for the project properties, the new-project
dialog, the
workspace summary card, the runtime service rows, and the agent
configuration
form, each asserting that no host path renders before the policy
resolves.
-
`server/src/__tests__/project-workspace-managed-sandbox-routes.test.ts`
— the
  `422` on all three write paths, the `cwd: null` pass-through, and the
  flag-off pass-through.

Manual check to reproduce: turn on Managed Environment Only in instance
experimental settings, then open a project, the new-project dialog, an
agent's
configuration, and a workspace page. No path, folder icon, or "Choose"
button
appears. Turn the setting off and each control returns with its stored
value.

No documentation change was needed. The operator-facing text for both
settings
lives in the feature catalog entry, which already states the contract
this pull
request now enforces across the UI.

## Risks

- Low. Both flags default to off, so a default instance is unchanged.
- The hidden fields are presentation only. No stored `adapterConfig`
value is
cleared, because an import carries adapter configuration written on
another
instance and clearing it would break that flow. Turning the setting off
shows
  every previous value again.
- The `422` is the one behavior change for an API caller, and only while
the
setting is on. `cwd: null` still passes so a stale path can be cleared.
- The route gate renders nothing until the flag query settles, so an
instance
with isolated workspaces on never flashes a redirect. An instance with
the
  feature off now redirects a bookmarked workspace URL to the dashboard.
- Every host-path guard fails closed while the settings query is in
flight, so a
default instance shows those controls a moment later than before on a
cold
load. That is the safe direction: the alternative flashes a path a
managed
  instance must never show.
- Two path surfaces stay on purpose, each with a comment: the SSH
"Remote
workspace path" is a path on the user's own remote host, and the
workspace file
browser shows workspace-relative paths. The instance Adapters page also
keeps
its "Local path" install option, since that page is an instance-admin
surface
the hosting operator can already hide through the hidden-settings
mechanism.

## Model Used

Claude (Anthropic), Claude Opus, 1M context window, extended thinking,
agentic
tool use through 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 (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-27 11:59:14 -07:00
Devin Foley 76f7019bdf
feat(server): derive hosted-tenant issue prefixes from the company name and follow renames (#12292)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Every company has an issue prefix. It is the visible half of each
task and case identifier, and a self-hosted company derives it from the
name it was created with
> - A hosted or managed instance does not use the create-company flow.
The trusted-header auth path claims the tenant company instead
> - That path minted the prefix from a hash of the stack id, and it
wrote a placeholder description that nobody chose
> - So a hosted company showed opaque task IDs such as `PC7F2A-14`, and
a rename never changed them
> - This pull request derives the prefix from the company name on that
path too. It re-derives the prefix when the name changes on a managed
instance, and it rewrites the stored issue and case identifiers so
existing tasks follow the rename
> - It also repairs each company that an earlier build claimed. The
repair runs once, on the next authenticated request
> - The benefit is that task IDs on a hosted instance read like the ones
on a self-hosted instance, and they stay correct after a rename

## Linked Issues or Issue Description

No public issue exists. The description below follows
`.github/ISSUE_TEMPLATE/enhancement.yml`.

**What existing behavior does this improve?**

The tenant company claim in `resolveCloudTenantActor`
(`server/src/middleware/auth.ts`) and the company update in
`companyService.update` (`server/src/services/companies.ts`). Both
decide the
`issue_prefix` and the `description` of a company on a hosted or managed
instance.

**Subsystem affected**

`server/` — REST API and orchestration services. One small hint was also
added
in `ui/`.

**Current behavior**

A self-hosted company gets its issue prefix from its name. "Acme
Robotics"
becomes `ACM`, and its tasks read `ACM-14`.

A hosted or managed instance claims the company through the
trusted-header auth
path. That path wrote a different prefix: `"PC"` plus the first four hex
characters of the SHA-256 of the stack id. The same path also wrote a
placeholder description, `"Provisioned by ... for stack <stack id>."`.

The result is a task ID such as `PC7F2A-14`. It says nothing about the
company.
A later rename of the company does not change it, because nothing
re-derives
the prefix after creation.

**Proposed behavior**

The claim path derives the prefix from the company name, exactly as the
create-company flow does. It writes no description.

On a managed instance, a rename re-derives the prefix. The stored issue
and
case identifiers move with it, so `ACM-14` becomes `NOR-14` when "Acme
Robotics" becomes "Northwind Traders". A rename that keeps the same
three-letter
base keeps the current prefix, including any disambiguating suffix.

A self-hosted instance is unchanged. A rename there still keeps the
prefix the
company was created with.

Companies that an earlier build already claimed get a one-time repair on
their
next authenticated request. The repair re-derives the prefix from the
current
name, re-keys the identifiers, and clears the placeholder description.

**Reason and benefit**

A task ID is the primary handle for a task. People type it, paste it
into chat,
and read it in a URL. On a hosted instance that handle was an opaque
hash, and
it disagreed with the company name that the same user chose during
signup. The
name is the only prefix source a hosted user ever supplies, so the
prefix now
follows it.

**Breaking changes**

Yes, on hosted and managed instances only. A company rename now rewrites
the
stored issue and case identifiers. Links that carry an old identifier
stop
resolving after the rename. The company settings page states this before
the
user saves. The one-time repair applies the same rewrite once to
companies that
carry the old hash prefix. Self-hosted behavior does not change.

## What Changed

- Added `server/src/services/issue-prefix.ts`. It holds the prefix
helpers that
used to live inside the `companyService` closure:
`ISSUE_PREFIX_FALLBACK`,
  `deriveIssuePrefixBase`, `issuePrefixSuffixForAttempt`, and
  `isIssuePrefixConflict`. The companies service now imports them.
- Added `pickAvailableIssuePrefix` to that module. It reads the prefixes
in one
base family and returns the first free candidate. A standalone `INSERT`
can
retry on a unique violation, because each failed statement is its own
implicit
transaction. A caller that already holds a transaction cannot, because
the
violation aborts the whole transaction. Such a caller picks first, then
writes.
- Added `rekeyCompanyIssueIdentifiers` to that module. It rewrites the
prefix of
the stored `issues.identifier` and `cases.identifier` values of one
company in
  the caller's transaction, and it returns the two row counts.
- `companyService.update` re-derives the prefix when the name changes on
a
managed instance, re-keys both tables in the same transaction, and
writes a
  `company.updated` activity entry after the commit.
- `resolveCloudTenantActor` claims the company with a name-derived
prefix and a
null description. The claim retries with the next suffix when the prefix
is
  taken.
- `resolveCloudTenantActor` also runs a one-time repair for companies
that carry
the old hash prefix. An exact-match fence on the update lets a
concurrent
rename win. The repair is idempotent, because its guards stop matching
after
  it lands.
- The rename takes a row lock on the company before it compares anything
against
it, and it re-keys from the prefix it reads under that lock. Only patch
and
environment facts gate the lock, so no stale read can steer the
decision. Two
overlapping updates would otherwise leave a company whose prefix
disagrees with
its own identifiers, in either direction: two renames, where the second
re-keys
from a prefix the first already moved; or a rename plus a stale form
that
resubmits the original name, where the second sees an unchanged name,
skips
re-derivation, and restores the old name on top of the first rename's
prefix.
Only a managed instance takes the lock, and only for an update that
carries a
  name.
- Both helpers compare an exact identifier head instead of a LIKE
pattern. A
  stored prefix is data, so it must never be read as a pattern.
- The company settings page shows a hint under the name field on a
managed
  instance: renaming can change the task ID prefix.

## Verification

Automated tests:

```
pnpm --filter @paperclipai/server exec vitest run \
  src/services/issue-prefix.test.ts \
  src/__tests__/companies-service.test.ts \
  src/__tests__/cloud-tenant-company-provisioning.test.ts \
  src/middleware/cloud-tenant-actor.test.ts \
  src/__tests__/auth-session-route.test.ts \
  src/__tests__/cloud-routes.test.ts \
  src/__tests__/cloud-instance.test.ts \
  src/__tests__/company-branding-route.test.ts \
  src/__tests__/company-cloud-floor.test.ts \
  src/__tests__/companies-route-cross-company-authz.test.ts \
  src/__tests__/companies-route-path-guard.test.ts \
  src/__tests__/company-portability.test.ts
pnpm --filter @paperclipai/ui exec vitest run
pnpm --filter @paperclipai/ui typecheck
```

New coverage:

- `server/src/services/issue-prefix.test.ts` covers the derivation, the
suffix
  ladder, the cause-chain walk of the unique-violation detector, and
  `pickAvailableIssuePrefix` against a stubbed select.
- `server/src/__tests__/companies-service.test.ts` covers a managed
rename
against a real Postgres database: the prefix moves, both identifier
tables are
re-keyed, and the activity entry is written. It also covers a same-base
rename,
  a collision that takes the suffixed candidate, a non-name patch, and a
self-hosted rename that leaves the prefix alone. Two more tests drive
the
overlap cases: two concurrent renames of the same company, and a rename
racing
a stale form that resubmits the original name. Both assert that the
surviving
name's base matches the company prefix and that the stored identifiers
sit on
  that prefix.
- `server/src/__tests__/cloud-tenant-company-provisioning.test.ts`
covers the
claim path and the repair against a real Postgres database: a
name-derived
prefix, a null description, a suffixed prefix on collision, the full
repair,
  a second pass that changes nothing, a description-only repair, and an
  operator-written description that the repair leaves alone.
- `ui/src/pages/CompanySettingsRenameHint.test.tsx` covers the hint on a
managed
  instance and its absence on a self-hosted instance.

The `substring` cast in `rekeyCompanyIssueIdentifiers` is load-bearing
and the
database tests prove it. The driver binds the offset as text. Without
the
`::int` cast Postgres resolves the SQL-regex overload of `substring`,
and every
identifier becomes NULL.

## Risks

- **Re-keying changes existing identifiers and URLs.** This is
deliberate, and
it happens on hosted and managed instances only. After a rename, a link
that
carries an old task identifier stops resolving. The settings page warns
about
  this before the user saves.
- **Identifiers inside comment text are not rewritten.** Only the
`identifier`
columns of `issues` and `cases` move. A task ID that someone typed into
a
  comment, a description, or a document keeps the old prefix.
- **A lost prefix race inside the rename transaction surfaces as a
conflict.**
The rename picks a free prefix and then writes, because a unique
violation
inside a transaction aborts the whole transaction. Two *different*
companies
renamed onto the same base at the same moment can still collide. The
loser
sees its PATCH fail with the unique violation. The write is retryable by
the
client, and the window is a single statement wide. Two renames of the
*same*
company no longer race: the row lock serializes them, and the second one
  re-keys from what the first committed.
- **The rename holds a row lock.** A managed rename takes `SELECT ...
FOR UPDATE`
on its own company row for the rest of the transaction. It is one row,
and no
other path in the transaction locks a company row, so there is no
lock-order
cycle. A self-hosted instance and every non-rename company update never
reach
  the lock.
- **The one-time repair is best effort.** It runs inside a try/catch and
logs a
warning on failure, so it never blocks authentication. A failed pass is
retried
  on the next request, because its guards still match.
- No schema change and no migration.

## Model Used

- Provider: Anthropic (Claude)
- Model: Claude Opus, model id `claude-opus-5[1m]`
- Context window: 1M
- Reasoning mode: extended thinking
- Capabilities used: agentic tool use through Claude Code (file edits,
shell,
  test runs against an embedded Postgres database)

## 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-27 11:34:42 -07:00
Devin Foley a4f1b3c533
Move company invites into a tab of the Members page (#12289)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Company settings include member management and invites, which are
one workflow: invite someone, watch the join request, manage the
membership
> - Today invites sit on a standalone settings page, costing a sidebar
entry and forcing people to bounce between two pages for one task
> - Merging invites into the Members page keeps the workflow in one
place without losing any capability
> - This pull request turns the Members page into a Members/Invites tab
bar, redirects the old URL, and keeps both operator-visibility keys
meaningful
> - The benefit is a tighter settings surface for every deployment, with
`company.invites` now hiding just the tab while `company.members` hides
the whole page

## Linked Issues or Issue Description

No public issue exists; describing the issue inline per the enhancement
template:

**What existing behavior does this improve?**
Company invites live on a standalone settings page separate from the
Members page, even though inviting someone and managing the resulting
membership are one workflow.

**Subsystem affected**
Web UI (company settings)

**Current behavior**
`/company/settings/invites` is its own page with its own sidebar entry
(gated by `company.invites`); the Members page (`company.members`) is
separate. The sidebar company menu's "Invite people" shortcut links to
the invites page and is not gated by the hidden-settings mechanism at
all.

**Proposed behavior**
The Members page carries a Members/Invites tab bar, addressable via
`?tab=invites`. The invite creation flow, latest-link panel, and invite
history move unchanged into an `InvitesSection` component. The old URL
redirects to the tab (still behind its `HiddenSettingsPageGate`).
`company.members` hides the whole page; `company.invites` hides just the
Invites tab, and the tab bar collapses when only Members remains. The
"Invite people" shortcut points at the tab and hides when either surface
is operator-hidden.

**Reason and benefit**
One settings surface for one workflow: fewer sidebar entries and no
bouncing between two pages to invite someone and then manage the
membership. Operators keep the same visibility controls, with a sharper
meaning for each key.

**Breaking changes**
None. Bookmarks to the old invites URL redirect to the tab, invite and
membership APIs are unchanged, and both hidden-settings keys keep
working.

## What Changed

- `ui/src/pages/CompanyInvites.tsx` →
`ui/src/components/access/InvitesSection.tsx` (page chrome and
breadcrumbs dropped; content unchanged), with its tests moved alongside.
- `ui/src/pages/CompanyAccess.tsx`: Members/Invites tabs via the shared
`PageTabBar`, `?tab=invites` search param, `company.invites` gating with
tab snap-back; legacy "Open Invites" button retargeted.
- `ui/src/App.tsx`: the invites route becomes a gated redirect to
`/company/settings/members?tab=invites`.
- `CompanySettingsSidebar` / `CompanySettingsNav`: standalone Invites
entry/tab removed; the old path maps to the members tab.
- `ui/src/components/SidebarCompanyMenu.tsx`: "Invite people" now links
to the tab and hides when `company.members` or `company.invites` is
hidden (closes an existing gating gap).
- Tests: moved invites tests, new tab coverage (default tab, deep link,
operator-hidden tab skips the invites fetch), sidebar/nav suites
updated.

## Verification

- `npx vitest run ui/src/components/CompanySettingsSidebar.test.tsx
ui/src/components/access/CompanySettingsNav.test.tsx
ui/src/pages/CompanyAccess.test.tsx
ui/src/components/access/InvitesSection.test.tsx` — 24 tests passing.
- `pnpm --filter @paperclipai/ui typecheck` — clean.

## Risks

- Low. Pure UI restructure: invite APIs, membership APIs, and the
hidden-settings registry keys are unchanged. Bookmarks to the old
invites URL redirect (and stay gated). The `company.invites` key's
meaning narrows from "hide the page" to "hide the tab", which is the
same effective surface.

## Model Used

Claude (Anthropic), model id `claude-fable-5`, extended thinking,
agentic tool use 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 (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-27 11:34:32 -07:00
Devin Foley 7551b63ef2
Remove the instance Heartbeats settings page (#12282)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Instance settings collect deployment-wide controls; one of them was
the Heartbeats page, an instance-wide list of scheduler heartbeat agents
with enable/disable toggles
> - The same controls live on each agent's own configuration surface, so
the standalone list duplicates them, and its framing no longer matches
how heartbeat agents are managed
> - Keeping a settings view that no longer makes sense costs every
deployment navigation noise and maintenance
> - This pull request removes the page, its route, its navigation
entries, and its hidden-settings key for all deployments
> - The benefit is a smaller, coherent settings surface, with operator
hidden-settings lists that still mention the retired key continuing to
work unchanged

## Linked Issues or Issue Description

No public issue exists; describing the issue inline per the enhancement
template:

**What existing behavior does this improve?**
The instance settings surface — specifically the Settings → Heartbeats
page, which listed scheduler heartbeat agents instance-wide with
enable/disable toggles. The view no longer makes sense as a standalone
settings page: the same controls are available on each agent's
configuration surface, and the instance-wide list framing does not match
how heartbeat agents are managed.

**Subsystem affected**
Cross-cutting: `ui/` (page, route, navigation), `packages/shared`
(settings-visibility registry), docs.

**Current behavior**
The page renders at `/company/settings/instance/heartbeats`, appears in
the settings sidebar and tab bar, and is hideable by hosting operators
via the `instance.heartbeats` key of `PAPERCLIP_HIDDEN_SETTINGS`.

**Proposed behavior**
The page, route, and navigation entries are removed for every
deployment. The `instance.heartbeats` registry key is retired; operator
lists that still send it are logged and ignored, so mixed-version fleets
keep working. Remembered settings paths pointing at the old page remap
to the settings root. Heartbeat APIs are unchanged.

**Reason and benefit**
A smaller, coherent settings surface with no duplicated controls; less
navigation noise and maintenance for every deployment.

**Breaking changes**
None functional. Bookmarks and remembered paths to the removed page land
on the settings root; `PAPERCLIP_HIDDEN_SETTINGS` lists that still
include `instance.heartbeats` log a warning and are otherwise honored
unchanged.

## What Changed

- Deleted `ui/src/pages/InstanceSettings.tsx` (the Heartbeats view) and
its route in `ui/src/App.tsx`.
- Removed the sidebar entry (`CompanySettingsSidebar`) and tab-bar item
(`CompanySettingsNav`).
- Removed `"/heartbeats"` from the remembered-settings-path allowlist;
remembered heartbeats paths now remap to the settings root.
- Retired the `instance.heartbeats` key from the shared
settings-visibility registry and the environment-variables doc;
documented that retired keys are ignored with a warning.
- Dropped the now-unused UI client wrapper for the instance
scheduler-agent list (`heartbeatsApi.listInstanceSchedulerAgents`); the
server endpoint stays.
- Removed the unused `schedulerHeartbeats` query key.

## Verification

- `npx vitest run packages/shared/src/settings-visibility.test.ts
ui/src/lib/instance-settings.test.ts
ui/src/components/CompanySettingsSidebar.test.tsx
ui/src/components/access/CompanySettingsNav.test.tsx` — 24 tests
passing.
- Full `ui` vitest suite: 4426 tests, 4 failures — all in files this PR
does not touch; 3 were load-induced timeouts that pass on rerun, and
`OnboardingWizard.test.tsx` "renders instead of throwing when the
browser denies storage access" fails identically on a clean master
checkout (pre-existing).
- `pnpm --filter @paperclipai/ui typecheck` and `pnpm --filter
@paperclipai/shared typecheck` — clean.
- Merged `master` to clear a conflict (see below) and re-ran the four
focused suites (24 passing), `ui/src/App.test.tsx` and
`ui/src/plugins/bridge.test.ts` (22 passing), and both typechecks — all
clean. Full CI is green on the merge commit.

## Merge With master

`master` gained the `company` → `organization` copy pass (#12243), which
reworded strings inside `ui/src/pages/InstanceSettings.tsx` — the page
this branch deletes — producing a modify/delete conflict. Resolved by
keeping the deletion: the page is going away, so the rewording of its
copy has nothing to apply to. Every other file merged cleanly, and
`master`'s rewording in `App.tsx`, `App.test.tsx`, and
`CompanySettingsSidebar.tsx` sits away from this branch's structural
removals, so both changes survive. The net diff against `master` is
unchanged from the pre-merge review: the same 13 files, 23 insertions,
330 deletions.

## Risks

- Low. Pure removal of a UI surface; heartbeat data and APIs are
untouched. Operators still listing `instance.heartbeats` in
`PAPERCLIP_HIDDEN_SETTINGS` get a warning log and otherwise unchanged
behavior (covered by the registry's unknown-key handling). Bookmarks and
remembered paths to the old page land on the settings root.

## Model Used

Claude (Anthropic), model id `claude-fable-5`, extended thinking,
agentic tool use 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 (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-27 11:31:43 -07:00
Devin Foley e34ed9801b
Let operators hide the Provider vaults and Proposals tabs (#12284)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Hosting operators (a managed cloud, an internal shared server) tune
the settings surface with `PAPERCLIP_HIDDEN_SETTINGS`, which today hides
whole pages
> - The Secrets page bundles four tabs, and two of them — Provider
vaults and Proposals — do not apply to deployments where the operator
provisions provider credentials itself
> - Hiding the whole Secrets page is too coarse: the Secrets and My
secrets tabs stay essential everywhere
> - This pull request adds per-tab visibility keys
(`company.secrets.vaults`, `company.secrets.proposals`) as a new
company-section registry group
> - The benefit is that any hosting operator can trim the Secrets page
to what fits their deployment, with self-hosted behavior unchanged by
default

## Linked Issues or Issue Description

No public issue exists; following the enhancement template:

**What existing behavior does this improve?**
`PAPERCLIP_HIDDEN_SETTINGS` can hide the whole Secrets page
(`company.secrets`) but not individual tabs. Operators of managed
deployments need to hide the Provider vaults and Proposals tabs while
keeping the rest of the page.

**Subsystem affected**
Settings visibility (`packages/shared/src/settings-visibility.ts`) and
the Secrets page UI (`ui/src/pages/Secrets.tsx`).

**Current behavior**
The Secrets page always renders all four tabs (Secrets, My secrets,
Provider vaults, Proposals), polls pending proposals for the badge, and
offers "manage vaults" affordances that jump to the vaults tab.

**Proposed behavior**
Two new registry keys, `company.secrets.vaults` and
`company.secrets.proposals`, hide the corresponding tab: the tab-bar
entry disappears, an active hidden tab snaps back to Secrets, the
manage-vaults affordances are suppressed, and the pending-proposals poll
stops. UI visibility only — the provider-config and proposal APIs stay
live for agents and integrations, matching the existing `company.*`
precedent. Nothing changes when the variable is unset.

**Reason and benefit**
Any hosting operator (a managed cloud, an internal shared server) can
trim the Secrets page to what fits their deployment — for example when
the operator provisions provider credentials itself, so the vault and
proposal flows do not apply — without losing the Secrets and My secrets
tabs, which stay essential everywhere.

**Breaking changes**
None. With `PAPERCLIP_HIDDEN_SETTINGS` unset (or set to existing keys
only) nothing changes; older app versions receiving the new keys ignore
them with a warning by design.

## What Changed

- `packages/shared/src/settings-visibility.ts`: new
`HIDEABLE_COMPANY_SECTIONS` group (`company.secrets.vaults`,
`company.secrets.proposals`), `HideableCompanySection` type,
`hidesCompanySection()` helper, wired into `HideableSettingKey` /
`HIDEABLE_SETTING_KEYS`, re-exported from the package index.
- `ui/src/pages/Secrets.tsx`: tab-bar filtering, hidden-tab snap-back
effect, gated pending-proposals query, conditional `onManageVaults` on
both the import button and dialog (the button's "AWS vault disabled —
manage" affordance renders nothing when vaults are hidden), hidden
`TabsContent` blocks.
- Docs: new bullet in `docs/deploy/environment-variables.md` under
"Hiding settings surfaces".
- Tests: registry membership/parse cases in
`settings-visibility.test.ts`; new render cases in
`Secrets.render.test.tsx` (hidden tabs absent + proposals poll skipped;
default render keeps both tabs and the poll).

## Verification

- `npx vitest run packages/shared/src/settings-visibility.test.ts
ui/src/pages/Secrets.render.test.tsx` — 43 tests passing.
- `pnpm --filter @paperclipai/shared typecheck` and `pnpm --filter
@paperclipai/ui typecheck` — clean.

## Risks

- Low. Nothing changes with `PAPERCLIP_HIDDEN_SETTINGS` unset (covered
by the default-render test). The keys are UI-visibility only, so
agent/integration API access is unaffected. Older app versions receiving
the new keys ignore them with a warning by design.

## Model Used

Claude (Anthropic), model id `claude-fable-5`, extended thinking,
agentic tool use 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 (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-27 11:28:15 -07:00
Devin Foley 325041cb00
fix(assets): accept identity-provider characters in image upload namespaces (#12288)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Humans oversee those agents in teams, so each person has a login and
a profile with an avatar
> - Avatars, logos and pasted images all go to one asset upload API,
which files each object under a namespace
> - The avatar namespace embeds the user id, and a deployment can take
user ids from an external identity layer, where a subject often holds
":", "|", "." or "@"
> - But the namespace validator accepted only letters, numbers, "/", "_"
and "-", so those users got a 400 "Invalid image metadata" error and
could not set a profile photo
> - This pull request widens the accepted characters, rejects "." and
".." path segments with a clear message, and cleans the namespace in the
upload client
> - The benefit is that profile photo upload works for every user, and a
namespace the API refuses now returns a message that says what is wrong

## Linked Issues or Issue Description

No existing issue or open pull request covers this. I searched the issue
and pull request lists for "avatar upload", "profile photo", "Invalid
image metadata" and "asset namespace" and found no duplicate. The bug
report follows.

**What happened?**

Profile photo upload fails. `ui/src/pages/ProfileSettings.tsx` sends the
namespace `profiles/${user.id}` to `POST
/api/companies/:companyId/assets/images`. When the user id comes from an
external identity layer it can contain ":", "|", "." or "@" — for
example `oidc:example|jane.example@example.com`.
`createAssetImageMetadataSchema` in
`packages/shared/src/validators/asset.ts` accepted only
`/^[a-zA-Z0-9\/_-]+$/`, so the route returned 400 "Invalid image
metadata" (`server/src/routes/assets.ts`). The image bytes were never
the problem, but the message pointed at the image, so the toast gave the
user nothing to act on.

A second case has the same cause. The agent instructions editor in
`ui/src/pages/AgentDetail.tsx` builds a namespace that ends with a
filename, such as `agents/<id>/instructions/SKILL.md`. The "." in the
filename also failed the check.

**Expected behavior**

A profile photo uploads for any user id the app itself issues, and an
image pasted into the agent instructions editor uploads for any
instruction filename. A namespace the API does refuse returns a message
that names the field and states the rule.

**Steps to reproduce**

1. Run Paperclip with an external identity provider, so `user.id` holds
an OIDC subject such as `oidc:example|jane.example@example.com`.
2. Open Settings, then Profile.
3. Choose an avatar image.
4. The upload fails and the page shows "Invalid image metadata".

Or, with no identity provider:

1. Open an agent, then the instructions editor, and select a file whose
name contains a "." such as `SKILL.md`.
2. Paste an image into the editor.
3. The upload fails with the same error.

**Paperclip version or commit**

`master` at eb86fcd49.

**Deployment mode**

Any deployment whose user ids come from an external identity layer. The
instructions-editor case reproduces on a plain self-hosted install too.

**Agent adapter(s) involved**

Not adapter-specific (core bug).

## What Changed

- `packages/shared/src/validators/asset.ts`: widen the namespace pattern
to `/^[a-zA-Z0-9\/_.:@|-]+$/`, and reject any "/"-separated segment
equal to "." or "..". A traversal attempt now gets a clean 400 from the
validator instead of an error from the storage provider.
- `packages/shared/src/validators/asset.ts`: add
`sanitizeAssetNamespace()`, which maps any string to a namespace the
schema accepts. It works per segment: it keeps the accepted characters,
turns the others into "-", collapses repeated dashes, drops empty and
dot-only segments, and caps the result at 120 characters. It returns
`undefined` when no segment survives, and the caller then sends no
namespace.
- `packages/shared/src/validators/asset.ts`: export
`ASSET_NAMESPACE_MAX_LENGTH` and `ASSET_NAMESPACE_RULE`, so the rule
text and the API error cannot drift apart.
- `ui/src/api/assets.ts`: run the namespace through
`sanitizeAssetNamespace()` in `uploadImage`. This is one choke point for
all callers, so no caller has to know the rule.
- `server/src/routes/assets.ts`: name the field in the 400 message —
`Invalid image metadata: "namespace" must be 1-120 characters of
letters, numbers, or / _ - . : @ |, and cannot contain "." or ".." path
segments`. The zod issue details stay in the response. The UI shows
`body.error`, so the toast is now actionable.
- Tests: a new `packages/shared/src/validators/asset.test.ts`
accept/reject matrix for the schema and the sanitizer; three cases in
`server/src/__tests__/assets.test.ts`; one case in
`ui/src/pages/ProfileSettings.test.tsx`.

## Verification

Targeted runs:

```
npx vitest run packages/shared/src/validators/asset.test.ts        # 22 passed
npx vitest run server/src/__tests__/assets.test.ts                 # 11 passed
npx vitest run ui/src/pages/ProfileSettings.test.tsx               # 2 passed
```

New cases:

- Schema: accepts identity-provider ids that hold ":", "|", "." and "@";
accepts `agents/<id>/instructions/SKILL.md`; rejects `profiles/bad
name!`, over-length input, and `.` or `..` segments.
- Sanitizer: passes identity-provider ids through unchanged, replaces
and collapses the other characters, drops the `.` and `..` segments
while keeping a segment of three or more dots, caps at 120 characters
without leaving a dot segment behind at the cut, and returns `undefined`
when nothing survives. One case asserts the sanitizer output always
parses.
- Route: 201 for `profiles/oidc:example|jane.example@example.com`, and
the storage service receives that namespace; 400 naming `namespace` for
`profiles/bad name!`; 400 for `profiles/../secrets`.
- UI: a session user id holding ":" and "|" uploads, and the namespace
reaches the API unchanged.

Typecheck:

```
pnpm --filter @paperclipai/shared typecheck   # clean
pnpm --filter @paperclipai/ui typecheck       # clean
cd server && npx tsc --noEmit -p tsconfig.json # clean
```

Package suites:

```
npx vitest run --project @paperclipai/shared --exclude "**/dist/**"   # 586 passed, 8 pre-existing failures in src/worktree-seed-source.test.ts
npx vitest run --project @paperclipai/ui --exclude "**/dist/**"       # 4402 passed
```

CI runs the server suite as ten shards (five general, five serialized),
which is the authoritative full run for this package. All shards pass on
this branch.

The `worktree-seed-source` failures reproduce on an unmodified checkout
of the same base commit and are unrelated to this change. The UI
failures seen in that run were 5-second test timeouts caused by running
two suites at once on one machine; each file passes when it runs alone.

No document states the namespace character rule — I checked `docs/` and
`doc/`, where the asset upload endpoint appears only in an OpenAPI
registry entry and a smoke-lab note, neither of which describes the
metadata fields. The rule now lives in one exported constant that the
API error reuses.

## Risks

Low risk.

- The wider character set does not widen what a caller can write to
disk. `server/src/storage/service.ts` already replaces every character
outside `[a-zA-Z0-9._-]` in each path segment, and
`server/src/storage/local-disk-provider.ts` already rejects "." and ".."
segments and any key that resolves outside the base directory. This
change moves the "." and ".." refusal earlier, to the validator, so the
caller gets a clear 400.
- The API is more permissive than before, so no request that used to
succeed can start failing.
- Namespaces stored before this change keep working. The namespace is
not a key that is looked up; it is a prefix under which new objects are
filed.
- One behavior change worth noting: the UI now cleans a namespace
instead of sending it as typed, so a caller that passes an unusable
namespace gets a cleaned prefix rather than a failed upload.

## Model Used

- Claude (Anthropic), Claude Opus, 1M context window, extended thinking,
agentic tool use through 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 (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-27 11:27:31 -07:00
Devin Foley d27998a230
Check the declared Lexical range with semver rules (#12319)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - People write almost every issue, goal, document, and chat message
through the rich markdown editor, which wraps `@mdxeditor/editor` on top
of Lexical
> - #12317 fixed a dependency split that broke that editor everywhere,
and it added `ui/src/lib/lexical-single-copy.test.ts` to keep the
Lexical graph honest
> - Greptile raised a P2 on the last assertion in that test. It compared
the range `@mdxeditor/editor` declares to a literal string, so it
rejected equivalent spellings such as `>=0.48.0 <0.49.0` even when the
resolved version satisfies them
> - A guard that fails on a healthy tree teaches people to delete it,
which would give back the protection #12317 just added
> - #12317 merged before the fix landed, so this pull request carries it
> - This pull request stops the test from reading semver ranges at all,
and asserts the mechanism that can actually break the graph
> - The benefit is a guard that fails only on a real problem, and a
smaller test file than before

## Linked Issues or Issue Description

Refs #12317 — this addresses the Greptile P2 left on that pull request.
No public issue exists, so the problem is described below with the bug
report template.

**What happened?**

`ui/src/lib/lexical-single-copy.test.ts` asserted the declared range
with an exact string comparison:

```ts
const [major, minor] = versionOf("lexical", requireFromUi).split(".");
expect(declared).toBe(`^${major}.${minor}.0`);
```

That accepts one spelling only. If `@mdxeditor/editor` published
`>=0.48.0 <0.49.0`, or `^0.48.2`, or `^0.47.0 || ^0.48.0`, the test
would fail even though the resolved Lexical version satisfies the
declared range. The test would report a dependency split that does not
exist.

**Expected behavior**

The test fails when the Lexical graph is actually split or forced. It
passes on any healthy tree, whatever range syntax the editor happens to
publish.

**Steps to reproduce**

1. Check out `master` at c8a136fb0.
2. Edit `ui/node_modules/@mdxeditor/editor/package.json` and change
`"lexical": "^0.48.0"` to the equivalent `"lexical": ">=0.48.0
<0.49.0"`.
3. Run `npx vitest run ui/src/lib/lexical-single-copy.test.ts`.
4. The range case fails, although 0.48.0 satisfies the range.

**Paperclip version or commit**

`master` at c8a136fb0. The assertion arrived with #12317.

**Relevant logs or output**

```
AssertionError: expected '>=0.48.0 <0.49.0' to be '^0.48.0' // Object.is equality
```

## What Changed

The test no longer reads semver ranges. It asserts the mechanism
instead.

- Replaced the range assertion with a check that no `lexical` or
`@lexical/*` key appears in the root `pnpm.overrides`. The failure
message names the fix.
- Removed the range assertion and the version-line comparison that stood
in for it. Both needed range syntax to mean something specific.
- Documented on the copy check why it now carries the whole guarantee.

Why this is sufficient, and stronger:

- An override is the only thing that can push a resolved version outside
the range a package declares. Remove the override and pnpm honours every
declared range by construction, so `@mdxeditor/editor` gets a Lexical it
supports. No parser needed.
- `@mdxeditor/editor` resolved its own Lexical from its own declared
range. The copy check asserts the app resolved the same version. So an
equal version proves the app sits on a line the editor supports,
transitively.
- It also closes the second half of the original bug. The old override
list could only pin the packages it named, and `@lexical/extension` was
reached transitively and never listed. A guard on "no Lexical override
at all" has no such blind spot, while a guard on "the override pins the
right version" would have.

The file is 65 lines shorter than the first attempt and no longer
carries a hand-rolled semver evaluator.

I considered two alternatives and rejected both. A `semver` dependency
in `ui` would work, but `ui` cannot resolve `semver` today and a new
direct dependency is a heavy way to buy one assertion. A larger
hand-rolled evaluator would have to cover partial versions, wildcards,
hyphen ranges, and spaced comparators before it stopped producing false
failures, which is a semver library with fewer tests.

Nothing outside this test file changes. The Lexical pins from #12317
stay as they are, and `pnpm-lock.yaml` is untouched because no manifest
changes.

## Verification

- `npx vitest run ui/src/lib/lexical-single-copy.test.ts` — 3 passed.
- `pnpm --filter @paperclipai/ui typecheck` — clean.
- Regression still caught. I restored the pre-#12317 pins, which are the
0.49.0 `pnpm.overrides` block and the 0.49.0 pins in `ui/package.json`,
ran `pnpm install`, and re-ran the guard. Two of the three cases failed,
and the new one reports the cause and the fix together:

```
AssertionError: Pin Lexical through ui/package.json instead. An override cannot
cover the packages it does not name, and it hides the range @mdxeditor/editor
declares.: expected [ 'lexical', …(9) ] to deeply equal []

AssertionError: expected '0.48.0' to be '0.49.0'
```

I then restored the current pins and re-ran the guard, which passed 3 of
3.
- `npx vitest run ui/src/lib/mention-deletion.test.ts
ui/src/lib/mention-aware-link-node.test.ts
ui/src/components/MarkdownEditor.test.tsx` — 42 passed, unchanged.

## Risks

- Low risk. The change touches one test file and no application code.
- The guard now forbids a legitimate tool. If someone later has a real
need for a Lexical `pnpm.overrides` entry, this test blocks it. That is
deliberate: an override is what broke the editor, and the block is one
line to remove with a reviewer looking at it. The failure message states
the supported alternative, which is pinning in `ui/package.json`.
- The guard no longer checks the declared range directly. It relies on
pnpm honouring declared ranges when no override is present, which is the
package manager's contract and the same property `pnpm install
--frozen-lockfile` depends on in CI.
- The ordering #12317 set out still holds: a Lexical upgrade must wait
for an `@mdxeditor/editor` release that supports it. Bumping `ui` past
the editor's range now produces two copies, which the copy checks fail
on.

> 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), Claude Opus, agentic tool use via Claude Code.

- Provider and model: Claude (Anthropic), Claude Opus
- Capability: agentic tool use through Claude Code, with repository
search, file edits, dependency installation, test and build runs

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes (the
reasoning lives in comments in the test file; no document states a
Lexical version or pinning policy)
- [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-27 11:26:06 -07:00
Devin Foley 2a3c86f91d
fix(ui): keep the rich editor available for angle-bracket prose (#12290)
## Thinking Path

> - Paperclip helps people manage AI agents and their work
> - People use the rich markdown editor to write task descriptions,
comments, and agent instructions
> - The editor disables HTML processing, but the markdown parser still
treats some angle-bracket text as HTML
> - A value such as `<name>` could therefore stop the rich editor and
show the raw-source fallback
> - The retry action used the same input, so it could not recover
> - This pull request escapes unsupported angle brackets only while the
editor processes the value
> - The benefit is that the rich editor works and stored markdown stays
unchanged

## Linked Issues or Issue Description

Fixes: #12197

Related prior attempt: #2696

**What happened?**

The rich markdown editor did not start when prose contained a bare angle
bracket, such as `<name>`. It showed the raw-source fallback. The retry
action repeated the same failure.

**Expected behavior**

The rich editor starts for normal prose. It stores the text in its clean
form because agent prompts can use this text.

**Steps to reproduce**

1. Open a task description, comment, or other field that uses the rich
markdown editor.
2. Enter `Rename <name> to the real name`.
3. Reload the field.
4. Observe the raw-source fallback.

**Paperclip version or commit**

`master` at d785b1921.

**Deployment mode**

All modes. The defect is in the UI package.

## What Changed

- Escape only angle brackets that the parser treats as unsupported HTML
constructs.
- Restore the clean markdown before the editor sends a value to its
parent.
- Keep code, autolinks, link destinations, and existing escapes
unchanged.
- Apply the same conversion to pasted and inserted markdown.
- Make the empty-editor fallback wait for editor initialization and
confirm the empty state.
- Re-arm the initial empty-change guard when the user retries the
editor.
- Add `MDE-PARSE`, `MDE-RENDER`, and `MDE-EMPTY` fallback codes.
- Correct the editor mock and add regression tests for parsing, round
trips, paste, insert, retry, and fallback behavior.

## Verification

- `npx vitest run ui/src/lib/angle-bracket-markdown.test.ts
ui/src/lib/blockquote-markdown.test.ts
ui/src/components/MarkdownEditor.test.tsx` — 106 tests passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm -r typecheck` — passed.
- `pnpm build` — passed.
- `pnpm check:token-gates` — reports nine existing color findings in
`ui/src/components/onboarding/PillGuy.tsx`. This pull request does not
change that file.
- `pnpm test:run` — the local run found four existing port-allocation
failures in unrelated workspace-runtime tests. The focused editor tests
passed. The pull request CI runs these suites on clean workers.

## Risks

- Low data risk. The conversion applies only while MDXEditor processes a
value. Stored markdown keeps the clean form.
- The scanner skips code, autolinks, link destinations, and existing
escapes to prevent content changes.
- The raw-source fallback can appear about 300 ms later because it now
confirms that the editor stayed empty.
- There is no database or API 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

- Original implementation: Anthropic Claude Opus, 1M context, with
agentic tool use in Claude Code.
- PR preparation and verification: OpenAI Codex with GPT-5, reasoning,
code execution, and tool use. The runtime did not expose the
context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (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-27 09:57:13 -07:00
Devin Foley c8a136fb02
Restore a single Lexical copy so the rich editor renders (#12317)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - People write almost every issue, goal, document, and chat message
through the rich markdown editor, which wraps `@mdxeditor/editor` on top
of Lexical
> - On `master` that editor renders its raw-source textarea instead. It
shows "Rich editor unavailable for this markdown" on every field, for
all content, including empty content
> - Lexical throws at render time because the project holds two copies
of `LexicalBuilder`. The root `pnpm.overrides` block forced the Lexical
family past the version range the editor supports, and it missed the
packages that are reached transitively
> - Every markdown surface in the product degrades to plain text
editing, so this is a full loss of a core authoring feature and not a
cosmetic problem
> - This pull request removes the Lexical entries from `pnpm.overrides`,
pins the app to Lexical 0.48.0, and adds a test that guards the
resolution graph
> - The benefit is that the rich editor renders again, and a future
override or bump that splits Lexical fails in CI instead of in the
browser

## Linked Issues or Issue Description

No public issue exists. The problem is described below with the bug
report template.

**What happened?**

The rich markdown editor falls back to its raw-source textarea on every
markdown field. The header reads "Rich editor unavailable for this
markdown. Showing raw source instead." The fallback appears for all
content, including empty content. React catches an error during the
first render of the editor.

The cause is dependency resolution. The root `package.json`
`pnpm.overrides` block pinned the Lexical family to `0.49.0`.
`@mdxeditor/editor@4.2.1` declares `lexical: "^0.48.0"`. A caret range
on a `0.x` version pins the minor, so `^0.48.0` means `>=0.48.0
<0.49.0`. The override therefore pushed the editor past its only
supported line. Lexical 0.49.0 also carries breaking `$config()` node
changes.

The override list was also incomplete. `@lexical/extension`,
`@lexical/history`, and `@lexical/internal` are reached transitively and
were never listed. Those packages stayed on 0.48 while the listed
packages moved to 0.49. The graph mixed the two lines, and the built
browser bundle carried two
`Symbol.for("@lexical/extension/LexicalBuilder")` registrations.

Commit 04432f82c (#10728) introduced this. It was a Dependabot bump from
0.46.0 to 0.49.0.

**Expected behavior**

The rich editor renders a live WYSIWYG surface on every markdown field.
The raw-source fallback stays reserved for real markdown parse failures.

**Steps to reproduce**

1. Check out `master` and run `pnpm install`.
2. Start the app and open any issue, goal, or document.
3. Look at any markdown field, for example the issue description.
4. The field shows the fallback banner and a plain textarea. The rich
toolbar is absent.

You can also see the split without the browser:

```
pnpm --filter @paperclipai/ui build
grep -ohE 'Symbol\.for\("@lexical/extension/LexicalBuilder"\)' ui/dist/assets/index-*.js | wc -l
```

On `master` this prints `2`. A correct graph prints `1`.

**Paperclip version or commit**

`master` at 1ba7b2cc7. The regression entered at 04432f82c (#10728).

**Relevant logs or output**

```
LexicalBuilder.fromEditor: The given editor was created with LexicalBuilder
0.48.0+dev.esm but this version is 0.49.0+dev.esm. A project should have
exactly one copy of LexicalBuilder
```

React reports the error in `<LexicalExtensionEditorComposer>`.
`MarkdownEditorRichErrorBoundary` catches it and shows the raw-source
fallback.

## What Changed

- Removed the Lexical entries from `pnpm.overrides` in the root
`package.json`. This drops `lexical`, `@lexical/clipboard`,
`@lexical/link`, `@lexical/list`, `@lexical/markdown`,
`@lexical/plain-text`, `@lexical/react`, `@lexical/rich-text`,
`@lexical/selection`, and `@lexical/utils`. The `rollup`, `react`, and
`react-dom` overrides stay as they are.
- Pinned `lexical` and `@lexical/link` to `0.48.0` in `ui/package.json`.
The app subclasses `LinkNode` for mention-aware links and registers it
into the Lexical instance that MDXEditor owns, so node identity needs
one copy. `@lexical/link@0.48.0` pins `lexical` at `0.48.0` exactly.
- Left `pnpm-lock.yaml` out of this commit on purpose. The `policy` job
blocks manual lockfile edits, regenerates the lockfile from the
manifests, and uploads it for the downstream `pnpm install
--frozen-lockfile` jobs. I did regenerate the lockfile locally to verify
the result. That local lockfile touched only Lexical and MDXEditor
entries and held no Lexical 0.49 reference.
- Added `ui/src/lib/lexical-single-copy.test.ts`. It asserts the app and
the editor resolve the same `lexical` file, that `@lexical/link` and the
transitively reached `@lexical/extension` sit on that version, and that
the version satisfies the range the editor declares.

`ui` is the only workspace package that declares a Lexical dependency,
so nothing else changes.

### Why the overrides can go

#9180 added the Lexical overrides for a good reason. At that time the
app used Lexical 0.46.0 and `@mdxeditor/editor` declared
`lexical@^0.35.0`. The two ranges could not meet, so an override was the
only way to force one copy.

That condition is gone. `@mdxeditor/editor@4.2.1` now declares `lexical:
"^0.48.0"`. When the app also declares `0.48.0`, pnpm resolves one copy
on its own and the override has nothing left to do.

The override is now a liability. It can only pin the packages it lists
by name. `@lexical/extension` did not exist when the list was written,
so the list never covered it, and the list silently stopped covering the
whole family. Natural resolution has no such blind spot, because every
Lexical package carries its own exact pin on the core.

The existing `lexical` alias in `ui/vite.config.ts` and
`ui/vitest.config.ts` stays. It still resolves at 0.48.0. Note that the
alias covers only the `lexical` core, which is why it did not prevent
this bug and why the new test reads the resolution graph with
`createRequire` instead of importing Lexical.

### Related pull requests

- #10728 — the Dependabot bump that introduced the regression.
Superseded by this change.
- #10724 — the companion core bump merged the same day.
- #9180 — added the overrides block that this change removes. See the
explanation above.
- #9179 — an older, still-open 0.46.0 bump. It is stale and this change
does not depend on it.

I found no open pull request that already fixes this problem.

## Verification

All commands ran from the repository root.

- `npx vitest run ui/src/lib/lexical-single-copy.test.ts` — 3 passed.
- Guard proven against the broken tree. I restored the pre-fix
`package.json`, `ui/package.json`, and `pnpm-lock.yaml`, ran `pnpm
install`, and re-ran the guard. It failed 2 of 3: `expected '0.48.0' to
be '0.49.0'` for `@lexical/extension`, and `expected '^0.48.0' to be
'^0.49.0'` for the declared range. I then restored the fix and `pnpm
install` reproduced the committed lockfile byte for byte.
- `pnpm --filter @paperclipai/ui typecheck` — clean. The Lexical API
calls in the app still compile on 0.48.
- `npx vitest run ui/src/lib/mention-deletion.test.ts
ui/src/lib/mention-aware-link-node.test.ts
ui/src/components/MarkdownEditor.test.tsx` — 42 passed.
- `pnpm --filter @paperclipai/ui build`, then the `grep` above — the
bundle now holds 1 `LexicalBuilder` registration. It held 2 before this
change.
- Full `ui` project: `npx vitest run --project '@paperclipai/ui'` — 4426
of 4431 passed. I re-ran the 5 failures in isolation. Four passed, so
they were load-induced 5 second timeouts in
`CompanyEnvironments.test.tsx` and `IssuesList.test.tsx`. The last one,
`OnboardingWizard.test.tsx > renders instead of throwing when the
browser denies storage access`, also fails on clean `master` and is
unrelated to this change.
- Real render probe. I wrote a throwaway test that renders
`MarkdownEditor` against the real `@mdxeditor/editor` with no mock.
Before this change it produced the fallback banner and the
`LexicalBuilder` error. After this change the editor mounts a live
`contenteditable` surface and logs no error. The probe was temporary and
is not part of this diff.

## Risks

- This pins the app's Lexical to the line the editor supports rather
than to the newest release. A future Lexical upgrade must wait for an
`@mdxeditor/editor` release that supports it. I checked published
`@mdxeditor/editor` through 4.2.3 and none supports Lexical 0.49, so
fixing forward is not possible today. The new test enforces that
ordering, so an upgrade attempt fails in CI with a clear reason.
- Dependabot will offer the Lexical 0.49 bump again. The new test will
fail that pull request. That is the intended behavior. A reviewer should
keep the bump closed until MDXEditor moves first.
- 04432f82c was a routine version bump and not a security fix, so this
change reintroduces no known advisory.
- The change is limited to dependency resolution and one new test. It
alters no application code.

> 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), Claude Opus, agentic tool use via Claude Code.

- Provider and model: Claude (Anthropic), Claude Opus
- Capability: agentic tool use through Claude Code, with repository
search, file edits, dependency installation, test and build runs

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes (no
document in the repository states a Lexical version or pinning policy,
so the reasoning is recorded in the new test's header comment)
- [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-27 09:26:42 -07:00
Nicky Leach 1de105c475
fix(observability): pin the Sentry browser SDK and gate the optional Sentry server peer on the exact version (#12270)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip uses separate server and browser packages for runtime
services and the board.
> - Sentry integrations need an exact SDK version and safe optional
loading.
> - A version range can select an SDK that the privacy tests did not
audit.
> - Missing peer metadata does not describe the optional server SDK
contract.
> - This pull request pins the browser SDK and gates the optional server
SDK on its exact version.
> - The benefit is a clear SDK contract with fail-open startup behavior.

## Linked Issues or Issue Description

**What happened?**

The browser package used the range ^10.71.0, so a lockfile refresh could
select a newer SDK. The server loaded @sentry/node dynamically but did
not declare its optional peer contract.

**Expected behavior**

The browser package must use the audited 10.71.0 version. The server
must load @sentry/node only when the installed peer matches 10.71.0. The
server must start when the optional peer is absent.

**Steps to reproduce**

1. Install the project dependencies.
2. Inspect the browser Sentry version and the server package metadata.
3. Start the server without installing @sentry/node.
4. Confirm that the server starts and that the dynamic Sentry bootstrap
does not load an unsupported peer version.

**Paperclip version or commit**

9c57c0f119

**Deployment mode**

Built from source with pnpm dev or pnpm build.

**Installation method**

Built from source.

**Agent adapter(s) involved**

Not adapter-specific (core change).

**Database mode**

Not database-related.

## What Changed

- Pin @sentry/browser to exactly 10.71.0 as a UI development dependency.
- Declare @sentry/node as an optional server peer dependency at 10.71.0.
- Gate the dynamic server bootstrap on the exact peer version.
- Add tests for the browser pin, peer metadata, version gate, and
fail-open loading.
- Document the supported server SDK version.
- Keep the lockfile unchanged because the pull request workflow
regenerates it for manifest changes.

## Verification

- Server tests pass with six expected skips when @sentry/node is absent.
- UI tests pass.
- The UI build emits the lazy Sentry browser chunk.
- git diff --check passes.
- GitHub pull request checks must pass after this pull request opens.
- Greptile must return a 5/5 score with no open findings.

## Risks

The exact version gate prevents Sentry startup when an unsupported SDK
version exists. The integration remains optional and fail-open. The
lockfile workflow must regenerate the lockfile before frozen downstream
jobs run. The label-gated Storybook visual job must not run until it can
restore the generated lockfile artifact.

## Model Used

OpenAI Codex, GPT-5, tool use and code review support, exact context
window details are managed by the execution platform.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-27 07:20:03 -07:00
scotttong 4d82f5eaea
copy: unify user-facing "company" wording to "organization" (#12243) 2026-08-27 01:04:55 -07:00
Tonio eb86fcd498
The agent, drawn as itself (#12274)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A new customer's first session ends in the tenant's agent arc:
create an agent, connect a model, review
> - Walking it turned up questions the arc had no business asking — a
role picker using a vocabulary the customer has not been given, a model
picker asking them to judge models they have not met — and chrome
restating what they had just watched happen
> - Each one costs a first-session customer attention at the exact
moment they are deciding what this product is
> - This pull request cuts the arc to what it must ask, and draws the
agent as itself so the arc has a visible subject
> - The benefit is three steps that each ask one thing, ending on an
agent that is visibly ready

## Linked Issues or Issue Description

No public issue exists. The changes come from walking the sign-up arc
end to end.

**What happened:**
The agent step asks for a role from a fixed enum before asking for a
name. The model step shows two "Recommended" badges (on both options),
an "Adapter type" eyebrow, and a model picker. The review step lists a
three-row checklist of work the customer just performed. The progress
strip is a full-width segmented bar.

**Expected behavior:**
The agent step asks for a name. The model step offers the two harnesses
and hides the rest behind advanced settings. The review step says the
agent is ready. The strip counts three discrete steps.

**Steps to reproduce:**
1. Sign up and enter the tenant wizard on the agent arc.
2. Observe the role select above the optional name field.
3. Continue to the model step: both options carry a "Recommended" badge,
and a model picker sits below.
4. Continue to review: a checklist restates the organization name,
agent, and model.

**Additional context:**
The brand pill assets (`pill-1-dormant.svg`, `pill-1-alive.svg`) are
transcribed verbatim into a component rather than approximated. The role
removal exposed a latent silent-failure path — see Risks.

## What Changed

- `PillGuy` renders the brand pill in two states; the arc holds one
instance, dormant through create and connect, alive on review.
- The agent step asks for a name only. The name is required; the role
picker is gone.
- `DEFAULT_AGENT_ROLE` (`general`) backs every onboarding hire, and
`agentRole` now defaults to it rather than empty.
- The model step drops both "Recommended" badges, the "Adapter type"
eyebrow, and the model picker; "More Agent Adapter Types" becomes
"Advanced settings"; the sub-line becomes "Paperclip works with your
existing subscription or API keys."
- The review step drops its checklist; the heading becomes "Let's get
started..." with "[name] is ready to work!".
- The progress strip renders three left-aligned dots at the previous
gap.
- Five e2e specs and both wizard unit suites migrate off
`#onboarding-agent-role`.

## Verification

Run the tenant suite:

```
cd ui && npx vitest run
```

- 4398 tests pass across 474 files; `npx tsc --noEmit` clean.
- Walked live in a local instance: agent step (dots, dormant pill, name
placeholder), model step (no badges/eyebrow/picker, "Advanced
settings"), review (pill alive, new copy, no checklist).
- The retargeted role test asserts the hire payload carries `role:
"general"` and the typed name — it is the test that catches the silent
failure below.

## Risks

- **A latent silent failure, now closed.** `handleGiveHeartbeat` returns
early when `agentRole` is empty. With the picker removed and no default,
Connect would have hired nobody and shown no error. The default closes
it; the guard stays for any future path that clears the role.
- **Behavioral change:** every onboarding hire is filed as `general`
rather than a chosen role. The role remains editable in the app.
- **Behavioral change:** the model is no longer chosen during
onboarding. Every adapter offered here resolves its own default in
`buildAdapterConfig`, and the model is changeable later.
- **Assets:** the pill carries its own gradient fills and does not
follow the theme. That is deliberate — the agent looks like itself on
either ground.

## Model Used

Claude Opus 5 (`claude-opus-5`) via Claude Code, 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
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] 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 5 <noreply@anthropic.com>
2026-08-26 21:39:55 -07:00
Nicky Leach 06cd21ed0f
fix(observability): declare the optional OpenTelemetry peer dependencies (#12249)
## Thinking Path

> - Paperclip manages AI agents for work.
> - Paperclip includes an observability path that operators can enable
for tracing.
> - The server loads several OpenTelemetry packages only when tracing is
enabled.
> - The documentation calls these packages optional peer dependencies,
but the server manifest does not declare them.
> - This gap hides supported versions and stops Dependabot from
maintaining the packages.
> - This pull request aligns package metadata, runtime checks, and
documentation with the opt-in tracing design.
> - The change gives operators clear installation behavior and keeps the
no-op default.

## Linked Issues or Issue Description

This pull request fixes a package metadata and installation defect.
Related observability work appears in
[#8476](https://github.com/paperclipai/paperclip/pull/8476) and
[#9672](https://github.com/paperclipai/paperclip/pull/9672).

The server documentation described optional OpenTelemetry peer
dependencies, but `server/package.json` did not declare them. Package
managers and Dependabot could not see the supported version ranges. The
UI and Claude local adapter also relied on automatic peer installation
for `yjs` and `@anthropic-ai/sdk`.

The package manifests now declare the optional runtime packages. A
default install does not install optional tracing peers. The server
keeps its no-op behavior when tracing is disabled or a peer is absent.

## What Changed

- Add seven optional OpenTelemetry packages to `server/package.json` and
mark each package as optional.
- Keep `@opentelemetry/api` as a normal dependency for the no-op
interface.
- Disable automatic peer installation in `.npmrc`.
- Declare `yjs` for the UI package and `@anthropic-ai/sdk` for the
Claude local adapter.
- Check declared peer versions before the server loads a dynamic
OpenTelemetry import.
- Keep the endpoint gate, dynamic imports, and fail-open behavior
unchanged.
- Update the observability and README documentation.
- Tell Dependabot that its npm parser does not read `peerDependencies`.

## Verification

- Targeted server tests pass: 34 passed and 2 skipped.
- The skipped tests require the real OpenTelemetry SDK and remain
pre-existing.
- The pull request workflow regenerates the lockfile because manifest
files and `.npmrc` changed.
- The policy job confirms that the pull request does not include
`pnpm-lock.yaml`.
- GitHub checks pass except `security/snyk (cryppadotta)`, which remains
pending after its authorized wait cap.
- Greptile Review reports 5/5 with no open findings.
- Server typecheck passes.

## Risks

- Optional peers can produce a diagnostic when the installed version
does not match the declared range.
- A missing optional peer does not stop the server.
- Disabling automatic peer installation can expose undeclared package
use in other workspaces.
- This pull request declares the affected packages and adds tests for
the changed behavior.
- This pull request makes no database or API changes.

## Model Used

OpenAI Codex, GPT-5, with repository inspection and pull request
preparation.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with Fixes / Closes /
Refs OR (b) described the issue in-PR following the relevant issue
template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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-26 17:34:53 -07:00
Dotta 75b6d22aac
fix(recovery): make silent-run detection UI-only (#12242)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The recovery service detects active runs that stop producing output.
> - The dashboard already shows suspicious and critical silence to the
board.
> - The recovery scan also creates delegated evaluation work for the
same signal.
> - Output silence alone does not prove that the run or source task
needs recovery.
> - This pull request keeps the signal and removes automatic recovery
artifacts.
> - The benefit is a visible watchdog signal without assignment changes,
wake requests, or issue noise.

## Linked Issues or Issue Description

- Refs #6596
- Refs #7036
- Refs #9475
- Refs #11544
- Refs #11839
- Refs #11961

## What Changed

- Keep the one-hour suspicious level and four-hour critical level in
active-run API summaries.
- Stop output silence from creating or changing issues, recovery
actions, comments, relations, assignments, and wake requests.
- Store snooze, continue, and false-positive decisions against the run
without an evaluation issue.
- Preserve terminal-source folding, orphan cleanup, and open legacy
evaluation links.
- Show informational watchdog copy and board controls without requiring
an evaluation-task link.
- Document the UI-only watchdog contract.
- Add focused server and UI coverage for artifact-free scans and board
decisions.

## Verification

- `pnpm -r typecheck`
- `pnpm exec vitest run
server/src/__tests__/heartbeat-active-run-output-watchdog.test.ts
ui/src/components/IssueRunLedger.test.tsx` (32 tests passed)
- `pnpm build`
- `pnpm check:token-gates`
- `git diff --check`
- `pnpm test:run` completed locally with 4,772 passing tests. It found
30 unrelated macOS test-harness failures in eight workspace, skill,
listener, and runtime exposure files. The failures use `/tmp` and
`/private/tmp` as different paths, require Linux `/proc` listener data,
or derive invalid HMR ports from the macOS ephemeral range.
- The full Linux CI matrix passed on the latest commit. It includes
build, typecheck, server tests, worker tests, serialization tests,
canary, and e2e tests.
- Greptile reviewed the latest commit at 5/5 with no actionable
findings.

## Risks

- The recovery scan keeps its existing result shape, but its created and
escalated counts remain zero for output silence.
- A false-positive decision now suppresses the signal for the full life
of that run.
- Open legacy evaluation issues remain visible and manually resolvable.
The scan does not refresh or reprioritize them.
- There is no database migration and no API schema change.

> I checked `ROADMAP.md`. This change corrects existing watchdog
behavior and does not duplicate planned core work.

## Model Used

- OpenAI Codex, GPT-5, with extended reasoning, 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 the focused tests and non-platform
gates 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-26 15:26:12 -05:00
Nicky Leach 8f1e3cfe24
feat(observability): add opt-in Sentry error monitoring for the server and the browser (#12190)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The server and the browser need clear error reports when an operator
enables external monitoring.
> - Paperclip already uses an opt-in OpenTelemetry pattern for server
traces.
> - Sentry can provide error reports for both runtime paths when the
operator sets one data source name.
> - This pull request adds one opt-in Sentry gate for the server and the
browser.
> - The benefit is faster diagnosis while the default setup sends no
Sentry data.

## Linked Issues or Issue Description

**What is improved?**

Paperclip gains optional error monitoring for server and browser
failures.

**Subsystem affected**

Cross-cutting (server, UI, and shared authentication data).

**Current behavior**

Paperclip has no built-in Sentry error capture for server failures or
browser boundary failures. Operators must inspect local logs and browser
tools.

**Proposed behavior**

When the operator sets `SENTRY_DSN`, the server and authenticated
browser use the same Sentry project. When the variable is absent, both
paths stay inactive. The server loads Sentry dynamically and fails open
when the optional package is absent.

**Reason and benefit**

Operators can inspect runtime errors in one Sentry project. The default
setup remains local and sends no monitoring data.

**Breaking changes**

None when `SENTRY_DSN` remains unset. Authenticated session responses
add the optional `sentryDsn` field.

**Additional context**

The implementation uses built-in Sentry privacy options. It disables
default HTTP context and breadcrumb integrations and keeps
`sendDefaultPii` false.

## What Changed

- Add an opt-in server Sentry gate with dynamic package loading and
fail-open behavior.
- Add the Sentry data source name to the authenticated session response.
- Add an authenticated browser Sentry gate and React error boundary
capture.
- Add tests for server, browser, route, and application error paths.
- Document activation, installation, privacy settings, capture behavior,
and operator controls.

## Verification

- Run `npx vitest run server/src/__tests__/sentry.test.ts`.
- Run `npx vitest run ui/src/lib/sentry.test.ts`.
- Run `npx vitest run server/src/__tests__/auth-routes.test.ts
server/src/__tests__/shutdown.test.ts`.
- Confirm that the full continuous integration suite passes on this pull
request.
- Leave `SENTRY_DSN` unset and confirm that the server and browser gates
stay inactive.
- Set `SENTRY_DSN` and install the optional Sentry packages before a
manual capture check.

## Risks

The operator controls the Sentry project and accepts the data risk when
the operator enables the feature. Error objects can contain messages,
stacks, or cause chains with private values. The default configuration
sends no data because the feature stays off without `SENTRY_DSN`. A
missing optional server package does not stop server boot.

## Model Used

OpenAI Codex, GPT-5, with tool use, repository inspection, GitHub CLI
operations, and code review support.

## 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-26 11:15:45 -07:00
Nicky Leach dc30dc4f34
fix(setup-token): pin the start guard to the served adapter (#12179)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Sandbox agents use adapter login routes to start authenticated
sessions
> - The setup-token start route accepted adapter types that later routes
and cleanup did not serve
> - This mismatch could create sessions that no route or reaper could
reach
> - The OpenAPI body schema and synchronous login capability defaults
also differed from the enforced behavior
> - This pull request pins the start guard to the served adapter, shares
the adapter constant, aligns the schema, and exposes login capabilities
early
> - The benefit is consistent session access, cleanup, API
documentation, and login UI behavior

## Linked Issues or Issue Description

Refs: #11730

Refs: #11286

**Subsystem affected**

Cross-cutting server and UI login behavior.

**Problem or motivation**

The setup-token start route accepted a non-served adapter type.
Follow-up routes and the reaper only handled the served adapter. This
could create an unreachable session that held its slot. The OpenAPI
schema and early capability defaults also did not match the route
behavior.

**Proposed solution**

Pin the start guard, follow-up key, and reaper filter to one exported
served-adapter constant. Derive the OpenAPI body from the strict shared
schema. Add the login capability projection to synchronous defaults.

**Alternatives considered**

Keep separate adapter constants and add another guard at each follow-up
route. This would preserve duplicate sources of truth and leave future
drift possible.

**Roadmap alignment**

This change supports the Cloud / Sandbox agents milestone in
`ROADMAP.md`.

## What Changed

- Reject a setup-token start request when its adapter type is not the
served adapter.
- Reuse one exported adapter constant for the start guard, follow-up
key, and reaper filter.
- Derive the company adapter login-sessions start body from the strict
shared schema.
- Add the `login` capability projection to the synchronous Claude and
Codex adapter defaults.
- Add regression coverage for the rejected non-served adapter request.

## Verification

- The setup-token route suite passes, including the non-served adapter
regression test.
- The setup-token session-service suite passes.
- The setup-token reaper suite passes.
- The OpenAPI suite passes.
- The server TypeScript check passes.
- The UI TypeScript check passes.
- GitHub Actions must confirm all required checks after pull request
creation.

## Risks

The start route now rejects adapter types that follow-up routes cannot
serve. No database migration exists. Revert the one commit to roll back
the change.

## Model Used

OpenAI Codex, GPT-5, exact runtime model ID not exposed, large context
window, reasoning, tool use, and code execution. The implementing
engineer used AI 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-25 19:48:44 -07:00
Devin Foley ca02d2463a
fix(ui): keep the installed service worker fresh on parked tabs (#12198)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The web UI registers a service worker (`/sw.js`) that caches the app
shell for an offline fallback.
> - Browsers only re-fetch a worker script on navigation or a ~24h
timer, and Paperclip is a parked-tab SPA: a tab can sit open for weeks
without one navigation.
> - An installed worker — and the shell it cached — can therefore keep
serving an old bundle long after a deploy, and the server let `sw.js`
inherit the generic 1h static TTL on top of that.
> - This pull request adds explicit update checks (tab-visible +
hourly), applies a discovered update with one reload while the tab is
hidden, and serves `sw.js` with `Cache-Control: no-cache`.
> - The benefit is that a deploy reaches every open tab within about an
hour, instead of some tabs silently running stale UI indefinitely.

## Linked Issues or Issue Description

Refs #11292 (the network-first `sw.js` fallback fix; this PR closes the
delivery gap that can keep clients pinned on a pre-#11292 worker).

**What happened?**

A browser that had an older cache-first worker installed kept rendering
a stale app shell — old feature set, old naming — while the server was
verified to be running the current release. Nothing on the client checks
for a new worker outside navigations, so a parked tab never picked up
the fixed worker, and `sw.js` was served with a 1h cache TTL that
further delayed update checks.

**Expected behavior**

Every open tab converges to the deployed bundle shortly after a release,
without users unregistering workers in DevTools or hard-reloading.

**Steps to reproduce**

Install a build's service worker, deploy a newer build, and leave the
tab parked (no navigation): the tab keeps running the old bundle
indefinitely; the worker update check only happens if the user
navigates, and even then a cached `sw.js` can answer it.

## What Changed

- New `ui/src/lib/service-worker-updates.ts`: registers `/sw.js`, runs
`registration.update()` when the tab becomes visible and on an hourly
timer, and on `controllerchange` of a previously-controlled page applies
the update with a single reload — only while the tab is hidden, so an
update never yanks the page mid-session; a takeover while visible defers
the reload to the next hidden transition. First-ever installs never
reload.
- `ui/src/main.tsx`: replaces the fire-and-forget `register()` with the
new module.
- New `server/src/static-ui-cache.ts` (`staticUiCacheControl`):
`index.html` and `sw.js` are served `Cache-Control: no-cache`; other
non-hashed statics keep the 1h default. `server/src/app.ts` uses it in
the static middleware.

## Verification

- `npx vitest run ui/src/lib/service-worker-updates.test.ts` — 8 tests:
registration, hidden-takeover reload (once), deferred reload on visible
takeover, no reload on first install, visibility-triggered and
timer-triggered update checks, cleanup, no-container no-op.
- `npx vitest run server/src/__tests__/static-ui-cache.test.ts` — 3
tests incl. the `sw.js.map` lookalike keeping the default TTL.
- `tsc -b` (ui) and `tsc --noEmit` (server) clean; `pnpm check:tokens`
clean.

## Risks

- Behavioral shift: tabs now reload once, while hidden, after a deploy
lands. Unsaved in-page state in a hidden tab is lost at that moment —
the same exposure as a browser discarding a background tab, which SPAs
must already tolerate.
- Self-hosted behavior is otherwise unchanged: same worker script, same
registration URL, one added conditional header.
- Low risk on the server side: the header change only widens
revalidation.

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — agentic
coding session with tool use and extended thinking.

## 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-25 16:12:02 -07:00
Devin Foley 9c03443c48
feat: hideable company settings pages, with import floored on cloud-managed instances (#12199)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The app can run self-hosted or as a cloud-managed instance, where a
hosting platform provisions the instance with its company already
materialized (the existing `isCloudManagedInstance()` predicate and
`cloud_managed` floors)
> - The company Import/Export surface lets an operator materialize whole
companies from an export bundle; on a cloud-managed instance this
bypasses the existing `cloud_managed` company-creation floor and
conflicts with platform-owned provisioning
> - Importing should be disabled on cloud-managed instances, while
export stays open as the data-portability escape hatch
> - This pull request floors every import route with 403
`code=cloud_managed` on cloud-managed instances and hides the Import UI
there, using the existing predicate and the established floor pattern
> - It also extends the operator-hidden settings registry with keys for
every top-level company settings page, so a hosting operator can hide
any of them with `PAPERCLIP_HIDDEN_SETTINGS` alone next time
> - The benefit is one consistent managed-instance policy: cloud-managed
instances cannot import companies, self-hosted installs keep the full
import surface unchanged

## Linked Issues or Issue Description

No public issue exists; the underlying problem follows the enhancement
template.

**What existing behavior does this improve?**

The company import surface (`/api/companies/import*`,
`/api/companies/:companyId/imports/*`) and its UI entry points on
cloud-managed instances.

**Subsystem affected**

Server routes (`server/src/routes/companies.ts`) and UI navigation/pages
(settings sidebar, settings tabs, org chart, `/company/import` route).

**Current behavior**

A cloud-managed instance floors direct company creation (`POST
/api/companies` answers 403 `cloud_managed`), but the import routes
still accept company bundles, so an import can materialize companies the
hosting platform did not provision. The UI offers Import entry points
that lead to a surface that is not available on cloud-managed instances.

**Proposed behavior**

On instances where `isCloudManagedInstance()` is true, every import
route answers 403 `code=cloud_managed` before auth and body work, and
the Import UI (sidebar entry, settings tab, org-chart button,
`/company/import` route) is hidden or redirected. Export remains fully
available. Self-hosted instances are unchanged.

**Reason and benefit**

Cloud-managed instances keep one consistent provisioning authority, and
users do not see an Import surface that dead-ends in a 403.

## What Changed

- `server/src/routes/companies.ts`: a router-level floor mounted at the
`/import` and `/:companyId/imports` prefixes. It covers the single-shot
upload, preview, job polling, chunked transfer
declare/part-upload/status/preview/apply, and the agent-safe per-company
import routes. It throws `forbidden(..., { code: "cloud_managed" })` on
cloud-managed instances, or `403 settings_operator_managed` when the
operator hides `company.import` — both before auth and body validation,
mirroring the company-creation floor.
- `packages/shared/src/settings-visibility.ts`: new
`HIDEABLE_COMPANY_PAGES` registry group — `company.members`,
`company.invites`, `company.secrets`, `company.export`, `company.import`
— with a `hidesCompanyPage` helper. The company General page stays
non-hideable (settings root). `company.import` floors its API; the other
keys are UI-visibility only, as documented in the registry, so
membership/invite/secret/export APIs stay live for agents.
- `ui/src/components/CloudManagedPageGate.tsx` (new): route gate that
redirects cloud-managed instances to `/company/settings`, modeled on
`HiddenSettingsPageGate`.
- `ui/src/App.tsx`: wraps the `company/import` route in
`CloudManagedPageGate`.
- `ui/src/components/CompanySettingsSidebar.tsx`,
`ui/src/components/access/CompanySettingsNav.tsx`,
`ui/src/pages/OrgChart.tsx`: hide the Import entry points when
`useCloudInstance()` reports a managed instance, and honor the new
`company.*` hidden-settings keys for every company page entry (sidebar
item, tab, org-chart buttons).
- `ui/src/App.tsx`: `HiddenSettingsPageGate` route gates for the members
(incl. the legacy access route), invites, secrets, export, and import
pages under their `company.*` keys.
- `docs/deploy/environment-variables.md`: documents the new keys and
their semantics; the CLI and board-operator guides note that import is
unavailable on cloud-managed instances.
- Tests: new `server/src/__tests__/company-import-cloud-floor.test.ts`
and `ui/src/components/CloudManagedPageGate.test.tsx`, registry cases in
`packages/shared/src/settings-visibility.test.ts`, plus cloud and
hidden-key cases in the sidebar, settings-nav, and org-chart suites.

## Verification

- TypeScript typechecks pass for every workspace package (`tsc` in
shared, server, ui; the runner's Rust leg needs a local cargo toolchain
and is covered by CI).
- `pnpm test` on this branch fails only in 9 files that also fail on a
clean `origin/master` checkout on the same machine
(environment-dependent suites: live-listener probes,
workspace/native-runtime spawns, skill materialization). Zero
branch-only failures against that baseline; every suite touched by this
change passes.
- `server/src/__tests__/company-import-cloud-floor.test.ts` asserts:
every import route answers 403 `cloud_managed` under the server-token
signal; the managed-config signal alone also floors; every import route
answers 403 `settings_operator_managed` when `company.import` is hidden;
hiding other company pages leaves import open; the floor applies before
auth and body validation; export stays open on cloud-managed instances;
self-hosted import preview and job polling still work.
- `packages/shared/src/settings-visibility.test.ts` covers the new
`company.*` keys and `hidesCompanyPage`.
- UI suites assert the Import tab, sidebar entry, and org-chart button
disappear on a cloud-managed instance while Export stays, that
`/company/import` redirects through the gate, and that the `company.*`
keys hide their sidebar entries and tabs.

## Risks

- Low risk for self-hosted installs: the floor is inert unless a cloud
signal (`PAPERCLIP_CLOUD_TENANT_SERVER_TOKEN` or
`PAPERCLIP_MANAGED_CONFIG`) is present, and the self-hosted paths are
regression-tested.
- On cloud-managed instances this is a deliberate behavioral removal:
import (including agent-driven safe imports and resumable transfers)
stops working the moment an instance runs this build. In-flight chunked
transfers on such instances cannot be applied afterward; they answer
403.
- CLI import commands against a cloud-managed instance now fail with the
`cloud_managed` error; the message names the reason.
- The new `company.*` keys change nothing unless an operator sets them:
`PAPERCLIP_HIDDEN_SETTINGS` unset keeps behavior identical, and older
images ignore unknown keys by design. The four non-import company keys
hide UI only; their APIs stay live, which the registry documents
explicitly.

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — agentic
coding session with tool use (code search, editing, 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)
- [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-25 16:06:35 -07:00
dependabot[bot] 0cedb45df3
build(deps-dev): bump typescript from 5.9.3 to 7.0.2 (#11880)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3
to 7.0.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/microsoft/TypeScript/releases">typescript's
releases</a>.</em></p>
<blockquote>
<h2>TypeScript 7.0.2</h2>
<p><a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/">https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/</a></p>
<p>This tag was originally released at: <a
href="https://github.com/microsoft/typescript-go/releases/tag/typescript%2Fv7.0.2">https://github.com/microsoft/typescript-go/releases/tag/typescript%2Fv7.0.2</a></p>
<h2>TypeScript 6.0.3</h2>
<p>For release notes, check out the <a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/">release
announcement blog post</a>.</p>
<ul>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.0%22">fixed
issues query for TypeScript 6.0.0 (Beta)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.1%22">fixed
issues query for TypeScript 6.0.1 (RC)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.2%22">fixed
issues query for TypeScript 6.0.2 (Stable)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.3%22">fixed
issues query for TypeScript 6.0.3 (Stable)</a>.</li>
</ul>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
</ul>
<h2>TypeScript 6.0</h2>
<p>For release notes, check out the <a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/">release
announcement blog post</a>.</p>
<ul>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.0%22">fixed
issues query for TypeScript 6.0.0 (Beta)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.1%22">fixed
issues query for TypeScript 6.0.1 (RC)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.2%22">fixed
issues query for TypeScript 6.0.2 (Stable)</a>.</li>
</ul>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
</ul>
<h2>TypeScript 6.0.1 RC</h2>
<p>For release notes, check out the <a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-rc/">release
announcement blog post</a>.</p>
<ul>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.0%22">fixed
issues query for TypeScript 6.0.0 (Beta)</a>.</li>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.1%22">fixed
issues query for TypeScript 6.0.1 (RC)</a>.</li>
</ul>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
</ul>
<h2>TypeScript 6.0 Beta</h2>
<p>For release notes, check out the <a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-beta/">release
announcement</a>.</p>
<ul>
<li><a
href="https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93&amp;q=milestone%3A%22TypeScript+6.0.0%22+is%3Aclosed+">fixed
issues query for Typescript 6.0.0 (Beta)</a>.</li>
</ul>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1e4744d682"><code>1e4744d</code></a>
Merge branch 'main' into ts7-release</li>
<li><a
href="a5a219c3b5"><code>a5a219c</code></a><code>microsoft/typescript-go#4558</code></li>
<li><a
href="ecfe30dce9"><code>ecfe30d</code></a>
Update status localization</li>
<li><a
href="5de25b5f8f"><code>5de25b5</code></a>
Hide executable name in TypeScript status</li>
<li><a
href="d7ce74a75d"><code>d7ce74a</code></a>
Show bundled TypeScript version for packaged servers</li>
<li><a
href="29be66a607"><code>29be66a</code></a>
Correct TS 7 release version to 7.0.2</li>
<li><a
href="ed2bd1bfa4"><code>ed2bd1b</code></a>
Merge branch 'main' into ts7-release</li>
<li><a
href="887307575c"><code>8873075</code></a>
Bump the github-actions group across 1 directory with 3 updates
(microsoft/ty...</li>
<li><a
href="9427131ae2"><code>9427131</code></a>
Set up stable / nightly extension split, other prep
(microsoft/typescript-go#...</li>
<li><a
href="d4eaca5460"><code>d4eaca5</code></a><code>microsoft/typescript-go#4549</code></li>
<li>Additional commits viewable in <a
href="https://github.com/microsoft/TypeScript/compare/v5.9.3...v7.0.2">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/~microsoft1es">microsoft1es</a>, a new
releaser for typescript 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>
Co-authored-by: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 14:49:05 -07:00
Devin Foley 9fc2f594ae
feat(ui): dashboard banner for paused imported agents with Resume all (#12142)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Company import parks every imported agent as a safety default
> - The only surface that offered to activate them was the post-import
checklist, which is UI-only and gone after a reload or an expired import
job
> - A company whose agents are all paused looks broken: tasks sit still
and the dashboard gives no explanation or fix
> - This pull request adds a dashboard banner for import-paused agents
with a one-click Resume all, and a generic banner when every agent is
paused
> - The benefit is a durable, reload-proof place to understand and fix
the parked state

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The company dashboard for a company whose agents are paused, in
particular after a company import.

**Subsystem affected**

Web UI — dashboard (`ui/src/pages/Dashboard.tsx`).

**Current behavior**

Imported agents arrive paused. The activation checklist on the import
page is the only activation surface and is lost on reload. The dashboard
shows paused counts in a metric card but no explanation and no action.
Tasks assigned to the paused agents never start.

**Proposed behavior**

When any agent carries the `import` pause reason, the dashboard shows a
warning banner ("N imported agents are paused and will not run") with a
**Resume all** action. It resumes each parked agent sequentially,
tolerates per-agent failures, and refreshes so the banner reflects
whatever remains paused. When no import pauses exist but every agent in
the company is paused, a generic all-paused banner links to the agents
page.

**Breaking changes**

None. Depends on the `import` pause reason introduced in #12140 (this
branch is stacked on it).

## What Changed

- New exported helper `derivePausedAgentBanner(agents)` deciding between
the imported banner, the all-paused banner, or none.
- Dashboard renders the banners via the shared `InlineBanner`, with a
sequential `agentsApi.resume` mutation for Resume all and query
invalidation for the agent list and dashboard stats.

## Verification

- `cd ui && npx vitest run src/pages/Dashboard.test.ts` — 4 tests pass
(no agents, imported preference, all-paused fallback, mixed-state null).
- `cd ui && pnpm run typecheck` — clean.
- Manual: import a company package with paused agents, open its
dashboard, click Resume all, and watch the banner clear as agents go
idle.

## Risks

- Low risk. Resume all reuses `POST /agents/:id/resume` with its
existing guards, sequentially, matching the import page's activation
checklist pattern. At current import sizes (tens of agents) this is
fast; a server-side bulk endpoint is the follow-up if imports grow to
hundreds of agents.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic) with extended thinking
and tool use, 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 (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-25 14:09:54 -07:00
Dotta 397de98193
feat(runner): add flagged Codex execution adapter (#12188)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Paperclip Runner now has protocol, provider, tool, package,
persistence, and hidden server boundaries.
> - The server still cannot select that path for a real agent heartbeat.
> - A new runtime must not change any existing direct adapter.
> - An experimental runtime must fail closed when its rollout flag is
off.
> - This pull request adds one guarded Codex vertical slice through
runnerd.
> - The benefit is a production-built runner path that users cannot
start by default.

## Linked Issues or Issue Description

Refs #11962

Refs #12111

Refs #12169

Refs #12176

**Subsystem affected**

Cross-cutting. The change affects the runner package, server
orchestration, shared settings, and adapter configuration UI.

**Problem or motivation**

The hidden PRP coordinator cannot execute a real heartbeat. The
application also needs an explicit rollout boundary before it can expose
the experimental runner. Existing direct adapters must keep their
current execution and finalization behavior.

**Proposed solution**

Add `paperclip_runner` as a Codex-only adapter behind the default-off
`enableNativeRunner` instance flag. Select the native runtime only for
that adapter. Persist the run binding before runnerd starts. Wait for
the durable PRP result and terminal event. Resume the real Codex
provider thread on later heartbeats. Keep persisted native runs readable
and recoverable after the flag changes.

**Alternatives considered**

The server could route `codex_local` through runnerd. That option would
change an existing adapter and weaken rollback safety. The server could
expose all providers now. That option would add unreviewed provider
behavior. The build could depend on a prebuilt runner binary. That
option would make source builds architecture-dependent and difficult to
verify.

**Roadmap alignment**

This work supports the shipped enforced-outcomes, governed-tool, and
self-healing-run milestones. It does not add a new roadmap surface. It
is the guarded execution step after the merged hidden runner boundaries.

**Additional context**

This is the next replacement for the closed large runner pull request.
Task-thread presentation remains a separate follow-up so this change can
preserve the current direct-adapter UI.

## What Changed

- Add `paperclip_runner` as an explicit Codex-only adapter.
- Add the default-off `enableNativeRunner` instance flag.
- Reject fresh create, hire, import, switch, and execution requests
while the flag is off.
- Allow edits to persisted runner agents while the flag is off.
- Recover an already persisted native run even after the flag is
disabled.
- Keep every built-in direct adapter on its existing runtime path.
- Persist an immutable native run binding and revisioned completion
contract before runnerd starts.
- Execute server to PRP to runnerd to Codex to server through the hidden
coordinator.
- Validate the durable result against the terminal event and exact
completion criteria before finalization.
- Preserve the Codex provider thread ID and use `thread/resume` on the
next heartbeat.
- Strip unsupported Codex configuration fields from the experimental
adapter.
- Build a target-native release runner binary from source and vendor it
into the server distribution.
- Install Rust only in the Docker build stage. Do not add a workflow or
lockfile change.
- Stop the runner process group on completion, cancellation, and forced
shutdown.

## Verification

- Run `pnpm --filter @paperclipai/paperclip-runner check:all`. All 69
TypeScript tests and 58 Rust tests pass. Protocol, conformance, replay,
formatting, and generated-file checks pass.
- Run the 12 focused adapter, settings, runtime-selection, coordinator,
direct-isolation, and real Codex integration test files. All 186 tests
pass.
- The real integration test uses PostgreSQL, HTTP, WebSocket, runnerd,
and a fake Codex app server. It proves one `thread/start` followed by
one `thread/resume`.
- Run `pnpm -r typecheck`.
- Run `pnpm build`.
- Run `pnpm check:token-gates`.
- Build the Docker `build` target from a clean context. Confirm that the
server distribution contains an executable `paperclip-runnerd` built
with Debian Rust 1.85.
- Start the server through the source-mode tsx entry point with the
package `dist` directory absent. Confirm the vendor shim resolves source
exports and the server boots.
- Run `pnpm test:run` twice. On this macOS host, 405 files pass and 1
file skips. Eight untouched workspace and loopback tests fail because
macOS resolves `/tmp` and `/var` through `/private` and because
PID-derived test ports exceed 65535. Linux CI must pass the full suite.
- Confirm that the diff contains 52 files. Confirm that it contains no
`.github` or `pnpm-lock.yaml` change.

## Risks

- The feature flag is off by default. A fresh native start fails with a
stable error while the flag is off.
- A persisted native run remains recoverable after the flag changes.
This prevents rollout changes from corrupting recorded work.
- Only local Codex execution is accepted. Other providers and remote
work modes fail closed.
- Existing direct adapters do not start runnerd, create native rows, use
native status arbitration, or enter native finalization.
- The runner receives its one-use bootstrap ticket through the child
environment. The server does not put the ticket in command arguments or
logs.
- The server validates the company, task, agent, run, runner, session,
completion contract, result, and terminal binding before it accepts
completion.
- The build compiles a target-native Rust binary. Cross-platform release
packaging remains a later concern. Source builds and Docker builds
compile for their current target.
- Docker needs enough build memory for the existing server TypeScript
compile. The Docker build stage sets a 4 GB V8 heap limit.

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

## Model Used

- OpenAI Codex with GPT-5. The exact deployment ID and context-window
size are not exposed. The model used agentic reasoning, repository
tools, code execution, and test execution.

## Checklist

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

> - Paperclip is the open source app people use to manage AI agents for
work
> - Chunked company-import transfers are deduplicated by content: a
byte-identical zip that already finished an apply is rejected
> - The rejection said only "this exact package was already imported by
a completed transfer" without saying where that import went
> - Users who could not find the earlier import read the rejection as
data loss and kept retrying, or exported again and created duplicate
companies
> - This pull request makes the declaration response carry the company
the completed apply created, and both clients name it in the error
> - The benefit is that the dedupe rejection now points at the existing
import instead of implying it vanished

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The `alreadyCompleted` rejection when re-declaring a chunked
company-import transfer.

**Subsystem affected**

Shared transfer contract
(`packages/shared/src/company-import-transfer.ts`), transfer declaration
route (`server/src/routes/companies.ts`), web import page, CLI import
command.

**Current behavior**

`POST /api/companies/import/transfers` returns `alreadyCompleted: true`
with no pointer to the earlier import. Web and CLI raise "This exact
package was already imported by a completed transfer. Re-export the
package to import it again."

**Proposed behavior**

The response includes an optional `company` field (`{id, name,
issuePrefix} | null`) resolved from the completed run's company link.
Web and CLI raise a shared message: `… It created the company
"Paperclip" (PAPA) — open it from the company switcher. Re-export the
package to import it again.` A company that was deleted since (or a link
that was never written) degrades to `null` and the original message.

**Breaking changes**

None. The new response field is optional; old clients ignore it.

## What Changed

- `CompanyImportTransferCreated` gains optional `company`, plus a shared
`buildAlreadyImportedMessage` used by both clients.
- The declaration route's `alreadyCompleted` branch resolves the landed
company null-safely via `companyService.getById`.
- Web (`ui/src/pages/CompanyImport.tsx`) and CLI
(`cli/src/commands/client/company.ts`) raise the shared message.

## Verification

- `cd packages/shared && npx vitest run
src/company-import-transfer.test.ts` — 3 tests (named company, id
fallback, no-company original message).
- `cd server && npx vitest run
src/__tests__/company-import-transfer-routes.test.ts` — 24 tests; the
re-declaration test now asserts the company payload and the
deleted-company null path.
- `cd cli && npx vitest run
src/__tests__/company-import-transfer.test.ts` — 17 tests; new test pins
the named-company message.
- `cd ui && npx vitest run src/pages/CompanyImport.test.tsx` — 23 tests.
- `pnpm run typecheck` clean in shared, server, ui, cli.

## Risks

- Low risk. The lookup runs only on the `alreadyCompleted` branch and is
null-safe; the transfer run is already scoped to the requesting actor
(user + instance context in the actor key), so the response never names
a company the caller did not import.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic) with extended thinking
and tool use, 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 (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-25 13:51:50 -07:00
Devin Foley 868e210a95
feat(ui): post-import landing CTAs on every outcome branch (#12143)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Company import ends on one of two success screens: the full outcome,
or a soft-success panel when the job's in-memory result expired before
it could be read
> - The soft-success panel named no company and offered no way in, and
the full outcome never said that paused agents stay resumable after
leaving the page
> - Users on the soft-success path concluded the import vanished and ran
it again, producing duplicate companies
> - This pull request gives every success branch a named landing with a
direct CTA into the new company and a pointer to the paused-agents
banner
> - The benefit is that a finished import always lands the user
somewhere actionable

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The outcome screens of the company import page.

**Subsystem affected**

Web UI — company import (`ui/src/pages/CompanyImport.tsx`).

**Current behavior**

The expired-job branch renders two sentences ("the company has been
added — open it to view it") with no company name and no link. The
full-outcome screen shows the activation checklist but does not say the
checklist's resume actions remain available on the dashboard, so users
treat the page as their only chance.

**Proposed behavior**

The expired branch keeps the landed company's name and dashboard path
when readable, renders an "Open company dashboard" button, and notes
that imported agents arrive paused and can be resumed from the dashboard
banner. When the company is unreadable it gives explicit switcher
guidance instead. The full-outcome screen states that paused items stay
resumable from the dashboard.

**Breaking changes**

None. Pure UI copy/state additions to an existing page.

## What Changed

- The `expired` import outcome now carries `companyName` and
`dashboardPath`, captured from the already-fetched company in
`onSuccess`.
- The expired panel renders the company name, a dashboard CTA
(`data-testid="import-expired-open-company"`), the paused-agents
pointer, and a switcher fallback.
- The full-outcome screen adds a line noting the dashboard offers the
same resume actions as the activation checklist.

## Verification

- `cd ui && npx vitest run src/pages/CompanyImport.test.tsx` — 24 tests
pass; the soft-success test now asserts the name, pointer, and CTA, and
a new test covers the unreadable-company fallback.
- `cd ui && pnpm run typecheck` — clean.

## Risks

- Low risk. The dashboard pointer references the paused-agents banner
shipping in #12142; until that merges the sentence still points at the
dashboard, where paused agents are already visible in the metric card.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic) with extended thinking
and tool use, 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 (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-25 13:51:46 -07:00
Devin Foley 11f6c754c9
feat: dedicated import pause reason with visible paused-assignee notices (#12140)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Company import parks every imported agent as a safety default, and
issue assignment wakes are dropped for paused agents
> - The pause was recorded as the generic reason "system" and was almost
invisible: the chat-style task thread showed nothing, the legacy notice
had no action, and the new-task dialog gave no hint
> - Users assigned tasks in an imported company, nothing ran, and there
was no explanation — the imported company looked broken
> - This pull request records a dedicated "import" pause reason and
makes the paused state visible and fixable where the user is looking
> - The benefit is that a silent no-op becomes an explained state with a
one-click resume

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Working with a company whose agents arrived paused from a company
import.

**Subsystem affected**

Shared constants (`PAUSE_REASONS`), company import service
(`server/src/services/company-portability.ts`), task thread and new-task
dialog UI.

**Current behavior**

Imported agents get `pauseReason: "system"`, the same value
plugin-managed and built-in agent pauses use. Assigning an issue to a
paused agent silently drops the wake. The chat-style task thread renders
no paused notice; the legacy thread's notice says "It was paused by the
system." with no action and only renders when the composer is shown.

**Proposed behavior**

Import writes `pauseReason: "import"`. The paused-assignee notice
explains the import pause, offers an inline "Resume agent" button
(suppressed for budget pauses, which clear on their own), and renders
for read-only viewers. The chat-style task thread shows the same notice
above the composer. The new-task dialog warns when the selected assignee
is paused.

**Breaking changes**

None. `PAUSE_REASONS` is widened, not changed; the column already stores
free-text values in other paths, and every consumer is an equality check
with a manual fallback, so an older client shows the generic fallback
copy for the new value.

## What Changed

- `packages/shared/src/constants.ts`: `"import"` added to
`PAUSE_REASONS`.
- `server/src/services/company-portability.ts`: the import pause patch
writes `pauseReason: "import"`.
- `ui/src/components/IssueChatThread.tsx`: `IssueAssigneePausedNotice`
gains import copy, a Resume button, test ids, and is exported; it now
renders even when the composer is hidden. New `onResumeAssignee` /
`resumeAssigneePending` props.
- `ui/src/components/TaskChatThread.tsx`: renders the paused-assignee
notice above the composer dock (the chat-style thread previously had no
paused surface at all).
- `ui/src/pages/IssueDetail.tsx`: wires a resume mutation
(`agentsApi.resume`) through both thread variants and invalidates the
company agent list.
- `ui/src/components/NewIssueDialog.tsx`: inline note when the chosen
assignee is paused, with import-specific copy.

## Verification

- `cd server && npx vitest run
src/__tests__/company-portability.test.ts` — 82 tests pass (pause pin
updated to `"import"`).
- `cd ui && npx vitest run src/components/IssueChatThread.test.tsx
src/components/NewIssueDialog.test.tsx
src/components/TaskChatThread.test.tsx` — 120 tests pass (new: notice
copy per reason, resume click, budget suppression, active-agent null
render, dialog note).
- `pnpm run typecheck` in `packages/shared`, `server`, and `ui` — clean.

## Risks

- Low risk. The resume action calls the existing `POST
/agents/:id/resume` route with its existing guards. Existing rows keep
`"system"` and fall back to the current generic copy; only new imports
write `"import"`.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic) with extended thinking
and tool use, 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 (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-25 13:51:42 -07:00
Devin Foley d2b9765cc8
feat(ui): offer enabling a skill for agents at install time (#12136)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The company skill library lets operators install skills, and each
agent has its own enabled-skill set
> - Installing a skill only writes the library row; no agent receives
the skill, and the UI says "Skill installed" with no attach step
> - Operators install a skill, ask an agent to use it, and the agent
truthfully reports the skill as not available — the install felt broken
> - This pull request adds an "Enable for agents" step to the install
dialog and enables the skill for the selected agents right after install
> - The benefit is that "install" defaults to a state where agents can
actually use the skill, and the toast is honest when they cannot

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Installing a skill from the catalog in the company Skills page.

**Subsystem affected**

Web UI — company skills catalog install flow
(`ui/src/pages/CompanySkills.tsx`).

**Current behavior**

Install writes a `company_skills` row and shows a "Skill installed"
toast. No agent is enabled for the skill. Agents resolve their skills
from their own desired-skill set, so they report the skill as not
installed. The operator has to find the separate "Add to agent" control
to make the install effective.

**Proposed behavior**

The install dialog shows an "Enable for agents" section for fresh
installs. It pre-selects every agent whose adapter supports skills.
After install, the page enables the skill for each selected agent
(skills sync with mode `add`). The success toast reports how many agents
received the skill, and warns when the skill is in the library with no
agents enabled.

**Breaking changes**

None. Updates and replacements of an existing skill do not show the new
section and behave as before.

## What Changed

- `InstallPreviewDialog` gains an "Enable for agents" section (fresh
installs only) built on the existing `AgentMultiSelect`, with agents
whose adapter lacks skills support disabled.
- New exported helper `defaultInstallAgentSelection` pre-selects every
skills-capable, non-required agent.
- The install mutation enables the skill for each selected agent via
`agentsApi.syncSkills(..., "add")` before invalidating queries, and
reports per-agent failures in a warning toast without failing the
install.
- Toast copy now distinguishes "enabled for N agents" from "in the
library but not enabled for any agent yet".

## Verification

- `cd ui && npx vitest run src/pages/CompanySkills.test.tsx` — 23 tests
pass, including three new ones: default-selection helper, confirm
payload carries the pre-selected agents, update/replace path skips the
section.
- `cd ui && pnpm run typecheck` — clean.
- Manual: install a catalog skill with two agents in the company; both
are pre-selected; after install the skill page lists both under "Used by
agents".

## Risks

- Low risk. Enablement uses the existing per-agent skills sync route
with mode `add`, so concurrent edits to an agent's desired set are not
overwritten. A per-agent sync failure surfaces as a warning toast and
never fails the install itself.

## Model Used

- Claude Fable 5 (`claude-fable-5`, Anthropic) with extended thinking
and tool use, 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 (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-25 13:51:35 -07:00
dependabot[bot] c5382b36ba
build(deps-dev): bump vite from 6.4.3 to 8.2.2 (#11887)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite)
from 6.4.3 to 8.2.2.
<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>plugin-legacy@8.2.2</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/plugin-legacy@8.2.2/packages/plugin-legacy/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.2.2</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.2.2/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>plugin-legacy@8.2.1</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/plugin-legacy@8.2.1/packages/plugin-legacy/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.2.1</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.2.1/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>create-vite@8.2.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/create-vite@8.2.0/packages/create-vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>plugin-legacy@8.2.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/plugin-legacy@8.2.0/packages/plugin-legacy/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.2.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.2.0/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.2.0-beta.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.2.0-beta.0/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.5</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.5/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.4</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.4/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.3</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.3/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.2</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.2/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.1</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.1/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>create-vite@8.1.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/create-vite@8.1.0/packages/create-vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>plugin-legacy@8.1.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/plugin-legacy@8.1.0/packages/plugin-legacy/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.1.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.0/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>plugin-legacy@8.1.0-beta.0</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/plugin-legacy@8.1.0-beta.0/packages/plugin-legacy/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v8.2.1...v8.2.2">8.2.2</a>
(2026-08-20)<!-- raw HTML omitted --></h2>
<h3>Features</h3>
<ul>
<li><strong>deps:</strong> widen <code>@vitejs/devtools</code> peer
range to v0.5.0 (<a
href="https://redirect.github.com/vitejs/vite/issues/23302">#23302</a>)
(<a
href="495d9ff5a7">495d9ff</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>bundled-dev:</strong> handle lazy request error (<a
href="https://redirect.github.com/vitejs/vite/issues/23291">#23291</a>)
(<a
href="3ba026dade">3ba026d</a>)</li>
<li><strong>bundled-dev:</strong> hot update through circular imports
instead of reloading (<a
href="https://redirect.github.com/vitejs/vite/issues/23259">#23259</a>)
(<a
href="3dbddefaaf">3dbddef</a>)</li>
<li><strong>config:</strong> resolve sourcemap paths against sourcemap
location (<a
href="https://redirect.github.com/vitejs/vite/issues/23239">#23239</a>)
(<a
href="05a003e6a1">05a003e</a>)</li>
<li><strong>css:</strong> don't pass empty targets to lightningcss (<a
href="https://redirect.github.com/vitejs/vite/issues/23295">#23295</a>)
(<a
href="2804636ff6">2804636</a>)</li>
<li><strong>define:</strong> fix match escaped dots to support
$-prefixed define keys (<a
href="https://redirect.github.com/vitejs/vite/issues/23249">#23249</a>)
(<a
href="dcf88bd2ad">dcf88bd</a>)</li>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23217">#23217</a>)
(<a
href="ba958bddfc">ba958bd</a>)</li>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/23218">#23218</a>)
(<a
href="83ecb2c805">83ecb2c</a>)</li>
<li><strong>module-runner:</strong> exclude completed modules from
in-flight cycle detection (fix <a
href="https://redirect.github.com/vitejs/vite/issues/22999">#22999</a>)
(<a
href="https://redirect.github.com/vitejs/vite/issues/23009">#23009</a>)
(<a
href="d9b10a98db">d9b10a9</a>)</li>
<li><strong>optimizer:</strong> close custom extension analysis bundles
(<a
href="https://redirect.github.com/vitejs/vite/issues/23207">#23207</a>)
(<a
href="8fb7675283">8fb7675</a>)</li>
<li>reduce Windows 8.3-short-name detection false-positives (<a
href="https://redirect.github.com/vitejs/vite/issues/23066">#23066</a>)
(<a
href="02cffa9e2d">02cffa9</a>)</li>
<li>respect <code>resolve.preserveSymlinks</code> when resolving root
(fix <a
href="https://redirect.github.com/vitejs/vite/issues/23197">#23197</a>)
(<a
href="https://redirect.github.com/vitejs/vite/issues/23198">#23198</a>)
(<a
href="8413052731">8413052</a>)</li>
<li><strong>ssr:</strong> rewrite computed key of destructing parameter
(<a
href="https://redirect.github.com/vitejs/vite/issues/23307">#23307</a>)
(<a
href="9db0b61d4c">9db0b61</a>)</li>
<li><strong>vite:</strong> update outdated upstream file links in
license comments (<a
href="https://redirect.github.com/vitejs/vite/issues/23285">#23285</a>)
(<a
href="c0f2fc607e">c0f2fc6</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong>build:</strong> note cssTarget precedence (<a
href="https://redirect.github.com/vitejs/vite/issues/23200">#23200</a>)
(<a
href="a20a35ec06">a20a35e</a>)</li>
</ul>
<h3>Miscellaneous Chores</h3>
<ul>
<li>fix ts errors in build test cases (<a
href="https://redirect.github.com/vitejs/vite/issues/23209">#23209</a>)
(<a
href="a0cfcf72f8">a0cfcf7</a>)</li>
</ul>
<h3>Code Refactoring</h3>
<ul>
<li>use JSON import attributes instead of readFileSync in constants (<a
href="https://redirect.github.com/vitejs/vite/issues/23258">#23258</a>)
(<a
href="1d9fa392a4">1d9fa39</a>)</li>
<li>use named regex constants over inline literals (<a
href="https://redirect.github.com/vitejs/vite/issues/22964">#22964</a>)
(<a
href="5c1c6c6097">5c1c6c6</a>)</li>
</ul>
<h3>Tests</h3>
<ul>
<li><strong>define:</strong> close rolldown bundler after generate (<a
href="https://redirect.github.com/vitejs/vite/issues/23231">#23231</a>)
(<a
href="b4d66fee14">b4d66fe</a>)</li>
<li><strong>module-runner:</strong> add TLA circular import case (<a
href="https://redirect.github.com/vitejs/vite/issues/23299">#23299</a>)
(<a
href="4a261f2428">4a261f2</a>)</li>
<li><strong>module-runner:</strong> simplify server-hmr tests (<a
href="https://redirect.github.com/vitejs/vite/issues/23300">#23300</a>)
(<a
href="599b44b660">599b44b</a>)</li>
<li><strong>ssr:</strong> add destructing assignment case for
moduleRunnerTransform (<a
href="https://redirect.github.com/vitejs/vite/issues/23308">#23308</a>)
(<a
href="cb77e2a93b">cb77e2a</a>)</li>
</ul>
<h3>Build System</h3>
<ul>
<li>use JSON import attributes instead of readFIleSync in rolldown
configs (<a
href="https://redirect.github.com/vitejs/vite/issues/23251">#23251</a>)
(<a
href="d615bcdb23">d615bcd</a>)</li>
</ul>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v8.2.0...v8.2.1">8.2.1</a>
(2026-08-06)<!-- raw HTML omitted --></h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>build:</strong> make client chunkImportMap work with
<code>sharedPlugins: true</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/23184">#23184</a>)
(<a
href="15f03073c9">15f0307</a>)</li>
<li><strong>bundled-dev:</strong> inject client script tag before chunk
scripts (<a
href="https://redirect.github.com/vitejs/vite/issues/23161">#23161</a>)
(<a
href="eac0cc84aa">eac0cc8</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="de1111ab0b"><code>de1111a</code></a>
release: v8.2.2</li>
<li><a
href="cb77e2a93b"><code>cb77e2a</code></a>
test(ssr): add destructing assignment case for moduleRunnerTransform (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23308">#23308</a>)</li>
<li><a
href="9db0b61d4c"><code>9db0b61</code></a>
fix(ssr): rewrite computed key of destructing parameter (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23307">#23307</a>)</li>
<li><a
href="8413052731"><code>8413052</code></a>
fix: respect <code>resolve.preserveSymlinks</code> when resolving root
(fix <a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23197">#23197</a>)
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23">#23</a>...</li>
<li><a
href="05a003e6a1"><code>05a003e</code></a>
fix(config): resolve sourcemap paths against sourcemap location (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23239">#23239</a>)</li>
<li><a
href="495d9ff5a7"><code>495d9ff</code></a>
feat(deps): widen <code>@vitejs/devtools</code> peer range to v0.5.0 (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23302">#23302</a>)</li>
<li><a
href="1d9fa392a4"><code>1d9fa39</code></a>
refactor: use JSON import attributes instead of readFileSync in
constants (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/2">#2</a>...</li>
<li><a
href="2804636ff6"><code>2804636</code></a>
fix(css): don't pass empty targets to lightningcss (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23295">#23295</a>)</li>
<li><a
href="599b44b660"><code>599b44b</code></a>
test(module-runner): simplify server-hmr tests (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23300">#23300</a>)</li>
<li><a
href="4a261f2428"><code>4a261f2</code></a>
test(module-runner): add TLA circular import case (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/23299">#23299</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite/commits/v8.2.2/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>
Co-authored-by: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 11:08:59 -07:00
dependabot[bot] 0b01593602
build(deps): bump lucide-react from 0.577.0 to 1.32.0 (#11885)
Bumps
[lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react)
from 0.577.0 to 1.32.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/lucide-icons/lucide/releases">lucide-react's
releases</a>.</em></p>
<blockquote>
<h2>Version 1.32.0</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(icons): added <code>car-battery</code> icon by <a
href="https://github.com/andreynaz4renko"><code>@​andreynaz4renko</code></a>
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4088">lucide-icons/lucide#4088</a></li>
<li>fix(categories): fixes emoji.json by <a
href="https://github.com/karsa-mistmere"><code>@​karsa-mistmere</code></a>
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4697">lucide-icons/lucide#4697</a></li>
<li>chore(deps): bump vue from 3.5.40 to 3.5.41 in the vue-deps group
across 1 directory by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4695">lucide-icons/lucide#4695</a></li>
<li>chore(dev): upgrade ESLint to latest compatible stack (v10) by <a
href="https://github.com/ericfennis"><code>@​ericfennis</code></a> with
<a href="https://github.com/Copilot"><code>@​Copilot</code></a> in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4378">lucide-icons/lucide#4378</a></li>
<li>feat(icons): add square-text by <a
href="https://github.com/samuelalake"><code>@​samuelalake</code></a> in
<a
href="https://redirect.github.com/lucide-icons/lucide/pull/4609">lucide-icons/lucide#4609</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/andreynaz4renko"><code>@​andreynaz4renko</code></a>
made their first contribution in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4088">lucide-icons/lucide#4088</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/lucide-icons/lucide/compare/1.31.0...1.32.0">https://github.com/lucide-icons/lucide/compare/1.31.0...1.32.0</a></p>
<h2>Version 1.31.0</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(icons): added <code>mail-badge</code> icon by <a
href="https://github.com/lazerg"><code>@​lazerg</code></a> in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4638">lucide-icons/lucide#4638</a></li>
<li>feat(icons): add angle by <a
href="https://github.com/samuelalake"><code>@​samuelalake</code></a> in
<a
href="https://redirect.github.com/lucide-icons/lucide/pull/4545">lucide-icons/lucide#4545</a></li>
<li>feat(icons): added <code>eject</code> icon by <a
href="https://github.com/ThibautMarechal"><code>@​ThibautMarechal</code></a>
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4043">lucide-icons/lucide#4043</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/lazerg"><code>@​lazerg</code></a> made
their first contribution in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4638">lucide-icons/lucide#4638</a></li>
<li><a
href="https://github.com/ThibautMarechal"><code>@​ThibautMarechal</code></a>
made their first contribution in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4043">lucide-icons/lucide#4043</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/lucide-icons/lucide/compare/1.30.0...1.31.0">https://github.com/lucide-icons/lucide/compare/1.30.0...1.31.0</a></p>
<h2>Version 1.30.0</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(icons): refine &amp; rename various emoji icons by <a
href="https://github.com/karsa-mistmere"><code>@​karsa-mistmere</code></a>
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4606">lucide-icons/lucide#4606</a></li>
<li>fix(scripts): removed toBeRemovedInVersion from all scripts and
tools by <a
href="https://github.com/karsa-mistmere"><code>@​karsa-mistmere</code></a>
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4674">lucide-icons/lucide#4674</a></li>
<li>feat(icons): added <code>audio-lines-x</code> icon by <a
href="https://github.com/karsa-mistmere"><code>@​karsa-mistmere</code></a>
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4590">lucide-icons/lucide#4590</a></li>
<li>feat(lab): added <code>chinese-character</code> icon to lab by <a
href="https://github.com/congemcd"><code>@​congemcd</code></a> in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4212">lucide-icons/lucide#4212</a></li>
<li>test(packages): updates unit test snapshots with
face-slightly-smiling by <a
href="https://github.com/karsa-mistmere"><code>@​karsa-mistmere</code></a>
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4676">lucide-icons/lucide#4676</a></li>
<li>ci(dev): fix post-release job by downloading lucide-font artifact by
name by <a
href="https://github.com/ericfennis"><code>@​ericfennis</code></a> with
<a href="https://github.com/Copilot"><code>@​Copilot</code></a> in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4675">lucide-icons/lucide#4675</a></li>
<li>feat(icons): add shield-lock icon by <a
href="https://github.com/Caisere"><code>@​Caisere</code></a> in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/3508">lucide-icons/lucide#3508</a></li>
<li>ci(security): Pin sha actions by <a
href="https://github.com/jguddas"><code>@​jguddas</code></a> in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4678">lucide-icons/lucide#4678</a></li>
<li>feat(icons): added <code>broom</code> icon by <a
href="https://github.com/karsa-mistmere"><code>@​karsa-mistmere</code></a>
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4683">lucide-icons/lucide#4683</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/congemcd"><code>@​congemcd</code></a>
made their first contribution in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4212">lucide-icons/lucide#4212</a></li>
<li><a href="https://github.com/Caisere"><code>@​Caisere</code></a> made
their first contribution in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/3508">lucide-icons/lucide#3508</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/lucide-icons/lucide/compare/1.29.0...1.30.0">https://github.com/lucide-icons/lucide/compare/1.29.0...1.30.0</a></p>
<h2>Version 1.29.0</h2>
<h2>What's Changed</h2>
<ul>
<li>fix(<code>@​lucide/lab</code>): Fix lab build by <a
href="https://github.com/ericfennis"><code>@​ericfennis</code></a> in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4618">lucide-icons/lucide#4618</a></li>
<li>feat(copilot): remove incorrect spaces clause from copilot
instructions by <a
href="https://github.com/karsa-mistmere"><code>@​karsa-mistmere</code></a>
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4623">lucide-icons/lucide#4623</a></li>
<li>feat(docs): remove Super and Noodle from showcase by <a
href="https://github.com/karsa-mistmere"><code>@​karsa-mistmere</code></a>
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4626">lucide-icons/lucide#4626</a></li>
<li>chore(deps-dev): bump <code>@​angular/platform-server</code> from
21.2.18 to 21.2.19 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4629">lucide-icons/lucide#4629</a></li>
<li>ci(security): improve security with adding permissions by <a
href="https://github.com/ericfennis"><code>@​ericfennis</code></a> in <a
href="https://redirect.github.com/lucide-icons/lucide/pull/4619">lucide-icons/lucide#4619</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="75b55160aa"><code>75b5516</code></a>
chore(dev): upgrade ESLint to latest compatible stack (v10) (<a
href="https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react/issues/4378">#4378</a>)</li>
<li><a
href="0f8d48b266"><code>0f8d48b</code></a>
test(packages): updates unit test snapshots with face-slightly-smiling
(<a
href="https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react/issues/4676">#4676</a>)</li>
<li><a
href="f229f83f0c"><code>f229f83</code></a>
chore(depedencies): Update dependencies (<a
href="https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react/issues/4553">#4553</a>)</li>
<li><a
href="5ff536e139"><code>5ff536e</code></a>
ci(release.yml): Fix workflow and remove <code>version</code> scripts in
package scripts...</li>
<li><a
href="07c885e6c1"><code>07c885e</code></a>
fix(docs): fix zephyr-cloud URL in readmes</li>
<li><a
href="50d8af5a10"><code>50d8af5</code></a>
docs(readme): Update readme files (<a
href="https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react/issues/4320">#4320</a>)</li>
<li><a
href="653e44b832"><code>653e44b</code></a>
feat(packages): use .mjs for ESM bundles (<a
href="https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react/issues/4285">#4285</a>)</li>
<li><a
href="7623e23f78"><code>7623e23</code></a>
feat(docs): add Zephyr Cloud to Hero Backers tier &amp; rework
updateSponsors scr...</li>
<li><a
href="dada0a8297"><code>dada0a8</code></a>
fix(lucide-react): Fix dynamic imports (<a
href="https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react/issues/4210">#4210</a>)</li>
<li><a
href="a6e648a66f"><code>a6e648a</code></a>
fix(lucide-react): correct client directives in RSC files (<a
href="https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react/issues/4189">#4189</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/lucide-icons/lucide/commits/1.32.0/packages/lucide-react">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: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-25 10:54:14 -07:00
dependabot[bot] f64123ba4b
build(deps-dev): bump @storybook/addon-docs from 10.5.8 to 10.5.10 (#11869)
Bumps
[@storybook/addon-docs](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/docs)
from 10.5.8 to 10.5.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/releases">@​storybook/addon-docs's
releases</a>.</em></p>
<blockquote>
<h2>v10.5.10</h2>
<h2>10.5.10</h2>
<ul>
<li>Core: Fetch static open-service snapshots relative to the document -
<a
href="https://redirect.github.com/storybookjs/storybook/pull/35945">#35945</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Pin oxc-resolver to 11.21.2 to keep tsconfig path aliases on
solution-style tsconfigs - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35929">#35929</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Dependencies: Bump Vitest to 4.1.6 (CVE-2026-47428) - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35530">#35530</a>,
thanks <a
href="https://github.com/anupamme"><code>@​anupamme</code></a>!</li>
<li>Docs: Declare the font on overlay surfaces so docs tooltips are not
left to inherit - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35966">#35966</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>ESLint Plugin: Bundle CSF helpers so the plugin loads without
storybook - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35950">#35950</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>React: Preserve discriminated union prop values in metadata
extraction - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35844">#35844</a>,
thanks <a
href="https://github.com/s-robertson"><code>@​s-robertson</code></a>!</li>
</ul>
<h2>v10.5.9</h2>
<h2>10.5.9</h2>
<ul>
<li>Addon-Pseudo-States: Fix pseudo-states rewriting for nested
functional selectors - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34318">#34318</a>,
thanks <a
href="https://github.com/filipw01"><code>@​filipw01</code></a>!</li>
<li>Core: Skip module-graph reverse-index mirror when a patch is a no-op
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35825">#35825</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Core: Split module-graph into hot revisions and cold index services
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35831">#35831</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Preview: Fix crash when initialising UrlStore on a docs path - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35521">#35521</a>,
thanks <a
href="https://github.com/TheSeydiCharyyev"><code>@​TheSeydiCharyyev</code></a>!</li>
<li>Pseudo-States: Make stylesheet rewrites WebKit-safe - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35629">#35629</a>,
thanks <a
href="https://github.com/ethriel3695"><code>@​ethriel3695</code></a>!</li>
<li>TanStack: Keep the layout id when cloning a standalone index file
route - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35660">#35660</a>,
thanks <a
href="https://github.com/Insik-Han"><code>@​Insik-Han</code></a>!</li>
<li>TanStack: Render real link hrefs in the Link mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35505">#35505</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>Webpack: Prevent long preview output filenames - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35533">#35533</a>,
thanks <a
href="https://github.com/zhangli091011"><code>@​zhangli091011</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md">@​storybook/addon-docs's
changelog</a>.</em></p>
<blockquote>
<h2>10.5.10</h2>
<ul>
<li>Core: Fetch static open-service snapshots relative to the document -
<a
href="https://redirect.github.com/storybookjs/storybook/pull/35945">#35945</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Pin oxc-resolver to 11.21.2 to keep tsconfig path aliases on
solution-style tsconfigs - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35929">#35929</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Dependencies: Bump Vitest to 4.1.6 (CVE-2026-47428) - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35530">#35530</a>,
thanks <a
href="https://github.com/anupamme"><code>@​anupamme</code></a>!</li>
<li>Docs: Declare the font on overlay surfaces so docs tooltips are not
left to inherit - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35966">#35966</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>ESLint Plugin: Bundle CSF helpers so the plugin loads without
storybook - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35950">#35950</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>React: Preserve discriminated union prop values in metadata
extraction - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35844">#35844</a>,
thanks <a
href="https://github.com/s-robertson"><code>@​s-robertson</code></a>!</li>
</ul>
<h2>10.5.9</h2>
<ul>
<li>Addon-Pseudo-States: Fix pseudo-states rewriting for nested
functional selectors - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34318">#34318</a>,
thanks <a
href="https://github.com/filipw01"><code>@​filipw01</code></a>!</li>
<li>Core: Skip module-graph reverse-index mirror when a patch is a no-op
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35825">#35825</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Core: Split module-graph into hot revisions and cold index services
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35831">#35831</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Preview: Fix crash when initialising UrlStore on a docs path - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35521">#35521</a>,
thanks <a
href="https://github.com/TheSeydiCharyyev"><code>@​TheSeydiCharyyev</code></a>!</li>
<li>Pseudo-States: Make stylesheet rewrites WebKit-safe - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35629">#35629</a>,
thanks <a
href="https://github.com/ethriel3695"><code>@​ethriel3695</code></a>!</li>
<li>TanStack: Keep the layout id when cloning a standalone index file
route - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35660">#35660</a>,
thanks <a
href="https://github.com/Insik-Han"><code>@​Insik-Han</code></a>!</li>
<li>TanStack: Render real link hrefs in the Link mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35505">#35505</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>Webpack: Prevent long preview output filenames - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35533">#35533</a>,
thanks <a
href="https://github.com/zhangli091011"><code>@​zhangli091011</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a2db7526e1"><code>a2db752</code></a>
Bump version from &quot;10.5.9&quot; to &quot;10.5.10&quot; [skip
ci]</li>
<li><a
href="8f56104894"><code>8f56104</code></a>
Bump version from &quot;10.5.8&quot; to &quot;10.5.9&quot; [skip
ci]</li>
<li>See full diff in <a
href="https://github.com/storybookjs/storybook/commits/v10.5.10/code/addons/docs">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-25 10:29:14 -07:00
dependabot[bot] 0286854db5
build(deps-dev): bump @storybook/react-vite from 10.5.8 to 10.5.10 (#11868)
Bumps
[@storybook/react-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite)
from 10.5.8 to 10.5.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/releases">@​storybook/react-vite's
releases</a>.</em></p>
<blockquote>
<h2>v10.5.10</h2>
<h2>10.5.10</h2>
<ul>
<li>Core: Fetch static open-service snapshots relative to the document -
<a
href="https://redirect.github.com/storybookjs/storybook/pull/35945">#35945</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Pin oxc-resolver to 11.21.2 to keep tsconfig path aliases on
solution-style tsconfigs - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35929">#35929</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Dependencies: Bump Vitest to 4.1.6 (CVE-2026-47428) - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35530">#35530</a>,
thanks <a
href="https://github.com/anupamme"><code>@​anupamme</code></a>!</li>
<li>Docs: Declare the font on overlay surfaces so docs tooltips are not
left to inherit - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35966">#35966</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>ESLint Plugin: Bundle CSF helpers so the plugin loads without
storybook - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35950">#35950</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>React: Preserve discriminated union prop values in metadata
extraction - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35844">#35844</a>,
thanks <a
href="https://github.com/s-robertson"><code>@​s-robertson</code></a>!</li>
</ul>
<h2>v10.5.9</h2>
<h2>10.5.9</h2>
<ul>
<li>Addon-Pseudo-States: Fix pseudo-states rewriting for nested
functional selectors - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34318">#34318</a>,
thanks <a
href="https://github.com/filipw01"><code>@​filipw01</code></a>!</li>
<li>Core: Skip module-graph reverse-index mirror when a patch is a no-op
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35825">#35825</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Core: Split module-graph into hot revisions and cold index services
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35831">#35831</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Preview: Fix crash when initialising UrlStore on a docs path - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35521">#35521</a>,
thanks <a
href="https://github.com/TheSeydiCharyyev"><code>@​TheSeydiCharyyev</code></a>!</li>
<li>Pseudo-States: Make stylesheet rewrites WebKit-safe - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35629">#35629</a>,
thanks <a
href="https://github.com/ethriel3695"><code>@​ethriel3695</code></a>!</li>
<li>TanStack: Keep the layout id when cloning a standalone index file
route - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35660">#35660</a>,
thanks <a
href="https://github.com/Insik-Han"><code>@​Insik-Han</code></a>!</li>
<li>TanStack: Render real link hrefs in the Link mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35505">#35505</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>Webpack: Prevent long preview output filenames - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35533">#35533</a>,
thanks <a
href="https://github.com/zhangli091011"><code>@​zhangli091011</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md">@​storybook/react-vite's
changelog</a>.</em></p>
<blockquote>
<h2>10.5.10</h2>
<ul>
<li>Core: Fetch static open-service snapshots relative to the document -
<a
href="https://redirect.github.com/storybookjs/storybook/pull/35945">#35945</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Pin oxc-resolver to 11.21.2 to keep tsconfig path aliases on
solution-style tsconfigs - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35929">#35929</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Dependencies: Bump Vitest to 4.1.6 (CVE-2026-47428) - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35530">#35530</a>,
thanks <a
href="https://github.com/anupamme"><code>@​anupamme</code></a>!</li>
<li>Docs: Declare the font on overlay surfaces so docs tooltips are not
left to inherit - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35966">#35966</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>ESLint Plugin: Bundle CSF helpers so the plugin loads without
storybook - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35950">#35950</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>React: Preserve discriminated union prop values in metadata
extraction - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35844">#35844</a>,
thanks <a
href="https://github.com/s-robertson"><code>@​s-robertson</code></a>!</li>
</ul>
<h2>10.5.9</h2>
<ul>
<li>Addon-Pseudo-States: Fix pseudo-states rewriting for nested
functional selectors - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34318">#34318</a>,
thanks <a
href="https://github.com/filipw01"><code>@​filipw01</code></a>!</li>
<li>Core: Skip module-graph reverse-index mirror when a patch is a no-op
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35825">#35825</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Core: Split module-graph into hot revisions and cold index services
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35831">#35831</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Preview: Fix crash when initialising UrlStore on a docs path - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35521">#35521</a>,
thanks <a
href="https://github.com/TheSeydiCharyyev"><code>@​TheSeydiCharyyev</code></a>!</li>
<li>Pseudo-States: Make stylesheet rewrites WebKit-safe - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35629">#35629</a>,
thanks <a
href="https://github.com/ethriel3695"><code>@​ethriel3695</code></a>!</li>
<li>TanStack: Keep the layout id when cloning a standalone index file
route - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35660">#35660</a>,
thanks <a
href="https://github.com/Insik-Han"><code>@​Insik-Han</code></a>!</li>
<li>TanStack: Render real link hrefs in the Link mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35505">#35505</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>Webpack: Prevent long preview output filenames - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35533">#35533</a>,
thanks <a
href="https://github.com/zhangli091011"><code>@​zhangli091011</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a2db7526e1"><code>a2db752</code></a>
Bump version from &quot;10.5.9&quot; to &quot;10.5.10&quot; [skip
ci]</li>
<li><a
href="8f56104894"><code>8f56104</code></a>
Bump version from &quot;10.5.8&quot; to &quot;10.5.9&quot; [skip
ci]</li>
<li>See full diff in <a
href="https://github.com/storybookjs/storybook/commits/v10.5.10/code/frameworks/react-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-25 09:55:45 -07:00
dependabot[bot] 3c328a7726
build(deps): bump @mdxeditor/editor from 3.55.0 to 4.2.1 (#11870)
Bumps [@mdxeditor/editor](https://github.com/mdx-editor/editor) from
3.55.0 to 4.2.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mdx-editor/editor/releases">@​mdxeditor/editor's
releases</a>.</em></p>
<blockquote>
<h2>v4.2.1</h2>
<h2><a
href="https://github.com/mdx-editor/editor/compare/v4.2.0...v4.2.1">4.2.1</a>
(2026-08-21)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>upgrade js-yaml to 4.3.1 to resolve GHSA-5p4m-2wfm-xmqj (<a
href="3ff7296ffd">3ff7296</a>)</li>
</ul>
<h2>v4.2.0</h2>
<h1><a
href="https://github.com/mdx-editor/editor/compare/v4.1.1...v4.2.0">4.2.0</a>
(2026-08-02)</h1>
<h3>Bug Fixes</h3>
<ul>
<li>declare the frontmatter node as a block-level decorator (<a
href="ebc4755212">ebc4755</a>),
closes <a
href="https://redirect.github.com/mdx-editor/editor/issues/957">#957</a></li>
<li>support links on selected images (<a
href="d8c442b69f">d8c442b</a>),
closes <a
href="https://redirect.github.com/mdx-editor/editor/issues/753">#753</a></li>
</ul>
<h3>Features</h3>
<ul>
<li>mdxeditor-full-height opt-in class for editors that fill their
parent (<a
href="cdeda5847e">cdeda58</a>),
closes <a
href="https://redirect.github.com/mdx-editor/editor/issues/953">#953</a></li>
</ul>
<h2>v4.1.1</h2>
<h2><a
href="https://github.com/mdx-editor/editor/compare/v4.1.0...v4.1.1">4.1.1</a>
(2026-07-29)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>clear resolvable security audit findings in the dev dependency tree
(<a
href="117dd84987">117dd84</a>)</li>
<li>respect configured heading shortcuts (<a
href="beacb4c3c2">beacb4c</a>)</li>
</ul>
<h2>v4.1.0</h2>
<h1><a
href="https://github.com/mdx-editor/editor/compare/v4.0.4...v4.1.0">4.1.0</a>
(2026-07-19)</h1>
<h3>Bug Fixes</h3>
<ul>
<li>harden Lexical adoption lifecycle edges (<a
href="859bf459af">859bf45</a>)</li>
<li>pass Playwright install flags through npm (<a
href="0b2d19b3bf">0b2d19b</a>)</li>
<li>remove stray Realm provider token (<a
href="c432da3a83">c432da3</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li>adopt Lexical 0.48 with compatibility gates (<a
href="90a1466d5e">90a1466</a>)</li>
<li>export Markdown from the active selection (<a
href="a7c3baee1c">a7c3bae</a>)</li>
<li>make search replacement state-backed (<a
href="b108652c55">b108652</a>)</li>
</ul>
<h2>v4.0.4</h2>
<h2><a
href="https://github.com/mdx-editor/editor/compare/v4.0.3...v4.0.4">4.0.4</a>
(2026-06-18)</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3ff7296ffd"><code>3ff7296</code></a>
fix: upgrade js-yaml to 4.3.1 to resolve GHSA-5p4m-2wfm-xmqj</li>
<li><a
href="b5bc01b2c9"><code>b5bc01b</code></a>
Merge pull request <a
href="https://redirect.github.com/mdx-editor/editor/issues/959">#959</a>
from alexander-neuschl-tu-dresden-de/patch-1</li>
<li><a
href="b607c8ce2d"><code>b607c8c</code></a>
Update package-lock.json for js-yaml 4.3.1</li>
<li><a
href="dda0c61c0b"><code>dda0c61</code></a>
Upgrade js-yaml to 4.3.1 to resolve high vulnerability</li>
<li><a
href="d8c442b69f"><code>d8c442b</code></a>
fix: support links on selected images</li>
<li><a
href="cdeda5847e"><code>cdeda58</code></a>
feat: mdxeditor-full-height opt-in class for editors that fill their
parent</li>
<li><a
href="ebc4755212"><code>ebc4755</code></a>
fix: declare the frontmatter node as a block-level decorator</li>
<li><a
href="117dd84987"><code>117dd84</code></a>
fix: clear resolvable security audit findings in the dev dependency
tree</li>
<li><a
href="88b7545e5d"><code>88b7545</code></a>
Merge branch 'pr-954'</li>
<li><a
href="2b1af77dff"><code>2b1af77</code></a>
Merge pull request <a
href="https://redirect.github.com/mdx-editor/editor/issues/952">#952</a>
from 11suixing11/fix/allowed-heading-shortcuts</li>
<li>Additional commits viewable in <a
href="https://github.com/mdx-editor/editor/compare/v3.55.0...v4.2.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-25 09:46:10 -07:00
dependabot[bot] a68af9ed9c
build(deps-dev): bump @storybook/addon-a11y from 10.5.8 to 10.5.10 (#11874)
Bumps
[@storybook/addon-a11y](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y)
from 10.5.8 to 10.5.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/releases">@​storybook/addon-a11y's
releases</a>.</em></p>
<blockquote>
<h2>v10.5.10</h2>
<h2>10.5.10</h2>
<ul>
<li>Core: Fetch static open-service snapshots relative to the document -
<a
href="https://redirect.github.com/storybookjs/storybook/pull/35945">#35945</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Pin oxc-resolver to 11.21.2 to keep tsconfig path aliases on
solution-style tsconfigs - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35929">#35929</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Dependencies: Bump Vitest to 4.1.6 (CVE-2026-47428) - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35530">#35530</a>,
thanks <a
href="https://github.com/anupamme"><code>@​anupamme</code></a>!</li>
<li>Docs: Declare the font on overlay surfaces so docs tooltips are not
left to inherit - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35966">#35966</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>ESLint Plugin: Bundle CSF helpers so the plugin loads without
storybook - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35950">#35950</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>React: Preserve discriminated union prop values in metadata
extraction - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35844">#35844</a>,
thanks <a
href="https://github.com/s-robertson"><code>@​s-robertson</code></a>!</li>
</ul>
<h2>v10.5.9</h2>
<h2>10.5.9</h2>
<ul>
<li>Addon-Pseudo-States: Fix pseudo-states rewriting for nested
functional selectors - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34318">#34318</a>,
thanks <a
href="https://github.com/filipw01"><code>@​filipw01</code></a>!</li>
<li>Core: Skip module-graph reverse-index mirror when a patch is a no-op
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35825">#35825</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Core: Split module-graph into hot revisions and cold index services
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35831">#35831</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Preview: Fix crash when initialising UrlStore on a docs path - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35521">#35521</a>,
thanks <a
href="https://github.com/TheSeydiCharyyev"><code>@​TheSeydiCharyyev</code></a>!</li>
<li>Pseudo-States: Make stylesheet rewrites WebKit-safe - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35629">#35629</a>,
thanks <a
href="https://github.com/ethriel3695"><code>@​ethriel3695</code></a>!</li>
<li>TanStack: Keep the layout id when cloning a standalone index file
route - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35660">#35660</a>,
thanks <a
href="https://github.com/Insik-Han"><code>@​Insik-Han</code></a>!</li>
<li>TanStack: Render real link hrefs in the Link mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35505">#35505</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>Webpack: Prevent long preview output filenames - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35533">#35533</a>,
thanks <a
href="https://github.com/zhangli091011"><code>@​zhangli091011</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md">@​storybook/addon-a11y's
changelog</a>.</em></p>
<blockquote>
<h2>10.5.10</h2>
<ul>
<li>Core: Fetch static open-service snapshots relative to the document -
<a
href="https://redirect.github.com/storybookjs/storybook/pull/35945">#35945</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Pin oxc-resolver to 11.21.2 to keep tsconfig path aliases on
solution-style tsconfigs - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35929">#35929</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Dependencies: Bump Vitest to 4.1.6 (CVE-2026-47428) - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35530">#35530</a>,
thanks <a
href="https://github.com/anupamme"><code>@​anupamme</code></a>!</li>
<li>Docs: Declare the font on overlay surfaces so docs tooltips are not
left to inherit - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35966">#35966</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>ESLint Plugin: Bundle CSF helpers so the plugin loads without
storybook - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35950">#35950</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>React: Preserve discriminated union prop values in metadata
extraction - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35844">#35844</a>,
thanks <a
href="https://github.com/s-robertson"><code>@​s-robertson</code></a>!</li>
</ul>
<h2>10.5.9</h2>
<ul>
<li>Addon-Pseudo-States: Fix pseudo-states rewriting for nested
functional selectors - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34318">#34318</a>,
thanks <a
href="https://github.com/filipw01"><code>@​filipw01</code></a>!</li>
<li>Core: Skip module-graph reverse-index mirror when a patch is a no-op
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35825">#35825</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Core: Split module-graph into hot revisions and cold index services
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35831">#35831</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Preview: Fix crash when initialising UrlStore on a docs path - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35521">#35521</a>,
thanks <a
href="https://github.com/TheSeydiCharyyev"><code>@​TheSeydiCharyyev</code></a>!</li>
<li>Pseudo-States: Make stylesheet rewrites WebKit-safe - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35629">#35629</a>,
thanks <a
href="https://github.com/ethriel3695"><code>@​ethriel3695</code></a>!</li>
<li>TanStack: Keep the layout id when cloning a standalone index file
route - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35660">#35660</a>,
thanks <a
href="https://github.com/Insik-Han"><code>@​Insik-Han</code></a>!</li>
<li>TanStack: Render real link hrefs in the Link mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35505">#35505</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>Webpack: Prevent long preview output filenames - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35533">#35533</a>,
thanks <a
href="https://github.com/zhangli091011"><code>@​zhangli091011</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a2db7526e1"><code>a2db752</code></a>
Bump version from &quot;10.5.9&quot; to &quot;10.5.10&quot; [skip
ci]</li>
<li><a
href="8f56104894"><code>8f56104</code></a>
Bump version from &quot;10.5.8&quot; to &quot;10.5.9&quot; [skip
ci]</li>
<li>See full diff in <a
href="https://github.com/storybookjs/storybook/commits/v10.5.10/code/addons/a11y">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-25 09:39:41 -07:00
dependabot[bot] 41726ae279
build(deps): bump react-resizable-panels from 4.12.2 to 4.12.3 (#11872)
Bumps
[react-resizable-panels](https://github.com/bvaughn/react-resizable-panels)
from 4.12.2 to 4.12.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/bvaughn/react-resizable-panels/releases">react-resizable-panels's
releases</a>.</em></p>
<blockquote>
<h2>4.12.3</h2>
<ul>
<li><a
href="https://redirect.github.com/bvaughn/react-resizable-panels/pull/730">730</a>:
Guard <code>CSSStyleSheet</code> construction to avoid throwing in
unsupported environments (<a
href="https://github.com/leo-yang-qiong"><code>@​leo-yang-qiong</code></a>)</li>
<li><a
href="https://redirect.github.com/bvaughn/react-resizable-panels/pull/736">736</a>:
Bugfix: Derived Panel constraints equality check</li>
<li><a
href="https://redirect.github.com/bvaughn/react-resizable-panels/pull/732">732</a>:
Bugfix: Prevent orphaned groups in &quot;pointerup&quot; edge case (<a
href="https://github.com/waterWang"><code>@​waterWang</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/bvaughn/react-resizable-panels/blob/main/CHANGELOG.md">react-resizable-panels's
changelog</a>.</em></p>
<blockquote>
<h2>4.12.3</h2>
<ul>
<li><a
href="https://redirect.github.com/bvaughn/react-resizable-panels/pull/730">730</a>:
Guard <code>CSSStyleSheet</code> construction to avoid throwing in
unsupported environments (<a
href="https://github.com/leo-yang-qiong"><code>@​leo-yang-qiong</code></a>)</li>
<li><a
href="https://redirect.github.com/bvaughn/react-resizable-panels/pull/736">736</a>:
Bugfix: Derived Panel constraints equality check</li>
<li><a
href="https://redirect.github.com/bvaughn/react-resizable-panels/pull/732">732</a>:
Bugfix: Prevent orphaned groups in &quot;pointerup&quot; edge case (<a
href="https://github.com/waterWang"><code>@​waterWang</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f9c422714a"><code>f9c4227</code></a>
4.12.2 -&gt; 4.12.3</li>
<li><a
href="30503d1dad"><code>30503d1</code></a>
Fix derived Panel constraints equality check (<a
href="https://redirect.github.com/bvaughn/react-resizable-panels/issues/736">#736</a>)</li>
<li><a
href="30aef6a448"><code>30aef6a</code></a>
Pending CHANGELOG</li>
<li><a
href="b1d574e504"><code>b1d574e</code></a>
fix: guard CSSStyleSheet construction with adoptedStyleSheets check (<a
href="https://redirect.github.com/bvaughn/react-resizable-panels/issues/730">#730</a>)</li>
<li><a
href="6649f42e56"><code>6649f42</code></a>
fix: don't resurrect stale group entries on pointer-up commit (Fixes <a
href="https://redirect.github.com/bvaughn/react-resizable-panels/issues/729">#729</a>)
(#...</li>
<li>See full diff in <a
href="https://github.com/bvaughn/react-resizable-panels/compare/4.12.2...4.12.3">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-25 09:24:39 -07:00
dependabot[bot] 50e324638c
build(deps): bump @assistant-ui/react from 0.15.14 to 0.15.16 (#11888)
Bumps
[@assistant-ui/react](https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react)
from 0.15.14 to 0.15.16.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/assistant-ui/assistant-ui/releases">@​assistant-ui/react's
releases</a>.</em></p>
<blockquote>
<h2><code>@​assistant-ui/react</code><a
href="https://github.com/0"><code>@​0</code></a>.15.16</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6136">#6136</a>
<a
href="f7bd2d9392"><code>f7bd2d9</code></a>
- fix: keep DevTools updates flowing when a subscriber throws (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6055">#6055</a>
<a
href="1f3eaa7789"><code>1f3eaa7</code></a>
- fix: contain SandboxHost render failures after teardown (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6110">#6110</a>
<a
href="48f95b1442"><code>48f95b1</code></a>
- chore: delete the dead <code>ensureBinding</code> and
<code>useRuntimeState</code> utilities (<a
href="https://github.com/samdickson22"><code>@​samdickson22</code></a>)</p>
<p><code>src/context/react/utils/ensureBinding.ts</code> and
<code>src/context/react/utils/useRuntimeState.ts</code> imported only
each other. Nothing
else in the repo referenced them, neither appears in the package barrel
or the
api-surface snapshot, and the <code>&quot;.&quot;</code>-only exports
map made them unreachable to
consumers. <code>ensureBinding</code> was an external caller of
<code>__internal_bindMethods</code>
that no longer had a caller of its own; the runtime classes bind
themselves in
their constructors, so nothing changes at runtime. The public API
surface is
unchanged and every other emitted file is byte-identical.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6156">#6156</a>
<a
href="9c65b511bc"><code>9c65b51</code></a>
- deprecate leftover Primitive.If and Empty wrappers on react-native and
react-ink, and point them at AuiIf (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
<p>ThreadIf now reads <code>thread.isEmpty</code> instead of
<code>messages.length === 0</code>, matching the loading-aware field
already used by ThreadEmpty and AuiIf. First-party examples and docs
samples that still called the leftover wrappers now use
<code>AuiIf</code> directly.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6084">#6084</a>
<a
href="ca9e72ce85"><code>ca9e72c</code></a>
- fix: resync trigger popover cursor after selection (<a
href="https://github.com/apps/rupic-app"><code>@​rupic-app</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6054">#6054</a>
<a
href="59e9a0881c"><code>59e9a08</code></a>
- fix: handle rejected asynchronous Markdown exports (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6098">#6098</a>
<a
href="b9b9dad28a"><code>b9b9dad</code></a>
- fix: drain unrevealed smooth text when a message completes before any
frame (<a
href="https://github.com/apps/rupic-app"><code>@​rupic-app</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6061">#6061</a>
<a
href="75dfbe3a2b"><code>75dfbe3</code></a>
- docs: document Escape-to-stop-speaking on ThreadPrimitive.Root (<a
href="https://github.com/samdickson22"><code>@​samdickson22</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6124">#6124</a>
<a
href="06b04a7976"><code>06b04a7</code></a>
- chore: update dependencies (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="fa309156e0"><code>fa30915</code></a>,
<a
href="b355aefbe2"><code>b355aef</code></a>,
<a
href="f7bd2d9392"><code>f7bd2d9</code></a>,
<a
href="4947ef4f9b"><code>4947ef4</code></a>,
<a
href="332f736e64"><code>332f736</code></a>,
<a
href="ef9254d5b2"><code>ef9254d</code></a>,
<a
href="9c65b511bc"><code>9c65b51</code></a>,
<a
href="5845ba7c56"><code>5845ba7</code></a>,
<a
href="1b30bfdaba"><code>1b30bfd</code></a>,
<a
href="365e763928"><code>365e763</code></a>,
<a
href="d19921d373"><code>d19921d</code></a>,
<a
href="996aa5723c"><code>996aa57</code></a>,
<a
href="21d6e87dc2"><code>21d6e87</code></a>,
<a
href="cd247e557b"><code>cd247e5</code></a>,
<a
href="f2b3ef8b63"><code>f2b3ef8</code></a>,
<a
href="1bf263ba20"><code>1bf263b</code></a>,
<a
href="19e52c4012"><code>19e52c4</code></a>,
<a
href="06b04a7976"><code>06b04a7</code></a>,
<a
href="a614b5e44d"><code>a614b5e</code></a>,
<a
href="07b51dbbc7"><code>07b51db</code></a>,
<a
href="92e52bd2c9"><code>92e52bd</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.15</li>
<li><code>@​assistant-ui/tap</code><a
href="https://github.com/0"><code>@​0</code></a>.9.14</li>
<li>assistant-stream@0.3.39</li>
</ul>
</li>
</ul>
<h2><code>@​assistant-ui/react</code><a
href="https://github.com/0"><code>@​0</code></a>.15.15</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6071">#6071</a>
<a
href="c3fd447f23"><code>c3fd447</code></a>
- feat: host assistant-cloud thread lists on AISDKThreads via
RemoteThreadList (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
<p>AISDKThreads({ cloud }) uses RemoteThreadList and remounts each
thread like useChatRuntime. Cloud history withFormat resolves
persistence per call so one adapter can serve many threads.
useExternalHistory waits for threadListItem.remoteId instead of latching
on the first empty paint.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5872">#5872</a>
<a
href="f9529bfdea"><code>f9529bf</code></a>
- feat: move useAssistantTransportRuntime into core/react (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5872">#5872</a>
<a
href="f9529bfdea"><code>f9529bf</code></a>
- fix: persist data message parts in aui/v0 cloud history (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5839">#5839</a>
<a
href="24a1af7607"><code>24a1af7</code></a>
- fix: validate MCP App resource responses (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5817">#5817</a>
<a
href="dab7b7af71"><code>dab7b7a</code></a>
- fix: dispose sandbox frames when bridge setup fails (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/assistant-ui/assistant-ui/blob/main/packages/react/CHANGELOG.md">@​assistant-ui/react's
changelog</a>.</em></p>
<blockquote>
<h2>0.15.16</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6136">#6136</a>
<a
href="f7bd2d9392"><code>f7bd2d9</code></a>
- fix: keep DevTools updates flowing when a subscriber throws (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6055">#6055</a>
<a
href="1f3eaa7789"><code>1f3eaa7</code></a>
- fix: contain SandboxHost render failures after teardown (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6110">#6110</a>
<a
href="48f95b1442"><code>48f95b1</code></a>
- chore: delete the dead <code>ensureBinding</code> and
<code>useRuntimeState</code> utilities (<a
href="https://github.com/samdickson22"><code>@​samdickson22</code></a>)</p>
<p><code>src/context/react/utils/ensureBinding.ts</code> and
<code>src/context/react/utils/useRuntimeState.ts</code> imported only
each other. Nothing
else in the repo referenced them, neither appears in the package barrel
or the
api-surface snapshot, and the <code>&quot;.&quot;</code>-only exports
map made them unreachable to
consumers. <code>ensureBinding</code> was an external caller of
<code>__internal_bindMethods</code>
that no longer had a caller of its own; the runtime classes bind
themselves in
their constructors, so nothing changes at runtime. The public API
surface is
unchanged and every other emitted file is byte-identical.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6156">#6156</a>
<a
href="9c65b511bc"><code>9c65b51</code></a>
- deprecate leftover Primitive.If and Empty wrappers on react-native and
react-ink, and point them at AuiIf (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
<p>ThreadIf now reads <code>thread.isEmpty</code> instead of
<code>messages.length === 0</code>, matching the loading-aware field
already used by ThreadEmpty and AuiIf. First-party examples and docs
samples that still called the leftover wrappers now use
<code>AuiIf</code> directly.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6084">#6084</a>
<a
href="ca9e72ce85"><code>ca9e72c</code></a>
- fix: resync trigger popover cursor after selection (<a
href="https://github.com/apps/rupic-app"><code>@​rupic-app</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6054">#6054</a>
<a
href="59e9a0881c"><code>59e9a08</code></a>
- fix: handle rejected asynchronous Markdown exports (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6098">#6098</a>
<a
href="b9b9dad28a"><code>b9b9dad</code></a>
- fix: drain unrevealed smooth text when a message completes before any
frame (<a
href="https://github.com/apps/rupic-app"><code>@​rupic-app</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6061">#6061</a>
<a
href="75dfbe3a2b"><code>75dfbe3</code></a>
- docs: document Escape-to-stop-speaking on ThreadPrimitive.Root (<a
href="https://github.com/samdickson22"><code>@​samdickson22</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6124">#6124</a>
<a
href="06b04a7976"><code>06b04a7</code></a>
- chore: update dependencies (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="fa309156e0"><code>fa30915</code></a>,
<a
href="b355aefbe2"><code>b355aef</code></a>,
<a
href="f7bd2d9392"><code>f7bd2d9</code></a>,
<a
href="4947ef4f9b"><code>4947ef4</code></a>,
<a
href="332f736e64"><code>332f736</code></a>,
<a
href="ef9254d5b2"><code>ef9254d</code></a>,
<a
href="9c65b511bc"><code>9c65b51</code></a>,
<a
href="5845ba7c56"><code>5845ba7</code></a>,
<a
href="1b30bfdaba"><code>1b30bfd</code></a>,
<a
href="365e763928"><code>365e763</code></a>,
<a
href="d19921d373"><code>d19921d</code></a>,
<a
href="996aa5723c"><code>996aa57</code></a>,
<a
href="21d6e87dc2"><code>21d6e87</code></a>,
<a
href="cd247e557b"><code>cd247e5</code></a>,
<a
href="f2b3ef8b63"><code>f2b3ef8</code></a>,
<a
href="1bf263ba20"><code>1bf263b</code></a>,
<a
href="19e52c4012"><code>19e52c4</code></a>,
<a
href="06b04a7976"><code>06b04a7</code></a>,
<a
href="a614b5e44d"><code>a614b5e</code></a>,
<a
href="07b51dbbc7"><code>07b51db</code></a>,
<a
href="92e52bd2c9"><code>92e52bd</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.15</li>
<li><code>@​assistant-ui/tap</code><a
href="https://github.com/0"><code>@​0</code></a>.9.14</li>
<li>assistant-stream@0.3.39</li>
</ul>
</li>
</ul>
<h2>0.15.15</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6071">#6071</a>
<a
href="c3fd447f23"><code>c3fd447</code></a>
- feat: host assistant-cloud thread lists on AISDKThreads via
RemoteThreadList (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
<p>AISDKThreads({ cloud }) uses RemoteThreadList and remounts each
thread like useChatRuntime. Cloud history withFormat resolves
persistence per call so one adapter can serve many threads.
useExternalHistory waits for threadListItem.remoteId instead of latching
on the first empty paint.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5872">#5872</a>
<a
href="f9529bfdea"><code>f9529bf</code></a>
- feat: move useAssistantTransportRuntime into core/react (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5872">#5872</a>
<a
href="f9529bfdea"><code>f9529bf</code></a>
- fix: persist data message parts in aui/v0 cloud history (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5839">#5839</a>
<a
href="24a1af7607"><code>24a1af7</code></a>
- fix: validate MCP App resource responses (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="75e3ef71be"><code>75e3ef7</code></a>
chore: update versions (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/6086">#6086</a>)</li>
<li><a
href="f7bd2d9392"><code>f7bd2d9</code></a>
fix(react): isolate devtools subscribers (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/6136">#6136</a>)</li>
<li><a
href="9c65b511bc"><code>9c65b51</code></a>
fix(react-native,react-ink): honor thread.isEmpty in leftover ThreadIf
(<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/6156">#6156</a>)</li>
<li><a
href="48f95b1442"><code>48f95b1</code></a>
chore(react): delete the dead ensureBinding and useRuntimeState
utilities (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/6">#6</a>...</li>
<li><a
href="06b04a7976"><code>06b04a7</code></a>
chore: update dependencies (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/6124">#6124</a>)</li>
<li><a
href="b9b9dad28a"><code>b9b9dad</code></a>
fix: drain smooth text when a message completes before an animation
frame (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/6">#6</a>...</li>
<li><a
href="10a0f3ade8"><code>10a0f3a</code></a>
test(react): vary live-completion fetcher and cacheKey independently (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/6062">#6062</a>)</li>
<li><a
href="b355aefbe2"><code>b355aef</code></a>
fix(core): prevent assistant frame origin downgrades (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/5823">#5823</a>)</li>
<li><a
href="75dfbe3a2b"><code>75dfbe3</code></a>
docs(react): document Escape-to-stop-speaking on ThreadPrimitive.Root
(<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/6061">#6061</a>)</li>
<li><a
href="ca9e72ce85"><code>ca9e72c</code></a>
fix(react): resync trigger cursor after selection (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/6082">#6082</a>)
(<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/6084">#6084</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/assistant-ui/assistant-ui/commits/@assistant-ui/react@0.15.16/packages/react">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-25 09:24:15 -07:00
dependabot[bot] 30f9888d3f
build(deps-dev): bump storybook from 10.5.5 to 10.5.10 (#11884)
Bumps
[storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/core)
from 10.5.5 to 10.5.10.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/releases">storybook's
releases</a>.</em></p>
<blockquote>
<h2>v10.5.10</h2>
<h2>10.5.10</h2>
<ul>
<li>Core: Fetch static open-service snapshots relative to the document -
<a
href="https://redirect.github.com/storybookjs/storybook/pull/35945">#35945</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Pin oxc-resolver to 11.21.2 to keep tsconfig path aliases on
solution-style tsconfigs - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35929">#35929</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Dependencies: Bump Vitest to 4.1.6 (CVE-2026-47428) - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35530">#35530</a>,
thanks <a
href="https://github.com/anupamme"><code>@​anupamme</code></a>!</li>
<li>Docs: Declare the font on overlay surfaces so docs tooltips are not
left to inherit - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35966">#35966</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>ESLint Plugin: Bundle CSF helpers so the plugin loads without
storybook - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35950">#35950</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>React: Preserve discriminated union prop values in metadata
extraction - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35844">#35844</a>,
thanks <a
href="https://github.com/s-robertson"><code>@​s-robertson</code></a>!</li>
</ul>
<h2>v10.5.9</h2>
<h2>10.5.9</h2>
<ul>
<li>Addon-Pseudo-States: Fix pseudo-states rewriting for nested
functional selectors - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34318">#34318</a>,
thanks <a
href="https://github.com/filipw01"><code>@​filipw01</code></a>!</li>
<li>Core: Skip module-graph reverse-index mirror when a patch is a no-op
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35825">#35825</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Core: Split module-graph into hot revisions and cold index services
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35831">#35831</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Preview: Fix crash when initialising UrlStore on a docs path - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35521">#35521</a>,
thanks <a
href="https://github.com/TheSeydiCharyyev"><code>@​TheSeydiCharyyev</code></a>!</li>
<li>Pseudo-States: Make stylesheet rewrites WebKit-safe - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35629">#35629</a>,
thanks <a
href="https://github.com/ethriel3695"><code>@​ethriel3695</code></a>!</li>
<li>TanStack: Keep the layout id when cloning a standalone index file
route - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35660">#35660</a>,
thanks <a
href="https://github.com/Insik-Han"><code>@​Insik-Han</code></a>!</li>
<li>TanStack: Render real link hrefs in the Link mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35505">#35505</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>Webpack: Prevent long preview output filenames - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35533">#35533</a>,
thanks <a
href="https://github.com/zhangli091011"><code>@​zhangli091011</code></a>!</li>
</ul>
<h2>v10.5.8</h2>
<h2>10.5.8</h2>
<ul>
<li>React: Fix RDT tsconfig selection for Vite project references - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35743">#35743</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Tanstack React: Remove <code>@​cloudflare/vite-plugin</code> from
the inherited Vite config - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35706">#35706</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
<li>Tanstack: Wait for router to load before rendering - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35784">#35784</a>,
thanks <a
href="https://github.com/huang-julien"><code>@​huang-julien</code></a>!</li>
<li>Test: Fix Illegal invocation when reading prototype.focus - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35528">#35528</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
</ul>
<h2>v10.5.7</h2>
<h2>10.5.7</h2>
<ul>
<li>Angular: Serve ancestor node_modules for addon-vitest in browser
mode - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35600">#35600</a>,
thanks <a
href="https://github.com/brandonroberts"><code>@​brandonroberts</code></a>!</li>
<li>Refactor: Update getVersionedPackages method to handle non-Storybook
packages correctly - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35769">#35769</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>v10.5.6</h2>
<h2>10.5.6</h2>
<ul>
<li>Dependencies: Pin `@testing-library/jest-dom` to `6.9.1` - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35614">#35614</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ESLint Plugin: Add plugin meta and document oxlint usage - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35655">#35655</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Vue: Skip docgen for module ids carrying a query - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35598">#35598</a>,
thanks <a
href="https://github.com/seanogdev"><code>@​seanogdev</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md">storybook's
changelog</a>.</em></p>
<blockquote>
<h2>10.5.10</h2>
<ul>
<li>Core: Fetch static open-service snapshots relative to the document -
<a
href="https://redirect.github.com/storybookjs/storybook/pull/35945">#35945</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Pin oxc-resolver to 11.21.2 to keep tsconfig path aliases on
solution-style tsconfigs - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35929">#35929</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Dependencies: Bump Vitest to 4.1.6 (CVE-2026-47428) - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35530">#35530</a>,
thanks <a
href="https://github.com/anupamme"><code>@​anupamme</code></a>!</li>
<li>Docs: Declare the font on overlay surfaces so docs tooltips are not
left to inherit - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35966">#35966</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>ESLint Plugin: Bundle CSF helpers so the plugin loads without
storybook - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35950">#35950</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>React: Preserve discriminated union prop values in metadata
extraction - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35844">#35844</a>,
thanks <a
href="https://github.com/s-robertson"><code>@​s-robertson</code></a>!</li>
</ul>
<h2>10.5.9</h2>
<ul>
<li>Addon-Pseudo-States: Fix pseudo-states rewriting for nested
functional selectors - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34318">#34318</a>,
thanks <a
href="https://github.com/filipw01"><code>@​filipw01</code></a>!</li>
<li>Core: Skip module-graph reverse-index mirror when a patch is a no-op
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35825">#35825</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Core: Split module-graph into hot revisions and cold index services
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35831">#35831</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Preview: Fix crash when initialising UrlStore on a docs path - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35521">#35521</a>,
thanks <a
href="https://github.com/TheSeydiCharyyev"><code>@​TheSeydiCharyyev</code></a>!</li>
<li>Pseudo-States: Make stylesheet rewrites WebKit-safe - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35629">#35629</a>,
thanks <a
href="https://github.com/ethriel3695"><code>@​ethriel3695</code></a>!</li>
<li>TanStack: Keep the layout id when cloning a standalone index file
route - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35660">#35660</a>,
thanks <a
href="https://github.com/Insik-Han"><code>@​Insik-Han</code></a>!</li>
<li>TanStack: Render real link hrefs in the Link mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35505">#35505</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>Webpack: Prevent long preview output filenames - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35533">#35533</a>,
thanks <a
href="https://github.com/zhangli091011"><code>@​zhangli091011</code></a>!</li>
</ul>
<h2>10.5.8</h2>
<ul>
<li>React: Fix RDT tsconfig selection for Vite project references - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35743">#35743</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Tanstack React: Remove <code>@​cloudflare/vite-plugin</code> from
the inherited Vite config - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35706">#35706</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
<li>Tanstack: Wait for router to load before rendering - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35784">#35784</a>,
thanks <a
href="https://github.com/huang-julien"><code>@​huang-julien</code></a>!</li>
<li>Test: Fix Illegal invocation when reading prototype.focus - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35528">#35528</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
</ul>
<h2>10.5.7</h2>
<ul>
<li>Angular: Serve ancestor node_modules for addon-vitest in browser
mode - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35600">#35600</a>,
thanks <a
href="https://github.com/brandonroberts"><code>@​brandonroberts</code></a>!</li>
<li>Refactor: Update getVersionedPackages method to handle non-Storybook
packages correctly - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35769">#35769</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>10.5.6</h2>
<ul>
<li>Dependencies: Pin <code>@testing-library/jest-dom</code> to
<code>6.9.1</code> - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35614">#35614</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ESLint Plugin: Add plugin meta and document oxlint usage - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35655">#35655</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Vue: Skip docgen for module ids carrying a query - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35598">#35598</a>,
thanks <a
href="https://github.com/seanogdev"><code>@​seanogdev</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a2db7526e1"><code>a2db752</code></a>
Bump version from &quot;10.5.9&quot; to &quot;10.5.10&quot; [skip
ci]</li>
<li><a
href="de77b08382"><code>de77b08</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/35929">#35929</a>
from storybookjs/valentin/sb-1821-pin-oxc-resolver</li>
<li><a
href="374b8b3451"><code>374b8b3</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/35966">#35966</a>
from storybookjs/valentin/docs-overlay-typography</li>
<li><a
href="2148cdd5af"><code>2148cdd</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/35950">#35950</a>
from storybookjs/fix/eslint-plugin-bundle-csf</li>
<li><a
href="b336e8f5c1"><code>b336e8f</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/35945">#35945</a>
from storybookjs/valentin/static-services-subpath-f...</li>
<li><a
href="8f56104894"><code>8f56104</code></a>
Bump version from &quot;10.5.8&quot; to &quot;10.5.9&quot; [skip
ci]</li>
<li><a
href="f31554b81e"><code>f31554b</code></a>
Backport the module-graph hot/cold split and no-op index skip to
10.5.9.</li>
<li><a
href="c1db83aae8"><code>c1db83a</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/35521">#35521</a>
from TheSeydiCharyyev/fix/35436-urlstore-docs-path</li>
<li><a
href="6ef7d1ae81"><code>6ef7d1a</code></a>
Bump version from &quot;10.5.7&quot; to &quot;10.5.8&quot; [skip
ci]</li>
<li><a
href="647e982151"><code>647e982</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/core/issues/35743">#35743</a>
from storybookjs/norbert/revive-34415-file-aware-ts...</li>
<li>Additional commits viewable in <a
href="https://github.com/storybookjs/storybook/commits/v10.5.10/code/core">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-25 09:23:29 -07:00
Tonio 88a0f885e8
Brand lockup, no idle env-check card, no Mission row (#12074)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - New customers meet the product through an onboarding arc that ends
in the tenant wizard
> - A staging walk of that arc found three rough edges: an ad-hoc icon
standing in for the brand, an environment-check card that narrates a
probe the flow already runs on its own, and a review checklist that
still lists a Mission the arc stopped asking for
> - Each one makes the product look less finished than it is at the
exact moment a customer decides what it is
> - This pull request renders the brand lockup, hides the idle
environment-check card while keeping the probe and its failure surface,
and drops the Mission row
> - The benefit is a first-session arc that reads as one product, with
no controls for questions nobody was asked

## Linked Issues or Issue Description

No public issue exists. The changes come from walking the sign-up arc on
a staging fleet.

**What happened:**
The model step shows an "Adapter environment check" card with a "Test
now" button even though pressing Connect runs the same probe and blocks
a failing hire. The review step lists "Mission" in its checklist
although onboarding no longer asks for one. The auth page renders a
sparkles icon beside the word "Paperclip" instead of the brand lockup.

**Expected behavior:**
The model step shows the check only when a probe has found something to
fix. The review checklist lists only what onboarding set up. The brand
renders as the lockup asset used across surfaces.

**Actual behavior:**
An idle card narrates a probe that runs regardless. A permanent
unchecked row marks a question nobody was asked. The brand is a generic
icon plus text.

**Steps to reproduce:**
1. Sign up on a staging fleet and enter the tenant wizard.
2. On "Create your first agent", choose a role and press Next: the model
step shows the "Adapter environment check" card before anything has been
probed.
3. Continue to Review: the checklist lists "Mission" as a permanently
unchecked row.

**Additional context:**
The Mission row outlived the removal of the mission step (#11935). The
environment probe itself still runs on Connect and blocks a failing
hire; only its idle card is at issue. The brand lockup lands across all
three surfaces in the same round — paperclip-cloud#270 and
paperclip-id#58 carry the other halves.

## What Changed

- `PaperclipLockup` renders the brand asset (mark + wordmark, one
geometry, `fill="currentColor"`); the auth page uses it in place of the
sparkles icon.
- The adapter environment check's idle card (explainer + "Test now") no
longer renders. The probe still runs on Connect and still blocks a
failing hire.
- The check's failure content still renders when a probe has found
something — the blocking error points the customer at "the reported
checks", so they stay visible.
- Connect retries a cached failed probe instead of reusing it. With
"Test now" gone, Connect is the only retry, and a stale fail would lock
out a machine the customer has since fixed.
- The review checklist drops its "Mission" row.

## Verification

Run the tenant suite:

```
cd ui && npx vitest run
```

- 4365 tests pass across 471 files; `npx tsc --noEmit` is clean.
- New test drives the wizard to the model step and asserts the
environment-check card is absent, anchored on "Connect a model" so an
unrendered step cannot pass as an absence.
- The review assertions anchor on the remaining rows ("Organization
name", "Agent created", "Model connected").

## Risks

- **Behavioral change:** a cached *failed* probe is re-run on Connect
instead of reused. Pass and warn results are still cached. This only
affects the retry path that "Test now" used to serve.
- **Hidden, not removed:** the environment check machinery is intact;
only the idle card is gone. A failing probe still blocks the hire and
still shows its checks.
- **Brand:** the wordmark now ships inside an SVG; its accessible name
carries the text. Screen readers announce "Paperclip" as before.

## Model Used

Claude Fable 5 (`claude-fable-5`) via Claude Code, with tool use and
code execution; earlier rounds on this branch's predecessor used Claude
Opus 5 (`claude-opus-5`).

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] 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 5 <noreply@anthropic.com>
2026-08-24 01:35:15 -07:00
Devin Foley c62bb4b16b
feat: environment delete with agent reassignment and consented sandbox destroy (#12053)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Environments define where agent runs execute: local, SSH, or
provider sandboxes
> - Operators can create and edit environments, but the UI has no way to
delete one
> - The server already exposes `DELETE /environments/:id` and a
delete-blast-radius preflight, but no UI consumes them, and a delete
blocked by reusable sandbox leases gives the operator no path forward
> - This pull request adds the delete flow to the environment
configuration page: a preflight-driven modal that reassigns dependent
agents, names the workspaces that hold blocking sandbox leases, and can
destroy those sandboxes with explicit consent
> - The benefit is that operators can retire stale environments from the
UI without database surgery, and dependent agents move to a chosen
replacement instead of silently falling back

## Linked Issues or Issue Description

Refs #8554
Refs #11124

**Subsystem affected**

Environments (server routes, environment runtime service, and the
environment settings UI).

**Problem or motivation**

The environment configuration page has no delete control. The server
delete endpoint exists, but nothing in the UI calls it. When reusable
sandbox leases block a delete, the 409 error names no owner, so the
operator cannot find the blocking workspace. Agents that use the
environment as their default lose it silently through the FK `on delete
set null`.

**Proposed solution**

Add a delete button with a confirmation modal on the environment edit
page. The modal reads the delete-blast-radius preflight. It offers a
dropdown to reassign dependent agents to another environment before the
delete. It lists each workspace that holds a blocking reusable sandbox
lease, with a link. When those leases are the only blocker, the confirm
button destroys the sandboxes inline
(`?destroyReusableSandboxLeases=true`) and then deletes. A failed
teardown falls back to `pending_cleanup` for the sweep, so no sandbox is
orphaned.

## What Changed

- `ui/src/pages/CompanyEnvironments.tsx`: delete button on the edit page
header, confirmation modal with agent reassignment select, lease-holder
list, impact notes, and a consent-labeled destroy-and-delete action
- `ui/src/api/environments.ts`: `deleteBlastRadius` and `remove` client
methods; `remove` takes an optional `destroyReusableSandboxLeases` flag
- `server/src/routes/environments.ts`: `DELETE /environments/:id`
accepts `?destroyReusableSandboxLeases=true`; it destroys the
environment's reusable sandbox leases first, but only when those leases
are the sole delete blocker, then re-checks the blast radius before it
deletes
- `server/src/services/environment-runtime.ts`: new
`destroyReusableSandboxLeasesForEnvironment` — destroys every reusable
sandbox lease an environment still owns while the environment config
(provider credentials) is still available
- `server/src/services/environments.ts`: the delete blast radius now
returns `reusableSandboxLeaseHolders` (lease id, workspace, issue) so
clients can name what blocks a delete
- `packages/shared/src/types/environment.ts`:
`EnvironmentDeleteReusableLeaseHolder` type on the blast radius
- Tests: route gating for the consent flag (destroy runs, mixed-blocker
rejection, surviving-lease rejection), runtime destroy scoped to an
environment, blast-radius holder join, and UI tests for the reassignment
flow, holder links, and the consent button

## Verification

- `npx vitest run server/src/__tests__/environment-routes.test.ts
server/src/__tests__/environment-service.test.ts
server/src/__tests__/environment-runtime.test.ts
ui/src/pages/CompanyEnvironments.test.tsx`
- Manual: open Settings → Environments → edit an environment. The trash
icon opens the modal. With agents on the environment, pick a
reassignment target and confirm; agents move and the environment
deletes. With reusable sandbox leases, the modal names the holding
workspaces and the confirm button reads "Destroy N sandboxes and
delete".

## Risks

- The consented path destroys provider sandboxes. It runs only when
reusable leases are the sole blocker, so a delete that would still be
rejected never destroys anything. A failed teardown routes to
`pending_cleanup` and the delete stays blocked until the sweep resolves
it.
- Agent reassignment issues one PATCH per agent from the client. A
mid-sequence failure leaves some agents reassigned; the reassignments
are valid on their own and the UI refreshes to the actual state.
- Hard blockers (managed local, instance default, pending cleanup) keep
the existing 409 behavior and disable the confirm button.

## Model Used

- Claude (Anthropic) — Fable 5, model id `claude-fable-5`, extended
thinking, agentic tool use via Claude Code 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
- [ ] 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-23 16:53:17 -07:00
Nicky Leach 63df7ad2b3
feat(login): use the login pseudo-terminal for Codex device login and de-Claude the shared channel (#12020)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent adapters use provider-specific login flows
> - Codex device login needs a live pseudo-terminal (PTY), while the
shared channel still uses Claude-specific names
> - The old streamed-exec path does not provide the prompt transport
that Codex needs
> - This pull request moves Codex device login to the shared login PTY
and removes the dead streamed-exec path
> - The benefit is one controlled login transport with fail-closed
capability checks and safer credential reads

## Linked Issues or Issue Description

**Problem or motivation**

Codex device login used a streamed-exec path that did not provide the
required prompt transport. The shared login channel also exposed
Claude-specific names outside Claude code.

**Expected behavior**

The host selects a fixed login command from trusted adapter data. Codex
login uses the provider login PTY. Providers without that capability
fail closed.

**Proposed solution**

Use a server-controlled session home, create and validate it as a fresh
0700 directory, read credentials from one validated descriptor, and
rename shared channel names to the neutral login PTY family.

**Alternatives considered**

Keep the shared login PTY as the single transport. Do not keep the
removed streamed-exec path because it cannot provide the required prompt
transport.

**Roadmap alignment**

This change supports the planned login transport work. It does not add a
separate roadmap item.

## What Changed

- Route Codex device login through the shared login PTY transport.
- Select the login command from a closed internal command key.
- Carry a server-controlled session home through the launch contract.
- Create and validate the session home as a fresh 0700 directory owned
by the login user.
- Read the credential file with descriptor-relative, no-follow path
walking and final descriptor checks.
- Gate the login route and run lease on the provider login PTY
capability.
- Rename shared channel names to the neutral login PTY family.
- Remove the streamed-exec transport value, selector field, driver
branch, and related tests.
- Hide Codex login in the user interface when the provider lacks the
login PTY capability.

## Verification

- Server unit suites pass: 89/89.
- Adapter-utils suites pass: 262/262.
- Codex-local suites pass: 326/326.
- Credential-read reader suite passes: 20/20.
- Daytona login PTY suite passes: 30/30.
- Device-login suites pass: 56/56.
- TypeScript checks pass for server, adapter-utils, and UI.
- GitHub Actions must pass after pull request creation.
- Greptile review must reach 5/5 with no open P2 findings,
recommendations, or follow-ups.

## Risks

- Providers without a login PTY capability lose Codex login support by
design.
- The credential read rejects invalid ownership, mode, type, path, and
size.
- The launch-time sandbox directory race remains outside the threat
model because the login runs inside the sandbox and a hostile sandbox
already controls its credential.

## Model Used

OpenAI Codex, GPT-5, tool use and code review assistance. The exact
context window and reasoning mode are not exposed by the runtime.

## 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-23 09:44:59 -07:00
Dotta f572e08678
fix(recovery): stop automatic stranded-task takeovers (#11961)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The recovery service restores execution when a task loses its live
path.
> - The service retries the original agent for a limited number of
attempts.
> - The old fallback could select a manager or an executive and wake
that agent.
> - That fallback changed the effective recovery owner without a board
decision.
> - This pull request keeps the source owner and gives the exhausted
recovery decision to the board.
> - The benefit is a clear ownership rule with no automatic task
takeover.

## Linked Issues or Issue Description

Refs: #11807

Refs: #11817

**What existing behavior does this improve?**

This improves stranded-task recovery in the server and the recovery
action card in the board UI.

**Subsystem affected**

Cross-cutting: server recovery orchestration, recovery observability,
board UI, and execution documentation.

**Current behavior**

Paperclip retries the original agent for a limited number of attempts.
After the retry limit, it can select a manager, task creator, CTO, or
CEO as a recovery owner. It can then wake that substitute agent. The
source task keeps its assignee, but the automatic substitute wake
creates an implicit takeover path.

**Proposed behavior**

Paperclip keeps the limited retry path for the original agent. If
recovery is exhausted or unsafe, Paperclip creates one board-owned
source recovery action. It keeps both source assignee fields. It does
not wake a substitute agent. The board can repair, retry the original
owner, explicitly reassign, or resolve the task.

**Reason and benefit**

Source task ownership must remain stable until a person or an approved
policy changes it. The new rule removes implicit manager and executive
takeover. It also gives operators clear evidence through the
`board_escalation_no_takeover_v1` routing marker.

**Breaking changes**

Automatic recovery no longer wakes a manager or executive after the
original-agent retry limit. Existing active agent-owned recovery actions
remain visible and can resolve. Paperclip does not schedule a new
takeover wake for those legacy actions.

## What Changed

- Route exhausted and unsafe stranded recovery to a board-owned source
action.
- Preserve agent and user assignee fields during automatic escalation.
- Keep limited same-agent continuity repair and provider quota
monitoring.
- Stop new manager, creator, CTO, and CEO recovery wakes.
- Keep legacy agent-owned recovery actions readable and resolvable.
- Add the routing marker to new board escalation evidence and
observability.
- Update recovery notices, the board UI card, tests, and execution
documentation.

## Verification

- Run `pnpm -r typecheck`.
- Run `pnpm build`.
- Run `pnpm check:token-gates`.
- Run `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-process-recovery.test.ts`.
- Run `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-workspace-branch-containment.test.ts`.
- Run the focused recovery and UI Vitest files changed by this pull
request.
- Confirm that a paused or over-budget source owner creates one board
action, keeps the source assignee, and creates no substitute wake.

## Risks

- Operators must now make the final recovery decision after the
original-agent limit.
- Legacy agent-owned actions use their stored contract. This avoids a
rollout-time ownership rewrite.
- No database migration or API response shape changes are included.
- The tests cover concurrent escalation, paused and over-budget owners,
legacy actions, provider quota monitoring, and UI presentation.

> 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 hosted exact model revision and context
window are not exposed. Reasoning, tool use, and code execution were
enabled.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-22 11:41:24 -05:00
Nicky Leach 10d2781a29
feat(sandbox): add the duplex bridge broker, gated transport selection, and fixed observability (#11769)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Sandbox adapters provide controlled execution for untrusted provider
environments.
> - The sandbox channel needs one persistent duplex transport with
strict host control.
> - The transport must remain off unless the instance setting and
provider capability both allow it.
> - The host must detect loss, bound resource use, and expose only safe
telemetry.
> - This pull request adds the broker, gated selection, kill-switch
wiring, fixed observability, and real-process proof.
> - The benefit is safer sandbox execution with bounded failure behavior
and inspectable transport results.

## Linked Issues or Issue Description

No public issue exists for this change. The related pull requests are
#11738 and #11750.

**Problem or motivation**

The sandbox duplex channel needs a host-controlled broker, strict
transport gates, bounded provider input, and safe loss telemetry.
Without these controls, a provider can cause replay, resource growth,
unsafe endpoint selection, or data exposure through telemetry.

**Proposed solution**

Add a host broker with nested time limits, request limits, one-shot
loss, and per-id deduplication. Select duplex transport only when the
instance setting and provider capability both equal true. Assign the
endpoint and nonce on the host. Reject invalid readiness data and use
the file bridge on failure. Add fixed redacted telemetry and a
real-process end-to-end test harness.

**Alternatives considered**

Keep the file bridge as the only transport. This avoids new channel
behavior but does not provide persistent duplex operation for supported
sandbox providers.

**Roadmap alignment**

This change supports the Cloud / Sandbox agents section in ROADMAP.md.

## What Changed

- Add the duplex bridge broker with bounded forward, response, and
gateway wait budgets.
- Bound concurrent requests, lifetime requests, and request-id bytes
before retention or forwarding.
- Select duplex transport only when both required gates are true.
- Assign the loopback port and nonce on the host and enforce a
liveness-only READY frame.
- Fall back to the file bridge after invalid readiness, contamination,
bind failure, or timeout.
- Carry the kill switch through the server, acpx engine, and six local
adapters.
- Add fixed, redacted duplex telemetry with a provider allowlist.
- Add a real-process end-to-end harness for readiness, round trips,
loss, and teardown.
- Add regression coverage for limits, loss, UTF-8 splits, concurrency,
and telemetry dimensions.

## Verification

- Adapter-utils, server, and Daytona typechecks pass locally.
- Adapter-utils tests pass, including the codec, broker,
execution-target sandbox, and real-process harness.
- Server kill-switch tests pass.
- Live Daytona tests pass with the required provider key and skip
without that key.
- The root pnpm-lock.yaml file has no diff.
- The branch contains ten commits after origin/master.

## Risks

- Duplex transport remains disabled unless both gates equal true.
- A provider remains an untrusted boundary and needs least-privilege
credentials and quotas.
- The server telemetry recorder stays deferred; the default recorder
does nothing.
- A provider that pre-binds the host port causes a fail-closed fallback
to the file bridge.
- The change adds no database migration and changes no root lockfile.

## Model Used

OpenAI GPT-5, exact model family GPT-5, large context window, reasoning,
and tool use. The model assisted with Git handoff validation and PR
preparation. The implementation commits came from the engineering
worktree.

## 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/... or 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
- [x] I searched the GitHub PR list for similar PRs and confirmed this
is not a duplicate

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-22 09:01:31 -07:00
Tonio 3ff636bc48
Drop the mission step from the wizard arc (#11935)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - New customers arrive through an onboarding arc that spans Paperclip
Cloud and the tenant app
> - Cloud's naming screen stopped asking for the company mission, but
the tenant wizard still decided its first step by asking whether the
company had one
> - Every Cloud-created company therefore looked mission-less on
arrival, so every walk detoured through a "Define your mission" screen
the design had already removed
> - This pull request removes that step from the arc, and makes step 1
create the company itself
> - The benefit is a shorter arc that matches the design, and a
three-step progress strip that now counts the steps that exist

## Linked Issues or Issue Description

No public issue exists. The problem was found by walking staging end to
end.

**What happened:**
A new customer signs in, names their organization, and waits for it to
build. The tenant wizard then asks "Define your mission" before it asks
for the first agent. Cloud no longer collects a mission, so this screen
appears for every new customer.

**Expected behavior:**
The wizard asks for the first agent, the model, and a review. The
progress strip counts three steps.

**Actual behavior:**
The wizard asks for the mission first. The progress strip counts five
segments, because the run does not enter on the agent arc.

**Additional context:**
Three merged pull requests built the mission-based step choice this
change removes: #11352, #11416 and #11429. The mission is now collected
later, inside the tenant app, so onboarding does not ask for it at all.

## What Changed

- `onboardingStepForCompany` always returns the agent step. The
`companyHasMission` parameter is removed, because it cannot change the
answer.
- `resolveRouteOnboardingOptions` no longer accepts `companyHasMission`.
- The dashboard no longer waits for the goal lookup before it opens the
wizard. That wait only chose a step, and the step is now fixed.
- Step 1 creates the company in a new `handleCreateCompany`. Company
creation used to sit at the end of `handleConfirmMission`.
- No company goal is written during onboarding.
- The three-step strip now shows on the agent, model and review steps,
because every Cloud-first run enters on the arc.
- The full-length bar drops its second segment. No run can fill it.
- The grow path keeps its step 2 questionnaire. Only the create path
skips ahead.
- Back from the agent step goes to the screen the run came from.
- Four end-to-end specs no longer drive the wizard through the mission
step.

## Verification

Run the tenant test suite:

```
cd ui && npx vitest run
```

- 4356 tests pass. 471 files pass.
- `npx tsc --noEmit` reports no errors.
- Fault injection: forcing `skipsMissionStep` to `true` fails the grow
questionnaire test. Removing the Back rule fails the Back test. Both
tests fail on the exact defect they guard.
- The three-step strip is asserted by an existing test. It checks `Step
1 of 3` and `aria-label="Create your first agent"`.

Manual check on staging after the paired Cloud change:

1. Open a new incognito window.
2. Sign in with a new account.
3. Name the organization.
4. Confirm the wizard shows "Create your first agent" and "Step 1 of 3".

## Risks

- **Behavioral change.** Onboarding no longer writes a company goal. An
agent hired during onboarding starts without a seeded mission. This is
intended. The mission moves to the tenant app.
- **Dead code.** `ONBOARDING_MISSION_STEP` and the mission screen stay
in the codebase, but nothing in the app opens them. They wait for the
surface that collects the mission later.
- **Grow path.** The grow path is unchanged, but it shares step 2 with
the removed screen. New tests cover it.
- **Superseded work.** #11352, #11416 and #11429 tuned the mission-based
step choice. This change removes the branch they tuned.

## Model Used

Claude Opus 5 (`claude-opus-5`), extended thinking, with tool use and
code execution through 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
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] 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 5 <noreply@anthropic.com>
2026-08-22 02:28:45 -07:00
Tonio 3d366ba15f
Rebuild the onboarding agent arc on the prototype's step design (#11905)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The onboarding wizard in `ui/` hires that first agent. It runs three
steps: create the agent, connect a model, and review
> - A standalone prototype holds the agreed design for these steps.
#10786 ported that prototype, but #11067 reverted it in full because the
port deleted `OnboardingWizard.tsx` while four pull requests were
editing that file
> - Those four pull requests have since merged. The revert said the port
can "re-land incrementally", and this is that re-land
> - This pull request takes the presentational layer from the prototype
only. It keeps master's wizard as the source of behaviour, so the eight
onboarding fixes merged since the revert stay in place
> - The benefit is that the three agent steps match the agreed design,
and no merged fix is lost to get there

## Linked Issues or Issue Description

Refs #10786 — the first attempt to land this design.
Refs #11067 — the revert that asked for it to re-land in smaller steps.

No public issue exists for the re-land. The problem is described below.

**Subsystem affected**

The `ui` package. The change touches the onboarding wizard, the agent
capsule,
and one Storybook story. It adds four small presentational components
under
`ui/src/components/onboarding/`.

**Current behavior**

The wizard's agent steps do not match the prototype. Each step shows a
small
heading beside an icon, above a form. The agent capsule sits below that
heading and does not animate. The agent gets a name but no role, so
every
first agent is created as `ceo`.

The wizard also shows a five-segment progress bar on these steps. A
walker who
enters on the agent step cannot reach the first two segments, so two of
the
five can never be filled.

**Proposed behavior**

The three steps use the prototype's card, its centred display heading,
and its
footer. One capsule sits above the heading and stays mounted across all
three
steps, so it reads as one object being built rather than three screens
that
each show their own.

A three-segment strip counts these steps for a walker who enters on
them. The
full-length bar stays for a walker who starts at step one, so that count
never
restarts partway.

The agent step gains a role. The options come from the agent role enum,
not
from the prototype's mock list.

**Reason and benefit**

The design is agreed and already built once. Re-landing it
presentation-first
keeps the behaviour that master gained after the revert.

Sourcing roles from the enum matters. The prototype offers "Coder",
which is
not a valid role — the enum uses `engineer` — so a walker who picked it
would
fail validation at hire time.

**Breaking changes**

None. The wizard keeps its routes, its draft format, and its hire call.
The
draft gains one optional field, `agentRole`. A draft saved before this
change
loads without it and falls back to the default.

## What Changed

- Add `ui/src/components/onboarding/`: `Stepper`, `OnboardingCard`,
`OnboardingHeading`, `FooterNav`, `AgentPreview`, and shared motion
constants
- Rebuild wizard steps 3–5 on those parts: one card, the capsule above a
  centred heading, and one footer
- Hold one `AgentCapsule` across the three steps. It springs in once,
then
  morphs from dashed slot to traced outline to filled
- Add `strokeDraw` to `AgentCapsule`. It traces the outline instead of
  cross-fading it. The dashed layer holds until the trace ends
- Add a role select to the agent step. Choosing a role fills the name,
unless
  the walker typed one
- Show one progress indicator per run, not two
- Label strip segments by destination, not by number
- Add `motion` to the `ui` package
- Add a Storybook story for the strip and the capsule states

## Verification

Run the tests:

```
pnpm --filter @paperclipai/ui exec vitest run
pnpm --filter @paperclipai/ui exec tsc -p tsconfig.json --noEmit
```

4235 tests pass. The typecheck is clean.

To see the steps, start the app and open `/<PREFIX>/onboarding` for a
company
that has a company-level goal. The wizard opens on the agent step. Step
three
requires a hire.

Three absence assertions were checked by fault injection. Each one fails
when
the old behaviour returns:

- put the step counter back, and the "shows no step counter" test fails
- default `strokeDraw` to true, and the cross-fade test fails
- restore the timer gate on the strip, and the indicator test fails

## Risks

Low to medium.

`motion` is one new dependency in `ui`. #11067 gave dependency weight as
one
of three reasons to revert #10786, so this branch carries the smallest
set
that works. `motion` drives the step transitions and the capsule
choreography,
and three files import it.

An earlier revision of this branch also added `three` and
`@types/three`. Both
are removed. They existed for the 3D backdrop, which belongs to the auth
and
welcome screens rather than to these three steps, so nothing on this
branch
imported them.

The role select changes what the wizard sends. Before this change every
first
agent was hired as `ceo`. Now the walker chooses. The values come from
the
enum, so the server accepts all of them.

Steps 1 and 2 keep the older design. They do not run on the Cloud-first
path,
where the company already exists.

## Model Used

Claude Opus 5 (`claude-opus-5`), with extended thinking, tool use, and
code
execution. Used for the code, 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
- [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 Opus 5 <noreply@anthropic.com>
2026-08-21 16:41:53 -07:00
Devin Foley adfbe2d4b9
feat(environments): refer to the managed default environment by name, not the sandbox driver key (#11838)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Managed deployments provision a platform-managed default environment
for agent runs; the UI shows this environment in selectors, the agent
form, run details, and the environments page
> - Those surfaces append the raw driver key to the environment name, so
users see labels like "Paperclip Computer (sandbox)", "Paperclip
Computer · sandbox", and fallback copy such as "Managed sandbox" and
"The sandbox has no ready authentication"
> - "sandbox" is infrastructure vocabulary, not the product name of the
environment; showing it next to the managed environment's name is
confusing and off-brand
> - This pull request renders platform-managed environments by name
alone and rewords the sandbox-phrased copy, while user-created
environments keep the driver suffix so mixed lists stay distinguishable
> - The benefit is that the default environment reads as one clear
product name everywhere, and self-hosted users lose nothing: their own
environments still show the driver

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Display of the platform-managed default environment across the UI.

**Subsystem affected**

UI (environment selectors, agent config form, environments page, agents
page, run details) and the claude-local/codex-local adapter auth checks.

**Current behavior**

The agent form labels the inherited default environment as "Name
(sandbox)". Environment selectors and the environments list render "Name
· sandbox". The agents page describes the environment as "<provider>
sandbox provider". The agent form's fallback label is "Managed sandbox".
Adapter auth checks say "The sandbox has no ready authentication for
this adapter."

**Proposed behavior**

Platform-managed environment rows (`metadata.managedByPaperclip`) render
their name alone. The fallback label is "Paperclip Computer". The agents
page describes managed environments as "Managed by Paperclip". Run
details omit the driver suffix for sandbox-driver environments (the
adjacent Provider entry already identifies the mechanism). Adapter auth
checks say "This environment has no ready authentication for this
adapter."

**Reason and benefit**

The managed environment carries a product name. Appending the raw driver
key ("sandbox") to it is noise and contradicts the product naming.
User-created environments keep the driver suffix, so mixed lists stay
distinguishable.

**Breaking changes**

None. Message text of the auth check is not read programmatically; the
UI keys off `ADAPTER_AUTH_MISSING_CHECK_CODE`. Rows without the managed
marker render exactly as before.

## What Changed

- New `environmentDisplayLabel` helper in
`ui/src/lib/managed-sandbox-environment.ts`: managed rows → name alone;
other rows → "Name · driver".
- `AgentConfigForm`: inherited-default label uses the helper; fallback
copy "Managed sandbox" → "Paperclip Computer"; environment options use
the helper.
- `ProjectProperties`, `CompanyEnvironments`: environment selector
options use the helper; the environments-list row hides the driver
suffix on managed rows; the managed detail page's fallback description
no longer says "sandbox".
- `Agents` page: managed environments are described as "Managed by
Paperclip" instead of "<provider> sandbox provider".
- `CommentThread` run details: the driver suffix is omitted for
sandbox-driver environments.
- claude-local and codex-local adapters: auth-missing check message/hint
reworded from "sandbox" to "environment" (ACP and environment-test
paths); claude-local probe/effort/login hints reworded the same way.
- Run status lines: "Syncing workspace to sandbox", "Exporting git
changes from sandbox", "Starting adapter in sandbox", and friends now
say "environment"; "Finalizing sandbox workspace" → "Finalizing
workspace". Templated transfer-progress lines map the `sandbox`
transport key to "environment" for display (`runtime-progress.ts`).
- Agent form sign-in panel: "Sign in to the sandbox" → "Sign in to the
environment"; "Authenticated. The sandbox has credentials now." → "…The
environment has credentials now."
- Feature catalog + instance settings card: "Managed Sandbox Only" →
"Managed Environment Only" (setting key unchanged; the card keeps its
alphabetical slot).
- Server agents routes: execution-target failure and test-identity copy
no longer say "sandbox"; workspace-mode label "Cloud sandbox" → "Cloud
environment".
- Tests: new `environmentDisplayLabel` unit cases; new `AgentConfigForm`
render case asserting the managed default renders without "(sandbox)" or
"· sandbox"; status-line assertions updated across adapter-utils, server
heartbeat/live-run, and UI chat suites.

## Verification

- `pnpm --filter @paperclipai/ui typecheck` — clean.
- `pnpm --filter @paperclipai/adapter-claude-local typecheck` and
`--filter @paperclipai/adapter-codex-local typecheck` — clean.
- `vitest run` for `managed-sandbox-environment.test.ts`,
`AgentConfigForm.render.test.tsx`, `CompanyEnvironments.test.tsx`,
`Agents.test.tsx`, `CommentThread.test.tsx`, `NewAgent.test.tsx` — all
green (118 tests across the two runs).

## Risks

Low risk. Cosmetic label changes only; no data or API changes. Rows
without `metadata.managedByPaperclip` render exactly as before, so
self-hosted deployments with their own environments see no change. The
only self-hosted-visible wording changes are the adapter auth-check
message and the driver suffix omission on sandbox-driver rows in run
details.

## Model Used

- Claude (Anthropic) — claude-fable-5 (Claude Fable 5), Claude Code CLI,
extended thinking, tool use.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes (no
docs reference these labels)
- [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-21 12:51:22 -07:00
Nicky Leach 5bc6031f79
fix(server,ui,claude-local): verify auth on the adapter Test lane and enforce managed-sandbox tenant binding (#11810)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Adapter Test checks whether an agent adapter can run with its
configured environment, and every local-driver adapter (Claude, Codex,
Gemini, OpenCode, Pi, Cursor, etc.) shares this Test route and its UI
resolution logic
> - The Claude ACP Test lane could report pass without checking local or
remote authentication, and the shared Test route and UI had gaps in
environment binding, probe safety, and managed-sandbox resolution that
affect every adapter that uses the Test button, not only Claude
> - This pull request verifies authentication on every Claude ACP
target, and closes the shared Test-route/UI gaps: tenant-binding on the
route, a managed-sandbox-only redirect that matches the real run path,
and a three-tier environment resolution in the UI
> - The benefit is a truthful Test result with safer probe execution and
tenant isolation, for Claude specifically and for every other local
adapter that shares this Test surface

## Linked Issues or Issue Description

**What happened?**

The Claude ACP Test lane returned `status: "pass"` without checking
authentication for some local and non-sandbox targets. Separately, the
shared `/companies/:companyId/adapters/:type/test-environment` route —
used by every local-driver adapter, not only Claude — accepted a foreign
environment id, and its UI resolution did not mirror the server's
managed-sandbox-only redirect.

**Expected behavior**

The Test lane checks the resolved credential and hello probe for every
Claude ACP target. The shared adapter Test route rejects a foreign
environment before it reveals environment details or starts a lease, for
any adapter type. The Test's environment resolution (UI and server)
matches the real run's three-tier resolution, including the
managed-sandbox-only redirect.

**Steps to reproduce**

1. Run the Claude ACP Test lane against a local target without a valid
credential.
2. Run the adapter Test route with an environment id from another
company (any adapter type).
3. Observe the pass result on step 1, or the missing tenant-binding
rejection on step 2.

**Paperclip version or commit**

`933749e01f74e82ce5d315c071be534d04e01158`

**Deployment mode**

Local dev (`pnpm dev`) and server route tests.

**Agent adapter(s) involved**

Claude Code directly (the ACP auth-verification work). The
tenant-binding guard, managed-sandbox-only redirect, and UI three-tier
resolution apply to the shared adapter Test route and affect every
local-driver adapter (Codex, Gemini, OpenCode, Pi, Cursor, etc.), not
only Claude — see "What Changed" below for the split between Claude-only
and shared changes.

**Database mode**

Not database-related.

**Access context**

Both board and agent paths use the affected Test surface, for every
local-driver adapter.

**Additional context**

Two commits that were previously bundled into this PR — a
`plugin-worker-manager` duplex-channel frame-bound fix and a
`workspace-runtime` exit-persist crash fix — are unrelated to the
adapter Test lane and have been split out into their own PRs: #11860 and
#11861.

## What Changed

Claude-only (`packages/adapters/claude-local`):

- Verify `CLAUDE_CODE_OAUTH_TOKEN` and run the hello probe for every
Claude ACP target.
- Keep `adapter_auth_missing` sandbox-only and report missing
non-sandbox credentials as a warning.
- Add a deny-by-default probe environment builder for the ACP and CLI
local probes.
- Log only fixed probe context and allowlisted classifications.
- Seed the host OAuth token into the hello probe environment.

Shared, cross-adapter (`server/src/routes/agents.ts`,
`ui/src/lib/adapter-test-environment.ts`,
`ui/src/components/AgentConfigForm.tsx`,
`ui/src/components/OnboardingWizard.tsx`):

- Add a company-binding guard and a binding assertion for the generic
`/companies/:companyId/adapters/:type/test-environment` route, so a
foreign-company environment id is rejected before any secret resolution
or sandbox lease, for every adapter type.
- Resolve all three server environment tiers (agent default, instance
default, local default) in the UI, and add the managed-sandbox-only
redirect so the Test probes the same target a real run would use.
- Enforce onboarding Test results: block hire on a failed environment
test.

- Add regression tests for authentication, tenant binding, probe safety,
diagnostics, and UI resolution.

## Verification

- Adapter suites pass for the Claude local server probe, remote, ACP,
auth, probe environment, and config paths.
- Server route tests pass, including the five tenant-binding cases.
- UI adapter Test environment resolver tests pass for all three
resolution tiers.
- Adapter package `tsc --noEmit` exits 0.
- Full CI must pass on this pull request.

## Risks

The probe environment now denies caller variables by default. A required
variable that is not on the allowlist could stop a probe from starting.
The route now rejects foreign environment ids with a fixed 403 response.
The managed-sandbox-only redirect changes where the Test (and the login
affordance) probes for every local-driver adapter under that policy, not
only Claude — operators running other local adapters under
managed-sandbox-only will see their Test target move from local to the
managed sandbox, matching what real runs already do. The change limits
secret and diagnostic exposure.

## Model Used

Original implementation: OpenAI Codex, GPT-5; exact context window not
exposed in that run; tool use and code execution.

This revision (commit split and title/description correction): Claude,
Sonnet 5 (claude-sonnet-5). The original title and description described
this PR as Claude-only; review found it also changes the shared adapter
Test route and UI resolution used by every local-driver adapter, and
carried two unrelated server fixes. Claude split those two commits into
#11860 and #11861 via `git rebase --onto` (verified byte-identical to
the original tree minus those commits) and rewrote this description to
reflect the actual scope. No functional code in this PR was authored by
Claude.

## 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-21 11:30:49 -07:00
scotttong a7e689b3c3
feat(ui): rename "Agent mode" to "Auto mode" and show full work-mode labels (#11866)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The task composer and the New Task dialog show a work-mode chip (how
the agent will run a task)
> - The default mode was labeled "Agent mode", and the New Task dialog
chip abbreviated every mode to one word ("Auto", "Plan", "Ask")
> - "Agent mode" is confusing because every mode runs an agent, and the
abbreviated chip hid what the label means
> - This pull request renames the mode to "Auto mode" and makes every
mode chip show the full label
> - The benefit is a clearer, consistent mode name in every place the
user selects a work mode

## Linked Issues or Issue Description

No public GitHub issue exists for this change. Description follows the
enhancement template:

**What existing behavior does this improve?**

The work-mode selector chips in the task composer and in the New Task
dialog.

**Subsystem affected**

UI (`ui/src/lib/work-mode-meta.ts`,
`ui/src/components/NewIssueDialog.tsx`).

**Current behavior**

The default work mode is labeled "Agent mode". The New Task dialog chip
shows a shortened label ("Auto", "Plan", "Ask") from a separate
`shortLabel` field.

**Proposed behavior**

The default work mode is labeled "Auto mode". Every chip shows the full
label ("Auto mode", "Plan mode", "Ask mode"). The `shortLabel` field is
removed so no surface can fall back to the short form.

**Reason and benefit**

"Agent mode" does not describe the behavior — all modes use an agent.
"Auto mode" states what the mode does. One label field keeps every
surface consistent.

**Breaking changes**

None. This is a display-string change only. No API, storage, or mode-key
changes.

## What Changed

- Renamed the `standard` work-mode label from "Agent mode" to "Auto
mode" in `ui/src/lib/work-mode-meta.ts`, the single source for all mode
chips.
- Changed the New Task dialog mode chip to render the full `label`
instead of `shortLabel`.
- Deleted the `shortLabel` field from `WorkModeMeta` so nothing can
silently regress to the short form.
- Updated unit tests and fixtures to pin the full labels.

## Verification

- Run `pnpm --filter @paperclipai/ui test --
src/lib/work-mode-meta.test.ts src/components/NewIssueDialog.test.tsx
src/components/IssueChatThread.test.tsx
src/components/task-chat/TaskChatComposer.test.tsx`. All tests pass. The
tests assert the labels are exactly "Auto mode", "Plan mode", and "Ask
mode".
- Manual: start the dev server, open the board, press `c` to open the
New Task dialog, and press Cmd+Period to cycle modes. The chip reads
"Auto mode", "Plan mode", then "Ask mode". The composer chip on an open
task shows the same labels.

## Risks

- Low risk. Display strings only. The chip is a few pixels wider in the
New Task dialog; no layout overflow was observed in any of the three
modes.

## Model Used

- Claude Fable 5 (Anthropic, model ID `claude-fable-5`), extended
thinking with tool use, run inside a Claude Code agent session.

## Checklist

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 10:28:55 -07:00
Nicky Leach 38d8f37172
fix(build): enforce Node 24 across Paperclip (#11792)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip runs across the CLI, server, adapters, plugins, CI, and
container images.
> - These surfaces declared different Node.js versions from 20 through
24.
> - A newer `@types/node` major can expose APIs that the supported
runtime does not provide.
> - Node.js 20 is no longer a suitable project baseline, and Node.js 24
is the current LTS line.
> - This pull request sets Node.js 24.11.0 as one repository-wide
baseline, adds a drift check, and gives users actionable startup
guidance when their runtime is too old.
> - The benefit is one clear runtime contract for development, release,
installation, and published packages.

## Linked Issues or Issue Description

Refs #2734

Refs #11727

Refs #739

## What Changed

- Require Node.js 24.11.0 or newer in all 42 package manifests and
runtime checks.
- Use Node.js 24 in GitHub Actions, Docker images, smoke images, sandbox
setup, portable installs, and esbuild targets.
- Align every direct `@types/node` declaration on `^24.0.0`.
- Prevent Dependabot from opening major `@types/node` upgrades without a
matching runtime decision.
- Add `.nvmrc` and a CI policy check for Node version drift.
- Update ACP version gates, tests, and user documentation for the new
minimum.
- Print a non-blocking warning on CLI and server startup when Node is
unsupported, with remediation through a version manager or the
documented downloaded `install.sh` workflow.
- Deduplicate that warning when `paperclipai run` boots the CLI and
server in the same process.

## Verification

- `node scripts/check-node-version-policy.mjs`
- `node --check scripts/check-node-version-policy.mjs`
- `node --check cli/esbuild.config.mjs`
- `node --check scripts/generate-npm-package-json.mjs`
- `bash -n scripts/install.sh scripts/test-install-sh-docker.sh
scripts/e2e-install-lifecycle.sh`
- Parsed all 42 package manifests and confirmed `engines.node` is
`>=24.11.0`.
- `git diff --check`
- `vitest run
packages/adapter-utils/src/sandbox-install-command.test.ts` passed with
3 tests.
- `vitest run cli/src/node-version.test.ts` passed with 4 tests.
- Directly exercised the shared warning helper for unsupported-version
messaging and same-process deduplication.
- The focused exe.dev suite could not resolve the locally unbuilt plugin
SDK from this isolated worktree. A full offline workspace install was
also blocked because the package-manager signature verifier requires
registry access. The full suite was not run locally; draft CI performs a
clean install and evaluates the wider impact.

## Risks

- This is a breaking runtime change for users, plugins, and deployments
that still use Node.js 20 or 22.
- Published workspace packages will now produce an engine warning or
failure in strict package managers on older Node.js releases.
- Node.js 24 can reveal dependency, native module, Playwright, or agent
CLI compatibility issues in CI.
- The bootstrap installer now installs Node.js 24 when the current
runtime is older than 24.11.0.
- The portable sandbox fallback is pinned to Node.js 24.11.0 and depends
on that upstream tarball remaining available.
- Unsupported runtimes continue booting after a warning, so a later
incompatibility can still fail at its point of use.
- The CLI and server share the warning policy through the published
`@paperclipai/shared` package; packaging checks must keep that subpath
export available.
- This PR does not commit `pnpm-lock.yaml` because repository policy
assigns lockfile generation to CI.

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

## Model Used

- OpenAI Codex based on GPT-5. The exact deployment ID and context
window are not exposed in this session. Reasoning, repository tools,
shell execution, and GitHub tools were enabled.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-21 10:17:52 -07:00
Michael Nguyen 773a9dade6
fix: preserve plugin detail-tab deep links and bridge the real JSX runtime (#11826)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Plugins can contribute detail tabs to project pages, and plugin UI
bundles render through a host-provided React bridge
> - A cold load of a plugin-tab deep link redirected to the Issues tab,
because the plugin-slots query is disabled until the project's company
resolves and a disabled query reports `isLoading: false`
> - The redirect made shareable plugin-tab URLs and browser
reload/back/forward unreliable for every detail-tab plugin
> - Separately, the bridge shim rebuilt `jsx`/`jsxs` on top of
`createElement(type, { children })`, which drops React's static-children
marking and floods the dev console with "unique key" warnings from
plugin components
> - This pull request holds the tab decision on a skeleton until
contributions actually load, and exposes the host's real
`react/jsx-runtime` on the plugin bridge with the old shim as fallback
> - The benefit is that plugin detail tabs survive direct open, reload,
back, and forward, and plugin surfaces stop emitting spurious React key
warnings

## Linked Issues or Issue Description

**What happened**

Opening a project URL with `?tab=plugin:<publisher>.<plugin>:<tab>`
directly, or reloading while on such a tab, redirected to the project's
Issues tab even though the plugin contribution was registered and
available. The dev console also showed "each child in a list should have
a unique key" warnings pointing at plugin-rendered components.

**Expected behavior**

Direct open, reload, back, and forward keep the requested plugin tab
when its contribution is available. When the contribution truly does not
exist, the page still falls back safely to the Issues tab. Plugin
components with static multi-child JSX render without React key
warnings.

**Steps to reproduce**

1. Install a plugin that contributes a project detail tab.
2. Open the tab, copy the URL, and open it in a new browser tab (or
press reload).
3. Observe the redirect to `/projects/<ref>/issues` before the
plugin-slots query has ever run.
4. With React in development mode, open any plugin tab that renders
sibling elements and observe key warnings in the console.

**Version or commit**

Reproduced on `master` at `733ffbf7c`.

**Deployment mode**

Local development instance (managed runtime).

## What Changed

- `ui/src/pages/ProjectDetail.tsx`: the plugin-tab fallback now waits
until the company is resolved and the plugin-slots query has finished
before it decides. While the decision is pending it renders the detail
`PageSkeleton` instead of navigating away. Loading a project that fails
to load still shows the error state.
- `ui/src/plugins/bridge-init.ts`: the plugin bridge registry now
exposes the host's real `react/jsx-runtime` module as `reactJsxRuntime`.
- `ui/src/plugins/slots.tsx`: the `react/jsx-runtime` shim served to
plugin bundles prefers the bridged runtime's `jsx`/`jsxs`/`Fragment` and
keeps the previous `createElement`-based implementation as a fallback.
- `ui/src/pages/ProjectDetail.test.tsx`: new tests for cold deep links —
pending slots query, disabled query before company resolution,
registered tab render, and fallback when the tab is not contributed.
- `ui/src/plugins/bridge.test.ts`: new tests that the bridge exposes the
real runtime and that unkeyed static children render through it without
key warnings.

## Verification

- `npx vitest run src/pages/ProjectDetail.test.tsx
src/plugins/bridge.test.ts` in `ui/` — 2 files, 18 tests, all pass.
- `pnpm check:token-gates` — all gates clean.
- Manual: open a project with a plugin detail tab, switch to the tab,
reload the browser, use back/forward, and open the URL in a fresh tab.
The tab persists. Remove the plugin and open the same URL. The page
falls back to the Issues tab.

## Risks

- Low risk. The fallback redirect still happens whenever contributions
finish loading without the requested tab; the change only defers it
until the answer is known, so an unavailable contribution cannot get
stuck on the skeleton.
- The jsx-runtime shim keeps the old `createElement` path as a fallback
when the bridged runtime is absent, so older bridge registries keep
working.

## Model Used

- Claude (Anthropic) — `claude-fable-5`, extended thinking enabled,
agentic tool use (file edits, shell, test execution) via Claude Code.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or similar PRs and linked
related PRs above where they exist
- [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
— *the branch predates this rule; squash-merge keeps the branch name off
`master`*
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
(code-level docs; no user-facing docs affected)
- [x] I have considered and documented any risks above
- [ ] 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*
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 23:22:49 -07:00
Devin Foley db4defdfbf
feat: operator-configurable settings visibility via PAPERCLIP_HIDDEN_SETTINGS (#11823)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The instance settings surface (Access, Plugins, Adapters, General,
Experimental) assumes the person at the keyboard operates the whole
instance
> - Operators who host Paperclip for others — a managed cloud or an
internal shared server — expose settings pages and toggles that do not
apply to their deployment, and the related mutation APIs stay open
> - A hosted tenant can open Plugins or Adapters, try an action, and hit
a confusing failure, because only a few hardcoded platform floors exist
> - This pull request adds a generic, operator-configured visibility
mechanism: one env var hides declared settings surfaces in the UI and
floors their mutation routes with a stable 403 code
> - The benefit is a clean hosted-tenant settings surface for any
operator, with zero behavior change for normal self-hosted instances

## Linked Issues or Issue Description

**Subsystem affected**

Instance settings (server routes and UI), the shared settings registry
in `packages/shared`, and the `/api/health` bootstrap payload.

**Problem or motivation**

An operator who hosts Paperclip for other people cannot hide settings
surfaces that the platform manages. Tenants see Access, Plugins, and
Adapters pages, backup retention, and host-level experimental toggles
that do nothing useful for them. The mutation APIs behind these surfaces
also stay open, so a tenant admin can attempt actions the platform must
control. ROADMAP.md names a cleaner shared deployment story as a goal
("Teams should be able to run the same product in hosted or semi-hosted
environments without changing the mental model").

**Proposed solution**

Add a declarative registry of hideable settings surfaces and one env
var, `PAPERCLIP_HIDDEN_SETTINGS`. The server parses the list at boot,
reports it on `/api/health`, and rejects value-changing writes to hidden
surfaces with a stable `settings_operator_managed` 403 code. The UI
reads the list from the health payload and removes the hidden pages,
sections, and toggles from navigation, routes, and page content. Unknown
keys warn and are ignored, so one list can roll across a fleet with
mixed app versions. With the variable unset, behavior is byte-identical
to today.

**Alternatives considered**

- Hardcode the hidden set for cloud instances in this repo: rejected,
because each hosting operator needs a different policy, and policy does
not belong in shared code.
- Deliver the hidden set through the managed-config document: rejected,
because that channel is cloud-specific and fail-closed on unknown
fields; a plain env var works for any operator, including self-hosted
shared servers.
- Lock the controls with a badge instead of hiding them: rejected for
these surfaces, because they are meaningless to tenants, not merely
platform-controlled; the existing managed-overlay lock stays the right
tool for controlled flags.

**Roadmap alignment**

Supports the "shared deployment story" item in ROADMAP.md: hosted and
semi-hosted deployments keep the same product with a settings surface
that matches what the tenant can actually do.

## What Changed

- New `packages/shared/src/settings-visibility.ts`: registry of hideable
surfaces (every instance settings page — profile, environments, access,
heartbeats, experimental, plugins, adapters; every Instance → General
section; every experimental flag as `instance.experimental.<key>`), the
`PAPERCLIP_HIDDEN_SETTINGS` parser, and the `settings_operator_managed`
error code. The General page stays visible as the settings root and
redirect target.
- New `server/src/services/settings-visibility.ts`: parse-once accessor;
unknown keys log one warning and are ignored.
- `/api/health` reports `hiddenSettings` on every response shape; the
field is omitted when nothing is hidden.
- Server floors on hidden surfaces, with same-value echo tolerance (the
`executionMode` precedent): field-backed general sections and
experimental keys reject value-changing PATCHes, and hiding the whole
Experimental page floors every toggle; plugin lifecycle and config
writes, adapter management writes, and the Access admin routes (reads
included) return 403 `settings_operator_managed`. Reads the app itself
needs (plugin `ui-contributions`, adapter metadata, plugin job trigger)
stay open. Pages without instance-scoped mutation routes are hidden in
the UI only.
- UI: new `useHiddenSettings` hook and `HiddenSettingsPageGate` route
gate (hidden pages redirect to the settings root); the settings sidebar
and tab bar drop hidden entries; remembered settings paths remap to the
default page; `InstanceGeneralSettings` skips hidden sections; every
`ExperimentalToggleCard` now carries its flag key and renders nothing
when hidden.
- Removed the dead `InstanceSidebar` component (referenced only by its
own test).
- Docs: `docs/deploy/environment-variables.md` documents the variable
and the key registry.

## Verification

- `pnpm vitest run` over the new and extended suites: shared registry
and parser, representative floor tests per route class (changed-value
403, same-value echo 200, unset env 200, page-level Experimental
hiding), the health field, the route gate, nav filtering, and
section/card hiding with one hidden example per surface kind — 168 tests
pass.
- Full root `pnpm typecheck` passes.
- Manual: booted a server with the variable set. `/api/health` lists the
keys; an unknown key logs one warning and the server boots; hidden pages
redirect; hidden sections and cards do not render; hidden-field PATCH
returns 403 with `details.code = "settings_operator_managed"`; a
same-value echo returns 200. Unset the variable: the full settings
surface returns and responses are byte-identical to master.

## Risks

- Low risk for self-hosted instances: with the variable unset, the
hidden set is empty, the health field is omitted, and no floor
activates.
- Flooring plugin config writes assumes hosted deployments configure
plugins through the platform. If a future bundled plugin needs
tenant-entered config, the floor needs a narrow carve-out.
- Hidden-key floors tolerate same-value echoes, so API clients that
round-trip full GET responses keep working.
- Hiding a toggle does not change its value; operators pair hiding with
the desired default where the value matters.

## Model Used

Claude Fable 5 (Anthropic, `claude-fable-5`) with extended thinking and
agentic tool use, driven through the Claude Code CLI (file edits, test
execution, and live-server verification loops).

## 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-20 17:54:44 -07:00
Michael Nguyen 0fa318b8da
feat(artifacts): bridge Markdown work products into the document review surface (#11822) 2026-08-20 17:28:01 -07:00
dependabot[bot] 733ffbf7c3
build(deps-dev): bump @vitejs/plugin-react from 4.7.0 to 6.0.5 (#11710)
Bumps
[@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react)
from 4.7.0 to 6.0.5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite-plugin-react/releases">@​vitejs/plugin-react's
releases</a>.</em></p>
<blockquote>
<h2>plugin-react@6.0.5</h2>
<h3>Fixed the react compiler preset filter to be linear (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1353">#1353</a>)</h3>
<p>The improved filter in v6.0.3 was non-linear and caused a performance
regression (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/issues/1349">#1349</a>).
The filter was changed to be linear to avoid that.</p>
<h2>plugin-react@6.0.4</h2>
<h3>Fixed <code>$RefreshSig$ is not defined</code> error when running
<code>vite dev</code> with <code>NODE_ENV=production</code></h3>
<p>When running <code>vite dev</code> with
<code>NODE_ENV=production</code>, the app errored with
<code>$RefreshSig$ is not defined</code>.
This error is now fixed.</p>
<h2>plugin-react@6.0.3</h2>
<p>No release notes provided.</p>
<h2>plugin-react@6.0.2</h2>
<h3>Allow all options in reactCompilerPreset (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1189">#1189</a>)</h3>
<p>This is a type only change. Only <code>compilationMode</code> and
<code>target</code> options were available for
<code>reactCompilerPreset</code>.</p>
<h2>plugin-react@6.0.1</h2>
<h3>Expand <code>@rolldown/plugin-babel</code> peer dep range (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1146">#1146</a>)</h3>
<p>Expanded <code>@rolldown/plugin-babel</code> peer dep range to
include <code>^0.2.0</code>.</p>
<h2>plugin-react@6.0.0</h2>
<h3>Remove Babel Related Features (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1123">#1123</a>)</h3>
<p>Vite 8+ can handle React Refresh Transform by Oxc and doesn't need
Babel for it. With that, there are no transform applied that requires
Babel. To reduce the installation size of this plugin, babel is no
longer a dependency of this plugin and the related features are
removed.</p>
<p>If you are using Babel, you can use
<code>@rolldown/plugin-babel</code> together with this plugin:</p>
<pre lang="diff"><code> import { defineConfig } from 'vite'
 import react from '@vitejs/plugin-react'
+import babel from '@rolldown/plugin-babel'
<p>export default defineConfig({
plugins: [</p>
<ul>
<li>
<pre><code>react({
</code></pre>
</li>
<li>
<pre><code>  babel: {
</code></pre>
</li>
<li>
<pre><code>    plugins: ['@babel/plugin-proposal-throw-expressions'],
</code></pre>
</li>
<li>
<pre><code>  },
</code></pre>
</li>
<li>
<pre><code>}),
</code></pre>
</li>
</ul>
<ul>
<li>
<pre><code>react(),
</code></pre>
</li>
<li>
<pre><code>babel({
</code></pre>
</li>
<li>
<pre><code>  plugins: ['@babel/plugin-proposal-throw-expressions'],
</code></pre>
</li>
<li>
<pre><code>}),
</code></pre>
]
})
</code></pre></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md">@​vitejs/plugin-react's
changelog</a>.</em></p>
<blockquote>
<h2>6.0.5 (2026-07-30)</h2>
<h3>Fixed the react compiler preset filter to be linear (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1353">#1353</a>)</h3>
<p>The improved filter in v6.0.3 was non-linear and caused a performance
regression (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/issues/1349">#1349</a>).
The filter was changed to be linear to avoid that.</p>
<h2>6.0.4 (2026-07-22)</h2>
<h3>Fixed <code>$RefreshSig$ is not defined</code> error when running
<code>vite dev</code> with <code>NODE_ENV=production</code></h3>
<p>When running <code>vite dev</code> with
<code>NODE_ENV=production</code>, the app errored with
<code>$RefreshSig$ is not defined</code>.
This error is now fixed.</p>
<h2>6.0.3 (2026-06-23)</h2>
<h3>Improve the react compiler preset filter to reduce false-positives
(<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1138">#1138</a>)</h3>
<p>Improved the filter in the react compiler babel preset to reduce the
false-positives so that less modules are processed by the react
compiler.</p>
<h2>6.0.2 (2026-05-14)</h2>
<h3>Allow all options in reactCompilerPreset (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1189">#1189</a>)</h3>
<p>This is a type only change. Only <code>compilationMode</code> and
<code>target</code> options were available for
<code>reactCompilerPreset</code>.</p>
<h2>6.0.1 (2026-03-13)</h2>
<h3>Expand <code>@rolldown/plugin-babel</code> peer dep range (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1146">#1146</a>)</h3>
<p>Expanded <code>@rolldown/plugin-babel</code> peer dep range to
include <code>^0.2.0</code>.</p>
<h2>6.0.0 (2026-03-12)</h2>
<h2>6.0.0-beta.0 (2026-03-03)</h2>
<h3>Remove Babel Related Features (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1123">#1123</a>)</h3>
<p>Vite 8+ can handle React Refresh Transform by Oxc and doesn't need
Babel for it. With that, there are no transform applied that requires
Babel. To reduce the installation size of this plugin, babel is no
longer a dependency of this plugin and the related features are
removed.</p>
<p>If you are using Babel, you can use
<code>@rolldown/plugin-babel</code> together with this plugin:</p>
<pre lang="diff"><code> import { defineConfig } from 'vite'
 import react from '@vitejs/plugin-react'
+import babel from '@rolldown/plugin-babel'
<p>export default defineConfig({
plugins: [</p>
<ul>
<li>
<pre><code>react({
</code></pre>
</li>
<li>
<pre><code>  babel: {
</code></pre>
</li>
</ul>
<p>&lt;/tr&gt;&lt;/table&gt;
</code></pre></p>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="68c0cb8796"><code>68c0cb8</code></a>
release: plugin-react@6.0.5 (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1362">#1362</a>)</li>
<li><a
href="555cdbc126"><code>555cdbc</code></a>
fix(react): make the react compiler preset filter linear (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1353">#1353</a>)</li>
<li><a
href="a00a9f8240"><code>a00a9f8</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1327">#1327</a>)</li>
<li><a
href="f4b549822e"><code>f4b5498</code></a>
release: plugin-react@6.0.4</li>
<li><a
href="7a4065906e"><code>7a40659</code></a>
fix(react): <code>$RefreshSig$ is not defined</code> with
NODE_ENV=production vite dev ...</li>
<li><a
href="98b32d47a3"><code>98b32d4</code></a>
fix(deps): update react 19.2.8 (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1298">#1298</a>)</li>
<li><a
href="8ae5449be2"><code>8ae5449</code></a>
fix: babel-plugin-react-compiler cannot be imported when used in a
framework ...</li>
<li><a
href="f09ea01b0b"><code>f09ea01</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1282">#1282</a>)</li>
<li><a
href="640fd358a0"><code>640fd35</code></a>
release: plugin-react@6.0.3</li>
<li><a
href="889efb02cd"><code>889efb0</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1249">#1249</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.0.5/packages/plugin-react">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>@​vitejs/plugin-react</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>
Co-authored-by: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-20 16:23:26 -07:00
Dotta cb0009b097
fix: preserve recovery retries across restarts (#11817)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The control plane must keep each active issue on a clear execution
or recovery path.
> - A missing issue disposition can require more than one bounded repair
attempt.
> - A server restart could lose that repair path or move source
ownership to the recovery owner.
> - A parked or expired retry could also make the user interface show a
false healthy state.
> - Concurrent recovery loops must not schedule the same repair attempt
twice.
> - This pull request keeps retry state durable, makes scheduling
atomic, and keeps source ownership stable.
> - The benefit is that recovery continues after a restart and operators
see the correct state.

## Linked Issues or Issue Description

**What happened?**

A run that ended without a valid issue disposition could lose its repair
path after a server restart. Manager recovery could also change the
source owner. In addition, a parked or expired retry could make the
issue look healthy when no active work existed. Concurrent
reconciliation could also schedule the same repair attempt twice.

**Expected behavior**

Paperclip must keep bounded source and manager repair attempts across
restarts. Recovery ownership must stay separate from source issue
ownership. The server and user interface must report only a live retry
as active work. Each repair attempt must be scheduled at most once per
company.

**Steps to reproduce**

1. Start an agent run on an issue.
2. End the run without a valid issue disposition.
3. Let the first repair attempt schedule a retry.
4. Restart the server, let the retry time pass without a live run, or
start two reconciliation loops together.
5. Observe that the repair path can stop, the issue can show a false
healthy state, or duplicate retries can be created.

**Paperclip version or commit**

The problem existed on `master` before candidate head
`d8e620fe86bade7df18decac332007f5821ae04f`.

**Deployment mode**

The problem affects self-hosted servers and local builds that use
automatic recovery.

## What Changed

- Persist bounded source-owner and manager repair lineages with stable
fingerprints and retry limits.
- Resume incomplete disposition repairs after a server restart.
- Keep recovery ownership separate from source issue ownership and
enforce source mutation authority.
- Project live retry evidence into issue and blocker summaries.
- Show recovery owner, return owner, attempt count, and retry state in
the board user interface.
- Treat expired or parked retries as attention states unless a queued or
running attempt exists.
- Atomically deduplicate disposition-repair wake requests with a
company-scoped partial unique index.
- Reuse the winning run when concurrent reconciliation loses the
uniqueness race, without duplicate scheduling activity.
- Honor disabled on-demand wake policy before recovery scheduling and
again before delayed retry promotion.
- Keep the new index migration safe for lagging seeded databases that
already contain the index.
- Add server and user interface tests for recovery, restart, ownership,
retry, concurrency, and blocker states.
- Update the implementation and execution semantics documents.

## Verification

- Focused server recovery and ownership suites: 282 tests passed on the
repaired base candidate.
- Focused user interface recovery suites: 128 tests passed on the
repaired base candidate.
- Atomic-deduplication schema and recovery suites: 111 tests passed on
the first Greptile repair.
- Recovery and scheduled-retry wake-policy suites: 126 tests passed at
`d8e620fe86bade7df18decac332007f5821ae04f`.
- The exact lagging-source migration-order test passed after the index
migration became idempotent: 1 test passed and 62 unrelated tests were
skipped.
- `@paperclipai/db` and `@paperclipai/server` typechecks passed at the
current head.
- Migration generation and migration safety checks passed for migration
`0226_tan_colossus.sql`.
- `pnpm check:token-gates` passed on the repaired base candidate.
- `pnpm -r typecheck` passed on the repaired base candidate.
- `pnpm build` passed on the repaired base candidate.
- `pnpm test:run` passed 4,540 tests on the repaired base candidate.
Four fixed-port cases met listeners that already existed on the host.
- The two unchanged fixed-port files passed in an isolated network
namespace: 129 tests passed and 27 tests were skipped.
- Independent Security and QA reviews approved
`63c0423aab54c66f2293a20b0fb3f3b013ee3ba8`; exact-head re-review is
required after automated checks settle on
`d8e620fe86bade7df18decac332007f5821ae04f`.

## Risks

- Recovery orchestration affects issue liveness and ownership. The new
paths use bounded attempts, stable fingerprints, row locks, authority
checks, and database uniqueness.
- A conservative attention state can show more warnings when a scheduled
retry has no queued or running attempt. It does not hide stopped work.
- Migration `0226_tan_colossus.sql` creates a partial unique index on a
known-large table. Migrations run transactionally, so `CONCURRENTLY` is
unavailable. The matching disposition-repair key namespace is introduced
by this release, so deployed databases have no matching rows before the
index is added.

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

## Model Used

- OpenAI Codex from the GPT-5 model family used agentic reasoning, tool
use, and code execution. The runtime did not expose the exact model ID
or context window.
- Anthropic Claude Opus 5 used a 1M context window, tool use, and code
execution for part of the user interface repair, as recorded in the
commit history.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 17:09:42 -05:00
Devin Foley de9645ab73
fix(ui): surface live runtime status in the task-chat tail before the first transcript token (#11802)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Runs on sandbox execution targets spend their first minutes in
preparation phases — config seed, workspace and skills sync into the
sandbox — before the agent CLI produces its first transcript token
> - The engine already reports these phases through the runtime-progress
mechanism (`onRuntimeProgress` →
`recordCurrentHeartbeatRunRuntimeProgress` → `currentStatusMessage` on
the live run), and the pre-task-chat issue view surfaced them in its
live status line
> - The chat-style task view's live tail dropped that affordance: with
zero renderable transcript entries it shows an opaque "Waiting for
transcript..." for minutes, which reads as a hang (and prompted a real
is-this-broken investigation on a healthy run)
> - This pull request surfaces the live run's `currentStatusMessage` as
the tail's empty-state message, with the generic wait text as fallback
> - The benefit is that operators watching a sandbox run see "Syncing
workspace to sandbox" instead of wondering whether the run is stuck —
for every adapter, with no adapter identities involved

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The chat-style task view's live transcript tail (`TaskChatThread` →
`TaskChatLiveTail`), introduced with the experimental chat-style task
view.

**Current behavior**

While a live run has no renderable transcript entries yet — the normal
state for the multi-minute sandbox preparation window — the tail shows a
static "Waiting for transcript...". The run's live
`currentStatusMessage` (e.g. "Syncing workspace to sandbox", emitted by
the sandbox-managed runtime's progress reporting) is available on the
same live-run object but unused by this surface, although the earlier
issue-chat view did display it.

**Proposed behavior**

When the tail is streaming a live run and no transcript rows exist yet,
the empty-state message prefers the run's `currentStatusMessage`; the
generic wait text remains the fallback when no runtime status has been
reported (e.g. local runs that produce output immediately, or the brief
pre-status window).

**Reason and benefit**

The preparation phases are real, reportable progress that the engine
already emits. Showing them turns a minutes-long apparent hang into a
legible status, for every adapter and execution target, using data the
view already receives.

## What Changed

- `ui/src/components/TaskChatThread.tsx`: the live tail's `emptyMessage`
prefers `liveRun.currentStatusMessage` (guarded to the run the tail is
actually streaming) over the static "Waiting for transcript..."
fallback. Queued runs keep "Waiting to start...".
- `ui/src/components/TaskChatThread.test.tsx`: a test covering both
branches — a live run with a runtime status shows it (and not the wait
text), and a run without one keeps the generic message.

## Verification

- `vitest run ui/src/components/TaskChatThread.test.tsx
ui/src/components/task-chat/TaskChatLiveTail.test.tsx`: 22/22 pass
(including the new test)
- `pnpm --filter @paperclipai/ui typecheck`: clean
- Reproduced live: a `claude_local` run on a Daytona sandbox environment
showed "Waiting for transcript..." for the full sync window; with this
change the same window shows the streamed preparation statuses

## Risks

- Low: a one-expression change to an empty-state string, active only
while a live run has produced no renderable transcript rows. The
fallback path is byte-identical to today.
- `currentStatusMessage` is truncated/humanized upstream by the
runtime-progress reporter; this surface renders it verbatim in the same
muted style as the wait text.

## Model Used

- Anthropic, **Claude Fable 5** (`claude-fable-5`) via Claude Code, with
repository, shell, and Git tooling. It traced the runtime-progress
mechanism end-to-end (engine emitter → heartbeat recorder → live-run API
→ both thread views), identified the dropped affordance in the
chat-style view, and wrote the fix 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
- [ ] 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-20 12:47:20 -07:00
David V 233c12f029
feat: add kimi-local adapter for Kimi Code CLI (CLI + ACP engines) (#9967)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Local agent adapters (`claude_local`, `gemini_local`, `grok_local`,
…) are the integration surface that lets Paperclip run coding CLIs on
the host machine
> - The Kimi Code CLI (`kimi`, Moonshot AI) has a documented
non-interactive mode, `kimi -p --output-format stream-json` with session
resume via `kimi -r`, but Paperclip has no built-in adapter for it
> - So Kimi users (especially Kimi membership / OAuth subscribers)
cannot onboard their CLI to Paperclip agent teams
> - This pull request adds a complete built-in `kimi_local` adapter
(both execution engines, session management, instructions + skills
delivery, thinking-effort control, environment test, UI and CLI modules,
docs) following the established `gemini_local`/`grok_local` package
pattern
> - Kimi Code ships an ACP server (`kimi acp`), so the adapter runs on
Paperclip's shared acpx engine by default (streaming transcript with
live tool status, like `claude_local`/`gemini_local`) and falls back to
a headless CLI lane (`kimi -p --output-format stream-json`) when ACP
prerequisites are unavailable
> - The benefit is that Kimi Code becomes a first-class Paperclip agent
lane: selectable in the UI, resumable across heartbeats, with the same
operating context (instruction bundle, skills, effort) and streaming
transcript the other local adapters get

## Linked Issues or Issue Description

- Supersedes #9880 (same branch; expanded from the CLI-only lane into a
complete adapter with the default ACP engine lane, control-plane skill
install, and live transcript wiring)
- Refs #9879 (adapter request for Kimi Code CLI, filed with this PR)
- Refs #163 (original Kimi support request)

Duplicate/related prior PRs, per the dedup search (both appear stale: no
updates or maintainer review since May 2026, and both target an older
Kimi CLI interface; calling them out for reviewer context per
CONTRIBUTING.md):

- Refs #6276 (`feat: add kimi-local adapter`): targets an older
array-based content format (`{type: think}`/`{type: text}` blocks), not
the current documented stream-json schema
- Refs #5202 (`feat(adapter): add Kimi CLI local adapter with Wire
protocol support`): builds on a `--wire` JSON-RPC interface that current
Kimi Code CLI (0.27.0) no longer documents; the current documented
headless interface is `-p --output-format stream-json`

This PR is a fresh implementation against current master and the
currently documented/verified Kimi CLI behavior (see Verification).
Happy to fold in anything useful from the earlier attempts if a reviewer
prefers.

## What Changed

- **New adapter package** `packages/adapters/kimi-local`
(`@paperclipai/adapter-kimi-local`), modeled on
`gemini-local`/`grok-local`:
- `src/server/execute.ts`: spawns `kimi -p <prompt> --output-format
stream-json` (argv array, no shell), `-m <model>` only when configured,
`-r <sessionId>` when the stored session cwd matches the run cwd,
automatic fresh-session retry on unrecoverable-session errors,
headless-safe env (`CI=1`, `NO_COLOR=1`, `KIMI_CODE_NO_AUTO_UPDATE=1`,
`TERM=dumb`; user-configured values win), full remote (ssh/sandbox)
execution lane with runtime install via `@moonshot-ai/kimi-code`
- **Instruction bundle delivery**: the prompt path directive now names
the sibling instruction files (`./HEARTBEAT.md`, `./SOUL.md`,
`./TOOLS.md`) alongside the prepended entry file, and local runs pass
`--add-dir <instructions-dir>` so Kimi can actually open them (matching
`claude_local`). Without this, only the entry file reached Kimi and
agents improvised the operating workflow that `HEARTBEAT.md` documents
- **Thinking effort**: a configured `effort` is forwarded as the
`KIMI_MODEL_THINKING_EFFORT` operational override (Kimi has no
per-invocation effort flag). It is only sent for models that advertise
`support_efforts` (currently `kimi-code/k3`) to avoid provider
rejections, and `medium` maps to `high` since Kimi has no medium tier
(`low`/`high`/`max` pass through)
- **Skills delivery**: desired Paperclip skills are delivered via Kimi's
`--skills-dir` flag from a dedicated per-run directory (a local
snapshot, or the synced snapshot on remote targets), so skills load
reliably and in isolation. Paperclip never overwrites the shared
`$KIMI_CODE_HOME/skills` home, so skills installed by the operator or
other agents are left intact. `--skills-dir` is only passed when at
least one skill is desired, so unconfigured agents keep Kimi's default
skill discovery
- **Live run status**: the adapter now forwards each streamed
stream-json line to `onEvent` (assistant `content` as an assistant
snippet, `tool_calls` as tool-name events), which drives the
issue-thread activity indicator (`currentToolName` /
`lastAssistantSnippet` / `lastEventAt`). Previously the adapter only
wrote the raw run log, so the issue thread showed a stale "no output for
N s" line with no tool or reasoning context while Kimi worked. Tool
results are omitted so the last meaningful "Using X" / snippet is not
overwritten by a generic label
- `src/server/parse.ts`: parses the verified Kimi stream-json event
shapes (`assistant` text, `assistant.tool_calls` with JSON-string
arguments, `tool` results, trailing `meta.session.resume_hint` for
session-id capture) plus failure classifiers (`kimi_auth_required`,
transient network, unrecoverable session). A signaled exit (null exit
code, not a timeout) is now reported as a failure rather than coalesced
to success, and the error message names the terminating signal
- `src/server/skills.ts`: lists/syncs Paperclip skills for the adapter's
skill-management surface
- `src/server/test.ts`: environment test covering CLI resolution + `kimi
--version`, cwd check, auth detection (OAuth credential dirs, keyed
`[providers.*]` in config.toml, or the `KIMI_MODEL_NAME` +
`KIMI_MODEL_API_KEY` env pair), and a live hello probe
- `src/ui/` (stdout-line parser for transcripts, config builder) and
`src/cli/` (stream event formatter) modules
- Root metadata: three managed model aliases
(`kimi-code/kimi-for-coding`, `kimi-code/kimi-for-coding-highspeed`,
`kimi-code/k3`), effort-capable-model metadata (`EFFORT_CAPABLE_MODELS`,
effort mapping helpers), `agentConfigurationDoc`
- Tests: 101 tests across parse, execute (args building, resume gating,
retry, auth error code, timeout, signaled-exit failure, effort
forwarding/gating/mapping, `--add-dir` instructions directive,
`--skills-dir` gating, `onEvent` runtime-event forwarding), ACP engine
(engine resolution, acpx config build, node-version gate), ACP
transcript delegation, environment test, UI parse/build-config
- **ACP engine lane (default)** (`src/server/acp.ts` + shared
`adapter-utils/acpx-engine`): Kimi Code ships an ACP server (`kimi
acp`), so `kimi_local` now runs on Paperclip's shared acpx engine by
default, matching `claude_local`/`codex_local`/`gemini_local`. The
issue-thread transcript streams live (assistant text deltas, tool calls
with a `pending`->`completed` status lifecycle) instead of the CLI
lane's bursty complete-message output. Registered `kimi_local -> "kimi"`
in `ACPX_ADAPTER_AGENT_IDS` and resolved the built-in agent command to
`kimi acp`; `execute.ts` dispatches to the ACP executor first with an
automatic CLI fallback when ACP prerequisites fail (`engine=acp`
requires ACP, `engine=cli` pins the headless lane); `index.ts` falls
back to the shared acpx session codec; the UI/CLI delegate `acpx.*`
events to the shared acpx transcript parser and event formatter. The
headless CLI lane (above) remains as the fallback
- **Registration** (one entry each, mirroring existing adapters): server
adapter registry + `BUILTIN_ADAPTER_TYPES`, `AGENT_ADAPTER_TYPES`
(shared), UI adapter registry + display registry (`Kimi Code`, Moon
icon) + capabilities defaults, CLI adapter registry, `Dockerfile`
(package copy + `npm install --global @moonshot-ai/kimi-code@latest`),
`vitest.config.ts` workspace, `scripts/release-package-manifest.json`
- **Behavioral sets** mirroring `gemini_local` (Kimi resumes sessions
the same way): `GIT_SENSITIVE_LOCAL_ADAPTER_TYPES`,
`SESSIONED_LOCAL_ADAPTERS` (heartbeat + recovery),
`REMOTE_MANAGED_ADAPTERS`, ssh/sandbox execution-target allow-lists,
`ADAPTER_DEFAULT_RULES_BY_TYPE` (`timeoutSec: 0`, `graceSec: 15`), and
`LEGACY_SESSIONED_ADAPTER_TYPES` + `ADAPTER_SESSION_MANAGEMENT` in
adapter-utils
- **UI touch-points**: New Agent default-model branch, AgentConfigForm
command map (`kimi_local: "kimi"`) + model defaults + a Kimi-specific
thinking-effort option list (`Low`/`High`/`Max`, reflecting Kimi's tiers
rather than borrowing Claude's), OnboardingWizard (command map, model
default, `kimi login` / `KIMI_MODEL_NAME + KIMI_MODEL_API_KEY` auth
hints, manual-debug command line), InviteLanding enabled adapters
- **Control-plane skill install** (`cli/src/commands/client/agent.ts`):
`paperclipai agent local-cli` seeded the Paperclip control-plane skills
into `~/.codex/skills` and `~/.claude/skills` so Codex/Claude agents
auto-discover the API reference every run. Kimi had no equivalent
target, so `kimi_local` agents began each session without the
control-plane skill and rediscovered routes (e.g. the company-scoped
`POST /api/companies/{companyId}/issues`) by trial and error. Added
`~/.kimi-code/skills` (honoring `KIMI_CODE_HOME`) as a third install
target for parity. Independent of the per-run `--skills-dir` delivery,
which only applies to explicitly configured skills.
- **Docs**: `docs/adapters/kimi-local.md` (prerequisites, auth options,
config fields including `effort`, session resume, instruction bundle,
skills delivery, control-plane skill install) + a row in
`docs/adapters/overview.md`

Out of scope (deliberately): model profiles, built-in agent
`allowedAdapterTypes` additions.

## Verification\n\nCurrent-master rebase verification (OpenAI Codex,
2026-08-03): 13 focused files / 231 tests pass; adapter-utils, server,
UI, CLI, and Kimi adapter typechecks pass; full repository build and UI
token gates pass. The branch is conflict-free against master at head
`1249df117c5e12e5771b9a570a6340866450619e`.\n\nAutomated (all from repo
root, pnpm 9.15.4, Node 22):

- `vitest run packages/adapters/kimi-local`: 89/89 pass (includes
coverage for the instruction `--add-dir` directive, effort
forwarding/gating/mapping, `--skills-dir` gating, the signaled-exit
failure path, and `onEvent` runtime-event forwarding with cross-chunk
line buffering)
- `vitest run server/src/__tests__/adapter-registry.test.ts
server/src/__tests__/adapter-routes.test.ts
server/src/services/heartbeat-stop-metadata.test.ts
ui/src/adapters/adapter-display-registry.test.ts`: 37/37 pass
- `vitest run cli/src/__tests__/skills.test.ts`: 13/13 pass (the
control-plane skill install target follows the existing Codex/Claude
install path, whose symlink logic is unchanged)
- `vitest run packages/shared`: 307/307 pass; `vitest run
packages/adapter-utils`: pass except one pre-existing, unrelated failure
(`mcp-isolation.integration.test.ts` requires Claude CLI ≥ 2.1.207; host
has 2.1.185, fails identically on unmodified master)
- `pnpm --filter @paperclipai/adapter-kimi-local typecheck|build`, plus
typecheck of `server`, `ui`, `cli`, `adapter-utils`: all clean
- `pnpm install --frozen-lockfile`: passes (the PR diff itself contains
no lockfile changes, per repo policy; verified against a locally
regenerated lockfile)
- `node scripts/check-no-git-push.mjs` and `node
scripts/check-forbidden-tokens.mjs`: pass
- CI note: the `policy` job's release-bootstrap step is expected to stay
red until a maintainer bootstraps the first npm publish of
`@paperclipai/adapter-kimi-local`; see the CI Note for Maintainers
comment. All other contributor-actionable checks are green.

Manual end-to-end (real Kimi CLI 0.27.0, OAuth login, dev server on an
isolated instance):

1. Server `GET /api/adapters` lists `kimi_local` as builtin with correct
capability flags; models endpoint returns the three Kimi models
2. `POST .../adapters/kimi_local/test-environment`: all checks pass,
including a live `kimi -p` hello probe
3. Created a `kimi_local` agent and invoked two heartbeats: run 1
spawned `kimi -p ... --output-format stream-json`, Kimi used its `Read`
tool, produced the expected answer, and the session id was captured from
the `session.resume_hint` meta event; run 2 resumed the **same** Kimi
session (`sessionIdBefore == sessionIdAfter`) via `-r`
4. UI: adapter appears in the New Agent dropdown; selecting it shows the
Kimi command placeholder, the three models, and the Kimi config fields;
the run transcript renders Kimi tool calls via the adapter's stdout
parser

The instruction-bundle, thinking-effort, and `--skills-dir` changes
landed after the manual run above. They are covered by the unit tests
listed under Automated, and the Kimi CLI flags they rely on
(`--add-dir`, `--skills-dir`, `KIMI_MODEL_THINKING_EFFORT`) were
confirmed against the installed Kimi Code CLI 0.27.0 (`kimi --help`,
config-file thinking-effort docs).

Screenshots (assets branch on the fork, not part of the diff):

![Kimi Code in the Add a new agent runtime
picker](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/00-kimicode.png)

![Adapter dropdown with Kimi
Code](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/01-adapter-dropdown-kimi.png)

![Kimi adapter selected: command, model, config
fields](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/02-kimi-adapter-selected.png)

![Kimi models in the model
dropdown](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/03-kimi-model-dropdown.png)

![Successful resumed heartbeat run (kimi_local invocation + parsed
transcript)](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/04-successful-resumed-run.png)

![Agents list showing the Kimi Code
label](https://raw.githubusercontent.com/hawikk/paperclip/assets/kimi-local-pr-screenshots/shots/05-agents-list.png)

## Risks

- Low risk to existing behavior: the change is additive, one new
workspace package plus single-entry registrations alongside existing
adapters; no existing adapter code paths are modified.
- The adapter invokes the locally installed `kimi` CLI; like other local
adapters, run behavior depends on the host's Kimi version. The parser is
written against the documented/verified 0.27.0 stream-json schema and
degrades gracefully (malformed lines are skipped, failures surface as
run errors).
- `--skills-dir` overrides Kimi's auto-discovery of user and project
skills for the run. This is intentional (paperclip-managed agents get a
reproducible, isolated skill set), and it is only passed when at least
one Paperclip skill is desired, so unconfigured agents keep default
discovery.
- Thinking effort is only forwarded to models that advertise
`support_efforts` (currently `kimi-code/k3`); `EFFORT_CAPABLE_MODELS`
must be extended when more Kimi models gain support, otherwise a
configured effort is silently ignored for them.
- `Dockerfile` now installs `@moonshot-ai/kimi-code@latest` globally
alongside the other agent CLIs, so image size increases slightly.
- Maintainer action needed for the npm bootstrap gate: the `policy`
job's release-bootstrap step fails until the first npm publish of
`@paperclipai/adapter-kimi-local` (the gate from #5146 that every new
adapter package has passed through). Enrollment with `publishFromCi:
true` is required by the manifest validator (dropping the entry,
`false`, or `private` are all rejected), so this is intentionally left
to a maintainer. Remaining CI lanes are expected to run once it is done.

## Model Used\n\n- **Current-master rebase, conflict adaptation, and
registry-parity coverage:** OpenAI, **GPT-5 Codex** (Codex agent; exact
serving model ID and context-window size were not exposed to the
runtime), with repository, shell, Git, and GitHub tooling. It preserved
Hawik’s commit authorship, reconciled ACPX and environment-capability
changes, added current registry tests, and ran the verification
above.\n- **Adapter implementation and initial review:** Moonshot AI,
**Kimi K3 Coding** (latest), via **Kimi Code CLI v0.27.0**
(`kimi-code/k3` alias, 1M-token context window, thinking mode, agentic
tool use). The CLI agent explored the repo, wrote the adapter
implementation (delegated to a coder sub-agent of the same model), ran
tests, and drafted the first version of this PR body. A second
model-driven review pass (read-only, same model) audited the diff for
security/correctness before submission; its findings (shell-quoting
hardening, auth-detection false positive, session-compaction
registration, test gaps) were fixed and are included.
- **Harness-context fixes and review responses:** Anthropic, **Claude
Opus 4.8** (`claude-opus-4-8`) via Claude Code. Diagnosed from run logs
that Kimi received only the entry instructions file (not the
`HEARTBEAT.md`/`SOUL.md`/`TOOLS.md` bundle) and that `effort` was never
wired, then implemented the instruction `--add-dir` delivery,
`KIMI_MODEL_THINKING_EFFORT` forwarding, and `--skills-dir` skill
delivery, added the accompanying tests and docs, and addressed the
automated review comments (preserving external skills on remote sync,
treating a signaled exit as a failure). Also extended the `paperclipai
agent local-cli` installer to seed the control-plane skills into
`~/.kimi-code/skills` for Codex/Claude parity, wired `onEvent` runtime
events so the issue-thread activity indicator reflects Kimi's tool and
reasoning output live, and built the ACP engine lane (`kimi acp` via the
shared acpx engine, default) so the transcript streams with live tool
status like the other ACP adapters. The Kimi CLI flags, subcommand, and
env var relied on here were verified against the installed Kimi Code CLI
0.27.0.
- All CLI behaviors claimed here (`-p`, `--output-format stream-json`,
`-r` resume, event shapes, `--add-dir`, `--skills-dir`,
`KIMI_MODEL_THINKING_EFFORT`) were verified empirically against the
installed Kimi CLI, not assumed.

## 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 *(only the release-bootstrap step
remains red, pending the maintainer npm publish described in Risks)*
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
*(will address all Greptile comments as they arrive)*
- [x] I will address all Greptile and reviewer comments before
requesting merge





---

## Maintainer Addendum (2026-08-20)

The shared acpx-engine and issue-chat changes (run-summary segmentation,
placeholder tool-event coalescing,
`ISSUE_CHAT_TRANSCRIPT_MAX_VISIBLE_ENTRIES` 30 → 400, live-reasoning UI)
have been **extracted to #11761** so the cross-adapter behavior changes
review and revert independently — both commits there preserve @hawikk's
authorship. This PR is now the kimi-specific adapter only (60 files,
+3,793/−8, essentially pure addition); the only shared-engine touch left
is the `kimi acp` command resolution. `publishFromCi` is `true` — the
package name is bootstrapped on npm.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Dotta <bippadotta@protonmail.com>
Co-authored-by: Devin Foley <devin@paperclip.ing>
2026-08-20 12:06:33 -07:00
Devin Foley 1d0e826767
feat(acpx/ui): adapter-declared capabilities for verbose streaming backends (no behavior change by default) (#11761)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Local agent adapters stream their work through the shared acpx
engine, which every ACP adapter (`claude_local`, `codex_local`,
`gemini_local`, custom ACP) runs on
> - Verbose streaming backends break two shared-engine behaviors: the
auto-posted run summary concatenates every text delta including the
thought stream (a long multi-tool run once auto-posted a ~50k character
monologue as an issue comment), and token-by-token tool-argument
streaming floods the run log with tens of thousands of
placeholder-titled in-progress events per run
> - These fixes were developed inside the `kimi_local` adapter PR, where
Kimi Code's streaming volume (~16,000 text deltas per run vs ~290 for a
comparable Claude run) surfaced both problems
> - Changing this behavior for all adapters at once is a fleet-wide
risk, and hardcoding adapter identities in shared code does not scale to
many adapters (or work at all for externally-shipped plugin adapters) —
so the behaviors become invocation-config parameters that an adapter's
own acpx config builder sets, with defaults preserving today's behavior
byte-for-byte
> - The benefit is that the machinery lands fully tested with zero
behavior change for existing adapters — pin tests prove it — the engine
carries no adapter identities, and any adapter (including `custom_acp`
configs for external backends) opts in declaratively

## Linked Issues or Issue Description

- Refs #9967 — extracted from the `kimi_local` adapter PR and
restructured to be inert by default; the commit preserves the original
author's (@hawikk) authorship.

**Current behavior**

When an acpx-engine run ends without the agent leaving a comment, the
auto-posted summary is every streamed text delta concatenated, thought
stream included. Backends that stream tool arguments emit tens of
thousands of placeholder-titled `in_progress` tool events into the
stored run log, pinning the live activity indicator to a generic "tool
call". There is no mechanism for an adapter to vary either behavior, and
shared code must never branch on adapter identities.

**Proposed behavior**

Two engine invocation-config parameters, read with behavior-preserving
defaults: `summaryStrategy` (`"full"` = existing concatenation, the
default; `"lastOutputSegment"` = segment output at tool starts, exclude
thought stream, post the last non-empty segment) and
`coalescePlaceholderToolUpdates` (`false` = never drop an event, the
default; `true` = coalesce placeholder-titled in-progress updates). An
adapter opts in from its own acpx config builder — the engine has no
per-adapter knowledge, no adapter identity appears anywhere in shared
code, and `custom_acp` agent configs can set the same knobs for external
verbose backends.

**Reason and benefit**

Existing adapters are provably unaffected — new pin tests assert the
default path's summary and tool-event output byte-for-byte, so any
future change that alters behavior for claude/codex/gemini/custom fails
the suite. The verbose-backend handling still lands fully tested,
activated declaratively by the adapter that needs it (the `kimi_local`
adapter PR sets both knobs in its config builder).

## What Changed

- `packages/adapter-utils/src/acpx-engine/execute.ts`: the run
preparation parses `summaryStrategy` and
`coalescePlaceholderToolUpdates` from the invocation config (validated,
defaulted); summary accumulation and `emitRuntimeEvent` branch on the
prepared values. The default path is the pre-existing code
(`textParts.join("")`, no event filtering). `buildAcpxRunSummary` is the
exported last-segment strategy.
- `packages/adapter-utils/src/acpx-engine/execute.test.ts`: a pin test
asserting the default path's exact summary (thought stream included) and
full tool-event stream (placeholder-titled in-progress updates present,
names restored); opt-in tests for each knob; a `buildAcpxRunSummary`
unit test.

- `ui/src/adapters/types.ts`: `UIAdapterModule` gains an optional
`transcriptPresentation` capability — `maxVisibleEntries` (issue-chat
transcript window, default 30) and `liveReasoningView` (`"ticker"`
default; `"scrollLog"` renders live reasoning in a scrollable
auto-following box with one entry per tool call).
- `ui/src/lib/issue-chat-messages.ts` and
`ui/src/components/IssueChatThread.tsx`: shared code resolves the hints
via `findUIAdapter(adapterType)` with today's defaults as fallback — no
adapter identities anywhere. The `scrollLog` rendering component ships
here but is unreachable until an adapter declares it.
- `ui/src/lib/issue-chat-messages.test.ts`: a capability test registers
a synthetic verbose adapter and asserts the wider window; the
pre-existing test keeps pinning the default 30-entry window.

No adapter declares any of this in this PR — every adapter renders and
summarizes exactly as before, and there is no per-adapter data anywhere.
The `kimi_local` adapter PR (#9967, stacked on this branch) is the first
consumer: it declares `transcriptPresentation` in its own UI module and
sets the engine knobs in its own acpx config builder.

## Verification

- Engine suite: 130/130 pass (126 existing + 4 new); chat suites
(`issue-chat-messages`, `IssueChatThread`, `RunChatSurface`): all pass
including the new synthetic-adapter capability test — 237 tests across
the touched surfaces
- The pin tests are the regression guard: the engine test encodes
today's summary text and tool-event sequence for a default-config run,
and the existing 30-entry-window test pins the default transcript
window, so "nothing changed for Claude/Codex users" is an executable
assertion, not a review judgment
- `pnpm --filter @paperclipai/adapter-utils --filter @paperclipai/ui
typecheck`: clean

## Risks

- Low: with no adapter setting the knobs, every code path taken in
production is the existing one. The only behavioral surface is additive
(an unused strategy and an unused filter), exercised by tests.
- The knobs are ordinary invocation-config keys, so a `custom_acp` agent
config can also set them — intended: an external verbose backend gets
the same handling without code changes. Both knobs only affect that
agent's own run summaries and run-log verbosity.

## Model Used

- Original implementation authored in #9967 by @hawikk (models
documented there: Moonshot AI Kimi K3 Coding via Kimi Code CLI 0.27.0,
OpenAI GPT-5 Codex, Anthropic Claude Opus 4.8). The commit preserves
that authorship.
- Extraction, restructuring into config-declared parameters, pin tests,
and verification: Anthropic, **Claude Fable 5** (`claude-fable-5`) via
Claude Code, with repository, shell, and Git tooling.

## 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: Hawik <davapa@gmail.com>
2026-08-20 10:37:58 -07:00
Dotta e826188e82
refactor(settings): unify settings and speed up exports (#11789)
## Thinking Path

> - Paperclip is the open source app that people use to manage AI agents
for work.
> - Operators use the settings area to control a company and its
Paperclip instance.
> - The current navigation separates related settings and uses duplicate
instance pages.
> - Company exports also do independent reads in sequence and do extra
work for previews.
> - Hardened workspace commands can differ from their saved command
after loopback binding.
> - This pull request makes these related operator workflows consistent
and faster.
> - The benefit is one clear settings area, faster exports, and stable
runtime command matching.

## Linked Issues or Issue Description

Refs #338

Related: #9834

**What existing behavior does this improve?**

This improves the company settings UI, company export preparation, and
workspace runtime command matching.

**Current behavior**

Company and instance settings use separate navigation and duplicate
pages. Export preparation reads many independent records in sequence.
Preview generation can also build an unused organization image. A
command with a forced loopback bind can fail to match its saved runtime
command.

**Proposed behavior**

Use one settings navigation and put general instance controls on the
company General page. Load independent export data with bounded
concurrency, skip unused preview image work, and load the export page
only when it is needed. Treat the loopback-bound form of a command as
the same runtime command.

**Reason and benefit**

Operators get one clear settings area. Large company exports need fewer
serialized reads. Export previews and initial UI loads do less work.
Hardened runtime services remain linked to their saved command
definitions.

**Breaking changes**

The obsolete instance General URL redirects to the unified settings
page. Access and Heartbeats remain available, and legacy bookmarks keep
their destinations. No API response shape or database schema changes.

## What Changed

- Unified company and instance settings navigation and removed duplicate
instance settings pages.
- Embedded general instance controls in the company General page and
kept access-sensitive navigation behavior.
- Preserved instance Access and Heartbeats controls in the unified
navigation and normalized old bookmarks to those destinations.
- Improved environment and access-state handling when workspace seed
requests overlap.
- Added bounded export reads, a lighter preview path, deferred export
preparation, and lazy export-page loading.
- Matched loopback-bound runtime commands to their saved command
definitions.
- Added focused shared, server, and UI regression tests.

## Verification

- `pnpm exec vitest run <18 changed test files>`: 18 files and 256 tests
passed.
- `pnpm check:token-gates`: passed all four token gates.
- `pnpm -r typecheck`: passed for all workspace projects.
- `pnpm build`: passed for all workspace projects.
- `pnpm test:run`: tests ran without a reported failure, but the runner
did not close after the server handoff tests. The process closed with
status 0 after an interrupt.
- Focused latest-head route tests: 2 files and 4 tests passed.
- GitHub latest-head checks: all completed without failure.
- Greptile: 5/5 with no unresolved review threads.

## Risks

- Medium risk: settings routes and navigation changed across several
operator roles.
- Medium risk: bounded export concurrency increases simultaneous
database reads. The limits stay below the normal pool size.
- Low risk: runtime command matching accepts only the known Tailscale
HTTPS loopback transformation.
- No migrations are included.

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

## Model Used

- OpenAI Codex with a GPT-5-family coding model. The runtime does not
expose the exact deployed model ID or context-window size. Reasoning,
tool use, and local code execution were enabled.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details

Exception: This task requires the existing execution branch. The harness
does not permit a branch rename.
- [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-20 12:15:16 -05:00
dependabot[bot] 416a273366
build(deps): bump jsdom and @types/jsdom (#11717)
Bumps [jsdom](https://github.com/jsdom/jsdom) and
[@types/jsdom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jsdom).
These dependencies needed to be updated together.
Updates `jsdom` from 28.1.0 to 30.0.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/jsdom/jsdom/releases">jsdom's
releases</a>.</em></p>
<blockquote>
<h2>v30.0.1</h2>
<ul>
<li>Fixed <code>getComputedStyle()</code> with <code>calc()</code> and
other functions throwing an exception, which regressed in v30.0.0. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
<li>Sped up up range operations on large documents (<a
href="https://github.com/leonidaz"><code>@​leonidaz</code></a>)</li>
</ul>
<h2>v30.0.0</h2>
<p>Breaking changes:</p>
<ul>
<li>Node.js minimum version raised to <code>^22.22.2 || ^24.15.0 ||
&gt;=26.0.0</code>.</li>
</ul>
<p>Other changes:</p>
<ul>
<li>Added <code>CSS.escape()</code> and <code>CSS.supports()</code>
functions. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
<li>Added <code>'background-position-x'</code> and
<code>'background-position-y'</code> CSS properties. (<a
href="https://github.com/olagokemills"><code>@​olagokemills</code></a>)</li>
<li>Fixed <code>getComputedStyle()</code> to convert length values into
pixels. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
<li>Fixed CSS function serialization, e.g., in the return value of
<code>getPropertyValue()</code>. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
<li>Fixed the type of error thrown by <code>document.evaluate()</code>
(<a href="https://github.com/dokson"><code>@​dokson</code></a>)</li>
</ul>
<h2>v29.1.1</h2>
<ul>
<li>Fixed <code>'border-radius'</code> computed style serialization. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
<li>Fixed computed style computation when using
<code>'background-origin'</code> and <code>'background-clip'</code> CSS
properties. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
<li>Significantly optimized initial calls to
<code>getComputedStyle()</code>, before the cache warms up. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
</ul>
<h2>v29.1.0</h2>
<ul>
<li>Added basic support for the ratio CSS type. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
<li>Fixed <code>getComputedStyle()</code> sometimes returning outdated
results after CSS was modified. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
</ul>
<h2>v29.0.2</h2>
<ul>
<li>Significantly improved and sped up <code>getComputedStyle()</code>.
Computed value rules are now applied across a broader set of properties,
and include fixes related to inheritance, defaulting keywords, custom
properties, and color-related values such as <code>currentcolor</code>
and system colors. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
<li>Fixed CSS <code>'background</code>' and <code>'border'</code>
shorthand parsing. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
</ul>
<h2>v29.0.1</h2>
<ul>
<li>Fixed CSS parsing of <code>'border'</code>,
<code>'background'</code>, and their sub-shorthands containing keywords
or <code>var()</code>. (<a
href="https://github.com/asamuzaK"><code>@​asamuzaK</code></a>)</li>
<li>Fixed <code>getComputedStyle()</code> to return a more functional
<code>CSSStyleDeclaration</code> object, including indexed access
support, which regressed in v29.0.0.</li>
</ul>
<h2>v29.0.0</h2>
<p>Breaking changes:</p>
<ul>
<li>Node.js v22.13.0+ is now the minimum supported v22 version (was
v22.12.0+).</li>
</ul>
<p>Other changes:</p>
<ul>
<li>Overhauled the CSSOM implementation, replacing the <a
href="https://www.npmjs.com/package/@acemir/cssom"><code>@acemir/cssom</code></a>
and <a
href="https://github.com/jsdom/cssstyle"><code>cssstyle</code></a>
dependencies with fresh internal implementations built on webidl2js
wrappers and the <a
href="https://www.npmjs.com/package/css-tree"><code>css-tree</code></a>
parser. Serialization, parsing, and API behavior is improved in various
ways, especially around edge cases.</li>
<li>Added <code>CSSCounterStyleRule</code> and
<code>CSSNamespaceRule</code> to jsdom <code>Window</code>s.</li>
<li>Added <code>cssMediaRule.matches</code> and
<code>cssSupportsRule.matches</code> getters.</li>
<li>Added proper media query parsing in <code>MediaList</code>, using
<code>css-tree</code> instead of naive comma-splitting. Invalid queries
become <code>&quot;not all&quot;</code> per spec.</li>
<li>Added <code>cssKeyframeRule.keyText</code> getter/setter
validation.</li>
<li>Added <code>cssStyleRule.selectorText</code> setter validation:
invalid selectors are now rejected.</li>
<li>Added <code>styleSheet.ownerNode</code>,
<code>styleSheet.href</code>, and <code>styleSheet.title</code>.</li>
<li>Added bad port blocking per the <a
href="https://fetch.spec.whatwg.org/#bad-port">fetch specification</a>,
preventing fetches to commonly-abused ports.</li>
<li>Improved <code>Document</code> initialization performance by lazily
initializing the CSS selector engine, avoiding ~0.5 ms of overhead per
<code>Document</code>. (<a
href="https://github.com/thypon"><code>@​thypon</code></a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6584485f09"><code>6584485</code></a>
30.0.1</li>
<li><a
href="0c51df6d80"><code>0c51df6</code></a>
Update dependencies and dev dependencies</li>
<li><a
href="32adb340bf"><code>32adb34</code></a>
Bump <code>@​asamuzakjp/dom-selector</code></li>
<li><a
href="70f014aa1d"><code>70f014a</code></a>
Speed up range operations on large documents</li>
<li><a
href="250d7ee387"><code>250d7ee</code></a>
Partially fix getComputedStyle with calc()</li>
<li><a
href="20a01fc4a5"><code>20a01fc</code></a>
30.0.0</li>
<li><a
href="8c8e583c4f"><code>8c8e583</code></a>
Precompute WPT expectation matches</li>
<li><a
href="f32245cfed"><code>f32245c</code></a>
Bump Node.js floor and dependencies</li>
<li><a
href="03ef23b451"><code>03ef23b</code></a>
Add background-position longhands</li>
<li><a
href="ded056f38d"><code>ded056f</code></a>
Test CSS.escape() with numeric IDs</li>
<li>Additional commits viewable in <a
href="https://github.com/jsdom/jsdom/compare/v28.1.0...v30.0.1">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 jsdom since your current version.</p>
</details>
<details>
<summary>Install script changes</summary>
<p>This version modifies <code>prepare</code> script that runs during
installation. Review the package contents before updating.</p>
</details>
<br />

Updates `@types/jsdom` from 28.0.0 to 30.0.0
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jsdom">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: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-20 10:11:28 -07:00
dependabot[bot] e1ce7a553c
build(deps): bump @assistant-ui/react from 0.14.14 to 0.15.14 (#11725)
Bumps
[@assistant-ui/react](https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react)
from 0.14.14 to 0.15.14.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/assistant-ui/assistant-ui/releases">@​assistant-ui/react's
releases</a>.</em></p>
<blockquote>
<h2><code>@​assistant-ui/react</code><a
href="https://github.com/0"><code>@​0</code></a>.15.14</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5760">#5760</a>
<a
href="a4ac7dd94e"><code>a4ac7dd</code></a>
- fix: release replay response readers after completion, cancellation,
and errors (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5764">#5764</a>
<a
href="47b9256d41"><code>47b9256</code></a>
- fix: generate unique in-memory thread IDs (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5789">#5789</a>
<a
href="d3fece3b17"><code>d3fece3</code></a>
- feat: give the composer its draft back when a send never reached the
backend. a runtime that rejects <code>onNew</code> with the new
<code>MessageNotSentError</code> restores the text, quote, and
attachments the composer cleared at dispatch time, as long as nothing
has claimed the composer since. that guard and that outcome are the ones
<code>cancelRun</code> already applies to a trailing user message, so
whichever fires first keeps the composer, and of several drafts queued
behind one turn only the most recent is still restorable. an edit
composer closes at dispatch, so a rejected edit is not restored. (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5828">#5828</a>
<a
href="685a06939e"><code>685a069</code></a>
- feat: wire <code>threads.reloadMainThread()</code> through the tap
<code>ExternalThread</code> path via a new <code>onRefetchThread</code>
callback, with the <code>refetchThread</code> capability derived from
its presence (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5757">#5757</a>
<a
href="092585b685"><code>092585b</code></a>
- fix: derive the suggestions scope from the thread so runtime-provided
suggestions render through <code>ThreadPrimitive.Suggestions</code> (<a
href="https://redirect.github.com/assistant-ui/assistant-ui/issues/5529">#5529</a>)
(<a href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="a90db30dbf"><code>a90db30</code></a>,
<a
href="cfb5fab251"><code>cfb5fab</code></a>,
<a
href="65e03a6973"><code>65e03a6</code></a>,
<a
href="d3fece3b17"><code>d3fece3</code></a>,
<a
href="4b75b8f967"><code>4b75b8f</code></a>,
<a
href="1e98bcf3f4"><code>1e98bcf</code></a>,
<a
href="82cbc1560b"><code>82cbc15</code></a>,
<a
href="e28a62d844"><code>e28a62d</code></a>,
<a
href="8bba3aaadc"><code>8bba3aa</code></a>,
<a
href="48af3c5c41"><code>48af3c5</code></a>,
<a
href="22fa20ffd1"><code>22fa20f</code></a>,
<a
href="00a630aa93"><code>00a630a</code></a>,
<a
href="417efee92b"><code>417efee</code></a>,
<a
href="1e1d52bd2f"><code>1e1d52b</code></a>,
<a
href="c98699d83b"><code>c98699d</code></a>,
<a
href="685a06939e"><code>685a069</code></a>,
<a
href="f59d24b3ee"><code>f59d24b</code></a>,
<a
href="092585b685"><code>092585b</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.13</li>
<li><code>@​assistant-ui/store</code><a
href="https://github.com/0"><code>@​0</code></a>.3.9</li>
<li>assistant-cloud@0.1.40</li>
<li><code>@​assistant-ui/tap</code><a
href="https://github.com/0"><code>@​0</code></a>.9.12</li>
</ul>
</li>
</ul>
<h2><code>@​assistant-ui/react</code><a
href="https://github.com/0"><code>@​0</code></a>.15.13</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5745">#5745</a>
<a
href="1df4327dc9"><code>1df4327</code></a>
- refactor: move ExternalThread, SingleThreadList, and the Assistant
augmentation namespace into <code>@​assistant-ui/core</code> (<a
href="https://github.com/Yonom"><code>@​Yonom</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="1df4327dc9"><code>1df4327</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.12</li>
</ul>
</li>
</ul>
<h2><code>@​assistant-ui/react</code><a
href="https://github.com/0"><code>@​0</code></a>.15.12</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5730">#5730</a>
<a
href="6c9560d145"><code>6c9560d</code></a>
- chore: drop dependencies that are never imported (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="f551562162"><code>f551562</code></a>,
<a
href="dc7b77dca6"><code>dc7b77d</code></a>,
<a
href="0ae51a8e8c"><code>0ae51a8</code></a>,
<a
href="d1b7097ca8"><code>d1b7097</code></a>,
<a
href="e319574df1"><code>e319574</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.11</li>
<li>assistant-stream@0.3.37</li>
</ul>
</li>
</ul>
<h2><code>@​assistant-ui/react-langgraph</code><a
href="https://github.com/0"><code>@​0</code></a>.14.23</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5730">#5730</a>
<a
href="6c9560d145"><code>6c9560d</code></a>
- chore: drop dependencies that are never imported (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5742">#5742</a>
<a
href="f551562162"><code>f551562</code></a>
- fix: skip RemoveMessage explicitly in convertLangChainMessages (<a
href="https://github.com/Yonom"><code>@​Yonom</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="f551562162"><code>f551562</code></a>,
<a
href="dc7b77dca6"><code>dc7b77d</code></a>,
<a
href="0ae51a8e8c"><code>0ae51a8</code></a>,
<a
href="d1b7097ca8"><code>d1b7097</code></a>,
<a
href="e319574df1"><code>e319574</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.11</li>
<li>assistant-stream@0.3.37</li>
</ul>
</li>
</ul>
<h2><code>@​assistant-ui/react-langgraph</code><a
href="https://github.com/0"><code>@​0</code></a>.14.22</h2>
<h3>Patch Changes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/assistant-ui/assistant-ui/blob/main/packages/react/CHANGELOG.md">@​assistant-ui/react's
changelog</a>.</em></p>
<blockquote>
<h2>0.15.14</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5760">#5760</a>
<a
href="a4ac7dd94e"><code>a4ac7dd</code></a>
- fix: release replay response readers after completion, cancellation,
and errors (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5764">#5764</a>
<a
href="47b9256d41"><code>47b9256</code></a>
- fix: generate unique in-memory thread IDs (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5789">#5789</a>
<a
href="d3fece3b17"><code>d3fece3</code></a>
- feat: give the composer its draft back when a send never reached the
backend. a runtime that rejects <code>onNew</code> with the new
<code>MessageNotSentError</code> restores the text, quote, and
attachments the composer cleared at dispatch time, as long as nothing
has claimed the composer since. that guard and that outcome are the ones
<code>cancelRun</code> already applies to a trailing user message, so
whichever fires first keeps the composer, and of several drafts queued
behind one turn only the most recent is still restorable. an edit
composer closes at dispatch, so a rejected edit is not restored. (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5828">#5828</a>
<a
href="685a06939e"><code>685a069</code></a>
- feat: wire <code>threads.reloadMainThread()</code> through the tap
<code>ExternalThread</code> path via a new <code>onRefetchThread</code>
callback, with the <code>refetchThread</code> capability derived from
its presence (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5757">#5757</a>
<a
href="092585b685"><code>092585b</code></a>
- fix: derive the suggestions scope from the thread so runtime-provided
suggestions render through <code>ThreadPrimitive.Suggestions</code> (<a
href="https://redirect.github.com/assistant-ui/assistant-ui/issues/5529">#5529</a>)
(<a href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="a90db30dbf"><code>a90db30</code></a>,
<a
href="cfb5fab251"><code>cfb5fab</code></a>,
<a
href="65e03a6973"><code>65e03a6</code></a>,
<a
href="d3fece3b17"><code>d3fece3</code></a>,
<a
href="4b75b8f967"><code>4b75b8f</code></a>,
<a
href="1e98bcf3f4"><code>1e98bcf</code></a>,
<a
href="82cbc1560b"><code>82cbc15</code></a>,
<a
href="e28a62d844"><code>e28a62d</code></a>,
<a
href="8bba3aaadc"><code>8bba3aa</code></a>,
<a
href="48af3c5c41"><code>48af3c5</code></a>,
<a
href="22fa20ffd1"><code>22fa20f</code></a>,
<a
href="00a630aa93"><code>00a630a</code></a>,
<a
href="417efee92b"><code>417efee</code></a>,
<a
href="1e1d52bd2f"><code>1e1d52b</code></a>,
<a
href="c98699d83b"><code>c98699d</code></a>,
<a
href="685a06939e"><code>685a069</code></a>,
<a
href="f59d24b3ee"><code>f59d24b</code></a>,
<a
href="092585b685"><code>092585b</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.13</li>
<li><code>@​assistant-ui/store</code><a
href="https://github.com/0"><code>@​0</code></a>.3.9</li>
<li>assistant-cloud@0.1.40</li>
<li><code>@​assistant-ui/tap</code><a
href="https://github.com/0"><code>@​0</code></a>.9.12</li>
</ul>
</li>
</ul>
<h2>0.15.13</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5745">#5745</a>
<a
href="1df4327dc9"><code>1df4327</code></a>
- refactor: move ExternalThread, SingleThreadList, and the Assistant
augmentation namespace into <code>@​assistant-ui/core</code> (<a
href="https://github.com/Yonom"><code>@​Yonom</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="1df4327dc9"><code>1df4327</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.12</li>
</ul>
</li>
</ul>
<h2>0.15.12</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5730">#5730</a>
<a
href="6c9560d145"><code>6c9560d</code></a>
- chore: drop dependencies that are never imported (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="f551562162"><code>f551562</code></a>,
<a
href="dc7b77dca6"><code>dc7b77d</code></a>,
<a
href="0ae51a8e8c"><code>0ae51a8</code></a>,
<a
href="d1b7097ca8"><code>d1b7097</code></a>,
<a
href="e319574df1"><code>e319574</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.11</li>
<li>assistant-stream@0.3.37</li>
</ul>
</li>
</ul>
<h2>0.15.11</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5723">#5723</a>
<a
href="94dc3e509f"><code>94dc3e5</code></a>
- chore: update dependencies (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/5722">#5722</a>
<a
href="7faf0fb1fb"><code>7faf0fb</code></a>
- fix: ExternalThread edit composer reads editing state live, so a
same-tick beginEdit + setText + send dispatches the edit; send before
beginEdit and double beginEdit now throw (legacy runtime parity) (<a
href="https://github.com/Yonom"><code>@​Yonom</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="94dc3e509f"><code>94dc3e5</code></a>,
<a
href="ab5796932c"><code>ab57969</code></a>]:</p>
<ul>
<li>assistant-stream@0.3.36</li>
<li>assistant-cloud@0.1.39</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6b73c50538"><code>6b73c50</code></a>
chore: update versions (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/5754">#5754</a>)</li>
<li><a
href="685a06939e"><code>685a069</code></a>
feat(core,react): wire reloadMainThread through the tap external thread
(<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/5828">#5828</a>)</li>
<li><a
href="d3fece3b17"><code>d3fece3</code></a>
fix(core,eve): return an undispatched send to the composer (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/5789">#5789</a>)</li>
<li><a
href="a4ac7dd94e"><code>a4ac7dd</code></a>
fix(react): release replay boundary stream readers (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/5760">#5760</a>)</li>
<li><a
href="47b9256d41"><code>47b9256</code></a>
fix(react): generate unique in-memory thread ids (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/5764">#5764</a>)</li>
<li><a
href="092585b685"><code>092585b</code></a>
fix(core): derive the suggestions scope from the thread's suggestions
(<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/5757">#5757</a>)</li>
<li><a
href="fb4025e93a"><code>fb4025e</code></a>
chore: update versions (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/5746">#5746</a>)</li>
<li><a
href="1df4327dc9"><code>1df4327</code></a>
refactor(core,react): move ExternalThread and SingleThreadList into <a
href="https://github.com/assistan"><code>@​assistan</code></a>...</li>
<li><a
href="21d8d65263"><code>21d8d65</code></a>
chore: update versions (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/5732">#5732</a>)</li>
<li><a
href="6c9560d145"><code>6c9560d</code></a>
chore: drop dependencies that are never imported (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/5730">#5730</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/assistant-ui/assistant-ui/commits/@assistant-ui/react@0.15.14/packages/react">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-20 08:59:37 -07:00
dependabot[bot] 6bf8f1d194
build(deps): bump react-dom and @types/react-dom (#11712)
Bumps
[react-dom](https://github.com/react/react/tree/HEAD/packages/react-dom)
and
[@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom).
These dependencies needed to be updated together.
Updates `react-dom` from 19.2.7 to 19.2.8
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/react/react/releases">react-dom's
releases</a>.</em></p>
<blockquote>
<h2>19.2.8 (July 21st, 2026)</h2>
<h2>React Server Components</h2>
<ul>
<li>Performance improvements when decoding
(<a
href="https://redirect.github.com/facebook/react/pull/37087">#37087</a>
by <a href="https://github.com/eps1lon"><code>@​eps1lon</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1dd4ecbdab"><code>1dd4ecb</code></a>
[FlightReply] Performance improvements when decoding (<a
href="https://github.com/react/react/tree/HEAD/packages/react-dom/issues/37087">#37087</a>)</li>
<li><a
href="b0d2fdb78b"><code>b0d2fdb</code></a>
[19.2.x] Update required references to GitHub repo (<a
href="https://github.com/react/react/tree/HEAD/packages/react-dom/issues/36753">#36753</a>)</li>
<li>See full diff in <a
href="https://github.com/react/react/commits/v19.2.8/packages/react-dom">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/react-dom` from 19.2.3 to 19.2.4
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom">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-20 08:38:47 -07:00
dependabot[bot] b4953985c1
build(deps): bump react and @types/react (#11721)
Bumps [react](https://github.com/react/react/tree/HEAD/packages/react)
and
[@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react).
These dependencies needed to be updated together.
Updates `react` from 19.2.7 to 19.2.8
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/react/react/releases">react's
releases</a>.</em></p>
<blockquote>
<h2>19.2.8 (July 21st, 2026)</h2>
<h2>React Server Components</h2>
<ul>
<li>Performance improvements when decoding
(<a
href="https://redirect.github.com/facebook/react/pull/37087">#37087</a>
by <a href="https://github.com/eps1lon"><code>@​eps1lon</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1dd4ecbdab"><code>1dd4ecb</code></a>
[FlightReply] Performance improvements when decoding (<a
href="https://github.com/react/react/tree/HEAD/packages/react/issues/37087">#37087</a>)</li>
<li><a
href="b0d2fdb78b"><code>b0d2fdb</code></a>
[19.2.x] Update required references to GitHub repo (<a
href="https://github.com/react/react/tree/HEAD/packages/react/issues/36753">#36753</a>)</li>
<li>See full diff in <a
href="https://github.com/react/react/commits/v19.2.8/packages/react">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/react` from 19.2.17 to 19.2.18
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react">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: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-20 08:20:34 -07:00
scotttong cbe6395cc5
fix(ui): task chat composer clears on send; align carets and composer with thread (#11772)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The task detail view uses a chat-style thread: agent turns, activity
phases, and a message composer.
> - A colleague's UX review found three defects: the composer draft
stayed visible after send, disclosure carets in turns and status pills
were misaligned, and the composer was not horizontally aligned with the
thread column.
> - These defects make the chat surface feel unpolished and cause
confusion about whether a message was sent.
> - This pull request fixes all three defects with small, targeted UI
changes and adds regression tests for each.
> - The benefit is a chat surface that behaves and aligns like users
expect from a messaging UI.

## Linked Issues or Issue Description

No public GitHub issue exists for this. Description of the underlying
problems:

**What happened?**

Three UI defects in the chat-style task view:

1. After a user pressed send, the composer kept the draft text until the
server round-trip finished. Fast typers could see stale text and doubt
the message was sent.
2. The disclosure carets on collapsed agent turns and the caret inside
the status pill did not share one alignment axis. They rendered at
different x-offsets and sizes.
3. The composer container had different horizontal padding than the
thread column above it, so the input box did not line up with the
message bubbles.

**Expected behavior**

The composer clears the instant a send starts. All disclosure carets sit
on one vertical axis with one size. The composer's left and right edges
align with the thread column.

**Steps to reproduce**

Open any task in the chat-style task view. Type a message and press
Enter — watch the composer text. Collapse and expand agent turns —
compare caret positions. Compare the composer's horizontal edges with
the message bubbles above it.

## What Changed

- `TaskChatComposer.tsx`: clear the draft synchronously when a send
starts instead of after the request resolves; restore the draft if the
send fails.
- `TaskChatTurn.tsx` and `TaskChatStatusPill.tsx`: use one shared caret
alignment (size, x-offset) for turn disclosure and status pill carets,
with supporting utility styles in `ui/src/index.css`.
- `TaskChatThread.tsx`: align the composer container with the thread
column padding.
- Added or updated unit tests in `TaskChatComposer.test.tsx`,
`TaskChatTurn.test.tsx`, `TaskChatActivityPhase.test.tsx`, and
`TaskChatThread.test.tsx`.

## Verification

- Run `pnpm vitest run
src/components/task-chat/TaskChatComposer.test.tsx
src/components/task-chat/TaskChatActivityPhase.test.tsx
src/components/task-chat/TaskChatTurn.test.tsx
src/components/TaskChatThread.test.tsx` in `ui/` — 4 files, 64 tests,
all pass.
- Manual: open a task in the chat view, send a message, and confirm the
composer clears immediately. Collapse/expand turns and confirm the
carets align. Compare composer edges with the thread column; the
alignment fixes were also pixel-verified with screenshots during local
review.

## Risks

- Low risk. All changes are render-layer only; no server or data
changes.
- The composer now clears optimistically. If a send fails, the draft is
restored, so no user text is lost.
- Caret alignment uses shared CSS utilities; visual regressions would
show in the existing component tests and in any screenshot diff.

## Model Used

- Implementation: OpenAI Codex CLI coding agent (Codex model family,
agentic tool use) via Paperclip's Codex adapter.
- Review, verification, rebase, and PR preparation: Anthropic Claude,
model id `claude-fable-5` (extended thinking, tool use), 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 (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 00:17:55 -07:00
Nicky Leach faab2620ad
feat(sandbox): add a duplex transport for Daytona behind a default-off kill switch (#11750)
## Thinking Path

> - Paperclip is an open source app that manages AI agents for work
> - Paperclip runs agents in local and remote sandbox environments
> - A sandbox needs a bounded channel for commands and asynchronous
input
> - Daytona needs a real pseudo-terminal transport for this channel
> - The sandbox gateway also needs a mode that handles channel loss
safely
> - This pull request adds the Daytona transport and gateway mode behind
a default-off kill switch
> - The benefit is a tested foundation for later transport selection

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting (multiple of the above): sandbox providers, plugin SDK,
server settings, and shared types.

**Problem or motivation**

The merged sandbox protocol has no runtime transport for Daytona. The
generated sandbox gateway also has no duplex mode. A later
transport-selection change needs both parts and a safe per-run gate.

**Proposed solution**

Add a Daytona `duplexCommandStream` transport over a raw
pseudo-terminal. Add a generated gateway mode named `duplex_v1`. Add the
`enableSandboxDuplexBridge` setting with a default value of `false`.
Keep transport selection disabled until a later pull request.

**Alternatives considered**

Keep the protocol unused until the transport-selection change. This
would delay provider tests and leave the gateway path without direct
coverage.

**Roadmap alignment**

This change supports the completed Roadmap item for cloud and sandbox
agents. It extends the merged sandbox channel foundation in pull request
#11738.

**Additional context**

The Daytona provider remains an untrusted boundary. Deployments must use
least-privilege provider credentials and provider-side quota controls.
Operators must name an owner for duplex telemetry retention before
rollout.

## What Changed

- Add the Daytona `duplexCommandStream` capability over a raw
pseudo-terminal.
- Add a launch wrapper that disables echo and newline translation for
NDJSON frames.
- Close channels on lease release, destroy, resume of a stopped worker,
and worker shutdown.
- Declare the capability in the Daytona manifest and set
`PLUGIN_VERSION` to `0.1.5`.
- Add the worker-to-host notification sink at `ctx.duplexChannel.data`
and `ctx.duplexChannel.exit`.
- Add the generated sandbox gateway mode
`PAPERCLIP_API_BRIDGE_MODE=duplex_v1`.
- Add channel-loss results of `409 outcome_indeterminate` and `503
bridge_unavailable`.
- Add the per-run setting `enableSandboxDuplexBridge`, with a default
value of `false`.
- Add unit tests, generated-source codec tests, lifecycle tests, and a
credential-gated live Daytona test.

## Verification

- Daytona suite: 185 tests pass.
- Adapter utilities: 754 tests pass and 4 tests skip.
- Plugin SDK: 62 tests pass.
- Shared package: 28 tests pass.
- Server duplex tests pass.
- Shared, plugin SDK, server, and Daytona TypeScript checks pass.
- The live Daytona test passes 3 cases when `DAYTONA_API_KEY` is set.
- The live Daytona test skips 3 cases without `DAYTONA_API_KEY`.
- CI must run the full workspace typecheck, test, and build gates after
PR creation.

## Risks

- The Daytona control plane and pseudo-terminal remain untrusted
boundaries.
- The duplex gateway changes behavior only when the mode and per-run
setting enable it.
- A lost channel fails requests without replay, so callers must handle
indeterminate outcomes.
- The transport-selection change must require both `duplexCommandStream
=== true` and `enableSandboxDuplexBridge === true`.
- The provider credential and quota limits need operator control before
rollout.

## Model Used

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

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and 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-19 17:14:47 -07:00
Nicky Leach b8a76081ec
feat(environments): expose boot-relevant drift attribution in the custom-image overview (#11751)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip lets operators prepare and use custom images for sandbox
environments
> - The custom-image overview detected drift but did not show which boot
source changed
> - Operators need the changed field and values to understand why a
template no longer matches
> - This pull request adds safe drift attribution to the overview API
and the out-of-sync banner
> - The benefit is faster diagnosis without exposing secrets or internal
snapshot data

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting (server and UI).

**Problem or motivation**

The custom-image overview reported drift without identifying the changed
boot source. Operators had to inspect other data to find the cause.

**Proposed solution**

Return a classified drift summary with changed paths and their prior and
current values. Show the boot-source field in the UI banner. Keep legacy
templates and unclassified drift on the generic message.

**Alternatives considered**

The change does not expose the full snapshot or fingerprint. This keeps
the overview contract small and avoids secret disclosure.

**Roadmap alignment**

The change supports the existing custom-image environment workflow and
does not duplicate a roadmap item.

## What Changed

- Add `activeTemplateDrift` to the custom-image overview response.
- Classify drift as `boot_source_drift`, `knob_only`, or `unclassified`.
- Return drifted paths with safe `from` and `to` values.
- Show the changed boot-source field and values in the out-of-sync
banner.
- Keep legacy templates fail-closed and exclude secrets, fingerprints,
and raw snapshots.
- Add server and UI tests for the new behavior.

## Verification

- `npx vitest run
server/src/__tests__/environment-custom-images-service.test.ts` passes
with 27 tests.
- `npx vitest run ui/src/pages/CompanyEnvironments.test.tsx` passes with
27 tests.
- `pnpm --filter @paperclipai/ui exec tsc --noEmit` passes.
- Review the overview response and banner cases for boot-source,
knob-only, and legacy drift.

## Risks

The overview response gains one optional field. Legacy templates remain
compatible because they return `unclassified` and keep the generic
banner. The service excludes secret values, fingerprints, and raw
snapshots.

## Model Used

OpenAI Codex, GPT-5, tool use and code execution enabled. The model
reviewed the handoff and managed 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] All Paperclip CI gates are green
- [x] 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-19 16:36:18 -07:00
dependabot[bot] 1b259d7be4
build(deps): bump radix-ui from 1.6.4 to 1.6.7 (#11726)
Bumps
[radix-ui](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/radix-ui)
from 1.6.4 to 1.6.7.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/radix-ui/primitives/blob/main/packages/react/radix-ui/CHANGELOG.md">radix-ui's
changelog</a>.</em></p>
<blockquote>
<h2>1.6.6, 1.6.7</h2>
<ul>
<li>Reverted breaking changes that caused compatibility issues with
React Server Components.</li>
</ul>
<h2>1.6.5</h2>
<ul>
<li>Republish through CI to attach provenance attestations. The previous
versions of these packages were published manually outside of CI and
therefore shipped without provenance; this patch re-releases the same
code through the CI pipeline so every package includes an
attestation.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/radix-ui/primitives/commits/1.6.7/packages/react/radix-ui">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: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-19 14:28:35 -07:00
Nicky Leach e0b64529b3
feat(auth): normalize agent login in the sandbox onto one session table and a capability contract (#11730)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Sandbox agents need a safe login path for each supported adapter
> - Codex device login and Claude setup-token login used separate
session stores and route logic
> - Separate stores made session lookup, expiry, and login capability
checks harder to keep consistent
> - This pull request unifies both flows on one session table and one
capability contract
> - The benefit is one company-scoped login model with public session
identifiers and shared lifecycle rules

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting (multiple of the above)

**Problem or motivation**

Codex and Claude sandbox login used separate session stores and
different route paths. This split increased the risk of inconsistent
company scoping, session lookup, and cleanup.

**Proposed solution**

Use `adapter_auth_sessions` for both login flows. Use public session
identifiers for API access. Select login behavior from projected adapter
capability data. Share the route spine, lease arguments, runner
lifecycle, and reaper rules.

**Alternatives considered**

Keep two session tables and add matching fixes to both routes. This
keeps duplicate logic and does not provide one capability contract, so
this pull request uses shared infrastructure.

**Roadmap alignment**

This change supports the shipped Cloud / Sandbox agents milestone in
`ROADMAP.md`.

## What Changed

- Unify Codex device login and Claude setup-token login on
`adapter_auth_sessions`.
- Return and look up sessions with company-scoped public session
identifiers.
- Enforce one active session for each company, owner, and adapter.
- Share the login route spine, sandbox lease arguments, runner
lifecycle, and missing-auth check.
- Add a standalone setup-token reaper with adapter-specific row
selection.
- Add optional login capability projection for adapters and drive route
and UI selection from that data.
- Rename the provider flag to `supportsLoginPty` and validate its
deprecated alias.
- Remove the old Claude setup-token session table and add the required
migrations.

## Verification

- Server typecheck passed with `tsc`.
- Database typecheck passed.
- UI typecheck passed with `tsc -b`.
- Codex login service and route suites passed.
- Setup-token session, route, and reaper suites passed.
- Adapter session schema, plugin validator, capability projection, UI
render, and Daytona suites passed.
- GitHub Actions must confirm the complete CI gate after pull request
creation.

## Risks

- The migrations remove short-lived in-flight login rows during
deployment. A login that spans the migration can continue until its
provider lease expires.
- The Codex credential store remains company-scoped. A cross-owner
credential race remains a documented, board-accepted risk.
- API clients that use internal session row identifiers no longer work.
The API accepts only public session identifiers.

## Model Used

Codex, GPT-5, exact runtime model ID not exposed in this handoff, large
context window, reasoning, and repository tool use. The implementing
engineer produced the code with AI 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-19 11:51:31 -07:00
dependabot[bot] 61b4fc02d4
build(deps-dev): bump @storybook/addon-a11y from 10.5.4 to 10.5.8 (#11713)
Bumps
[@storybook/addon-a11y](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y)
from 10.5.4 to 10.5.8.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/releases">@​storybook/addon-a11y's
releases</a>.</em></p>
<blockquote>
<h2>v10.5.8</h2>
<h2>10.5.8</h2>
<ul>
<li>React: Fix RDT tsconfig selection for Vite project references - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35743">#35743</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Tanstack React: Remove <code>@​cloudflare/vite-plugin</code> from
the inherited Vite config - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35706">#35706</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
<li>Tanstack: Wait for router to load before rendering - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35784">#35784</a>,
thanks <a
href="https://github.com/huang-julien"><code>@​huang-julien</code></a>!</li>
<li>Test: Fix Illegal invocation when reading prototype.focus - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35528">#35528</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
</ul>
<h2>v10.5.7</h2>
<h2>10.5.7</h2>
<ul>
<li>Angular: Serve ancestor node_modules for addon-vitest in browser
mode - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35600">#35600</a>,
thanks <a
href="https://github.com/brandonroberts"><code>@​brandonroberts</code></a>!</li>
<li>Refactor: Update getVersionedPackages method to handle non-Storybook
packages correctly - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35769">#35769</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>v10.5.6</h2>
<h2>10.5.6</h2>
<ul>
<li>Dependencies: Pin `@testing-library/jest-dom` to `6.9.1` - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35614">#35614</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ESLint Plugin: Add plugin meta and document oxlint usage - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35655">#35655</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Vue: Skip docgen for module ids carrying a query - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35598">#35598</a>,
thanks <a
href="https://github.com/seanogdev"><code>@​seanogdev</code></a>!</li>
</ul>
<h2>v10.5.5</h2>
<h2>10.5.5</h2>
<ul>
<li>CLI: Update AI setup instructions to msw-storybook-addon v3 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35512">#35512</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Core: Upgrade `ws` to fix security advisories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35584">#35584</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ReactNative: Telemetry framework detection fix - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35560">#35560</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>SyntaxHighlighter: Fix PrismJS dark mode mismatch - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35541">#35541</a>,
thanks <a
href="https://github.com/hxy-asdw"><code>@​hxy-asdw</code></a>!</li>
<li>TanStack: Preserve explicit route ids on pathful clones - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35499">#35499</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Resolve mock redirects through Vite's resolver - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35501">#35501</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Respect routeOverrides component overrides in stories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35497">#35497</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md">@​storybook/addon-a11y's
changelog</a>.</em></p>
<blockquote>
<h2>10.5.8</h2>
<ul>
<li>React: Fix RDT tsconfig selection for Vite project references - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35743">#35743</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Tanstack React: Remove <code>@​cloudflare/vite-plugin</code> from
the inherited Vite config - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35706">#35706</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
<li>Tanstack: Wait for router to load before rendering - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35784">#35784</a>,
thanks <a
href="https://github.com/huang-julien"><code>@​huang-julien</code></a>!</li>
<li>Test: Fix Illegal invocation when reading prototype.focus - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35528">#35528</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
</ul>
<h2>10.5.7</h2>
<ul>
<li>Angular: Serve ancestor node_modules for addon-vitest in browser
mode - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35600">#35600</a>,
thanks <a
href="https://github.com/brandonroberts"><code>@​brandonroberts</code></a>!</li>
<li>Refactor: Update getVersionedPackages method to handle non-Storybook
packages correctly - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35769">#35769</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>10.5.6</h2>
<ul>
<li>Dependencies: Pin <code>@testing-library/jest-dom</code> to
<code>6.9.1</code> - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35614">#35614</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ESLint Plugin: Add plugin meta and document oxlint usage - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35655">#35655</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Vue: Skip docgen for module ids carrying a query - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35598">#35598</a>,
thanks <a
href="https://github.com/seanogdev"><code>@​seanogdev</code></a>!</li>
</ul>
<h2>10.5.5</h2>
<ul>
<li>CLI: Update AI setup instructions to msw-storybook-addon v3 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35512">#35512</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Core: Upgrade <code>ws</code> to fix security advisories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35584">#35584</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ReactNative: Telemetry framework detection fix - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35560">#35560</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>SyntaxHighlighter: Fix PrismJS dark mode mismatch - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35541">#35541</a>,
thanks <a
href="https://github.com/hxy-asdw"><code>@​hxy-asdw</code></a>!</li>
<li>TanStack: Preserve explicit route ids on pathful clones - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35499">#35499</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Resolve mock redirects through Vite's resolver - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35501">#35501</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Respect routeOverrides component overrides in stories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35497">#35497</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6ef7d1ae81"><code>6ef7d1a</code></a>
Bump version from &quot;10.5.7&quot; to &quot;10.5.8&quot; [skip
ci]</li>
<li><a
href="7c6fb3a5ec"><code>7c6fb3a</code></a>
Bump version from &quot;10.5.6&quot; to &quot;10.5.7&quot; [skip
ci]</li>
<li><a
href="80195a019c"><code>80195a0</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y/issues/35486">#35486</a>
from storybookjs/valentin/cli-local-postinstall-aut...</li>
<li><a
href="3126f0a14a"><code>3126f0a</code></a>
Bump version from &quot;10.5.5&quot; to &quot;10.5.6&quot; [skip
ci]</li>
<li><a
href="05a52b7a88"><code>05a52b7</code></a>
Bump version from &quot;10.5.4&quot; to &quot;10.5.5&quot; [skip
ci]</li>
<li>See full diff in <a
href="https://github.com/storybookjs/storybook/commits/v10.5.8/code/addons/a11y">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@storybook/addon-a11y&package-manager=npm_and_yarn&previous-version=10.5.4&new-version=10.5.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-19 10:40:11 -07:00
dependabot[bot] 05e89db1d0
build(deps): bump react-i18next from 17.0.10 to 17.0.11 (#11711)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [react-i18next](https://github.com/i18next/react-i18next) from
17.0.10 to 17.0.11.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md">react-i18next's
changelog</a>.</em></p>
<blockquote>
<h2>17.0.11</h2>
<ul>
<li>chore: <code>html-parse-stringify</code> updated to
<code>^4.0.1</code>. The parser powering <code>&lt;Trans&gt;</code> is
now actively maintained under the i18next org (<a
href="https://github.com/i18next/html-parse-stringify">i18next/html-parse-stringify</a>)
after years without upstream releases. 4.x brings modern dual ESM/CJS
packaging with an <code>exports</code> map, zero runtime dependencies,
reworked TypeScript types and a long list of parser fixes (literal
<code>&lt;</code> in text, multiline/CRLF attribute values, comments
containing <code>&gt;</code>, doctype handling, quote-aware bracket
handling).</li>
<li>refactor(Trans): the internal <code>escapeLiteralLessThan</code>
scanner (~80 lines) is replaced by the parser's new
<code>allowedTags</code> option with identical semantics: only numbered
tags, kept basic HTML tags and known component names are parsed as
markup, any other tag-shaped sequence in the translation stays literal
text. Rendered output is unchanged (all 493 tests pass, including the <a
href="https://redirect.github.com/i18next/react-i18next/issues/1880">#1880</a>
and <a
href="https://redirect.github.com/i18next/react-i18next/issues/1893">#1893</a>
escaping cases).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="274e2e6078"><code>274e2e6</code></a>
17.0.11</li>
<li><a
href="3c4c563115"><code>3c4c563</code></a>
build</li>
<li><a
href="1e7ccae22a"><code>1e7ccae</code></a>
chore: html-parse-stringify ^4.0.1 + 17.0.11 changelog</li>
<li><a
href="4412e25621"><code>4412e25</code></a>
html-parse-stringify ^4.0.0: replace escapeLiteralLessThan with the
parser's ...</li>
<li><a
href="5787979e38"><code>5787979</code></a>
ci: types-compat falls back to latest when no <code>@​types/react</code>
pre-release tag ex...</li>
<li>See full diff in <a
href="https://github.com/i18next/react-i18next/compare/v17.0.10...v17.0.11">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=react-i18next&package-manager=npm_and_yarn&previous-version=17.0.10&new-version=17.0.11)](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-19 10:38:23 -07:00
dependabot[bot] 81c9fdc5aa
build(deps): bump react-router-dom from 7.18.1 to 7.18.2 (#11715)
Bumps
[react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom)
from 7.18.1 to 7.18.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/remix-run/react-router/blob/react-router-dom@7.18.2/packages/react-router-dom/CHANGELOG.md">react-router-dom's
changelog</a>.</em></p>
<blockquote>
<h2>v7.18.2</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies:
<ul>
<li><a
href="https://github.com/remix-run/react-router/releases/tag/react-router@7.18.2"><code>react-router@7.18.2</code></a></li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="69a653ee6a"><code>69a653e</code></a>
Release v7.18.2 (<a
href="https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom/issues/15354">#15354</a>)</li>
<li>See full diff in <a
href="https://github.com/remix-run/react-router/commits/react-router-dom@7.18.2/packages/react-router-dom">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=react-router-dom&package-manager=npm_and_yarn&previous-version=7.18.1&new-version=7.18.2)](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-19 10:36:08 -07:00
dependabot[bot] 6eb0739ff5
build(deps-dev): bump tailwindcss from 4.3.2 to 4.3.3 (#11718)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps
[tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss)
from 4.3.2 to 4.3.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tailwindlabs/tailwindcss/releases">tailwindcss's
releases</a>.</em></p>
<blockquote>
<h2>v4.3.3</h2>
<h3>Fixed</h3>
<ul>
<li>Support <code>--watch --poll[=ms]</code> in
<code>@tailwindcss/cli</code> when filesystem events are unreliable or
unavailable (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20297">#20297</a>)</li>
<li>Canonicalization: match arbitrary hex colors against theme colors
case-insensitively (e.g. <code>bg-[#fff]</code> and
<code>bg-[#FFF]</code> → <code>bg-white</code>) (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20298">#20298</a>)</li>
<li>Prevent Preflight from overriding Firefox's native
<code>iframe:focus-visible</code> outline styles (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20292">#20292</a>)</li>
<li>Ensure <code>theme('colors.foo')</code> in JS plugins resolves
correctly when both <code>--color-foo</code> and
<code>--color-foo-bar</code> exist (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20299">#20299</a>)</li>
<li>Ensure fractional opacity modifiers work with named shadow sizes
like <code>shadow-sm/12.5</code>, <code>text-shadow-sm/12.5</code>,
<code>drop-shadow-sm/12.5</code>, and <code>inset-shadow-sm/12.5</code>
(<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20302">#20302</a>)</li>
<li>Parse selectors like <code>[data-foo]div</code> as two selectors
instead of one (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20303">#20303</a>)</li>
<li>Ensure <code>@tailwindcss/postcss</code> rebuilds when a
preprocessor like Sass changes the input CSS without changing the input
file on disk (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20310">#20310</a>)</li>
<li>Ensure CSS nesting is handled even when Lightning CSS isn't run,
such as in <code>@tailwindcss/browser</code> and Tailwind Play (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20124">#20124</a>)</li>
<li>Prevent achromatic theme colors from shifting hue when mixed in
polar color spaces like <code>oklch</code> (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20314">#20314</a>)</li>
<li>Ensure <code>--spacing(0)</code> is optimized to <code>0px</code>
instead of <code>0</code> so it remains a <code>&lt;length&gt;</code>
when used in <code>calc(…)</code> (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20319">#20319</a>)</li>
<li>Load <code>@parcel/watcher</code> only when needed in
<code>@tailwindcss/cli --watch</code> mode, so one-off builds and
<code>--watch --poll</code> work when <code>@parcel/watcher</code> can't
be loaded (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20325">#20325</a>)</li>
<li>Use explicit platform fonts instead of <code>system-ui</code> and
<code>ui-sans-serif</code> so CJK text respects the page's
<code>lang</code> attribute on Windows (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20318">#20318</a>)</li>
<li>Prevent <code>@tailwindcss/upgrade</code> from rewriting ignored
files when run from a subdirectory (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20329">#20329</a>)</li>
<li>Ensure earlier <code>@source</code> rules pointing to nested files
are scanned when later <code>@source</code> rules point to files in
parent folders (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20335">#20335</a>)</li>
<li>Prevent <code>@tailwindcss/vite</code> from triggering full page
reloads when scanned files are processed by Vite but haven't been loaded
as modules yet (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20336">#20336</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md">tailwindcss's
changelog</a>.</em></p>
<blockquote>
<h2>[4.3.3] - 2026-07-16</h2>
<h3>Fixed</h3>
<ul>
<li>Support <code>--watch --poll[=ms]</code> in
<code>@tailwindcss/cli</code> when filesystem events are unreliable or
unavailable (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20297">#20297</a>)</li>
<li>Canonicalization: match arbitrary hex colors against theme colors
case-insensitively (e.g. <code>bg-[#fff]</code> and
<code>bg-[#FFF]</code> → <code>bg-white</code>) (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20298">#20298</a>)</li>
<li>Prevent Preflight from overriding Firefox's native
<code>iframe:focus-visible</code> outline styles (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20292">#20292</a>)</li>
<li>Ensure <code>theme('colors.foo')</code> in JS plugins resolves
correctly when both <code>--color-foo</code> and
<code>--color-foo-bar</code> exist (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20299">#20299</a>)</li>
<li>Ensure fractional opacity modifiers work with named shadow sizes
like <code>shadow-sm/12.5</code>, <code>text-shadow-sm/12.5</code>,
<code>drop-shadow-sm/12.5</code>, and <code>inset-shadow-sm/12.5</code>
(<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20302">#20302</a>)</li>
<li>Parse selectors like <code>[data-foo]div</code> as two selectors
instead of one (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20303">#20303</a>)</li>
<li>Ensure <code>@tailwindcss/postcss</code> rebuilds when a
preprocessor like Sass changes the input CSS without changing the input
file on disk (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20310">#20310</a>)</li>
<li>Ensure CSS nesting is handled even when Lightning CSS isn't run,
such as in <code>@tailwindcss/browser</code> and Tailwind Play (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20124">#20124</a>)</li>
<li>Prevent achromatic theme colors from shifting hue when mixed in
polar color spaces like <code>oklch</code> (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20314">#20314</a>)</li>
<li>Ensure <code>--spacing(0)</code> is optimized to <code>0px</code>
instead of <code>0</code> so it remains a <code>&lt;length&gt;</code>
when used in <code>calc(…)</code> (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20319">#20319</a>)</li>
<li>Load <code>@parcel/watcher</code> only when needed in
<code>@tailwindcss/cli --watch</code> mode, so one-off builds and
<code>--watch --poll</code> work when <code>@parcel/watcher</code> can't
be loaded (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20325">#20325</a>)</li>
<li>Use explicit platform fonts instead of <code>system-ui</code> and
<code>ui-sans-serif</code> so CJK text respects the page's
<code>lang</code> attribute on Windows (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20318">#20318</a>)</li>
<li>Prevent <code>@tailwindcss/upgrade</code> from rewriting ignored
files when run from a subdirectory (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20329">#20329</a>)</li>
<li>Ensure earlier <code>@source</code> rules pointing to nested files
are scanned when later <code>@source</code> rules point to files in
parent folders (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20335">#20335</a>)</li>
<li>Prevent <code>@tailwindcss/vite</code> from triggering full page
reloads when scanned files are processed by Vite but haven't been loaded
as modules yet (<a
href="https://redirect.github.com/tailwindlabs/tailwindcss/pull/20336">#20336</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c2b24dd15f"><code>c2b24dd</code></a>
4.3.3 (<a
href="https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss/issues/20334">#20334</a>)</li>
<li><a
href="e48c5e8047"><code>e48c5e8</code></a>
Fix weird character rendering on Windows with Japanese locale (<a
href="https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss/issues/20318">#20318</a>)</li>
<li><a
href="35a3e9c515"><code>35a3e9c</code></a>
Always produce <code>\&lt;length&gt;</code> value when optimizing
<code>--spacing(0)</code> (<a
href="https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss/issues/20319">#20319</a>)</li>
<li><a
href="4af47fbe94"><code>4af47fb</code></a>
Fix hues in achromatic theme colors to be <code>none</code> (<a
href="https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss/issues/20314">#20314</a>)</li>
<li><a
href="5835691d21"><code>5835691</code></a>
Handle CSS nesting natively (<a
href="https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss/issues/20124">#20124</a>)</li>
<li><a
href="67c745efde"><code>67c745e</code></a>
Fix bug in attribute selector parsing (<a
href="https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss/issues/20303">#20303</a>)</li>
<li><a
href="2683903b86"><code>2683903</code></a>
Support fractional opacity modifiers for named shadow sizes (<a
href="https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss/issues/20302">#20302</a>)</li>
<li><a
href="04588b1e8f"><code>04588b1</code></a>
Fix theme() in JS plugins returning unresolved object instead of DEFAULT
valu...</li>
<li><a
href="b53fa096c9"><code>b53fa09</code></a>
fix: exclude iframes from focus-visible auto outline in Preflight (<a
href="https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss/issues/20292">#20292</a>)</li>
<li><a
href="ef79119d4e"><code>ef79119</code></a>
Bump dependencies (<a
href="https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss/issues/20300">#20300</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tailwindlabs/tailwindcss/commits/v4.3.3/packages/tailwindcss">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tailwindcss&package-manager=npm_and_yarn&previous-version=4.3.2&new-version=4.3.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-19 10:34:15 -07:00
Dotta e1df4c6068
fix(workspaces): keep deferred seed databases reliable (#11706)
## Thinking Path

> - Paperclip manages agent work in isolated execution workspaces.
> - A workspace depends on a valid database seed before it can run.
> - Deferred seed failures were hidden behind a successful provision
status.
> - The seed restore also had two possible owners for the embedded
PostgreSQL process.
> - That allowed the target database to stop while the restore was still
running.
> - This pull request makes seed failures visible and gives the seed
process sole lifecycle ownership.
> - The benefit is that workspace provisioning reports the real result
and does not stop its own target database.

## Linked Issues or Issue Description

Related: #11684

**What happened?**

Initial worktree provisioning could report success before its deferred
database seed completed. The seed restore could also reuse a target
embedded PostgreSQL process with another shutdown owner. This could stop
the target database during the restore.

**Expected behavior**

Workspace status must show a failed deferred seed as a failure. The seed
restore must own the target embedded PostgreSQL process until restore,
migration, and validation finish.

**Steps to reproduce**

1. Provision a worktree with deferred database seeding.
2. Make the seed manifest end in a failed state while the command exits
with code 0.
3. Observe that the provision status remains successful on `master`.
4. Start a seed restore against an already-running target embedded
PostgreSQL process.
5. Observe that another lifecycle owner can stop the target during
restore.

**Paperclip version or commit**

`51a843e135`

**Deployment mode**

Local dev with execution workspaces and embedded PostgreSQL.

## What Changed

- Add a first-class `workspace_seed` operation for deferred database
seeds.
- Require terminal, verified seed evidence before the seed operation
succeeds.
- Surface the seed phase and failure metadata in workspace status and UI
state.
- Give the seed process exclusive lifecycle ownership of the target
embedded PostgreSQL process.
- Suppress imported embedded-Postgres exit hooks without removing
existing host listeners.
- Record a credential-safe shutdown diagnostic in failed seed manifests.

## Verification

- The original deferred-seed commit passed 4 server tests, 24
workspace-status UI tests, shared/server/UI typechecks, and the UI token
gate.
- The original PostgreSQL-lifecycle commit passed 3 lifecycle tests, 3
ownership/diagnostic tests, 1 real embedded-Postgres seed integration,
and the affected package typechecks.
- No local tests were rerun after the clean cherry-pick because the
operator requested the shortest landing path.
- Review the automatic PR checks for the clean `origin/master` replay.

## Risks

- A live target database now causes an early error instead of being
reused. The error includes recovery guidance.
- Workspace consumers must handle the new `workspace_seed` operation
type. Shared types and UI state handling are updated in this pull
request.

> 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, GPT-5, high-reasoning mode, with repository, shell, and
GitHub tool use. The runtime does not expose a more specific deployment
suffix or context-window value.

## 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-19 10:41:29 -05:00
dependabot[bot] 59c4b5a9c7
build(deps): bump @base-ui/react from 1.6.0 to 1.7.0 (#11518)
Bumps
[@base-ui/react](https://github.com/mui/base-ui/tree/HEAD/packages/react)
from 1.6.0 to 1.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mui/base-ui/releases">@​base-ui/react's
releases</a>.</em></p>
<blockquote>
<h2>v1.7.0</h2>
<h3>General changes</h3>
<ul>
<li>Restore visible focus after keyboard close in Safari and Firefox (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5093">#5093</a>)
by <a href="https://github.com/lyzno1"><code>@​lyzno1</code></a></li>
<li>Type <code>render</code> callback props based on the rendered
element (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5104">#5104</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Reduce popup bundle size (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5233">#5233</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Reduce store bundle size (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5250">#5250</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Keep unpositioned popups at the viewport origin (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5299">#5299</a>)
by <a
href="https://github.com/flaviendelangle"><code>@​flaviendelangle</code></a></li>
<li>Correct layout and passive effect timing (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5337">#5337</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Remove redundant lifecycle synchronization (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5341">#5341</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Prevent stale cleanup from clearing registered part IDs (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5340">#5340</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Complete popup unmounting after a canceled exit transition (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5401">#5401</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Fix rendered trigger ID ownership (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5110">#5110</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Prevent unwanted flip with capped scrollable content (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5120">#5120</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Fix <code>collisionPadding</code> off-by-one on the biased side (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5143">#5143</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Reduce shared popup bundle size (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5192">#5192</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Mount popup subtrees synchronously when opening in React 17 (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5309">#5309</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Fix auto-resize origin for left-anchored popups (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5370">#5370</a>)
by <a href="https://github.com/sai6855"><code>@​sai6855</code></a></li>
<li>Avoid redundant re-renders during lazy flipping (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5372">#5372</a>)
by <a href="https://github.com/mdm317"><code>@​mdm317</code></a></li>
<li>Strip implementation-only types from published <code>.d.ts</code>
files (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5165">#5165</a>)
by <a
href="https://github.com/michaldudak"><code>@​michaldudak</code></a></li>
<li>Fix <code>usePreviousValue</code> equality comparison (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5264">#5264</a>)
by <a href="https://github.com/lyzno1"><code>@​lyzno1</code></a></li>
</ul>
<h3>Accordion</h3>
<ul>
<li>Remove the implicit <code>dir</code> attribute from
<code>&lt;Accordion.Root&gt;</code> (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5117">#5117</a>)
by <a
href="https://github.com/chuganzy"><code>@​chuganzy</code></a></li>
</ul>
<h3>Alert Dialog</h3>
<ul>
<li>Prevent <code>&lt;AlertDialog.Root&gt;</code> from reopening after
remounting with a reused handle (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5109">#5109</a>)
by <a
href="https://github.com/michaldudak"><code>@​michaldudak</code></a></li>
</ul>
<h3>Autocomplete</h3>
<ul>
<li>Respect locale when filtering (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5195">#5195</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Add <code>input-press</code> to change event details (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5356">#5356</a>)
by <a
href="https://github.com/chuganzy"><code>@​chuganzy</code></a></li>
<li>Add <code>cancel-open</code> to change event details (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5376">#5376</a>)
by <a
href="https://github.com/chuganzy"><code>@​chuganzy</code></a></li>
<li>Reset list scroll position on filter (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5231">#5231</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Remove dead code and deduplicate handlers (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5194">#5194</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Fix listbox separator semantics (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5399">#5399</a>)
by <a
href="https://github.com/sarthakmalik0810"><code>@​sarthakmalik0810</code></a></li>
</ul>
<h3>Avatar</h3>
<ul>
<li>Show <code>&lt;Avatar.Fallback&gt;</code> immediately when
<code>delay={0}</code> (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5147">#5147</a>)
by <a
href="https://github.com/chuganzy"><code>@​chuganzy</code></a></li>
</ul>
<h3>Button</h3>
<ul>
<li>Fix keyboard click handling for custom elements (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/4838">#4838</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
</ul>
<h3>Checkbox</h3>
<ul>
<li>Remove redundant validation input ref (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5188">#5188</a>)
by <a
href="https://github.com/nami8824"><code>@​nami8824</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/mui/base-ui/blob/master/CHANGELOG.md">@​base-ui/react's
changelog</a>.</em></p>
<blockquote>
<h2>v1.7.0</h2>
<p><em>Aug 4, 2026</em></p>
<h3>General changes</h3>
<ul>
<li>Restore visible focus after keyboard close in Safari and Firefox (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5093">#5093</a>)
by <a href="https://github.com/lyzno1"><code>@​lyzno1</code></a></li>
<li>Type <code>render</code> callback props based on the rendered
element (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5104">#5104</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Reduce popup bundle size (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5233">#5233</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Reduce store bundle size (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5250">#5250</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Keep unpositioned popups at the viewport origin (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5299">#5299</a>)
by <a
href="https://github.com/flaviendelangle"><code>@​flaviendelangle</code></a></li>
<li>Correct layout and passive effect timing (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5337">#5337</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Remove redundant lifecycle synchronization (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5341">#5341</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Prevent stale cleanup from clearing registered part IDs (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5340">#5340</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Complete popup unmounting after a canceled exit transition (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5401">#5401</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Fix rendered trigger ID ownership (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5110">#5110</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Prevent unwanted flip with capped scrollable content (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5120">#5120</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Fix <code>collisionPadding</code> off-by-one on the biased side (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5143">#5143</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Reduce shared popup bundle size (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5192">#5192</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Mount popup subtrees synchronously when opening in React 17 (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5309">#5309</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Fix auto-resize origin for left-anchored popups (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5370">#5370</a>)
by <a href="https://github.com/sai6855"><code>@​sai6855</code></a></li>
<li>Avoid redundant re-renders during lazy flipping (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5372">#5372</a>)
by <a href="https://github.com/mdm317"><code>@​mdm317</code></a></li>
<li>Strip implementation-only types from published <code>.d.ts</code>
files (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5165">#5165</a>)
by <a
href="https://github.com/michaldudak"><code>@​michaldudak</code></a></li>
<li>Fix <code>usePreviousValue</code> equality comparison (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5264">#5264</a>)
by <a href="https://github.com/lyzno1"><code>@​lyzno1</code></a></li>
</ul>
<h3>Accordion</h3>
<ul>
<li>Remove the implicit <code>dir</code> attribute from
<code>&lt;Accordion.Root&gt;</code> (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5117">#5117</a>)
by <a
href="https://github.com/chuganzy"><code>@​chuganzy</code></a></li>
</ul>
<h3>Alert Dialog</h3>
<ul>
<li>Prevent <code>&lt;AlertDialog.Root&gt;</code> from reopening after
remounting with a reused handle (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5109">#5109</a>)
by <a
href="https://github.com/michaldudak"><code>@​michaldudak</code></a></li>
</ul>
<h3>Autocomplete</h3>
<ul>
<li>Respect locale when filtering (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5195">#5195</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Add <code>input-press</code> to change event details (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5356">#5356</a>)
by <a
href="https://github.com/chuganzy"><code>@​chuganzy</code></a></li>
<li>Add <code>cancel-open</code> to change event details (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5376">#5376</a>)
by <a
href="https://github.com/chuganzy"><code>@​chuganzy</code></a></li>
<li>Reset list scroll position on filter (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5231">#5231</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Remove dead code and deduplicate handlers (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5194">#5194</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
<li>Fix listbox separator semantics (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5399">#5399</a>)
by <a
href="https://github.com/sarthakmalik0810"><code>@​sarthakmalik0810</code></a></li>
</ul>
<h3>Avatar</h3>
<ul>
<li>Show <code>&lt;Avatar.Fallback&gt;</code> immediately when
<code>delay={0}</code> (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5147">#5147</a>)
by <a
href="https://github.com/chuganzy"><code>@​chuganzy</code></a></li>
</ul>
<h3>Button</h3>
<ul>
<li>Fix keyboard click handling for custom elements (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/4838">#4838</a>)
by <a href="https://github.com/atomiks"><code>@​atomiks</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="254f4744f0"><code>254f474</code></a>
[release] v1.7.0 (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5404">#5404</a>)</li>
<li><a
href="9222cdac25"><code>9222cda</code></a>
[meter] Format clamped values (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5409">#5409</a>)</li>
<li><a
href="becc19ace9"><code>becc19a</code></a>
[combobox][autocomplete][select] Fix listbox separator semantics (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5399">#5399</a>)</li>
<li><a
href="7cc0eef3a3"><code>7cc0eef</code></a>
[drawer] Fix click-only outside dismissal after swipe (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5392">#5392</a>)</li>
<li><a
href="1a2ca3c9f8"><code>1a2ca3c</code></a>
[all components] Fix canceled exit unmount (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5401">#5401</a>)</li>
<li><a
href="166e8ac015"><code>166e8ac</code></a>
[popups] Fix quadratic dev-mode trigger registration check (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5400">#5400</a>)</li>
<li><a
href="071e892016"><code>071e892</code></a>
[all components] Avoid unused popup handle attachments (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5394">#5394</a>)</li>
<li><a
href="cbc87d195a"><code>cbc87d1</code></a>
[select] Stabilize scroll arrow cleanup test (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5402">#5402</a>)</li>
<li><a
href="3b5715cc70"><code>3b5715c</code></a>
[all components] Fix popup handle lifecycle regressions (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5387">#5387</a>)</li>
<li><a
href="54cfcc1883"><code>54cfcc1</code></a>
[typescript] Preserve published internals types (<a
href="https://github.com/mui/base-ui/tree/HEAD/packages/react/issues/5386">#5386</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/mui/base-ui/commits/v1.7.0/packages/react">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@base-ui/react&package-manager=npm_and_yarn&previous-version=1.6.0&new-version=1.7.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-19 00:45:59 -07:00
Dotta a2bf936f9a
feat(workspaces): sign the workspace login handoff and gate readiness (#11671)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Managed worktree services run isolated Paperclip instances with
cloned databases.
> - A reachable service was reported as ready even when its database,
runtime identity, or login path was not usable.
> - The first candidate added verified database seeding and managed
repair in #11665.
> - This pull request consolidates that candidate with signed login
handoff and a complete readiness contract.
> - Post-QA fixes close five defects in repair identity, repair
responses, UI retry, seed journal handling, and seed-source trust.
> - The benefit is a workspace that either opens safely or reports one
accurate recovery action.

## Linked Issues or Issue Description

No public GitHub issue exists for this work, so the problem is described
here.

**What happened**

Managed workspace URLs could return HTTP 200 and report ready while
login failed. QA also found cases where repair used the wrong instance
identity, returned a generic error, left the UI stuck, rejected a safe
journal lag, or trusted a mutable workspace manifest.

**Expected behavior**

Opening a ready workspace signs the board user in to the correct
isolated instance. Provisioning and repair use a registered source and
report a structured recovery state.

**Actual behavior**

Entry depended on a password copied into the clone. Several failure
paths could publish stale readiness, hide the repair precondition, or
trust state that the workspace could modify.

**Additional context**

This pull request includes the commits first published in #11665. That
pull request keeps the original base head for review history. This
consolidated pull request is the merge candidate. Related open readiness
work includes #11575 and #11621.

## What Changed

- Adds a short-lived, signed, single-use login ticket. It binds the
user, workspace, instance, and runtime origin.
- Exchanges the ticket through Better Auth. It creates the session and
cookie through the supported adapter path.
- Adds protected workspace readiness fields for the database, clone
data, login handoff, seed phase, and runtime identity.
- Fails readiness closed when the guest has no company or
execution-workspace binding.
- Binds ticket issuance to the exact cloned user and active company
membership selected for the handoff.
- Verifies every current active board identity through the exact-user
handoff before publication or reuse.
- Gates managed runtime publication on the readiness contract and the
recorded worktree instance identity.
- Refreshes runtime work products from the live runtime row after a port
change.
- Adds one workspace access card with ready, degraded, repairing, and
failed states.
- Uses the runtime response identity for repair. It returns structured
repair precondition errors.
- Lets a valid source journal lag converge during provisioning.
- Binds seed and repair manifests to a source registered outside the
agent-writable worktree.
- Clears recovered UI errors so a successful retry can open the
workspace.
- Makes runtime tests register canonical sources and avoid ports owned
by live host listeners.
- Keeps Vitest on source suites when compiled `dist` trees exist.
- Isolates CLI and adapter tests from ambient AWS and runtime API
environment variables.
- Preserves a 404 response for cross-company workspace ID lookups before
runtime authorization.
- Makes concurrent single-flight coverage independent of
path-canonicalization scheduling order.

## Verification

The following checks passed on the integrated head:

```sh
pnpm -r typecheck
pnpm build
pnpm check:token-gates
pnpm --filter @paperclipai/db check:migrations
```

- The server source lane passed 420 files and 4,953 tests. Five tests
were skipped.
- The CLI lane passed 57 files and 385 tests.
- The database lane passed 26 files and 97 tests.
- The shared package passed 58 files and 506 tests.
- The adapter utility lane passed 640 tests. Four tests were skipped.
- The Claude adapter passed 220 tests. One test was skipped.
- The Codex adapter passed 323 tests.
- The OpenClaw adapter passed 13 tests.
- The OpenCode adapter passed 42 tests.
- The plugin SDK passed 45 tests.
- The workspace runtime suite passed 124 tests.
- The caller-scoped readiness and handoff suite passed 52 tests.
- The workspace provisioning shell suite passed 7 tests.
- The runtime exposure suite passed 17 tests while live host mappings
occupied fixed test ports.
- `git diff --check` passed and the worktree is clean.

The serialized route lane will run in GitHub CI with its normal shards.
No deployment or active-workspace migration was performed.

## Risks

- This is a medium-risk authentication and runtime-readiness change.
- The login ticket uses exact origin, workspace, instance, and user
binding. It has a short expiry and a one-time nonce.
- Runtime publication is stricter. A real readiness, identity, per-user
handoff, or control-plane database disagreement now blocks publication.
- This pull request supersedes #11665 as the merge candidate. Close
#11665 after this pull request merges.
- No new database migration is included. The lockfile and workflow files
are unchanged.
- Deployment and active-workspace migration are intentionally outside
this pull request.

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

## Model Used

Claude Opus 5 (`claude-opus-5[1m]`), 1M context, extended thinking, tool
use, and code execution produced the main candidate. OpenAI GPT-5
(`gpt-5`) through Codex, with agentic reasoning, tool use, and code
execution, integrated the post-QA fixes and hardened the test gates. The
Codex context-window size was not exposed.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have 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-19 02:37:02 -05:00
dependabot[bot] 3dd2d21e4d
build(deps-dev): bump @storybook/react-vite from 10.5.0 to 10.5.8 (#11514)
Bumps
[@storybook/react-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite)
from 10.5.0 to 10.5.8.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/releases">@​storybook/react-vite's
releases</a>.</em></p>
<blockquote>
<h2>v10.5.8</h2>
<h2>10.5.8</h2>
<ul>
<li>React: Fix RDT tsconfig selection for Vite project references - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35743">#35743</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Tanstack React: Remove <code>@​cloudflare/vite-plugin</code> from
the inherited Vite config - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35706">#35706</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
<li>Tanstack: Wait for router to load before rendering - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35784">#35784</a>,
thanks <a
href="https://github.com/huang-julien"><code>@​huang-julien</code></a>!</li>
<li>Test: Fix Illegal invocation when reading prototype.focus - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35528">#35528</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
</ul>
<h2>v10.5.7</h2>
<h2>10.5.7</h2>
<ul>
<li>Angular: Serve ancestor node_modules for addon-vitest in browser
mode - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35600">#35600</a>,
thanks <a
href="https://github.com/brandonroberts"><code>@​brandonroberts</code></a>!</li>
<li>Refactor: Update getVersionedPackages method to handle non-Storybook
packages correctly - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35769">#35769</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>v10.5.6</h2>
<h2>10.5.6</h2>
<ul>
<li>Dependencies: Pin `@testing-library/jest-dom` to `6.9.1` - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35614">#35614</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ESLint Plugin: Add plugin meta and document oxlint usage - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35655">#35655</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Vue: Skip docgen for module ids carrying a query - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35598">#35598</a>,
thanks <a
href="https://github.com/seanogdev"><code>@​seanogdev</code></a>!</li>
</ul>
<h2>v10.5.5</h2>
<h2>10.5.5</h2>
<ul>
<li>CLI: Update AI setup instructions to msw-storybook-addon v3 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35512">#35512</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Core: Upgrade `ws` to fix security advisories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35584">#35584</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ReactNative: Telemetry framework detection fix - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35560">#35560</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>SyntaxHighlighter: Fix PrismJS dark mode mismatch - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35541">#35541</a>,
thanks <a
href="https://github.com/hxy-asdw"><code>@​hxy-asdw</code></a>!</li>
<li>TanStack: Preserve explicit route ids on pathful clones - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35499">#35499</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Resolve mock redirects through Vite's resolver - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35501">#35501</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Respect routeOverrides component overrides in stories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35497">#35497</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
</ul>
<h2>v10.5.4</h2>
<h2>10.5.4</h2>
<ul>
<li>ReactNative: Telemetry framework detection fix - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35560">#35560</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>SyntaxHighlighter: Fix PrismJS dark mode mismatch - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35541">#35541</a>,
thanks <a
href="https://github.com/hxy-asdw"><code>@​hxy-asdw</code></a>!</li>
</ul>
<h2>v10.5.3</h2>
<h2>10.5.3</h2>
<ul>
<li>Dependencies: Upgrade TypeScript to 6.0.3 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34971">#34971</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>v10.5.2</h2>
<h2>10.5.2</h2>
<ul>
<li>Angular-Vite: Drop <code>@​angular/platform-browser-dynamic</code>
peer dependency - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35457">#35457</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Angular-Vite: Widen TypeScript peer dependency range to support
TypeScript 6 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35455">#35455</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Include chromatic packages in ecosystem identifier - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35170">#35170</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>TanStack: Fix createServerFn validator mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35185">#35185</a>,
thanks <a
href="https://github.com/sjh9714"><code>@​sjh9714</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/storybookjs/storybook/blob/next/CHANGELOG.md">@​storybook/react-vite's
changelog</a>.</em></p>
<blockquote>
<h2>10.5.8</h2>
<ul>
<li>React: Fix RDT tsconfig selection for Vite project references - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35743">#35743</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Tanstack React: Remove <code>@​cloudflare/vite-plugin</code> from
the inherited Vite config - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35706">#35706</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
<li>Tanstack: Wait for router to load before rendering - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35784">#35784</a>,
thanks <a
href="https://github.com/huang-julien"><code>@​huang-julien</code></a>!</li>
<li>Test: Fix Illegal invocation when reading prototype.focus - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35528">#35528</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
</ul>
<h2>10.5.7</h2>
<ul>
<li>Angular: Serve ancestor node_modules for addon-vitest in browser
mode - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35600">#35600</a>,
thanks <a
href="https://github.com/brandonroberts"><code>@​brandonroberts</code></a>!</li>
<li>Refactor: Update getVersionedPackages method to handle non-Storybook
packages correctly - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35769">#35769</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>10.5.6</h2>
<ul>
<li>Dependencies: Pin <code>@testing-library/jest-dom</code> to
<code>6.9.1</code> - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35614">#35614</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ESLint Plugin: Add plugin meta and document oxlint usage - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35655">#35655</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Vue: Skip docgen for module ids carrying a query - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35598">#35598</a>,
thanks <a
href="https://github.com/seanogdev"><code>@​seanogdev</code></a>!</li>
</ul>
<h2>10.5.5</h2>
<ul>
<li>CLI: Update AI setup instructions to msw-storybook-addon v3 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35512">#35512</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Core: Upgrade <code>ws</code> to fix security advisories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35584">#35584</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ReactNative: Telemetry framework detection fix - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35560">#35560</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>SyntaxHighlighter: Fix PrismJS dark mode mismatch - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35541">#35541</a>,
thanks <a
href="https://github.com/hxy-asdw"><code>@​hxy-asdw</code></a>!</li>
<li>TanStack: Preserve explicit route ids on pathful clones - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35499">#35499</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Resolve mock redirects through Vite's resolver - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35501">#35501</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Respect routeOverrides component overrides in stories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35497">#35497</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
</ul>
<h2>10.5.4</h2>
<ul>
<li>ReactNative: Telemetry framework detection fix - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35560">#35560</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>SyntaxHighlighter: Fix PrismJS dark mode mismatch - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35541">#35541</a>,
thanks <a
href="https://github.com/hxy-asdw"><code>@​hxy-asdw</code></a>!</li>
</ul>
<h2>10.5.3</h2>
<ul>
<li>Dependencies: Upgrade TypeScript to 6.0.3 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34971">#34971</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>10.5.2</h2>
<ul>
<li>TanStack: Fix createServerFn validator mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35185">#35185</a>,
thanks <a
href="https://github.com/sjh9714"><code>@​sjh9714</code></a>!</li>
<li>TanStack: Support pathless layout routes (id-only) in story routing
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35465">#35465</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>Tanstack-react: Add missing Hydrate export - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35111">#35111</a>,
thanks <a
href="https://github.com/arun-357"><code>@​arun-357</code></a>!</li>
<li>Tanstack-react: Keep JSX-only component references during dead-code
elimination - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35206">#35206</a>,
thanks <a
href="https://github.com/yatishgoel"><code>@​yatishgoel</code></a>!</li>
<li>Vitest: Fix coverage toggle crash on Vite 6 by clearing closed
Vitest instance on restart - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35461">#35461</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
</ul>
<h2>10.5.1</h2>
<ul>
<li>Angular-Vite: Drop <code>@​angular/platform-browser-dynamic</code>
peer dependency - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35457">#35457</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Angular-Vite: Widen TypeScript peer dependency range to support
TypeScript 6 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35455">#35455</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Include chromatic packages in ecosystem identifier - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35170">#35170</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6ef7d1ae81"><code>6ef7d1a</code></a>
Bump version from &quot;10.5.7&quot; to &quot;10.5.8&quot; [skip
ci]</li>
<li><a
href="647e982151"><code>647e982</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite/issues/35743">#35743</a>
from storybookjs/norbert/revive-34415-file-aware-ts...</li>
<li><a
href="7c6fb3a5ec"><code>7c6fb3a</code></a>
Bump version from &quot;10.5.6&quot; to &quot;10.5.7&quot; [skip
ci]</li>
<li><a
href="43f2316559"><code>43f2316</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite/issues/35490">#35490</a>
from storybookjs/valentin/remove-tsconfig-baseurl</li>
<li><a
href="3126f0a14a"><code>3126f0a</code></a>
Bump version from &quot;10.5.5&quot; to &quot;10.5.6&quot; [skip
ci]</li>
<li><a
href="05a52b7a88"><code>05a52b7</code></a>
Bump version from &quot;10.5.4&quot; to &quot;10.5.5&quot; [skip
ci]</li>
<li><a
href="3327dc4469"><code>3327dc4</code></a>
Bump version from &quot;10.5.3&quot; to &quot;10.5.4&quot; [skip
ci]</li>
<li><a
href="9ac273930a"><code>9ac2739</code></a>
Bump version from &quot;10.5.2&quot; to &quot;10.5.3&quot; [skip
ci]</li>
<li><a
href="b4b00f2766"><code>b4b00f2</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite/issues/34971">#34971</a>
from storybookjs/valentin/upgrade-typescript-6</li>
<li><a
href="518f711cb3"><code>518f711</code></a>
Bump version from &quot;10.5.1&quot; to &quot;10.5.2&quot; [skip
ci]</li>
<li>Additional commits viewable in <a
href="https://github.com/storybookjs/storybook/commits/v10.5.8/code/frameworks/react-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>
Co-authored-by: Priya Raman <priya@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-19 00:27:37 -07:00
Dotta d1cd9c37f4
fix(ui): keep Archive available on every inbox item (#11636)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Mine inbox combines tasks, failed runs, approvals, and access
requests
> - Each item needs a consistent action that removes it from the inbox
> - Task rows keep the Archive action separate from the unread marker
> - Other rows used one slot for both actions, so an unread marker hid
Archive
> - This pull request gives every Mine inbox row the same Archive action
> - The benefit is consistent hover and swipe cleanup for every inbox
item type

## Linked Issues or Issue Description

**What happened?**

Unread failed runs, approvals, and join requests showed a blue unread
marker but no Archive button. The user had to mark the item as read
before the old leading Archive control became available.

**Expected behavior**

Every Mine inbox item shows Archive on hover. Every item also keeps its
swipe-to-archive behavior.

**Steps to reproduce**

1. Open the Mine inbox.
2. Add an unread failed run, approval, or join request.
3. Hover the new item.
4. Observe that Archive is missing before this change.

**Paperclip version or commit**

Current `master` at `0e9b03832d`.

**Deployment mode**

Local development from source.

**Additional context**

PR #1860 improves the accessibility of the existing swipe gesture. PR
#9704 changes server-side archive resurfacing rules. Neither PR adds the
missing hover action to unread non-task rows.

## What Changed

- Reused the task-row Archive action for failed runs, approvals, and
join requests.
- Kept the unread marker visible in its own leading slot.
- Kept retry, approve, and reject actions beside the new trailing
Archive action.
- Added regression coverage for hover Archive and swipe wrappers on
every non-task Mine row type.
- Updated a test comment so the repository token gate does not treat
documented color literals as UI source.

## Verification

- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/Inbox.test.tsx src/components/IssueRow.test.tsx
src/components/SwipeToArchive.test.tsx` — 46 tests passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm check:token-gates` — passed.
- `pnpm -r typecheck` — passed.
- `pnpm test:run` — 4,284 tests passed. Ten unrelated runtime-exposure
tests cannot claim fixed ports 42000 and 52000 because host Tailscale
listeners already own them.
- `pnpm build` — passed.
- GitHub CI on the latest head — all required checks passed.
- Greptile — 5/5 confidence with no open review threads.

## Risks

- Low risk. The change only affects Mine inbox row actions and
regression tests.
- The server archive and dismiss APIs do not change.
- Retry, approve, and reject behavior does not change.
- No documentation change is required because this fix restores the
expected inbox 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 Codex based on GPT-5. The runtime did not expose a dated model
ID or context-window size. Reasoning, terminal tool use, code execution,
and GitHub integration were enabled.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-18 15:06:24 -05:00
dependabot[bot] 1b86af995f
build(deps-dev): bump @storybook/addon-docs from 10.5.0 to 10.5.8 (#11513)
Bumps
[@storybook/addon-docs](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/docs)
from 10.5.0 to 10.5.8.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/releases">@​storybook/addon-docs's
releases</a>.</em></p>
<blockquote>
<h2>v10.5.8</h2>
<h2>10.5.8</h2>
<ul>
<li>React: Fix RDT tsconfig selection for Vite project references - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35743">#35743</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Tanstack React: Remove <code>@​cloudflare/vite-plugin</code> from
the inherited Vite config - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35706">#35706</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
<li>Tanstack: Wait for router to load before rendering - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35784">#35784</a>,
thanks <a
href="https://github.com/huang-julien"><code>@​huang-julien</code></a>!</li>
<li>Test: Fix Illegal invocation when reading prototype.focus - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35528">#35528</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
</ul>
<h2>v10.5.7</h2>
<h2>10.5.7</h2>
<ul>
<li>Angular: Serve ancestor node_modules for addon-vitest in browser
mode - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35600">#35600</a>,
thanks <a
href="https://github.com/brandonroberts"><code>@​brandonroberts</code></a>!</li>
<li>Refactor: Update getVersionedPackages method to handle non-Storybook
packages correctly - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35769">#35769</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>v10.5.6</h2>
<h2>10.5.6</h2>
<ul>
<li>Dependencies: Pin `@testing-library/jest-dom` to `6.9.1` - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35614">#35614</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ESLint Plugin: Add plugin meta and document oxlint usage - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35655">#35655</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Vue: Skip docgen for module ids carrying a query - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35598">#35598</a>,
thanks <a
href="https://github.com/seanogdev"><code>@​seanogdev</code></a>!</li>
</ul>
<h2>v10.5.5</h2>
<h2>10.5.5</h2>
<ul>
<li>CLI: Update AI setup instructions to msw-storybook-addon v3 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35512">#35512</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Core: Upgrade `ws` to fix security advisories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35584">#35584</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ReactNative: Telemetry framework detection fix - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35560">#35560</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>SyntaxHighlighter: Fix PrismJS dark mode mismatch - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35541">#35541</a>,
thanks <a
href="https://github.com/hxy-asdw"><code>@​hxy-asdw</code></a>!</li>
<li>TanStack: Preserve explicit route ids on pathful clones - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35499">#35499</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Resolve mock redirects through Vite's resolver - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35501">#35501</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Respect routeOverrides component overrides in stories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35497">#35497</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
</ul>
<h2>v10.5.4</h2>
<h2>10.5.4</h2>
<ul>
<li>ReactNative: Telemetry framework detection fix - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35560">#35560</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>SyntaxHighlighter: Fix PrismJS dark mode mismatch - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35541">#35541</a>,
thanks <a
href="https://github.com/hxy-asdw"><code>@​hxy-asdw</code></a>!</li>
</ul>
<h2>v10.5.3</h2>
<h2>10.5.3</h2>
<ul>
<li>Dependencies: Upgrade TypeScript to 6.0.3 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34971">#34971</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>v10.5.2</h2>
<h2>10.5.2</h2>
<ul>
<li>Angular-Vite: Drop <code>@​angular/platform-browser-dynamic</code>
peer dependency - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35457">#35457</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Angular-Vite: Widen TypeScript peer dependency range to support
TypeScript 6 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35455">#35455</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Include chromatic packages in ecosystem identifier - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35170">#35170</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>TanStack: Fix createServerFn validator mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35185">#35185</a>,
thanks <a
href="https://github.com/sjh9714"><code>@​sjh9714</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/storybookjs/storybook/blob/next/CHANGELOG.md">@​storybook/addon-docs's
changelog</a>.</em></p>
<blockquote>
<h2>10.5.8</h2>
<ul>
<li>React: Fix RDT tsconfig selection for Vite project references - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35743">#35743</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>Tanstack React: Remove <code>@​cloudflare/vite-plugin</code> from
the inherited Vite config - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35706">#35706</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
<li>Tanstack: Wait for router to load before rendering - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35784">#35784</a>,
thanks <a
href="https://github.com/huang-julien"><code>@​huang-julien</code></a>!</li>
<li>Test: Fix Illegal invocation when reading prototype.focus - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35528">#35528</a>,
thanks <a
href="https://github.com/FrancoKaddour"><code>@​FrancoKaddour</code></a>!</li>
</ul>
<h2>10.5.7</h2>
<ul>
<li>Angular: Serve ancestor node_modules for addon-vitest in browser
mode - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35600">#35600</a>,
thanks <a
href="https://github.com/brandonroberts"><code>@​brandonroberts</code></a>!</li>
<li>Refactor: Update getVersionedPackages method to handle non-Storybook
packages correctly - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35769">#35769</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>10.5.6</h2>
<ul>
<li>Dependencies: Pin <code>@testing-library/jest-dom</code> to
<code>6.9.1</code> - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35614">#35614</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ESLint Plugin: Add plugin meta and document oxlint usage - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35655">#35655</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Vue: Skip docgen for module ids carrying a query - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35598">#35598</a>,
thanks <a
href="https://github.com/seanogdev"><code>@​seanogdev</code></a>!</li>
</ul>
<h2>10.5.5</h2>
<ul>
<li>CLI: Update AI setup instructions to msw-storybook-addon v3 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35512">#35512</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
<li>Core: Upgrade <code>ws</code> to fix security advisories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35584">#35584</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>ReactNative: Telemetry framework detection fix - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35560">#35560</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>SyntaxHighlighter: Fix PrismJS dark mode mismatch - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35541">#35541</a>,
thanks <a
href="https://github.com/hxy-asdw"><code>@​hxy-asdw</code></a>!</li>
<li>TanStack: Preserve explicit route ids on pathful clones - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35499">#35499</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Resolve mock redirects through Vite's resolver - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35501">#35501</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>TanStack: Respect routeOverrides component overrides in stories - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35497">#35497</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
</ul>
<h2>10.5.4</h2>
<ul>
<li>ReactNative: Telemetry framework detection fix - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35560">#35560</a>,
thanks <a
href="https://github.com/ndelangen"><code>@​ndelangen</code></a>!</li>
<li>SyntaxHighlighter: Fix PrismJS dark mode mismatch - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35541">#35541</a>,
thanks <a
href="https://github.com/hxy-asdw"><code>@​hxy-asdw</code></a>!</li>
</ul>
<h2>10.5.3</h2>
<ul>
<li>Dependencies: Upgrade TypeScript to 6.0.3 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/34971">#34971</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
</ul>
<h2>10.5.2</h2>
<ul>
<li>TanStack: Fix createServerFn validator mock - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35185">#35185</a>,
thanks <a
href="https://github.com/sjh9714"><code>@​sjh9714</code></a>!</li>
<li>TanStack: Support pathless layout routes (id-only) in story routing
- <a
href="https://redirect.github.com/storybookjs/storybook/pull/35465">#35465</a>,
thanks <a
href="https://github.com/unpunnyfuns"><code>@​unpunnyfuns</code></a>!</li>
<li>Tanstack-react: Add missing Hydrate export - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35111">#35111</a>,
thanks <a
href="https://github.com/arun-357"><code>@​arun-357</code></a>!</li>
<li>Tanstack-react: Keep JSX-only component references during dead-code
elimination - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35206">#35206</a>,
thanks <a
href="https://github.com/yatishgoel"><code>@​yatishgoel</code></a>!</li>
<li>Vitest: Fix coverage toggle crash on Vite 6 by clearing closed
Vitest instance on restart - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35461">#35461</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
</ul>
<h2>10.5.1</h2>
<ul>
<li>Angular-Vite: Drop <code>@​angular/platform-browser-dynamic</code>
peer dependency - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35457">#35457</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Angular-Vite: Widen TypeScript peer dependency range to support
TypeScript 6 - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35455">#35455</a>,
thanks <a
href="https://github.com/valentinpalkovic"><code>@​valentinpalkovic</code></a>!</li>
<li>Core: Include chromatic packages in ecosystem identifier - <a
href="https://redirect.github.com/storybookjs/storybook/pull/35170">#35170</a>,
thanks <a
href="https://github.com/yannbf"><code>@​yannbf</code></a>!</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6ef7d1ae81"><code>6ef7d1a</code></a>
Bump version from &quot;10.5.7&quot; to &quot;10.5.8&quot; [skip
ci]</li>
<li><a
href="7c6fb3a5ec"><code>7c6fb3a</code></a>
Bump version from &quot;10.5.6&quot; to &quot;10.5.7&quot; [skip
ci]</li>
<li><a
href="3126f0a14a"><code>3126f0a</code></a>
Bump version from &quot;10.5.5&quot; to &quot;10.5.6&quot; [skip
ci]</li>
<li><a
href="05a52b7a88"><code>05a52b7</code></a>
Bump version from &quot;10.5.4&quot; to &quot;10.5.5&quot; [skip
ci]</li>
<li><a
href="3327dc4469"><code>3327dc4</code></a>
Bump version from &quot;10.5.3&quot; to &quot;10.5.4&quot; [skip
ci]</li>
<li><a
href="9ac273930a"><code>9ac2739</code></a>
Bump version from &quot;10.5.2&quot; to &quot;10.5.3&quot; [skip
ci]</li>
<li><a
href="b4b00f2766"><code>b4b00f2</code></a>
Merge pull request <a
href="https://github.com/storybookjs/storybook/tree/HEAD/code/addons/docs/issues/34971">#34971</a>
from storybookjs/valentin/upgrade-typescript-6</li>
<li><a
href="518f711cb3"><code>518f711</code></a>
Bump version from &quot;10.5.1&quot; to &quot;10.5.2&quot; [skip
ci]</li>
<li><a
href="c253a0667d"><code>c253a06</code></a>
Bump version from &quot;10.5.0&quot; to &quot;10.5.1&quot; [skip
ci]</li>
<li>See full diff in <a
href="https://github.com/storybookjs/storybook/commits/v10.5.8/code/addons/docs">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@storybook/addon-docs&package-manager=npm_and_yarn&previous-version=10.5.0&new-version=10.5.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-18 12:52:23 -07:00
Nicky Leach 120ae5428f
feat(server): add a one-click relink action for detached custom-image templates (#11641)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip environments can use captured custom images for agent runs
> - A configuration fingerprint change can detach a valid custom-image
template
> - Operators need a safe way to confirm that the image still matches
the boot source
> - This pull request adds a guarded relink action with drift
classification and audit logging
> - The benefit is a deliberate relink without a new sandbox boot or
provider snapshot

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting environment, server, and UI behavior.

**Problem or motivation**

A custom-image template detaches when the environment configuration
fingerprint changes. The runtime then uses the base image, even when the
boot source did not change. The only prior remedy required a full
re-capture.

**Proposed solution**

Add an operator-triggered relink action. Classify configuration drift
from a server-owned boot-relevant snapshot. Relink knob-only drift
without confirmation. Require explicit confirmation for boot-source or
unclassified drift. Guard the route for instance administrators and
record a safe activity event.

**Alternatives considered**

Keep requiring a full re-capture. This adds a sandbox boot and provider
snapshot for cases where the image remains correct.

**Roadmap alignment**

The roadmap has no matching custom-image relink item. This change
addresses an environment operation gap.

**Additional context**

The relink response exposes raw drift values only in the transient 409
response to the instance administrator. The service never persists or
logs fingerprints or configuration values. Reserved identity-path
segments fail closed.

## What Changed

- Add `relinkActiveTemplate` with drift classification and conditional
fingerprint update.
- Persist a server-owned boot-relevant configuration snapshot during
capture.
- Add the guarded relink route with strict request validation and
activity logging.
- Add the relink action and confirmation flow to the environment page.
- Add service, route, UI, and OpenAPI coverage.

## Verification

- Run the focused service suite: `pnpm vitest run
server/src/services/environment-custom-images-service.test.ts`.
- Run the focused route suite: `pnpm vitest run
server/src/routes/environment-custom-image-routes.test.ts`.
- Run the focused UI suite: `pnpm vitest run
ui/src/pages/CompanyEnvironments.test.tsx`.
- Run server and UI TypeScript checks.
- Confirm the OpenAPI snapshot matches the new route.
- Confirm all required GitHub checks pass on commit
`e46fdcfe94a719be854adf8849d30714e5b70b93`.
- Confirm Greptile reports 5/5 with no unresolved review threads.

## Risks

The relink action can keep an image after configuration drift. The
service requires explicit confirmation for boot-source or unclassified
drift. Reserved path segments produce a safe unresolved marker and never
enter stored values.

## Model Used

OpenAI GPT-5 Codex. The model used repository inspection, GitHub
operations, and PR preparation with tool use and code execution. The
runtime did not expose a context-window value or a separate
reasoning-mode value.

## 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-18 11:43:39 -07:00
Dotta 6b8e42168e
Add governed secret alias confirmation cards (#11486)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents need scoped secret bindings to use external services safely.
> - Agents could not request an existing secret under a new config name
without an internal secret identifier.
> - Existing binding proposals were only visible in Settings and did not
create an issue-thread approval path.
> - A confirmation card could record acceptance without proving that the
binding was created.
> - This pull request extends the existing secret proposal system with
safe source references and governed issue-thread confirmation cards.
> - The benefit is a one-click flow that creates the binding or shows a
clear failure without exposing secret material.

## Linked Issues or Issue Description

Related prerequisite: #11482.

**Subsystem affected**

Cross-cutting: server REST APIs, shared interaction contracts, database
proposal schema, and issue-thread UI.

**Problem or motivation**

An agent can need an existing bound secret under a second config name.
The agent cannot safely discover the internal secret identifier. The
existing proposal is also easy for the operator to miss because it only
appears in Settings. A generic confirmation can record acceptance
without executing the binding.

**Proposed solution**

Let an agent create a binding proposal from one of its existing config
paths. Mint a server-owned, human-only confirmation card on the
checked-out issue. Recheck the operator's target-agent permission under
the proposal row lock. Execute the existing proposal transaction after
card acceptance. Store an `executed` or `failed` result on the card.
Render the complete lifecycle in the issue thread and attention
resolver.

**Alternatives considered**

A new alias subsystem would duplicate proposal quotas, expiry,
authorization, and binding synchronization. A text-only issue comment
would not provide a governed action or an execution result. An
agent-supplied card payload would permit metadata smuggling. This change
uses the existing proposal transaction and a server-owned payload
instead.

**Roadmap alignment**

This change extends the completed "Secrets Manager with per-agent
access" roadmap item. It preserves scoped bindings and audited
resolution. The required GitHub search found no other open duplicate
issue or pull request.

## What Changed

- Added safe source-config-path binding proposals and preserved
user-secret ownership checks.
- Added a proposal-to-interaction link and an idempotent database
migration.
- Minted human-only `request_confirmation` cards with server-owned
`secretProposal` metadata.
- Rejected agent-supplied governed metadata and agent addressees.
- Rechecked `agent_config:update` authority under the proposal lock
before execution.
- Recorded `executed` or `failed` results and posted a failure comment
when no binding was created.
- Settled failed accepted proposals atomically and mirrored rejection,
withdrawal, and expiry in both directions.
- Emitted `secret.binding.created` for new agent binding writes.
- Added a dedicated issue-thread card for pending, executed, failed,
rejected, withdrawn, and expired states.
- Showed only the source label, target agent, config path, skeptical
justification, expiry, and safe failure code.
- Replaced resolved attention-query entries immediately with the
stitched server result.
- Added focused server, database, UI, and state-transition tests.
- Added Storybook fixtures for every review state and documented the API
and agent behavior.

## Verification

- `pnpm exec vitest run
ui/src/components/IssueThreadInteractionCard.test.tsx
ui/src/components/AttentionInteractionResolver.test.ts` — 58 passed.
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm check:token-gates`
- `pnpm build-storybook`
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/db typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/db check:migrations`
- `NODE_ENV=test pnpm exec vitest run
server/src/__tests__/issue-thread-interaction-routes.test.ts
server/src/__tests__/secret-proposals-routes.test.ts
server/src/__tests__/secrets-routes.test.ts
server/src/__tests__/agents-service-secret-bindings.test.ts` — 142
passed.
- `NODE_ENV=test pnpm --filter @paperclipai/db exec vitest run
src/company-secret-proposals-migration.test.ts --silent` — 1 passed.
- `pnpm -r typecheck`
- `pnpm test:run` — server 4,175 passed, UI 4,109 passed; the CLI
AWS-doctor case passes 8/8 with runtime-injected static AWS credential
variables unset.
- `pnpm build`
- `git diff --check origin/master...HEAD`

## Risks

- Migration `0221` adds one nullable foreign key and one index. It uses
idempotent guards.
- The accept route performs a governed write after it records card
acceptance. A failed write is visible and settles the proposal as
rejected.
- Concurrent proposal and card resolution must use
proposal-before-interaction lock order. A race test covers direct
approval against card rejection.
- The new audit event increases activity rows for newly added agent
bindings. It does not include secret values or fingerprints.
- The card includes only safe proposal metadata. It does not include
secret value, fingerprint, version, or internal secret identifiers.
- The UI uses the stitched resolution result. Focused tests cover
immediate cache replacement and every terminal state.

> This work extends an existing completed roadmap capability. The GitHub
duplicate search returned no other open related work.

## Model Used

- OpenAI Codex with model ID `gpt-5`. The runtime did not expose its
context-window size. Reasoning, repository tools, code execution,
database integration tests, UI rendering, and GitHub tools were enabled.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-18 09:44:24 -05:00
scotttong a1278e6ded
fix(markdown): make rendered code blocks follow the active theme (#11591)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents report their work in task chat, and that chat renders
markdown through `MarkdownBody`
> - Agents post shell commands, API payloads and diffs, so a fenced code
block is one of the most frequent things a person reads in the product
> - The rendered code block was pinned to two literal colors, so it
stayed dark in light mode and was the only dark surface on a light page
> - The `prose` and the `prose-invert` variables held the same two
values, so the theme could not change the block at all
> - This pull request binds the block to the theme tokens that already
exist and already carry a `.dark` override
> - The benefit is that a code block now matches the page in both modes,
and it follows any future change to the theme automatically

## Linked Issues or Issue Description

No public issue exists. The problem is described below.

**What happened?**
A fenced code block in rendered markdown is always dark. It uses the
background `#1e1e2e` and the text color `#cdd6f4` in light mode and in
dark mode. In light mode the block is the only dark surface on the page.
The rule is in `ui/src/index.css`. The variables `--tw-prose-pre-bg` and
`--tw-prose-invert-pre-bg` are set to the same literal value, so
`prose-invert` cannot change it.

**Expected behavior**
A code block uses a light surface with dark text in light mode. It uses
a dark surface with light text in dark mode. It follows the theme like
every other surface in the app.

**Steps to reproduce**
1. Start the app and open a task that contains a fenced code block in
its chat.
2. Set the theme to light.
3. Look at the code block. The block is dark. The page is light.
4. Set the theme to dark. The block does not change.

**Paperclip version or commit**
Reproduced on `master` at `4af55ba6b`.

**Agent adapter(s) involved**
Not adapter-specific (core bug). The defect is in the UI render path.

**Deployment mode**
Local development server.

## What Changed

- Bind `.paperclip-markdown pre` to `--muted`, `--foreground`,
`--border` and `--radius-lg` in `ui/src/index.css`. Remove the `#1e1e2e`
and `#cdd6f4` literals.
- Set the four `--tw-prose-*-pre-*` variables to the same tokens, so
`prose` and `prose-invert` both follow the theme.
- Apply the same four tokens to `.paperclip-mdxeditor-content pre`.
- Change the fill of the copy button and the wrap button to
`--background` in `ui/src/components/MarkdownBody.tsx`. The old fill was
`color-mix(in oklab, var(--muted) 92%, var(--background) 8%)`. That
value is almost equal to the block's new `--muted` surface, so the
buttons would nearly disappear.
- Update the stale comment above the rule. The comment said "Dark theme
code blocks".
- Add `ui/src/components/MarkdownCodeBlockStyles.test.ts`. It fails if a
literal color returns to any themed code surface.
- Export `codeBlockActionStyle` from `MarkdownBody.tsx` so the new test
can read it.

No new design token is added. Every token used here is already defined
in `ui/src/index.css`, and each one already has a `.dark` override.

## Verification

Automated:

```bash
cd ui && npx vitest run --config vitest.config.ts src/components/MarkdownCodeBlockStyles.test.ts src/components/MarkdownBody.test.tsx src/components/MarkdownBody.wrap.test.tsx src/components/MarkdownAccentStyles.test.ts
```

59 tests pass.

`MarkdownCodeBlockStyles.test.ts` is new. It guards the defect directly.
It reads `index.css` and asserts that each themed code surface rides a
token and holds no hex, `rgb()` or `hsl()` literal. I confirmed the test
fails on the original defect: restoring `#1e1e2e` and `#cdd6f4` fails 2
of the 5 tests.

Manual. Open a task that has a fenced code block in its chat. Read the
computed style of `.paperclip-markdown pre` in the browser. The measured
values are:

| Property | Light | Dark |
| --- | --- | --- |
| background | `oklch(0.97 0 0)` | `oklch(0.269 0 0)` |
| color | `oklch(0.145 0 0)` | `oklch(0.985 0 0)` |
| border | `oklch(0.922 0 0)` | `oklch(1 0 0 / 0.1)` |
| radius | `8px` | `8px` |

These values are `--muted`, `--foreground`, `--border` and `--radius-lg`
for each mode. Toggle the theme and confirm the block changes with the
page.

## Risks

Low risk. The change is CSS and one inline style value. There is no
migration and no schema change.

Two behavioral notes for the reviewer:

1. The corner radius changes from `calc(var(--radius) - 3px)` (5px) to
`var(--radius-lg)` (8px). This matches the approved design and removes
an arbitrary offset. It is a small visual change on every code block.
2. The CodeMirror theme inside the MDXEditor still uses the Catppuccin
literals (`ui/src/index.css`, the `.paperclip-mdxeditor .cm-editor`
rules). That surface is an editor, not a rendered snippet, and a change
there needs a full light CodeMirror theme. A code block therefore looks
light when it is rendered and dark while a person edits it. This is
intentional in this pull request. Tell me if you want it in scope.

Syntax highlighting, line numbers and diff rows are not in this pull
request. The repository has no highlighter dependency and no syntax or
diff tokens. Those are separate changes.

## Model Used

Claude Opus 5 (`claude-opus-5`), via Claude Code. Extended thinking was
on. Tool use was on, with file edit, shell, browser automation and the
Paper design tool. The design was produced first in Paper, then read
back through the design tool for exact token values rather than from
screenshots.

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

Notes on the unchecked boxes:

- Branch name. The branch is
`claude/paperclip-code-snippet-styling-202f7d`. It describes the change,
but the `202f7d` suffix comes from the local worktree name. It carries
no ticket id. I did not rename it, because the branch was already named
when this work was requested. Tell me if you want it renamed before
review.
- CI and Greptile. Not yet run at the time of opening.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 21:36:56 -07:00
scotttong 1c366a9059
fix(server): reject invalid agent credentials instead of downgrading to the local user actor (#11589)
<!-- Write all pull request text in Simplified Technical English
(ASD-STE100): short sentences, one instruction per sentence, simple
approved vocabulary, and the active voice. -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server authenticates each agent request in `actorMiddleware`
before it attributes chat comments
> - When an agent bearer token failed verification, the middleware
called `next()` with no error and the request continued without an agent
actor
> - The request then fell back to the local user actor, so the server
stored agent replies as user comments
> - The task chat UI renders user comments in blue bubbles, so agent
messages appeared as blue user bubbles
> - This pull request rejects invalid agent credentials with 401 instead
of a silent downgrade
> - The benefit is that agent messages keep agent attribution, and
broken credentials fail loudly with a clear retry message

## Linked Issues or Issue Description

**What happened?**

A user cancelled an onboarding question card. The agent posted a
follow-up reply. The reply appeared in a blue bubble, which the UI
reserves for human messages. The agent run held an expired local agent
JWT. The auth middleware could not verify the token, called `next()`
without an actor, and the request fell back to the local user identity.
The server stored the agent comment as a user comment.

**Expected behavior**

Agent messages always render as agent bubbles. A request with invalid
agent credentials must fail with 401 so the adapter can refresh
credentials and retry. It must not post content under a human identity.

**Steps to reproduce**

1. Start a local Paperclip instance.
2. Give an agent run an expired or malformed agent JWT.
3. Let the agent post an issue comment through the API bridge.
4. Before this change: the comment is stored with the local user
identity and renders as a blue bubble. After this change: the request
fails with 401 and a message that tells the caller to obtain fresh
credentials.

## What Changed

- `server/src/middleware/auth.ts`: a bearer token that fails
verification now produces a 401 `unauthorized` error instead of a silent
fall-through to the anonymous/local-user actor.
- The 401 message states the cause: expired token, unverifiable token,
empty bearer token, missing agent record, agent record in another
company, terminated agent, or agent pending approval.
- The API-key path now also rejects an agent record whose company does
not match the key.
- `packages/adapter-utils/src/execution-target.ts`: the bridge proxy now
writes a `comment id: <id>` marker to the run log for each posted issue
comment, so misattributed comments can be traced to a run.
- `ui/src/components/task-chat/task-chat-adapter.test.ts`: a regression
test asserts that a recovered `local-board` comment with a derived agent
author renders as an agent bubble, not a user bubble.
- `server/src/__tests__/agent-auth-middleware.test.ts` and
`packages/adapter-utils/src/execution-target-sandbox.test.ts`: new tests
cover each rejection path and the log marker.

## Verification

- Run `pnpm vitest run src/__tests__/agent-auth-middleware.test.ts` in
`server/` — 14 tests pass.
- Run `pnpm vitest run execution-target-sandbox` at the repo root — 44
tests pass.
- Run `pnpm vitest run
src/components/task-chat/task-chat-adapter.test.ts` in `ui/` — 4 tests
pass.
- Manual check: post an issue comment with an expired agent JWT; the API
returns 401 with a retry message and no comment is stored.

## Risks

- Behavioral shift: requests that previously continued as anonymous or
local-user actors after a failed agent-token verification now receive
401. Any caller that relied on the silent downgrade must refresh its
credentials. This is the intended fix, and the adapters already handle
401 with a credential refresh.
- No schema or migration changes. Low risk otherwise.

> 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`, via Claude Code with
extended thinking and tool use (agent harness with shell, file, and git
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
- [ ] All Paperclip CI gates are green
- [x] 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>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 21:36:11 -07:00
Dotta 8087661bb8
fix: bound workspace Git scans (#11572)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Workspaces let users and agents inspect files that belong to an
issue
> - Changed-file views use full-tree Git status scans
> - Many issue views could start those scans at the same time and make
the server unresponsive
> - Route-level limits did not protect the process or coalesce work for
one repository
> - This pull request adds one bounded scheduler for every expensive
workspace Git scan
> - It also starts browser scans only when the file panel is open and
visible
> - The benefit is bounded child-process use and responsive health
checks during request storms

## Linked Issues or Issue Description

**What happened?**

Many changed-file requests could start full `git status --porcelain=v1
-z --untracked-files=all` scans at the same time. One production
incident produced about 270 direct Git child processes. The Node process
stayed alive but stopped answering health requests in time.

**Expected behavior**

Paperclip must bound expensive Git work across all companies, actors,
issues, repositories, and browser tabs. Duplicate requests for one
worktree must share work. Excess requests must fail fast with a
retryable response. Hidden or closed file panels must not start scans.

**Steps to reproduce**

1. Open changed-file views for many issue and actor keys.
2. Send requests for two large workspace roots at the same time.
3. Observe that route-level limiter keys allow many full Git scans to
run together.
4. Observe delayed health responses and accumulated Git children.

**Paperclip version or commit**

Reproduced on master before commit `43ab441f0f`.

**Deployment mode**

Self-hosted server with local workspace repositories.

## What Changed

- Add a process-wide scheduler with configurable concurrency, queue
capacity, timeout, and cache TTL.
- Add fair admission, a bounded queue, canonical worktree keys,
single-flight joins, and bounded result caching.
- Add subprocess timeouts, TERM-to-KILL escalation, bounded output,
waiter cancellation, and slot cleanup.
- Route full-tree status work from file resources, workspace runtime,
execution workspaces, and adapter overlay sync through the scheduler.
- Return stable retryable `503` and `504` error codes for saturation and
timeout.
- Add structured logs with safe workspace hashes, durations, queue
state, cache use, joins, and terminal outcomes.
- Gate UI queries on panel and document visibility. Cancel queries on
close, hide, unmount, and workspace change.
- Disable focus and reconnect bursts. Keep one explicit refresh action
and a retryable unavailable state.
- Document the 10-second default freshness tradeoff and all
configuration variables.
- Add unit, route, UI, adapter, and deterministic 500-request load
coverage.

## Verification

- `pnpm -r typecheck`
- `pnpm build`
- `pnpm check:token-gates`
- `pnpm --filter @paperclipai/server exec vitest run
src/services/workspace-git-operation-scheduler.test.ts
src/__tests__/file-resources-git-scan-load.test.ts --reporter=dot` — 16
tests passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/WorkspaceFileBrowser.test.tsx
src/lib/page-visibility.test.ts --reporter=dot` — 38 tests passed.
- `pnpm --filter @paperclipai/adapter-utils exec vitest run
src/git-workspace-sync.test.ts --reporter=dot` — 16 tests passed.
- Existing file-resource, workspace-runtime, and execution-workspace
regression selections passed.
- Two cleanup safety regressions prove failed scans preserve the
worktree before archive and at the final deletion fence.
- Before: the incident produced about 270 Git children and health
requests timed out.
- After: 500 concurrent requests across 500 issue keys, 73 actors, and
two roots started two underlying scans. Peak scan concurrency was 2. All
500 requests succeeded. Health p99 was 4.94 ms. The harness found zero
unreaped children.
- The full local Vitest run passed 4,267 tests. Ten existing fixed-port
HTTPS exposure tests could not run because this host already owns
Tailnet listeners on ports 42000 and 52000. Clean GitHub CI is the final
full-suite result.
- Latest-head GitHub CI passed all required test, typecheck, build,
canary, e2e, policy, and security gates.
- Greptile completed at 5/5 with zero unresolved comments,
recommendations, or follow-ups.

## Risks

- Changed-file results can be up to 10 seconds old by default. Explicit
refresh remains available.
- A full queue returns a retryable `503` instead of waiting without a
bound.
- A scan that exceeds the default 8-second deadline returns a retryable
`504` and terminates its process group.
- Operators can tune all limits with documented environment variables.
Safe defaults protect local and shared servers.

> 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, GPT-5 family. The runtime does not expose the exact
deployment ID or context-window size. High reasoning, tool use, and code
execution were enabled.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-17 22:11:30 -05:00
Nicky Leach c1c46f1e4e
feat: Claude login on the new-agent page before agent creation (#11347)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Claude local adapter supports subscription login through a
sandbox
> - The new-agent page must show login before the user creates an agent
> - Test results must not expose raw sandbox diagnostics or secret
values
> - This pull request adds the login UI to both Test lanes and closes
the diagnostic boundary
> - The branch also adds durable cleanup recovery for failed sandbox
teardown
> - Reusable sandboxes must retain both their recorded teardown
configuration and a valid lifecycle path until destruction succeeds
> - The benefit is a usable login flow with fixed public checks,
redacted server logs, and recoverable sandbox cleanup

## Linked Issues or Issue Description

Related public work:
[#9488](https://github.com/paperclipai/paperclip/pull/9488) adds
first-class recognition for `CLAUDE_CODE_OAUTH_TOKEN` in headless and
remote runs. Related public issue:
[#2681](https://github.com/paperclipai/paperclip/issues/2681) requests
Claude Code subscription support. This pull request adds the login
transport and new-agent UI flow that those changes do not provide.

**Subsystem affected:** Claude local adapter, server login probes,
sandbox provider setup, cleanup recovery, and the new-agent UI.

**Problem or motivation:** The Test lanes did not show the sandbox login
panel in all supported cases. Test results also exposed raw probe
diagnostics, and JSON escapes could end secret redaction early.

**Proposed solution:** Surface the login capability through the bundled
provider manifest. Prepare the same probe runtime in the ACP lane. Send
diagnostics only to redacted server logs. Keep Test checks on fixed
public messages. Normalize login URL hints to allowlisted HTTPS Claude
and Anthropic hosts. Consume JSON escapes during redaction. Preserve
failed sandbox cleanup state across retries and restarts, and prevent
deletion from severing the lifecycle context of a live reusable sandbox.

**Alternatives considered:** Keep raw diagnostics in Test checks or
trust login URL text from the sandbox. Both choices increase information
exposure. Keep separate probe behavior in the ACP lane. That choice
would leave the two Test lanes inconsistent.

## What Changed

- Surface the sandbox login panel on both Test lanes.
- Reconcile the bundled Daytona plugin manifest so
`supportsSetupTokenLogin` reaches the UI capability gate.
- Prepare the ACP Test lane with the same probe runtime as the CLI Test
lane.
- Add the `claude_acp_login_probe_unavailable` warning when the ACP
probe cannot run.
- Send raw sandbox diagnostics only to redacted server logs.
- Keep Test checks on fixed public messages in the ACP, managed-config,
and CLI paths.
- Normalize login URL hints to allowlisted HTTPS Claude and Anthropic
hosts.
- Redact JSON and escaped-JSON secret values, including escaped quotes
and backslashes.
- Preserve orphan cleanup records across provider failures, restarts,
and unavailable plugins.
- Atomically block environment deletion while a live reusable sandbox
lease still depends on it.
- Verify pending cleanup destroys plugin sandboxes with the provider
configuration recorded on the lease, even after the current environment
configuration changes.

## Verification

- Head under review: `506b7fa2d83c36bfa5fd722ee9d95b0c7431c241`.
- Focused environment route/service/runtime coverage passes: 196 tests
across 3 files.
- `pnpm -r typecheck` passes.
- `pnpm build` passes.
- The full Vitest run completed with 4,754 passing and 28 failing tests.
All 23 source-test failures reproduce unchanged on parent head
`58cfe61a33191ce03d965d65085d26064b4888ba`; the other 5 are duplicate
executions from stale `server/dist` output. The failures are unrelated
macOS path/listener and scheduler-fixture failures, so there is no new
bad commit for bisect to localize.
- All required CI checks pass for the current head, including build,
typecheck/release registry, all server and workspace shards, serialized
server suites, canary, and e2e.
- A fresh Greptile review for `506b7fa2d83c36bfa5fd722ee9d95b0c7431c241`
reports 5/5, “safe to merge,” with no blocking failure remaining.

## Risks

- A probe or redaction change could hide useful server diagnostics.
- An allowlist change could reject a valid Claude login URL.
- Cleanup recovery changes could affect provider teardown ordering.
- An environment with a live reusable sandbox can no longer be deleted
until the owning issue or execution workspace completes teardown.
- The implementation keeps public Test messages fixed and sends detail
to redacted server logs.

## Model Used

OpenAI GPT-5 via Codex — exact model ID: GPT-5; tool use and code
execution enabled; extended reasoning enabled. The implementation author
used AI-assisted development.

## 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 documented the result
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation or confirmed no separate
documentation change is needed
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-17 13:42:51 -07:00
Michael Nguyen d2fb05d225
Fix issue document deep-link routing (#11551) 2026-08-17 10:53:46 -07:00
Dotta 4c349fe6b7
feat(runtime): managed Tailscale HTTPS lifecycle, durable runtime leases, and bounded control recovery (#11525)
<!-- Simplified Technical English (ASD-STE100). -->

> **Stacked pull request.** This targets #11524. Merge #11524 first.
Review only the second commit, `feat(runtime): managed Tailscale HTTPS
lifecycle...`.

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip starts and supervises managed runtime services, so an
agent's branch can be previewed while the agent works
> - The previous pull request added the host broker, the shared
contract, and the database columns, but no code used them
> - A managed runtime can only be exposed over HTTPS if it holds a
stable loopback port pair for the whole life of the service. The current
control path cannot promise this: two controls can race the same
execution workspace, a stranded control can stay `running` forever, and
a start can adopt a port it does not own
> - This pull request adds the HTTPS lifecycle and the control-path
hardening that the lifecycle depends on
> - The benefit is that a managed preview becomes reachable from another
device, and a managed control now always reaches a terminal state

## Linked Issues or Issue Description

No public GitHub issue exists. The change follows the feature request
template.

**Subsystem affected**

Managed workspace runtime services, workspace operations, the execution
workspace routes, and the workspace runtime UI.

**Problem or motivation**

A managed runtime service is reachable only on loopback, so a preview
cannot be opened from a phone or a second computer. Exposing it safely
needs an exclusively held port pair. Three existing gaps block that.
Overlapping controls can race the same workspace. A control whose owner
dies stays `running` and blocks the lane forever. Port allocation does
not confirm that the process holding a port is the process Paperclip
spawned.

**Proposed solution**

Add the exposure lifecycle on top of the broker from #11524: reserve
before spawn, expose after readiness, validate the public URL, and
remove on stop. In the same change, make managed controls mutually
exclusive per workspace, give each control a durable issue-owned lease
and a terminal state, and verify port ownership before use.

**Alternatives considered**

- Add HTTPS exposure without the control hardening. This was rejected
because a raced or stranded control makes exposure point at the wrong
process.
- Guard the lane with an in-memory lock only. This was rejected because
the lock does not survive a server restart, so the lane can be lost or
double-claimed.
- Trust the requested bind address. This was rejected because a checkout
that predates managed HTTPS overwrites `PAPERCLIP_BIND` from its own
`--bind` argument, and then binds the wildcard address.

**Roadmap alignment**

This completes the managed workspace runtime capability that already
exists. It adds no new product surface beyond the HTTPS link.

**Additional context**

This is the second of three pull requests. The third adds central
mediation of leased port pairs.

## What Changed

Exposure lifecycle:

- Add the server-side broker client and the exposure lifecycle manager.
The manager reserves the mapping before spawn, exposes after backend
readiness, validates the public URL, and removes the mapping on stop.
- Default managed worktree runtimes to `tailscale_https`, read exposure
intent from legacy `expose` blocks, and backfill runtimes that are still
HTTP-only.
- Verify listener ownership for the app port and its Vite HMR companion
before the broker is asked to expose anything. An unrelated listener on
either port fails the start closed.
- Force the loopback bind through argv instead of environment hints.
Leave a non-Paperclip service's `--bind` argument alone.
- Probe loopback for readiness instead of the public URL, and give Vite
HMR its own loopback-bound server in middleware mode.
- Preserve operator-declared Serve mappings across the managed
lifecycle, so cleanup never removes a mapping that Paperclip did not
create.
- Name which listener predicate denied an expose, so an operator can act
on the message.

Control-path hardening:

- Make `start`, `stop`, `restart`, and job `run` mutually exclusive per
execution workspace. An overlap gets `409
workspace_runtime_control_in_progress`, and authorization is still
checked first.
- Take a durable exclusivity lease on the execution workspace, owned by
the controlling issue. A different issue gets `409
workspace_runtime_lease_conflict` before any operation is recorded.
Board and operator actions bypass the lease.
- Give every control a terminal state. Each control stamps its owning
process and pid, heartbeats while it runs, and has a wall-clock ceiling.
Recovery of a stranded control uses a compare-and-swap on `updated_at`,
so a live owner is never stolen.
- Bound readiness probes, verify allocated port ownership on POSIX and
Windows, harden sibling port allocation, and reconcile desired runtimes
on server startup.
- Surface exposure state and bounded runtime errors in the workspace
runtime UI.
- Record the new behavior in `doc/DEVELOPING.md`.

## Verification

Focused checks, all run on this branch:

- `npx tsc --noEmit -p server/tsconfig.json` — 139 errors, exactly the
count on `master`. All 139 come from the unbuilt
`@paperclipai/plugin-sdk` package.
- `pnpm --filter @paperclipai/ui typecheck` — clean.
- Server suites, 177 tests pass across 9 files:
`workspace-runtime.test.ts`, `workspace-runtime-leases.test.ts`,
`workspace-runtime-control-recovery.test.ts`,
`execution-workspace-runtime-control-conflict.test.ts`,
`execution-workspace-runtime-lease-route.test.ts`,
`workspace-operations-reconciliation.test.ts`,
`workspace-runtime-start-terminality.test.ts`, `app-hmr-port.test.ts`,
and `workspace-runtime-ready-comment.test.ts`.
- Exposure unit suites, 77 tests pass: `src/services/runtime-exposure/`
and `workspace-runtime-exposure-backfill.test.ts`.
- UI: `WorkspaceRuntimeControls.test.tsx` and
`WorkspaceServiceControlBar.test.tsx` — 34 tests pass.

**One suite is red on the development host and is expected to be green
in CI.** `server/src/services/workspace-runtime-exposure.test.ts` has 10
failures on the machine used to write this branch. The cause is host
contamination, not the code. That machine already runs an HTTPS canary
that holds ports 42000, 42001, 52000, and 52001 on a tailnet address.
The suite allocates from the same range, so the new listener-ownership
check correctly reports:

```
listener_ownership_mismatch — port 42000 is bound to 100.123.243.20, 127.0.0.1,
fd7a:115c:a1e0:0:0:0:dd3a:f314 ... instead of loopback only
```

A CI runner has no listener on those ports, so the check sees loopback
only and the suite passes. Please confirm this from the CI result on
this pull request rather than from a local run on a host that already
exposes a managed runtime. This is a real weakness of the current test
fixture, and the third pull request in the series removes it by
allocating the pair through a central mediator instead of a stubbed
availability check.

`workspace-runtime-https-live-exercise.test.ts` needs a live `tailscale`
host and was not run locally.

## Risks

- This is the behavior-bearing pull request of the three, so it carries
the most risk.
- Two new `409` responses appear on managed control routes. A caller
that assumed a control always starts must handle a conflict. Board and
operator actions are deliberately exempt, so an agent lease cannot lock
an operator out.
- Managed worktree runtimes now default to `tailscale_https`. If the
host has no working broker, the start fails closed and reports the
exposure failure instead of silently serving plain HTTP. This is
intended, and it is the reason the failure message names the denying
predicate.
- Startup reconciliation touches persisted runtime rows. It is scoped to
desired state and does not resurrect a service that never came up.
- The lease has a 30-minute time to live and explicit release paths, so
a crashed owner cannot hold a lane forever.
- No migration runs in this pull request. The tables and columns land in
#11524.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR.

## Model Used

Claude Opus 5 (`claude-opus-5`), 1M context window, 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, with the one
host-contaminated suite explained above
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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-17 06:23:33 -04:00
Tonio 55464204a6
test(ui): wait for conditions in the last four fixed-turn loops (#11523)
The remaining instances of the pattern #11499 and #11521 replaced in the routing
tests, found by grepping `attempt < N` across the suite. Budgets of 20, 25 and
30 turns rather than 3 and 5, which is why they surfaced far less often -
SkillStudio was among the failures seen while verifying the earlier PRs.

Three of the four were reimplementations of `vi.waitFor` down to rethrowing the
last error, differing only in bounding on turns rather than on time. The fourth
was the text variant. Two `flushReact` helpers became dead with the loops that
used them and are removed.

Verified on the mechanism, since budgets this large pass until the machine is
loaded and so prove nothing by passing: a throwaway probe drove a 30-turn loop
and `vi.waitFor` against a value landing at turn 60. The loop throws,
`vi.waitFor` reaches it. Not a lateral move between arbitrary bounds.

This closes one spelling of the pattern, not the class, and the sweep that found
these was too narrow. Two other shapes do the same thing and a grep for
`attempt < N` cannot see either: a fixed-cycle helper, `flushReact(cycles = 4)`
in AgentToolsTab.test.tsx, and fixed-duration sleeps in AgentToolsTab,
CompanyContext, AgentConfigForm.render, Artifacts, Search and
ImportFromVaultDialog. `AgentToolsTab > autosaves installed apps for the current
agent` failed one of three full-suite runs here, holds both shapes, and is
untouched by this change. Refs #11484.

ui typecheck clean; the four files pass; full suite passes two of three runs,
the third failing only on that pre-existing instance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 00:58:16 -07:00
Tonio 65907aa41f
test(ui): wait for the route, not three turns, in the cases-routing test (#11521)
The instance #11499 named but did not include. `App.cases-routing.test.tsx`
carried the identical fixed-turn loop that PR replaced in its sibling
`App.activity-routing.test.tsx` - three macrotasks instead of five, otherwise
the same helper - so it fails the same way when the suite runs many workers in
parallel and the container has not filled yet. It was one of the failures
observed while verifying #11499.

Same one-line replacement: `vi.waitFor` retries against a time budget, so a
loaded worker gets more turns rather than a failure. The two helpers are
identical again.

Verified on the mechanism rather than on a green run, because the old loop
passes in isolation too - that is what made this a flake and not a failure. A
throwaway probe drove both helpers against a container whose text lands after
ten macrotasks: the three-turn loop throws, `vi.waitFor` resolves. That is the
condition a loaded CI worker creates. The probe was deleted rather than
committed; it tests a test helper and had one question to answer.

This closes one named instance, not the class. The full ui suite passed three
consecutive times with no failure in any file, but the other instances seen
during #11499's verification - TaskChatComposer, RequestCollapsedSidebar -
simply did not recur, so they are rarer rather than fixed. Refs #11484.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 00:42:54 -07:00
Tonio e07d605dfc
test(ui): wait for conditions, not durations, in three flaky tests (#11499)
Three tests yielded a fixed number of macrotasks before asserting - five in one
case, one in another - which is ample on an idle machine and not when the suite
runs many workers in parallel. The container was still empty, or the state had
not landed, and the assertion failed on behaviour that works. `vi.waitFor`
retries against a time budget instead, so a loaded worker gets more turns
rather than a failure.

`DocumentAnnotationPopover` is a different race and is fixed differently. The
popover element is in the DOM as soon as React commits, while the effect that
registers the document-level keydown and pointerdown listeners runs afterwards.
A test dispatching in that gap loses the event outright, and a lost event
cannot be recovered by retrying an assertion - so the render is wrapped in
`act` to flush passive effects, and the waits only cover the smaller race that
remains.

Refs #11484.

Verified stable over six consecutive runs of the three files, and the full ui
suite passes. Other instances of the same class remain: the full suite still
shows an occasional failure in a different unrelated test on each run.
`App.cases-routing.test.tsx:104-108` is the clearest one - the identical
fixed-turn loop this PR replaced in its sibling `App.activity-routing.test.tsx`,
three turns instead of five - and takes the same one-line fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 00:29:42 -07:00
Tonio 870c305410
refactor(ui): drop the TZ pin now the fixtures are anchored (#11508)
#11480 pinned `TZ: "UTC"` in the vitest config because several suites asserted
local-time renders from UTC instants. That made the suite green everywhere, but
by suppressing the variable rather than fixing what depended on it: afterwards
no test could observe non-UTC behaviour, and a fixture quietly regaining a
local-time dependency would not be caught.

#11478 anchored those fixtures to the clock under test, which is the real fix,
so the pin now carries only its cost. Removed.

The order was load-bearing and is now satisfied. Measured on master before
#11478 landed, removing the pin failed four date-dependent tests at UTC+9 and
ten at UTC+12 - IssueProperties, IssueThreadInteractionCard, SummarySlotCard
and attention, all of which that PR anchors. Re-measured on master at
40e7add71 with the pin removed: 4117 pass at UTC+14, UTC+9 and UTC-11, against
a control of 4117 with the pin. The prerequisite is demonstrated rather than
assumed.

No test accompanies this, and the prefix says so. The change deletes
configuration, and what verifies it is the existing suite run at several
offsets - not expressible as a test case without a harness that re-runs vitest
under a different TZ.

A caution for anyone reading a failure here later: an earlier pass at this
misread the parallel-worker flakes from #11499 as timezone failures, because a
single run per zone showed a clean east-of-UTC pattern that was really noise.
Date-dependent failures are consistent across runs and name date-handling
tests; the flakes vary between runs and name unrelated pages.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 00:18:13 -07:00
Tonio 6d0adbfb5d
refactor(ui): retire the shared-cache reasoning the account key made obsolete (#11507)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Three places in the UI turn the company list into an authorization
verdict: the invite landing page, the onboarding draft gate, and company
auto-selection
> - Each grew a defense when one `["companies"]` cache entry answered
for every account, and each documented that hazard at length
> - #11488 keyed the entry by account, so the hazard those comments
describe can no longer happen
> - The comments stayed, and a comment that describes a trap that no
longer exists is how the next reader removes a mechanism that is still
holding something up
> - This pull request replaces that reasoning with what the mechanisms
actually do now, and removes the one condition that genuinely went dead
> - The benefit is that the next person to simplify these gates has
accurate reasons to work from

## Linked Issues or Issue Description

No public issue exists. Follow-up to #11488, #11430 and #11417. The
problem follows.

**What happened?**

The three gates were written against a shared, account-less company
cache. #11488 keyed that entry by account, which made the documented
hazard impossible — but the documentation stayed. Each gate now carries
a long explanation of a cross-account leak that the key prevents, while
the mechanism it explains is in fact still required for a different and
unrelated reason.

That is a maintenance hazard in a specific direction: a reader who
checks the comment against the code concludes the mechanism is obsolete,
removes it, and reintroduces a failure the comment never mentioned.

**Expected behavior**

The reasoning next to each gate describes why the gate is there now.

**Steps to reproduce**

Read the comment above `ownershipDecidable` in `OnboardingWizard.tsx`
against `master`. It justifies `isSuccess` on the grounds that "after an
account switch the retained value is the previous account's list", which
the account-keyed entry makes impossible.

**Paperclip version or commit**

`master` at `0817fbad9`.

## What Changed

- `ui/src/pages/InviteLanding.tsx` — dropped the
`Boolean(sessionQuery.data)` conjunct from `membershipListIsCurrent`;
rewrote the comment.
- `ui/src/components/OnboardingWizard.tsx` — replaced the shared-cache
explanation above the ownership gate with the reason the gate still
exists.
- `ui/src/hooks/useSignOut.ts` — corrected the sweep's rationale, which
cited the company list as its example of data the next account could
read.

### The one dead condition

`membershipListIsCurrent` tested `Boolean(sessionQuery.data) &&
companiesQuery.isFetchedAfterMount`. The first term cannot be false when
the second is true: the query is `enabled` only while a session exists,
so the flag cannot be set without one. The lapsed-session case it looked
like it covered is covered by the keying instead — the observer re-keys
to the anonymous entry and holds no data to leak.

Tests pass with it removed, but that only shows no test distinguishes
it, which is why the reasoning above is recorded in the code rather than
left for the next reader to redo.

### What is deliberately kept

Each gate turned out to be load-bearing for a reason that has nothing to
do with accounts:

- **InviteLanding** still waits for a list fetched this mount. A pending
query reads as an empty list, which reads as "not a member", which
auto-accepts an invite the customer may already hold.
- **OnboardingWizard** still forces a fetch with `staleTime: 0`. A
cached list is the right account's but can be thirty seconds old, so a
company created moments ago in another tab is missing from it — and
missing reads as "you do not own this", which *deletes* the draft rather
than withholding it.
- **CompanyProvider** still clears the live selection on an account
change. That is component state and does not change key with the query.

Removing them as redundant is the mistake the stale comments invited;
this change is what makes that argument harder to make by accident.

## Verification

- `pnpm tsc -b` in `ui`: clean.
- `InviteLanding.test.tsx`, `OnboardingWizard.test.tsx`,
`CompanyContext.test.tsx`, `useSignOut.test.tsx`,
`companies-query.test.ts`: **67 passed**, run twice.

No behaviour change is claimed and none is intended: the only
non-comment edit is the removal of a condition that cannot alter the
expression's value.

**Not done:** no browser run. Nothing here is observable at runtime.

## Risks

Low. Comments, plus one condition shown to be unreachable-false.

The risk that remains is a documentation risk in the other direction: if
the keying is ever reverted or bypassed, these comments will understate
what the gates protect against. They name #11488 so that connection is
findable.

**This does not close the class.** Account-scoped entries other than the
list — `["companies", id]`, stats, and the rest — still survive an
account change that skips the sign-out button. That is unclaimed work,
and larger than this.

## Model Used

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

## Checklist

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

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

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 22:34:55 -07:00
Tonio 40e7add71c
test(ui): make the suite independent of the machine timezone (#11478)
Several suites asserted local-time renders from UTC instants, or built date
fixtures from the real clock, so they only held where local time happened to
match. CI runs in UTC and never reported it; a contributor anywhere else saw
failures on a clean checkout.

Seven files, anchored to the clock under test rather than to the machine's.
The set grew twice while being fixed: the two tests the issue named surfaced
three more at UTC+9, and those surfaced two more at UTC+14.

`StatusCards/format` is the interesting one. `rollupUpdatesToday` filters on the
UTC calendar day to match the server token cap, while the fixtures were built
on the local day. West of UTC that lands `iso(0)` in the previous UTC day for
the stretch between UTC midnight and local midnight - about seven hours a day
at UTC-7 - and east of UTC+12 "today at local noon" is already yesterday in UTC
outright. Either way the rows it means to count drop out. A run crossing
midnight UTC splits the same way.

Fixes #11476.

Deliberately left: IssueProperties.test.tsx:1515-1517 still pin the minute of
three timestamps against a UTC fixture. They pass at every offset tried,
including UTC+5:45, and the minute there is load-bearing - it distinguishes
Created from Started from Completed - so it wants more care than mechanical
anchoring.

Full ui suite 4113 pass. The TZ pin added by #11480 is still in place here and
is now redundant; #11508 removes it, stacked on this branch so it cannot land
without the anchoring it depends on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 22:22:45 -07:00
Tonio 0817fbad92
fix(ui): scope invite membership checks to the signed-in account (#11417)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Which companies a person belongs to is an authorization fact the
server owns, and the UI caches the answer under a single `["companies"]`
key
> - That cache entry carries no account identity, and `main.tsx` sets
`staleTime: 30_000` for every query, so for thirty seconds after a
sign-in the previous account's list is served with no request at all
> - The invite landing page reads that list to decide whether the person
is already a member of the inviting company
> - A list that arrives with no loading state and no error therefore
looks authoritative while describing somebody else
> - This pull request makes the page trust only a list it fetched
itself, for the account signed in now
> - The benefit is that a membership decision stops depending on cache
freshness, which nothing in the app guarantees

## Linked Issues or Issue Description

No public issue exists. Refs #11380, #11382. The problem follows.

**What happened?**

`InviteLanding` read the shared `["companies"]` cache entry as proof of
membership in two places:

- The post-sign-in redirect called
`fetchQuery(companiesListQueryOptions)`, which returns the cached entry
without a request while it is inside the app-wide `staleTime`.
- An effect cleared the pending invite token whenever the cached list
contained the invited company.

Neither checked that the list belonged to the account signed in now. A
second account signing in on a warm tab, or a session that lapses
server-side, is enough to reach both.

**Expected behavior**

The page decides membership from a company list fetched for the current
session.

**Steps to reproduce**

1. On a self-hosted instance in `authenticated` mode, sign in as account
A, which belongs to company X.
2. Within thirty seconds, open an invite link for company X and sign in
as account B, which does not belong to it.
3. The page reads A's cached list, finds company X, and treats B as
already a member.

**Paperclip version or commit**

`master` at `2a4b4bc63`.

## What Changed

- `ui/src/pages/InviteLanding.tsx` — the membership query sets
`staleTime: 0` so it revalidates on mount, and the verdict is withheld
until that fetch lands, keyed on `isFetchedAfterMount`. The
token-clearing effect and the "already a member" branch both read
through that gate.
- `ui/src/pages/InviteLanding.tsx` — the post-sign-in path cancels
anything still in flight for the previous session, then forces a fetch
for the new one with `staleTime: 0`.
- `ui/src/pages/Auth.tsx` — sign-in resets the companies query instead
of invalidating it. Invalidation leaves the previous account's list
readable, and its fetch running, until the refetch returns.
- `ui/src/pages/InviteLanding.test.tsx` — coverage for the warm-cache
case, the token-clearing effect, and the `local_trusted` exemption.

### `local_trusted` is exempt

Those instances have no accounts, so the shared list is the only
identity there is. `membershipIsAccountScoped` is false there and the
gate stays open.

### Rebased onto the account-keyed cache

#11488 landed while this was open and keys the company list by account,
so the page can no longer reach another account's list at all. Two
things changed here as a result:

- The post-sign-in read now calls `fetchCompanyListForCurrentAccount`,
which replaces the `cancelQueries` plus forced `fetchQuery` this PR
originally carried. The helper is strictly stronger: it detaches the
in-flight `/companies` request inside the query function, and it
resolves the account identity past the session invalidation immediately
above rather than trusting the session entry still in the cache.
- The observer reads through `useCompanyListQuery`.

The mount-scoped `isFetchedAfterMount` gate is **kept**, not removed.
Its purpose has narrowed — cross-account leakage is now structurally
impossible, so what remains is holding the verdict until this page has a
list rather than acting on a pending one. It is still load-bearing:
disabling it fails two tests here. Removing a defense in the same change
that rebases onto a new foundation is the wrong order; that is a
follow-up once the keying has proven itself.

`Auth.tsx` can safely reset, because it navigates away on success and
`InviteLanding` mounts fresh afterward. Measurements of exactly when
that rewind does and does not bite are in
[#11380](https://github.com/paperclipai/paperclip/pull/11380#issuecomment-5300984911).

## Verification

- `InviteLanding.test.tsx`, `Auth.test.tsx`, `companies-query.test.ts`,
`CompanyContext.test.tsx` together: **51 passed**, run twice.
- `pnpm tsc -b`: clean.
- `InviteLanding.test.tsx` and `Auth.test.tsx` together: 21 passed.

Both failures are pre-existing and unrelated. Each reproduces on a tree
that does not contain this change, in files this change does not touch:

| Failure | Why it fails |
| --- | --- |
| `IssueProperties.test.tsx` | Timezone-dependent: expects `4:08 PM`,
gets `9:08 AM` |
| `StatusCards/format.test.ts` | Time-of-day dependent: "only counts
updates started today" breaks near midnight |

**Not done:** no manual two-account run in a browser. The path needs two
accounts on an `authenticated` instance, which a local dev instance
cannot exercise.

## Risks

Low. The failure direction is a membership verdict withheld for one
extra round trip, which resolves itself; the direction it removes is one
account's membership granted to another, which does not.

It adds one request per invite-page mount, on a query key the app
already uses.

**This does not close the class.** The shared list is still unscoped for
every other consumer. #11380 clears it on sign-out and #11382 handles
the onboarding draft gate; all three are needed, because an account can
change without passing through any one of those paths.

## Model Used

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

## Checklist

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

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

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 21:55:50 -07:00
Tonio 327f59cac2
refactor(ui): key the company list by account (#11488)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Which companies a person belongs to is an authorization fact the
server owns, and the UI caches the answer for speed
> - It cached that answer under one `["companies"]` key with no account
attached, while `main.tsx` sets `staleTime: 30_000` for every query
> - So for thirty seconds after an account change, one person's list
answered questions asked about another, arriving with no loading state
and no error
> - Three separate consumers each grew their own defense against this,
and each was a place to forget one
> - This pull request keys the entry by account, so a list belonging to
someone else is not distrusted but unreachable
> - The benefit is that the protection stops depending on every future
consumer remembering to defend itself

## Linked Issues or Issue Description

No public issue exists. Refs #11380, #11382, #11417, #11430. The problem
follows.

**What happened?**

The company list lived in a single cache entry, `["companies"]`,
carrying no record of which account it was fetched for. Combined with
the app-wide 30s `staleTime`, any read within that window after an
account change returned the previous account's list — from cache, with
no request, no loading state and no error.

Every consumer that treats the list as an authorization fact had to know
this and defend itself:

- `InviteLanding` reads it to decide whether you already belong to the
inviting company (#11417).
- `OnboardingWizard` reads it to decide whether a saved draft belongs to
you (#11382, merged).
- `CompanyProvider` reads it to pick and persist your active company
(#11430).

All three defenses are correct. The problem is structural: the fourth
consumer has to invent a fourth one.

**Expected behavior**

A cached company list can only answer questions about the account it was
fetched for.

**Steps to reproduce**

1. On a self-hosted instance in `authenticated` mode, sign in as account
A, which belongs to company X.
2. Within thirty seconds, have account B become the session in that tab
— a second tab signing in, or A's session lapsing server-side.
3. Any consumer reading the company list receives A's list, and nothing
in the query result indicates it is not B's.

**Paperclip version or commit**

`master` at `ac91b7f3b`, which includes #11430.

## What Changed

- `ui/src/lib/queryKeys.ts` — `companies.list(userId)` replaces
`companies.all` as the list's entry. `companies.all` remains the prefix,
so it still matches for invalidation.
- `ui/src/api/companies-query.ts` — `companyListQueryOptions(userId)`
builds the keyed options; `useCompanyListQuery()` is the only observer
entry point and holds until the session settles, because the key cannot
be built before then; `fetchCompanyListForCurrentAccount(queryClient)`
covers imperative paths; `useAccountIdentity()` exposes the session
identity the key is built from.
- `ui/src/api/companies-query.ts` — the `/companies` detach moved into
the query function.
- `ui/src/context/CompanyContext.tsx` — drops the session-watching
refetch machinery the key now makes unnecessary (`removeQueries`, the
explicit replacement fetch, the awaiting gate). It still clears the live
selection on an account change, because that is component state and does
not change key with the query.
- `ui/src/pages/InviteLanding.tsx`,
`ui/src/components/OnboardingWizard.tsx` — read through the
account-aware API.
- Tests — the account-keyed guarantee, prefix invalidation still
reaching the list, the detach inside the query function, and updates
where suites seeded the old shared key.

### The existing defenses are deliberately left in place

The per-consumer gates in #11382, #11417 and #11430 are now belt and
braces. They are also what will catch this refactor if it is wrong
somewhere, so removing them in the same change that moves the foundation
would be the wrong order. Simplifying them is a follow-up, once this has
proven itself.

### Why `retry: 1` appears in CompanyProvider

An earlier measurement on #11430 found a retry on the replacement fetch
changed no outcome, because `removeQueries` made the observer rebind and
issue a second request for free. Keying by account removes that
mechanism and the free attempt with it. The retry now carries the
property the incidental refetch used to — a single blip during an
account change should not leave the customer with no companies until
they find "Try again". #11430's test for that property is unchanged and
still passes, which is how the gap was caught.

### A regression this went through, kept for the record

Gating the query on the session settling meant that while the account
was unknown the query was *disabled*, and a disabled query reports
`isLoading: false` with no data — which the provider defaults to an
empty list and reads as "asked, and owns nothing". That is the
destructive branch #11477 had just fixed, reached through a different
door: it would have cleared the customer's stored company on every cold
boot. #11477's test caught it during the rebase. `useCompanyListQuery`
now reports the wait for the account as part of the wait for the list.

### What this does not do

It does not scope the rest of the per-account cache. `["companies",
id]`, stats, and every other account-scoped entry still survive an
account change; that is the cache-lifetime work in #11380.

## Verification

- `pnpm vitest run` in `ui`: **4018 passed, 1 failed**.
- `pnpm tsc -b` in `ui`: clean.
- `companies-query.test.ts`: 6 passed. `CompanyContext.test.tsx`: 17
passed. `OnboardingWizard.test.tsx`: 13 passed.
`InviteLanding.test.tsx`: 13 passed.

The failure is the pre-existing timezone-dependent
`IssueProperties.test.tsx`, fixed by #11478.

Two behaviours are asserted rather than assumed, because the refactor is
only safe if they hold: that invalidating the `companies` prefix still
marks the account-keyed list stale (19 call sites depend on it), and
that the query function detaches the in-flight `/companies` request
before fetching.

**Not done:** no manual two-account run in a browser. The path needs two
accounts on an `authenticated` instance, which a local dev instance
cannot exercise.

## Risks

Moderate, and worth reading before approving.

**It touches `InviteLanding.tsx`, which #11417 also modifies**, so one
of the two will need a rebase — the conflict is mechanical (both change
how the same query is read).

This was #11481, which GitHub closed automatically when its base branch
(#11430's) was deleted on merge; reopening a pull request whose base
branch is gone is not permitted, so it continues here against `master`
with the same head and the same review already recorded on #11481.

**The list now waits for the session query.** The key cannot be built
before the account is known. In the app the session is already fetched
at boot by many components, so this is a dependency rather than an extra
request, but it does serialize: on a cold boot the list waits for the
session to land. Every test that renders a company-list consumer now
needs a session in the cache, which is why several suites gained a seed.

**A missing mock surfaces as a passing gate rather than an error.** The
detach inside the query function meant suites whose `companiesApi` mock
lacked `detachInflightList` had their query function throw, which read
as "decided" in the onboarding gate and mounted the wizard early. Fixed
in the affected suites; worth knowing as a failure mode.

## Model Used

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

## Checklist

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

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 21:23:56 -07:00
Dotta fd472d02ba
Show ordered live blocker work in task chat (#11487)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The task view shows operators why work cannot continue
> - The redesigned task thread now shows direct and ultimate blockers
> - But it does not show the ordered task queue while a blocker chain
has live work
> - This pull request adds a compact ordered live-work queue to the
redesigned thread
> - The benefit is that operators can see completed, running, and queued
dependencies without opening the larger legacy notice

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The redesigned task thread blocker summary is improved. The merged
predecessor is #11456.

**Current behavior**

The redesigned task thread shows compact direct and ultimate blocker
links. It does not show the ordered queue when the blocker tree has live
work. The legacy task view shows this queue in a larger notice.

**Proposed behavior**

Show a compact blue live-work queue at both ends of the redesigned task
thread. Order completed tasks first, then running tasks, then queued
tasks. Show a live terminal leaf as `Now running`. Return to the amber
blocker links when no live dependency remains.

**Reason and benefit**

Operators can see the active dependency order without leaving the
redesigned task view. The compact presentation preserves the new
thread's low-chrome layout.

**Breaking changes**

None. The change only adds UI for blocker data that the task view
already receives.

## What Changed

- Shared the live blocker ordering helper between the legacy notice and
the redesigned task thread.
- Added compact ordered dependency links at the top and bottom of the
redesigned thread.
- Added a separate `Now running` link for a live terminal blocker leaf.
- Preserved the compact amber blocker rows when live work is not
present.
- Added component tests and a Storybook state for the new presentation.

## Verification

- `pnpm exec vitest run ui/src/components/TaskChatThread.test.tsx
ui/src/components/IssueBlockedNotice.test.tsx`
- `pnpm check:token-gates`
- `pnpm -r typecheck`
- `pnpm test:run`
- `pnpm build`
- `pnpm --filter @paperclipai/ui build-storybook`
- Captured and reviewed the new Storybook state in a headless browser.

## Risks

- Low risk. The queue appears only for blocked tasks whose blocker
attention state is `covered` and whose dependency set contains live
work.
- The API does not provide an explicit queue position. The UI preserves
the existing legacy ordering rule: completed, running, queued, then
numeric task identifier.

> 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, GPT-5. The runtime does not expose the exact snapshot or
context-window size. Reasoning, code execution, repository tools, and
browser automation were enabled.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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-16 15:10:05 -04:00
Tonio ac91b7f3b2
fix(ui): scope the company selection to the signed-in account (#11430)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Every company-scoped screen reads the active company from
`CompanyProvider`, which picks one from the `["companies"]` list and
remembers it in localStorage
> - That cache entry is shared app-wide and carries no account identity,
so it survives a change of account in the tab
> - The provider therefore auto-selects from whatever list is cached,
which can belong to the account that just went away
> - This pull request makes the provider watch the account and refuse to
derive a selection from a list fetched for a different one
> - The benefit is that the app stops pointing at a company the
signed-in account may not be able to see

## Linked Issues or Issue Description

No public issue exists. Refs #11380, #11382, #11417. The problem
follows.

**What happened?**

`CompanyProvider` auto-selects a company from the shared `["companies"]`
cache entry and writes that id to `localStorage`. Nothing ties that
entry to an account. When the account changes in the tab, the previous
account's list is still served, so the provider can select — and persist
— a company belonging to the account that just went away. Company-scoped
screens then render against a company the current account may not be
able to see.

Signing in through `Auth.tsx` invalidates the entry, so the in-app
sign-in path is covered. Two paths are not: a session that lapses
server-side, and a second account signing in on another tab. The
sign-out sweep in #11380 does not cover them either, because neither
presses the sign-out button.

**Expected behavior**

The company selection is derived only from a company list fetched for
the account that is signed in now.

**Steps to reproduce**

1. On a self-hosted instance in `authenticated` mode, sign in as account
A, which belongs to company X.
2. In a second tab, sign in as account B, which does not belong to
company X.
3. Return to the first tab. The session query refetches and reports
account B, while the company list is still account A's.
4. The provider keeps company X selected and leaves its id in
`localStorage`.

**Paperclip version or commit**

`master` at `6542ad1f4`.

## What Changed

- `ui/src/context/CompanyContext.tsx` — the provider observes
`queryKeys.auth.session`. On a change of session user it clears the live
selection, removes the shared company list, and holds auto-select until
a list fetched for the new account lands. The stored id is left alone on
purpose: `resolveBootstrapCompanySelection` re-validates it, so an
account signing back in keeps its company while an unrelated account
cannot inherit it.
- `ui/src/context/CompanyContext.tsx` — an errored list is treated as
undecided rather than as "no companies". With `retry: false` a single
network blip sticks, and the empty-list branch read it as proof the
account owns nothing and cleared the stored selection.
- `ui/src/api/client.ts` — new `detachInflightGet(path)`. GET coalescing
keys on the request path alone, so a `/companies` request issued under
the previous session could be joined by the replacement fetch and answer
it with the previous account's companies. Detaching leaves that request
to settle for its own callers and makes the next call issue a fresh one.
- `ui/src/api/companies.ts` — `companiesApi.detachInflightList()` wraps
that for the list path.
- `ui/src/context/CompanyContext.tsx` — `companyListUnavailable`
separates "no usable list because a request failed" from "this account
owns nothing", and `retryCompanies` gives a recovery action that
fetches. Both are derived from the query rather than tracked beside it;
a second copy of "did the last attempt succeed" drifted out of step
during review, reporting a failure over a later empty list that was
simply the truth.
- `ui/src/components/SidebarCompanyMenu.tsx` — renders "Couldn't load
companies" and a Try again item in place of "No companies", which is a
claim about the account that a failed request cannot support. This is
the menu `Sidebar` mounts, so it is the only place a customer can act on
the failure.
- `ui/src/components/CompanySwitcher.tsx` — the same treatment. The
application does not render this component (its only mount is a
Storybook story), so it is kept in step rather than relied on.
- `ui/src/context/CompanyContext.test.tsx`,
`ui/src/components/SidebarCompanyMenu.test.tsx`,
`ui/src/api/client.test.ts` — coverage for the account switch, a
same-account re-observation not churning, the detached GET, the failed
replacement and its recovery, a single blip self-healing, unavailability
not outliving the failure, and the sidebar rendering the recovery action
for a failure but plain "No companies" for an account that owns nothing.

### No `retry` override on the replacement fetch

The obvious fix for a failed replacement is a retry, and it is not
load-bearing here. A transient failure already gets a second attempt:
the observer rebinds to a fresh query on the render those state updates
schedule, and issues its own request — measured as two attempts with or
without the option. Retries would only add failed round trips before a
real outage is reported, and the outage is what needs a way out, which
is what `companyListUnavailable` and `retryCompanies` provide.

### Why `removeQueries` here, and why that does not generalise

Removal notifies no observer. What rebinds them at this call site is the
render the surrounding state updates schedule; every observer re-binds
to a fresh query on the next render. A caller without that guarantee
would leave mounted observers serving the previous account's value, so
this is not a pattern to lift elsewhere — the sign-out sweep in #11380
must use `resetQueries` instead, and its measurements are at
[#11380](https://github.com/paperclipai/paperclip/pull/11380#issuecomment-5300984911).

The inverse caveat holds for a local reset under an observer that stays
mounted, which is why #11417 and #11382 avoid `resetQueries`.

## Verification

- `pnpm vitest run` in `ui`: **4014 passed, 1 failed**.
- `pnpm tsc -b` in `ui`: clean.
- `CompanyContext.test.tsx`: 16 passed. `SidebarCompanyMenu.test.tsx`:
15 passed. `client.test.ts`: 9 passed.

The failure is pre-existing and unrelated: `IssueProperties.test.tsx`
expects `4:08 PM` and gets `9:08 AM`, a timezone-dependent assertion. It
reproduces on a tree without this change, and #11478 fixes it.

Each new test was confirmed to fail against the implementation it
covers, by reverting that change and re-running rather than by assuming.
The account-switch test fails without the fix (the selection stays on
the previous account's company and no refetch is issued); the
flag-clearing test fails without its clause (an empty list keeps reading
as "couldn't load").

**Not done:** no manual two-account run in a browser. The path needs two
accounts on an `authenticated` instance, which a local dev instance
cannot exercise.

## Risks

Low. The failure direction is a company selection withheld for one extra
round trip, which resolves when the list arrives. The direction it
removes is one account's company selected and persisted for another.

It adds one company-list request per account change, on a query key the
app already uses. It adds no request at boot: the session query it
observes is already fetched app-wide.

**This does not close the class.** Company-scoped entries other than the
list — `["companies", id]`, stats, and the rest of the per-account cache
— still survive an account change. That is the cache-lifetime work in
#11380, not this provider's.

## Model Used

Claude Opus 5 (`claude-opus-5`), through Claude Code. Extended thinking
enabled. Tool use enabled: file read and edit, shell for typecheck and
test runs, and a scratch vitest harness to measure `removeQueries` and
`resetQueries` notification behaviour against the installed
`@tanstack/query-core` 5.101.4.

## Checklist

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

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 11:51:03 -07:00
Dotta 10d0555189
fix(interactions): authorize resolvers consistently (#11376)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Issue interactions give agents and people a structured decision
record.
> - Resolver routes used different authorization rules.
> - Some routes blocked valid agents, including task watchdogs with
normal issue access.
> - The API did not show who could resolve a pending interaction.
> - This pull request gives every interaction kind one resolver policy
evaluator.
> - The benefit is a clear decision path with consistent governance and
company isolation.

## Linked Issues or Issue Description

Fixes: #8087

Refs: #7403

Related PR: #11082 proposes board-only confirmation rules. This change
keeps human-only review as an explicit policy.

**What happened?**

Agents could create issue interactions. Some resolver routes still
required board access.

This left valid agent confirmations pending. Task watchdogs could see
the same problem without board identity.

**Expected behavior**

Every interaction kind must use one resolver policy contract.

The contract must support `anyone`, `not_creator`, and `human_only`. It
must also apply all normal governance controls.

**Steps to reproduce**

1. Create a `request_confirmation` interaction as an agent.
2. Resolve it with another authorized agent.
3. Observe the board-only denial.

**Paperclip version or commit**

The problem exists on `master` before this change.

**Deployment mode**

Local development with `pnpm dev`.

## What Changed

- Add canonical policies for `anyone`, `not_creator`, and `human_only`.
- Use one server evaluator for every interaction kind.
- Apply named addressees, company limits, review rules, and task
watchdog scope.
- Charge cross-issue resolutions to the existing per-run action limit.
- Return the effective resolver audience in attention and interaction
data.
- Show the audience, governance choices, and denial reasons in the board
UI.
- Add telemetry, API documents, product documents, and regression
fixtures.
- Add migration provenance for safe legacy behavior.
- Make migration `0218` safe for complete replays and partial prior
runs.

## Product Rules

- An interaction records a response. It does not grant authority for the
next action.
- `anyone` lets any authorized issue participant respond.
- `not_creator` requires a responder other than the interaction creator.
- `human_only` requires an authorized person.
- A named addressee, company policy, or governed action can narrow the
audience.
- These controls cannot widen the audience.
- A task watchdog uses the same rules as an ordinary agent.
- A task watchdog does not receive board authority.
- An agent resolution on another issue uses the shared cross-issue
action limit.
- Legacy pending interactions keep their earlier restrictions.
- The UI shows the effective audience and a permanent denial reason.

## Verification

- `pnpm --filter @paperclipai/db check:migrations`
- `pnpm --filter @paperclipai/db typecheck`
- `pnpm exec vitest run
packages/db/src/issue-thread-interaction-resolver-policy-migration.test.ts`
- The focused PostgreSQL test applies migration `0218` twice.
- The test also completes a partial prior run and preserves existing
provenance.
- The latest GitHub head has 29 successful checks.
- The opt-in Storybook visual check skipped as expected.
- Greptile reports 5/5 with no open comments.

## Risks

- New interaction writes use `anyone` by default.
- Callers must select `not_creator` or `human_only` when they need
stricter review.
- Legacy pending interactions keep the old creator and human
restrictions.
- Migration `0218` fills only missing provenance fields during recovery.
- Cross-issue resolutions can reach the existing action limit.
- The shared evaluator affects every interaction kind.
- Route, service, database, shared contract, and UI tests cover these
rules.

> This work matches the Agent Reviews and Approvals direction in
`ROADMAP.md`. It does not duplicate a planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime does not expose the exact deployment ID
or context window.

The agent used reasoning, repository tools, shell commands, 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 linked public issues or described the issue with the
required labels
- [x] I have not referenced internal Paperclip issues or links
- [x] My branch name describes the change and contains no internal
ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented the risks
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open comments
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-16 13:46:50 -05:00
Dotta d6acb48551
feat(ui): show a calm in-flight notice when a live run is on the issue (#11423)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - When an agent run ends without a recorded disposition, Paperclip
raises a "missing disposition" handoff so the work does not stall
silently
> - The issue page shows that handoff as an amber alarm: "This task
still needs a next step."
> - The server tells the UI whether the issue has a live continuation,
and an earlier change used that flag to hide the alarm while a
correction run is active
> - Hiding it removed the false alarm but replaced it with nothing, so a
reader cannot tell "nothing is wrong" from "nothing is tracked"
> - This pull request puts a quiet informational line where the alarm
was, and links the live run
> - The benefit is that the page stays honest in both states: it is calm
while an agent works, and it is loud only when the issue is really stuck

## Linked Issues or Issue Description

No public GitHub issue exists for this gap. Description follows
`.github/ISSUE_TEMPLATE/enhancement.yml`:

**What existing behavior does this improve?**

The missing-disposition handoff notice on the issue page. It is the
amber banner that reads "This task still needs a next step."

**Subsystem affected**

Web UI — `ui/src/components/IssueBlockedNotice.tsx`.

**Current behavior**

An issue with an outstanding missing-disposition handoff shows nothing
at all in the blocked-notice slot while a correction run is live.
`IssueBlockedNotice` calls `isSuccessfulRunHandoffRequired()`. That
helper returns `false` when `successfulRunHandoff.hasLiveContinuation`
is set. The component then renders no handoff content. Two tests
asserted the empty render.

**Proposed behavior**

The page states, quietly, that a correction run is in progress. It also
states that the alarm returns if the run stops without choosing a next
step. The reader can open the live run from that line. The amber alarm
does not change when no run is live.

**Reason and benefit**

Silence and "healthy" look the same. A user who saw the alarm earlier
cannot tell whether the handoff was resolved, whether the alert was
withdrawn, or whether an agent is working on it now. One muted line
removes that ambiguity. It also keeps the loud state meaningful, because
the alarm now appears only when the issue is really stuck.

**Breaking changes**

None. The change is presentational and adds no API or data-shape change.

## What Changed

- Added `SuccessfulRunHandoffInFlightNotice` to
`ui/src/components/IssueBlockedNotice.tsx`. It renders a muted row with
a pulsing live dot and this copy: "A correction run is in progress — the
agent is working. This alert returns if the run stops without choosing a
next step."
- The notice links the live run when the server sends `liveRunId` and
the handoff has an `assigneeAgentId`. It shows the short run id as plain
text when no agent id is available, and it shows no run reference when
`liveRunId` is absent.
- Liveness reads either the server `hasLiveContinuation` flag or the
fresher client `liveIssueIds` set. This matches the rule that already
suppressed the alarm.
- The amber alarm is unchanged when no live continuation exists. The
unpromoted scheduled-retry carve-out still shows the alarm, so the
"Retry now" control stays reachable.
- The calm line also renders above the blocker notice when an issue has
blockers and a live run at the same time.
- Storybook: added `InFlightNotice` and `LivenessComparison` stories to
`ui/storybook/stories/successful-run-handoff.stories.tsx`, and removed a
duplicated panel from the overview story.
- Tests: the two cases that asserted an empty render now assert the calm
line. New cases cover a missing `liveRunId`, a missing agent id, a
handoff that is not required, and the two "alarm is unchanged" guards.

## Verification

Run the component and helper suites from `ui/`:

```
cd ui && NODE_ENV=test npx vitest run \
  src/components/IssueBlockedNotice.test.tsx \
  src/components/IssueChatThread.test.tsx \
  src/components/IssueChatThreadSystemNotice.test.tsx \
  src/lib/successful-run-handoff.test.ts
```

Result: 4 files, 111 tests, all pass.

Also run:

- `cd ui && npx tsc -b --force` — clean.
- `node scripts/check-token-gates.mjs` — all gates clean.

Manual check in Storybook (`pnpm --dir ui storybook`), story
`Paperclip/Successful Run Handoff → Liveness Comparison`:

- The alarm panel keeps its 4 remediation bullets, its amber surface,
and its run chips.
- The calm panel shows one 39 px muted row, no bullets, and a working
link to the live run.
- Measured contrast of the calm text against its rendered surface:
4.58:1 in light mode and 6.52:1 in dark mode. Both pass WCAG AA for
normal text.

## Risks

Low risk. The change is limited to one presentational component and its
stories. It adds a render path where the component previously returned
`null`, so a surface that expected an empty render now shows one muted
row. No server, API, or data-shape change. The amber alarm path and the
scheduled-retry carve-out are covered by tests that assert the calm line
does not appear.

## Model Used

Claude Opus 5 (Anthropic), model id `claude-opus-5[1m]`, 1M context
window, 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: ClaudeCoder <claudecoder@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-16 13:20:33 -05:00
Tonio add65ba4a2
test(ui): make the suite pass outside UTC (#11480)
The ui suite was green in CI and red on a clean checkout in any other
timezone. GitHub's runners default to UTC, so nothing ever reported it. A
contributor elsewhere sees two failures on their first run, which reads as
"this project is broken" rather than "your clock differs from the runner's".

Two independent causes.

`IssueProperties` supplies a UTC instant and asserts on the local-time string
the UI renders from it - "2026-07-17T16:08:00.000Z" is expected to read
"Today, 4:08 PM". That holds only where local time is UTC. Pinned with
`env: { TZ: "UTC" }` rather than rewritten: those assertions are about what a
person sees, and "4:08 PM" is worth more to a reader than an expectation
computed from the same formatter the component uses, which would pass whatever
that formatter did.

`StatusCards/format` was wrong in two ways at once, and the pin hides only one,
so it is fixed directly. `rollupUpdatesToday` filters on the *UTC* day
boundary, while the test built fixtures from local noon on the real clock.
East of UTC+12, "today at local noon" is already yesterday in UTC and the rows
the test means to count are filtered out; and any run crossing midnight UTC
lands `iso(0)` and the function's default `now` on different days. The
fixtures now come from a fixed instant, passed as `now` - the parameter exists
for this, and the sibling test already used it.

Each fix was confirmed load-bearing by removing it under TZ=Pacific/Auckland.
Without the pin, IssueProperties fails; without the fixed instant, StatusCards
fails even with the pin removed, so neither rides on the other.

Full ui suite 4017 pass, 0 fail, in UTC, Pacific/Auckland and Asia/Kolkata.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 10:15:38 -07:00
Dotta 9e9f744f58
Show blocker links in the task chat (#11456)
<!-- Write all pull request text in Simplified Technical English
(ASD-STE100): short sentences, one instruction per sentence, simple
approved vocabulary, and the active voice. -->

## Thinking Path

> - Paperclip helps operators supervise agent work through tasks and
task threads.
> - The redesigned task thread shows the current work and its state.
> - A blocked task did not show the dependency that prevented progress.
> - Operators had to leave the thread to find the direct and final
blockers.
> - This pull request adds compact blocker links at the top and bottom
of the task thread.
> - The benefit is that operators can identify and open the relevant
tasks without adding a large notice to the thread.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The redesigned task thread did not show which task directly blocked the
current task or which task ultimately blocked its dependency chain.

**Subsystem affected**

`server/`, `packages/shared/`, and `ui/` task-blocker presentation.

**Current behavior**

A blocked task can open in the redesigned thread without a visible
dependency link at the top or bottom of the conversation.

**Proposed behavior**

Show one compact amber row for the direct blocker. Show a second row for
the selected final blocker when one exists. Render the rows at both ends
of the thread.

**Reason and benefit**

Operators can see the reason for the blocked state and open the relevant
task from the conversation. The compact rows preserve thread density.

**Breaking changes**

None. The new blocker-attention fields are optional. Existing clients
remain compatible.

## What Changed

- Added a compact task-chat component for direct and selected final
blocker links.
- Added the blocker rows to the top and bottom of populated and empty
task threads.
- Added link-ready blocker-attention details so an intermediate selected
task stays on its correct direct chain.
- Included blocker-link changes in the thread content key so pinned
threads follow a newly added bottom row.
- Added component, scrolling, server contract, and Storybook coverage
for the new states.

## Verification

- `pnpm exec vitest run ui/src/components/TaskChatThread.test.tsx
server/src/__tests__/issue-blocker-attention.test.ts` (38 tests passed)
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm check:token-gates`

## Risks

- Low risk. The rows only render while the task status is `blocked` and
an unresolved blocker is available.
- Long titles are truncated to keep each blocker on one line. The full
task label remains available in the link title.
- Older server payloads keep the original leaf-selection behavior
because the new sampled details are optional.

> 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 run used tool-enabled reasoning and code
execution. The context-window size was not exposed to the run.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-16 13:09:17 -04:00
Tonio e384d0a2bd
fix(ui): keep the stored company when the company request fails (#11477)
An error is not an answer, and this branch is destructive.

`companiesListQueryOptions` sets `retry: false`, so a request that fails before
ever succeeding leaves `data` undefined - which `CompanyProvider` defaults to
`{ companies: [], unauthorized: false }`. That is indistinguishable from "this
account was asked, and owns nothing", so `shouldClearStoredCompanySelection`
returned true and the effect removed the customer's stored company. With
`refetchOnWindowFocus: true`, a blip on focus during a cold load was enough,
and the next visit drops them onto whichever company sorts first.

The predicate now takes `errored` and refuses to clear on it. Required rather
than optional, so the compiler made both existing call sites state their
answer instead of inheriting a default.

Not clearing costs nothing: a stored id that no longer resolves is ignored by
`resolveBootstrapCompanySelection`, which checks it against the current list
before using it. Clearing wrongly costs the customer's selection, which cannot
be recovered.

Scoped deliberately. This file had been described as carrying the same defect
as the onboarding draft gate and the sign-out sweep, and that was overstated.
Those two *trusted* a stale list to answer "does this account own this
company?". This one validates membership against the current list and only
picks a default, so a stale list here self-corrects rather than leaking. The
failed-request branch is the part that is genuinely wrong, and it is the only
part changed. The transient re-decision during a background refetch is real,
self-correcting, and left alone.

Tested at both levels, because the predicate alone would not have caught it:
the provider is what defaults a failed request to an empty list, so the wiring
is where the decision goes wrong. Removing the guard fails both.

ui typecheck clean; full ui suite 4016 pass, with only the timezone-dependent
IssueProperties failure already present on master.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 09:58:46 -07:00
Tonio 0023c5c4a6
refactor(ui): record why sign-out resets rather than removes (#11473)
Comment only. Salvaged from `claude/sign-out-cache-note`, written while #11380
was in progress and never opened as a PR; that branch is deleted with this.

#11380 landed the reasoning for `resetQueries` but not the evidence, and not
the part that stops someone reversing it later.

The choice was measured rather than argued. Against query-core 5.101.4,
removal produced 0 notifications and left the observer holding the signed-out
account's session; reset produced 3 and null. The consequence of the former is
not only a stale read - CloudAccessGate's redirect fires on the session going
empty, so it never runs.

The opposite advice really does hold for a local reset of a key an observer is
still mounted against: reset rewinds the update counters `isFetchedAfterMount`
derives from while that observer keeps its bind-time baseline, so anything
gated on the flag withholds forever. Sign-out is not that case, because it
resets the session too and the consumer unmounts on the redirect.

Two sessions reached opposite recommendations on this API within a day, both
correct about different situations, which is the kind of thing a later reader
re-litigates without a note in the file.

The first revision of this note named the wrong consumers - InviteLanding and
the onboarding draft gate, neither of which reads `isFetchedAfterMount`.
`AppsConnect` is the only one on master and is what it names now.

ui typecheck clean; 13 pass across the sign-out suites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 09:44:36 -07:00
Tonio 38752c4e5e
fix(ui): clear account-scoped query caches on sign-out (#11380)
Sign-out invalidated two query keys - the auth session and health - and left
everything else in the cache. `invalidateQueries` also keeps serving the old
data while it refetches, so it was insufficient even for the two it did touch.
The company list was never touched at all, which is how one account's
companies could still be in hand when the next account signed in.

The self-hosted path now resets every account-scoped entry. Scoping is an
allowlist of instance-scoped roots rather than a list of things to clear, so a
query key added later is account-scoped unless someone deliberately says
otherwise - forgetting this file fails closed.

`health` is the only exemption. It carries deployment mode, bootstrap state
and Cloud metadata, nothing account-scoped, and `useCloudInstance` observes it
with `enabled: false` and leaves the fetch to CloudAccessGate. Dropping the
entry would strand every such observer on `null` until the gate happened to
refetch, flipping Cloud instances into their self-hosted rendering mid
sign-out. It is refreshed in place instead.

`resetQueries` rather than `removeQueries`: removal empties the cache without
notifying the observers already subscribed, so a mounted `useQuery` keeps
returning its last result until an unrelated re-render rebuilds it.
CompanyProvider sits above the router and stays mounted across the whole
sign-out and sign-in cycle, so that is the common case here rather than a
corner one. Reset notifies them, so the old data leaves the cache and
everything reading it.

The cloud path is untouched: it is a top-level navigation, and the document
reload builds a new QueryClient with nothing left to clear.

This is the root cause behind the onboarding draft-ownership gate added in
#11382. That gate stays, and its comment now says why: this fix covers the
sign-out button, not the question. An account can change without it - a
session lapsing server-side, a second account signing in on a warm tab, a
caller supplying the company context from somewhere else - so the gate stays
independent rather than deferring to this.

ui typecheck clean; full ui suite 4014 pass, with only the timezone-dependent
IssueProperties failure already present on master.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 07:55:14 -07:00
Tonio b38d6ddb81
fix(onboarding): choose the launcher's step from the company's mission too (#11429)
The last of the three ways into onboarding for a company that already exists.
The route resolver and the dashboard both pick the step from whether the
company already has its mission; the "Add Agent" card on `/{prefix}/onboarding`
hardcoded the mission step. So the one entry point whose own copy reads "Add
another agent to X" was the one that stopped to ask X for the mission it
already had.

It now calls `onboardingStepForCompany`, like the other two. `matchedCompany`
moves above the early return because a hook cannot be called after it.

An unsettled or failed lookup still reads as "no mission" and costs the step,
which the customer can answer - the same fail-open rule the other callers
follow, and safe now that confirming the mission updates the company's existing
goal rather than adding a second one.

`OnboardingRoutePage` is exported so this can be driven directly. The
alternative was the whole `<App>` route table, which is a much heavier harness
for a question about one button's argument.

Four cases, and the first fails against the hardcoded step. The button lookup
asserts it matched something before clicking, because a lookup that silently
matches nothing turns the click into a no-op and the test into decoration.

This is the last piece of #11259 that had not landed.

ui typecheck clean; full ui suite 4010 pass. The one failure, in
IssueProperties, is timezone-dependent and reproduces on master.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 01:54:47 -07:00
Tonio 6542ad1f4d
fix(onboarding): carry an existing company's mission into the wizard (#11416)
Ports the substance of #11259, which predated the recent onboarding work and
was still open. One of the parts it solves is a regression #11352 introduced.

A company that already has its mission opens on the agent step, which is the
point of #11352. What that missed is that the mission field is filled only by
the step being skipped, and that the field is not decoration:
`composeCeoInstructions` seeds the lead agent's instructions from it, and the
Review checklist reads it. So every Cloud-seeded company hired its lead agent
with no Mission line at all, having been routed there precisely *because* it
had a mission. Before #11352 those companies dead-ended on the mission step; a
dead end became a quiet data loss, which is worse, because it completes.

`selectExistingCompanyMission` reads the company's own goal back into the shape
the mission field holds, and the wizard hydrates from it - only when the field
is empty, so a customer editing their mission is never overwritten by the
stored copy. The marker recording that hydration travels with the field it
describes, cleared wherever `companyGoal` is.

`isExistingCompanyMissionUnresolved` holds the hire while that read is
outstanding, counting an in-flight refetch over cached goals as unresolved.
That is the rule #11382 settled on a day earlier for a different consumer -
`isFetching`, not `isLoading`, because retained data is not an answer to the
question being asked now. #11259 had it first, on 11 August.

`canGoBackFromOnboardingStep` and `canJumpToOnboardingStep` bound how far back
a run can walk by the step it entered on. The Back button already applied that
rule inline; the progress bar applied only the "already completed" half, so a
run holding a company could still jump to step 1 - the step whose job is to
create one. The entry step is captured once, when the wizard opens, for the
same reason the step itself is.

`planMissionPersistence` came with them and turned out to be required rather
than tidying. Hydration sets `createdCompanyGoalId` from the company's existing
goal, and confirming the mission read that id as "already written" and skipped
the write, discarding the customer's edit. That skip was safe only while the id
could arrive one way - by writing. A goal in hand now means update it.

Each piece was checked by removing it and confirming a specific case fails.
The hydration case asserts on `saveInstructionsFile`'s content, the actual
consumer, rather than on the mission textarea, because the entry path never
renders that field and the navigation bound now prevents reaching it. One
caveat recorded rather than smoothed over: the reopen case fails only when
both marker-clears are removed, since `reset()` also clears the company id and
the next introduction routes through `clearCompanyScopedState`. They are kept
as one invariant rather than one guard plus a coincidence.

ui typecheck clean; full ui suite 4004 pass. Two failures remain, in
IssueProperties and StatusCards/format; both are date-dependent, both
reproduce on master with these changes stashed, and neither file is touched
here.

Co-Authored-By: Jannes Stubbemann <stubbi@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 01:42:03 -07:00
Tonio 484b1f626c
fix(onboarding): verify draft ownership against a list fetched this session (#11382)
#11370 stopped onboarding restoring a saved draft when the company list had
*errored*. It still trusted the list when the list looked healthy — and the
wider door was exactly that. `main.tsx` sets `staleTime: 30_000` app-wide and
`Auth.tsx` invalidates rather than resets on sign-in, so `invalidateQueries`
keeps serving the previous account's companies with `isLoading` false and no
error at all. On a self-hosted instance, where sign-out does not reload the
page, signing in as a second account in the same warm tab could restore the
first account's draft. No request had to fail.

The wizard now judges ownership against a list it fetched for the current
session: its own `useQuery` on the shared key with `staleTime: 0`, gated so it
runs only when a parseable draft exists and adds no request otherwise.

Every clause of that gate earns its place, and each was verified by removing
it and watching a specific case fail:

- `isSuccess` ties the answer to this session. React Query retains the last
  good `data` when a refetch fails, so after an account switch the retained
  value is the previous account's list; a failed refetch flips status to error
  and this rejects it.
- The `unauthorized` check catches the opposite error. `companiesListQueryOptions`
  folds 401 and 403 into `{ companies: [], unauthorized: true }` rather than
  throwing, so an auth blip arrives as a *successful* empty list and would
  otherwise read as "this account owns nothing" and delete the draft.
- The mount gate keys on `isFetching`, not `isLoading`. `isLoading` is false
  whenever retained data exists, so a refetch over a warm cache mounted the
  wizard undecided — and with the wizard open, the persist effect overwrote
  the customer's own draft with defaults before the answer arrived. It still
  releases on failure, so the "Get Started" dead end stays fixed.
- An unreadable draft is judged, and cleared, before any of the above, and
  does not enable the query at all.

`isFetchedAfterMount` was in an earlier revision and is deliberately not here:
it is true after a failed refetch too, so it rejects nothing `isSuccess` has
not, and no test could distinguish it.

Worth recording how the first defect survived a check. I fault-injected it,
saw a test fail, and concluded the guard worked. It was failing for an
unrelated reason — the inner wizard mounted during the fetch and locked its
state initializers to defaults, so the draft could not appear whatever the
gate decided. Fixing the mount gate exposed the real behaviour. An injection
is only evidence if the failure it produces is the one being claimed.

This narrows onboarding only. The general fault is that a sign-out leaves
account-scoped caches in place, and account changes that skip the button —
a session lapsing server-side, a second account in a warm tab — reach the same
stale list. Tracked separately; this defence should not be removed as
redundant when that lands.

ui typecheck clean; full ui suite 3963 pass, with only the timezone-dependent
IssueProperties failure already present on master.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 22:59:41 -07:00
Nicky Leach fdb9a4880d
fix(security): route paperclipai CLI guidance through safe npx form (CWE-78) (#11400)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip provides CLI commands and guidance for operators and
agents
> - The `pnpm paperclipai` script can pass argument values through a
shell
> - Shell re-parsing can execute command substitutions inside quoted
values
> - This pull request routes guidance through inert-argv `npx
paperclipai` commands and adds regression coverage
> - The benefit is safer operator guidance across documentation and
runtime hints

## Linked Issues or Issue Description

This pull request fixes a command-injection-class defect in Paperclip
CLI guidance.

**What happened?**

The `pnpm paperclipai <sub> --flag "$VALUE"` form can re-parse argument
values through a shell. A command substitution inside a quoted value can
execute on the host.

**Expected behavior**

Paperclip guidance must pass CLI values as inert argument values.
Host-derived values must not appear in copyable commands.

**Steps to reproduce**

1. Run a Paperclip guidance command that uses the `pnpm paperclipai`
script.
2. Provide a quoted value that contains a command substitution.
3. Observe that the shell can evaluate the substitution before the CLI
starts.
4. Compare the result with the `npx paperclipai` form.

**Paperclip version or commit**

`5670984b75d109950c968542a0111ebb6967f4da`

**Deployment mode**

All deployment modes that show or use the affected CLI guidance.

**Installation method**

Built from source and installed CLI guidance.

**Agent adapter(s) involved**

Not adapter-specific (core bug).

**Database mode**

Not database-related.

**Access context**

Both.

**Additional context**

The earlier merged PR
[#11343](https://github.com/paperclipai/paperclip/pull/11343) used the
unsafe `pnpm exec paperclipai` form. This fresh PR replaces that
guidance with the safe `npx paperclipai` form.

## What Changed

- Standardize documentation and runtime hints on `npx paperclipai`.
- Remove the broken `pnpm exec paperclipai` guidance.
- Use a static `<host>` placeholder in private-hostname guidance.
- Add regression tests for unsafe forms, continued lines, static hosts,
and offline guidance.

## Verification

- `git diff --check
origin/master...origin/fix/paperclipai-cli-npx-safe-invocation` passes.
- The branch adds `server/src/__tests__/cli-invocation-safety.test.ts`
and updates private-hostname tests.
- CI must run the new tests, typecheck, lint, and build checks.
- Local Vitest execution was not available because this worktree has no
installed Vitest binary.

## Risks

- The change affects operator and agent documentation text.
- The runtime hints now show `<host>` instead of a request-derived host
value.
- No database schema or migration changes exist.
- CI will detect any missed unsafe invocation or type error.

## Model Used

OpenAI GPT-5, exact model ID `gpt-5`, with tool use and code-review
assistance. The model used repository inspection, Git operations, and PR
preparation.

## 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] CI ran the test suites and they pass; local test execution was
unavailable in this worktree
- [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 addressed all Greptile and reviewer comments before requesting
merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-14 22:11:16 -07: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
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
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
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
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
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
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
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
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
Constantine 68c699687e
fix(ui): keep selected run lifecycle and logs current (#10169)
## Thinking Path

> - Paperclip's agent-run detail view combines live lifecycle events,
detail queries, and persisted shell logs.
> - Queued runs do not have a log reference yet, so polling the log
endpoint returns expected-but-noisy 404s.
> - Simply disabling queued log polling can strand the selected detail
cache at `queued` or leave terminal lifecycle fields stale.
> - The detail cache therefore needs both live lifecycle patches and an
HTTP fallback while the run is active.
> - Both the selected-run query and the shared transcript hook must
avoid reading persisted logs before execution starts.
> - This pull request disables queued log polling in both paths, patches
lifecycle fields, invalidates authoritative detail data, and polls run
state only while active.
> - The benefit is quiet queued runs, reliable queued-to-running
transitions, and complete final log handoff.

## Linked Issues or Issue Description

No exact duplicate found. I searched open PRs for `queued run`, `log
polling`, and `run lifecycle`; related results addressed stale queued
execution or dashboards rather than selected-run log/detail
synchronization.

**What happened?**

Opening or rendering a queued run caused repeated `GET
/api/heartbeat-runs/:id/log` 404s from both the selected detail and
shared transcript hydration paths. A queued-only detail guard could also
leave the selected run stuck at `queued` when live delivery was missed,
or stop before fetching final `logRef` and terminal fields.

**Expected behavior**

Queued runs should not request unavailable logs. The selected detail
should transition through running to terminal via live events or bounded
HTTP fallback, refresh authoritative detail data, fetch final logs, and
then stop polling.

**Steps to reproduce**

1. Open the detail page for a run waiting behind the agent concurrency
limit.
2. Observe repeated log-endpoint 404s while status is queued.
3. Let the run start with WebSocket delivery unavailable or delayed.
4. Observe stale detail state without a run-detail fallback.

**Environment**

- Paperclip base: `14f20be92b86a49ff2c35495e5b0fa4d719998ef`
- Deployment: self-hosted, built from source
- Adapter scope: visible with Hermes-backed agents but not
adapter-specific

- [x] I searched open PRs for queued-run log polling and selected-run
lifecycle synchronization; no exact duplicate was found.

## What Changed

- Poll shell logs only while a run is `running`, never while `queued`.
- Defer shared persisted-transcript hydration and its live WebSocket
until a queued run becomes `running`; terminal runs still receive one
persisted-log hydration attempt.
- Patch selected run status, invocation metadata, errors, and
start/finish times from lifecycle events.
- Invalidate selected run detail on lifecycle events to hydrate
authoritative `logRef`, result, usage, and excerpts.
- Poll run detail every 5 seconds while queued and every 15 seconds
while running, then stop at terminal status.
- Add regression coverage for queued/running/terminal polling and
lifecycle cache handoff.

## Verification

- `pnpm exec vitest run ui/src/context/LiveUpdatesProvider.test.ts
ui/src/pages/AgentDetail.progress.test.ts` — 42 passed.
- `pnpm exec vitest run
ui/src/components/transcript/useLiveRunTranscripts.test.tsx` — 11
passed.
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm --filter @paperclipai/ui build`
- Exact final diff independently reviewed: **APPROVE**, no blocking
findings.
- UI-only local cutover completed with the Paperclip server PID
unchanged.

## Risks

- The selected run performs one lightweight detail GET every 5 seconds
while queued and every 15 seconds while running if it remains open.
Polling stops at terminal status.
- Lifecycle events still patch immediately; polling is only the fallback
and authoritative hydration path.
- No API, schema, or migration changes.

> This is a bug fix, not roadmap feature work.

## Model Used

OpenAI Codex `gpt-5.6-sol`, with tool use, code execution, repository
inspection, and independent read-only review agents.

## 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/described the search above
- [x] I have described the issue in-PR following the bug template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have considered documentation; no user-facing documentation
change is required
- [x] I have considered and documented 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: cucurigoo <cucurigoo@users.noreply.github.com>
2026-08-12 16:43:31 -07:00
Constantine 106955bdfd
fix(ui): gate summary built-in requests by feature flag (#10170)
## Thinking Path

> - Paperclip's web UI conditionally exposes experimental summary and
built-in-agent capabilities.
> - Summary cards depend on the built-in Summarizer agent, but the two
capabilities have independent feature flags.
> - `SummarySlotCard` and the reusable `BuiltInAgentGate` enabled
built-in-agent lookups without requiring `enableBuiltInAgents`.
> - When built-in agents were disabled, those surfaces called a server
route that was intentionally unavailable and generated avoidable 404s.
> - The client query should obey both server-side feature gates.
> - This pull request adds the missing gate and a cross-flag regression.
> - The benefit is consistent feature-flag behavior and no request loop
against a disabled endpoint.

## Linked Issues or Issue Description

No exact duplicate found. I searched open PRs for `SummarySlotCard`,
`BuiltInAgentGate`, `enableBuiltInAgents summaries`, and `built-in
agents 404`. Related PR #10116 gates `SidebarAgents`; this PR
deliberately excludes that file and covers the remaining summary/gate
callers.

**What happened?**

`SummarySlotCard` called `builtInAgentsApi.list` whenever summaries were
enabled, and `BuiltInAgentGate` called it whenever a company was
selected. The server rejects that route when `enableBuiltInAgents` is
false, so the disabled configuration produced repeated 404 requests.

**Expected behavior**

Built-in-agent queries should run only when built-in agents are enabled;
the summary-specific query also requires summaries to be enabled.

**Steps to reproduce**

1. Enable summaries.
2. Disable built-in agents.
3. Render a page containing `SummarySlotCard` or `BuiltInAgentGate`.
4. Observe a request to the disabled built-in-agents route.

**Environment**

- Paperclip web UI
- Cross-flag configuration: summaries enabled, built-in agents disabled

- [x] I searched open PRs for the affected component, feature flags, and
404 behavior; no exact duplicate was found.

## What Changed

- Require both `enableSummaries` and `enableBuiltInAgents` in
`SummarySlotCard`.
- Make `BuiltInAgentGate` resolve experimental settings before enabling
its built-in-agent query and fail open when the feature is disabled.
- Add cross-flag regressions for both callers.
- Leave `SidebarAgents` to related PR #10116 rather than duplicating it.

## Verification

- `pnpm exec vitest run ui/src/components/SummarySlotCard.test.tsx
ui/src/components/BuiltInAgentGate.test.tsx` — passed as part of a
41-test built-in UI group.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm --filter @paperclipai/ui build` — passed in combined deployment
staging.
- UI-only local cutover completed with the Paperclip server PID
unchanged.
- The complete UI fix was staged after a summary-only cutover exposed
the remaining reusable-gate caller.

## Risks

- Low risk: this changes only whether one query is enabled under a
feature-flag combination where the server route is unavailable.
- No API, schema, migration, authentication, or persistence changes.
- Rollback is a single commit revert.

> This is a bug fix, not roadmap feature work.

## Model Used

OpenAI Codex `gpt-5.6-sol`, with tool use, code execution, repository
inspection, and read-only review-agent evidence.

## 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 described
the search above
- [x] I have described the issue in-PR following the bug template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have considered documentation; no user-facing documentation
change is required
- [x] I have considered and documented 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: cucurigoo <cucurigoo@users.noreply.github.com>
2026-08-12 16:43:22 -07:00
Constantine b42efe2063
fix(ui): gate built-in agent sidebar query (#10116)
## Thinking Path

> - Paperclip is the open-source control plane people use to manage AI
agents and their work.
> - The sidebar renders company agents and, when enabled, their built-in
lifecycle state.
> - The built-in-agents API is intentionally unavailable when the
experimental feature is disabled.
> - `SidebarAgents` nevertheless queried that endpoint whenever a
company was selected, producing routine 404s in normal feature-off
installations.
> - The query must be gated by the shared instance setting, including
its unresolved state, without exposing stale cached lifecycle badges.
> - This pull request adds that gate and focused disabled, unresolved,
and enabled regressions.
> - The benefit is a quiet sidebar network path with unchanged behavior
for installations that enable built-in agents.

## Linked Issues or Issue Description

### Pre-submission checklist

- [x] I searched existing open and closed issues/PRs and found no exact
duplicate. Related but not duplicate: #4149.
- [x] I reproduced this on current `master`.
- [x] I confirmed the error originates in Paperclip's sidebar query
gating, not an adapter, provider, or local configuration.

### What happened?

With `enableBuiltInAgents: false`, mounting `SidebarAgents` for a
selected company still called `GET
/api/companies/:companyId/built-in-agents`. The server correctly
returned 404 because the experimental feature was disabled.

### Expected behavior

The sidebar must not call the built-in-agent endpoint until the shared
experimental setting resolves to exactly `true`. Cached built-in
lifecycle state must also remain hidden while disabled.

### Steps to reproduce

1. Set `enableBuiltInAgents` to `false`.
2. Open any company so the sidebar agent list mounts.
3. Observe a request to `/api/companies/:companyId/built-in-agents` and
a 404 response.

### Environment

- Paperclip commit: `f2f168f6a10a24c924516808f414baba52b1c080`
- Deployment mode: self-hosted server
- Installation method: built from source
- Adapter: not adapter-specific (core UI bug)
- Database mode: not database-related
- Access context: board (human operator)
- Node.js: `v22.22.3`
- Operating system: Linux
- Relevant config: `{"enableBuiltInAgents": false}`
- Relevant output: redacted HTTP 404 from
`/api/companies/:companyId/built-in-agents`
- Privacy: all instance-local identifiers, paths, and output were
omitted or redacted.

## What Changed

- Read instance experimental settings in `SidebarAgents` through the
shared React Query key.
- Enable the built-in-agent list query only when `enableBuiltInAgents`
is explicitly `true`.
- Ignore cached built-in lifecycle data while the feature is disabled.
- Add regressions for disabled, unresolved, and enabled settings states.

## Verification

- RED before implementation: the disabled-feature regression failed
because `builtInAgentsApi.list` was called once.
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/SidebarAgents.test.tsx` — 24 tests passed.
- `pnpm --filter @paperclipai/ui exec tsc -p tsconfig.json --pretty
false` — passed.
- `git diff --check` — passed.
- The existing test file emits pre-existing React `act(...)` warnings
while passing.

## Risks

- Low risk: request gating only; no API, schema, migration, or visible
UI contract changes.
- A stale experimental-settings cache could delay enabling the query
until the normal settings invalidation/refetch path runs; this is the
same shared query key already used elsewhere.
- Cached built-in statuses are deliberately hidden whenever the setting
is not literal `true`.

> 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 `gpt-5.6-sol` was used with reasoning, repository/file
tools, shell command execution, and delegated read-only code review. The
runtime did not expose a context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes (no
documentation change needed; this enforces the existing feature flag)
- [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: cucurigoo <cucurigoo@users.noreply.github.com>
2026-08-12 16:43:04 -07:00
Dirk 8b1483e601
fix(ui): render board approval payload prose as markdown (#9817)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The approval queue is how agents surface decisions that need a
human, so the approval card is often the only thing an operator reads
before approving or rejecting
> - Agents author those payloads in markdown, because markdown is what
they produce everywhere else in the product
> - `ApprovalPayload.tsx` renders the four prose fields of a board
approval as bare text nodes, while `CommentThread` on the same page
renders through `MarkdownBody` — same authors, two different renderers
> - So the operator sees literal `##`, `**bold**`, backticks and
`[link](url)` in the payload, and correctly formatted text in the
comments directly below it
> - This pull request routes those four fields through the existing
`MarkdownBody` component
> - The benefit is that the highest-stakes text in the product becomes
readable, with no new dependency and no schema change

## Linked Issues or Issue Description

Refs #4911 — prior art, see the note at the bottom of this description.

No open issue covers this, so per (B) here is the bug report:

**What happened:** On a board approval, the `summary`,
`recommendedAction`, `nextActionOnApproval` and `risks` fields display
raw markdown source. Headings appear as literal `##` mid-paragraph,
inline code keeps its backticks, links show as `[text](url)`, and both
levels of a nested bullet list collapse into one run-on paragraph.

**What was expected:** The same rendering the comment thread further
down the same page already gives, since both are agent-authored
markdown.

**Steps to reproduce:** Open any `request_board_approval` whose
`summary` contains markdown — headings, a nested list, code spans or
links.

**Where:** `ui/src/components/ApprovalPayload.tsx`,
`BoardApprovalPayloadContent`.

## What Changed

- `ui/src/components/ApprovalPayload.tsx`: import `MarkdownBody` and
render `summary`, `recommendedAction`, `nextActionOnApproval` and each
`risks` entry through it instead of `<p>` / `<span>` text nodes.
`MarkdownBody` defaults `softBreaks` to `true`, which is the same
behaviour `CommentThread` opts into explicitly, so paragraph handling
matches the comments.
- `stripLeadingListMarker`: risks already render inside a custom bullet
row, so an authored leading `-` / `*` / `•` would nest a second bullet
inside the first. One leading marker is stripped per entry.
- The risk bullet dot gains `shrink-0` so it keeps its shape next to
block-level markdown content.
- `title` stays plain text — it is a one-liner and markdown there is
noise.
- `proposedComment` stays a verbatim `<pre>` block — it is draft text
intended to be posted elsewhere, so it must not be reinterpreted.
- `ui/src/components/ApprovalPayload.test.tsx`: tests for markdown
rendering in all four fields, the leading-list-marker strip, and that
`title` and `proposedComment` remain verbatim.

## Verification

- `npx vitest run ui/src/components/ApprovalPayload.test.tsx` — 5
passed.
- `npx vitest run ui/src/components/ApprovalPayload.test.tsx
ui/src/components/CommentThread.test.tsx` — 12 passed, confirming the
shared `MarkdownBody` path is not disturbed.
- Manual, measured rather than eyeballed: I ran a patched build in a
throwaway container beside an unpatched one and pointed both at the same
real approval payload, then counted nodes in the rendered DOM.

  | | unpatched | patched |
  |---|---|---|
  | `.paperclip-markdown` nodes | 0 | 3 |
  | raw backticks in visible text | yes | no |
  | rendered `h2` | 0 | 5 |
  | rendered `li` | 0 | 16 |

## Risks

Low, and confined to the board approval card.

- Rendering scope widens from text to markdown on four fields. A payload
that contains markdown-significant punctuation but was authored as prose
could render differently than before. This is the intended change, and
it matches how the same author's text is already rendered in comments on
the same page.
- `stripLeadingListMarker` removes one leading list marker per risk
entry. A risk that genuinely begins with a literal hyphen followed by a
space loses that hyphen. Chosen over the alternative of a visible double
bullet on the common case.
- No schema change, no migration, no new dependency. `MarkdownBody` is
already used elsewhere in the same directory.

## Model Used

Claude Opus 4.8 (`claude-opus-4-8`), via Claude Code, with extended
thinking and tool use (repository search, file editing, local test
execution, and headless-browser DOM measurement of the before/after
renders).

## 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 — no
docs describe this rendering behaviour, so there was nothing to update
- [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
- [x] I will address all Greptile and reviewer comments before
requesting merge

---

### On the prior PR

@alxhrzg opened #4911 for this same bug first, and reached the same
conclusion I did: route the four fields through `MarkdownBody`. Credit
for spotting it and for the diagnosis goes there.

That PR has been conflicting against base and untouched since May.
Rather than let the fix sit, this PR reapplies the idea on current
`master` and adds what #4911 was missing: test coverage, the
nested-bullet fix for `risks`, and the `shrink-0` on the bullet dot. I
could not push to #4911 directly as it is on another contributor's fork.

@alxhrzg, if you would rather finish #4911 yourself, I am happy to close
this and hand over the tests and the two risk-row fixes for you to take
across.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-12 16:08:22 -07:00
Devin Foley 61a5b7c6f9
fix(ui): stop the selection ping-pong on archived company URLs (#11300)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The UI keeps a selected company in `CompanyProvider` with two
writers: a bootstrap effect that repairs invalid selections, and a
Layout route-sync effect that selects the company the URL prefix names
> - The route-sync matches the URL against the full company list
(archived included), while the bootstrap resolver only accepted
companies from the sidebar-filtered non-archived list
> - On any archived company's URL the two effects overwrite each other's
selection in a synchronous loop until React throws error #185 ("Maximum
update depth exceeded") and unmounts the root to a blank page — armed by
remembered last-visited paths, back/forward navigation, or bookmarks, on
first load and client navigation alike
> - This pull request makes an already-selected company only need to
exist, keeping the sidebar filter for fresh-boot resolution where no
explicit selection exists
> - The benefit is that archived company URLs render instead of blanking
the entire app

## Linked Issues or Issue Description

No existing issue. Description follows the bug template:

**What happened?**

Opening (or back-navigating to) a URL whose company prefix belongs to an
archived company blanked the whole app with `Minified React error #185`.
Console in dev mode: "Maximum update depth exceeded. This can happen
when a component calls setState inside useEffect…". A workspace whose
first/seeded company was archived hit this on every load of its
remembered URL.

**Expected behavior**

An archived company's URL renders its pages (the company still exists
and its API routes serve data). The sidebar simply does not feature
archived companies, and fresh boots still land on a non-archived
company.

**Steps to reproduce**

1. Create two companies; archive one (`PATCH /api/companies/:id` with
`status: "archived"`).
2. Navigate to `/{archivedPrefix}/dashboard` — direct load or
client-side back-navigation.
3. Before this fix: React #185 and an unmounted blank page (reproduced
deterministically by the new e2e test).

## What Changed

- `ui/src/context/CompanyContext.tsx`:
`resolveBootstrapCompanySelection` keeps an explicitly selected company
that exists in the full company list; stored-id and default resolution
still prefer sidebar (non-archived) companies.
- `ui/src/context/CompanyContext.test.tsx`: resolver keeps an
archived-but-existing selection; a truly deleted selection is still
replaced.
- `tests/e2e/archived-company-url.spec.ts`: end-to-end regression
driving both field shapes (direct load and back-navigation onto an
archived company URL); it failed with the exact #185 console errors
before the fix and passes after.

## Verification

- `pnpm vitest run src/context …` in `ui/` — 122 tests pass (includes
the new resolver cases).
- `npx playwright test --config tests/e2e/playwright.config.ts
archived-company-url` — fails before the fix (captured "Maximum update
depth exceeded" console errors), passes after.
- `pnpm typecheck` in `ui/` — clean.

## Risks

Low risk. The only behavioral change is that a selection naming an
archived-but-existing company survives the bootstrap repair — previously
that state was unreachable without crashing. Boots with no valid
selection behave exactly as before (non-archived preferred), covered by
the existing and new resolver tests.

## 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-driven crash reproduction).

## 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 15:40:33 -07:00
scotttong 0ee0543e5b
feat(ui): refine the chat-style task workflow (#11263)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue detail view is the main place where people read work and
guide agents.
> - The chat-style task view needs clear messages, controls, properties,
and document feedback.
> - Dense metadata and disconnected controls make active work harder to
scan.
> - This pull request refines the existing chat-style task workflow
across desktop and mobile layouts.
> - The benefit is a clearer issue thread with faster access to the
controls that guide work.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The change improves the chat-style issue detail view that was introduced
in [#10606](https://github.com/paperclipai/paperclip/pull/10606) and
expanded in
[#10707](https://github.com/paperclipai/paperclip/pull/10707).

**Current behavior**

The issue thread spreads task controls across the page. Agent-turn
metadata competes with the message content. Document annotation comments
use inline placement that limits the document reading area. The mobile
composer can overlap the bottom navigation.

**Proposed behavior**

The issue view keeps the thread focused on message content. It moves
supporting controls into the properties area, adds searchable
assignment, restores the sub-task tree, docks document comments in a
side gutter, and keeps the mobile composer clear of navigation.

**Reason and benefit**

People can scan active work faster and find task controls without
leaving the issue. The layout also gives documents and mobile
conversations more usable space.

**Breaking changes**

None. The change updates presentation and interaction behavior in the
existing issue UI.

## What Changed

- Refined task-chat message spacing, metadata, agent bubbles, and
composer alignment.
- Added searchable assignment and restored sub-task navigation in the
properties pane.
- Moved document annotation comments into a right-side gutter.
- Kept the mobile composer above the auto-hiding bottom navigation.
- Added and updated focused component tests for the changed
interactions.

## Verification

- `pnpm check:token-gates`
- `TZ=UTC pnpm --filter @paperclipai/ui exec vitest run
src/components/InlineEntitySelector.test.tsx
src/components/IssueDocumentAnnotations.test.tsx
src/components/IssueProperties.test.tsx
src/components/TaskChatThread.test.tsx src/pages/IssueDetail.test.tsx`
- The token gates report 3/3 clean.
- The focused test run passes 124 tests in 5 files.
- Visual snapshot baselines were not updated. This follows the
`doc/design/DECISION-SHEET.md` entry "Per-change snapshot verification
demoted to dormant (Jul 13 2026)."

## Risks

- The changes affect several related issue-detail layouts. A browser
review should cover desktop and mobile widths before merge.
- The monitor-row test formats time in the host timezone. The
verification command sets `TZ=UTC` to match CI.

> 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 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
- [ ] 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 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-12 11:41:40 -07:00
Devin Foley ff5fd62d07
Resolve the environment secret companyId context on first save (#11291)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Environments give agent runs an execution target, and their env vars
can carry secrets, so writes need a company scope for secret bindings
> - `PATCH /environments/:id` resolves that scope from a query param,
existing bindings, or a single-membership actor — and fails closed
otherwise
> - A fresh environment has no bindings yet, so an admin whose
memberships cannot pin one company gets a 422 on the very first env var
save and can never create the first binding
> - #11200 made this reachable from the UI: it opened the envVars-only
edit on the managed sandbox row, but the UI never sends the company it
already knows
> - This pull request sends the page's selected company on environment
updates and adds a server fallback to the instance's only company
> - The benefit is that the first env var save works on fresh
environments, while multi-company ambiguity still fails closed

## Linked Issues or Issue Description

Refs #11200

**What happened?**

On a fresh platform-managed sandbox environment, the first "Save
environment variables" in the UI fails with HTTP 422: "Environment
secret management requires a companyId context during the
instance-scoped transition." The same failure hits any environment
update that touches `envVars` or `config` when the environment has no
secret bindings yet and the actor's memberships do not name exactly one
company (for example an instance admin provisioned without a membership
row, or a user in two companies).

**Expected behavior**

The save succeeds. The client tells the server which company scopes the
secret bindings, and on a single-company instance the server can resolve
the only possible scope by itself.

**Steps to reproduce**

1. Provision a platform-managed sandbox environment (managed-config
`environments` entry) so the row exists with no secret bindings.
2. Sign in as an instance admin whose memberships do not resolve to
exactly one company.
3. Open Environments, edit the managed row, add an env var, and save.
4. The PATCH returns 422 with the companyId-context error.

## What Changed

- `ui/src/api/environments.ts`: `environmentsApi.update` accepts an
optional `companyId` and sends it as the `companyId` query param the
server already reads. Renamed the `customImageCompanyQuery` helper to
`companyIdQuery` since it now serves plain updates and probes too.
- `ui/src/pages/CompanyEnvironments.tsx`: both the managed envVars-only
save and the general environment save pass `selectedCompanyId`.
- `server/src/routes/environments.ts`:
`resolveEnvironmentSecretContextCompanyId` falls back to the instance's
only company when exactly one exists, mirroring the existing fallback in
`resolveCustomImageCompanyId`. Multi-company instances still fail
closed.
- Tests: three new route tests (explicit query context for a
multi-company actor, single-company-instance fallback for a
membership-less admin, fail-closed regression on a multi-company
instance), and updated the SSH-probe and probe-ambiguity tests for the
new fallback.

## Verification

- `cd server && pnpm vitest run
src/__tests__/environment-routes.test.ts` — 78 pass.
- `cd ui && pnpm vitest run src/pages/CompanyEnvironments.test.tsx` — 22
pass.
- Full `server` and `ui` vitest suites and `tsc --noEmit` on both
packages pass locally.
- Manual: on a managed instance, edit the managed sandbox environment,
add an env var, save. Before: 422. After: the save persists and the
PATCH carries `?companyId=`.

## Risks

- Low risk. The explicit query param path already existed on the server;
the UI now uses it.
- Behavioral shift: on single-company instances, environment
secret-context resolution (including `POST /environments/:id/probe`) now
resolves the only company instead of returning no context. That lets
probes resolve secret-backed config where they previously returned a 422
asking for an explicit companyId. Multi-company instances keep the
fail-closed behavior, covered by a regression test.
- Self-hosted single-company instances gain the same first-save fix; no
schema or config changes.

## Model Used

Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — extended
thinking, agentic tool use.

## Checklist

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

> - Paperclip is the open source app people use to manage AI agents for
work
> - The UI registers a service worker (`ui/public/sw.js`) with a
network-first fetch handler whose cache is an offline fallback
> - The fallback hands `event.respondWith` the result of
`caches.match(...)`, which resolves `undefined` on a cache miss — and in
the navigation branch, `caches.match("/") || offlineResponse` never uses
the fallback because `caches.match` returns a promise, which is always
truthy
> - When the network fetch rejects (server restart, deploy, brief
outage) and the cache misses, the browser fails the request with
`Uncaught (in promise) TypeError: Failed to convert value to
'Response'`, so navigation breaks outright instead of degrading to the
offline page
> - This pull request awaits the cache lookups and guarantees a real
`Response` on every path
> - The benefit is that brief server unavailability degrades to the
offline fallback instead of a dead navigation

## Linked Issues or Issue Description

No existing issue. Description follows the bug template:

**What happened?**

Navigating while the server was briefly unavailable (mid-restart)
produced `The FetchEvent for "…" resulted in a network error response:
the promise was rejected.` and `sw.js:1 Uncaught (in promise) TypeError:
Failed to convert value to 'Response'.` The navigation failed instead of
showing the offline fallback.

**Expected behavior**

A failed navigation serves the cached app shell when present, otherwise
the "Offline" 503 response. A failed asset fetch serves its cache entry
when present, otherwise a proper network-error response. `respondWith`
always receives a real `Response`.

**Steps to reproduce**

1. Load the app so `sw.js` is active; ensure `/` is not in the service
worker cache (fresh cache version).
2. Restart or stop the backend.
3. Navigate to any page: the fetch rejects, `caches.match` misses, and
the browser logs the conversion TypeError with a failed navigation.

## What Changed

- `ui/public/sw.js`: the fetch fallback awaits `caches.match(...)` and
returns the "Offline" 503 for navigations and `Response.error()` for
assets when the cache misses.
- `ui/src/lib/sw-offline-fallback.test.ts`: evaluates the real `sw.js`
in a sandboxed scope and covers the three fallback paths; the two
miss-path tests fail against the previous code.

## Verification

- `pnpm vitest run src/lib/sw-offline-fallback.test.ts` in `ui/` — 3
tests pass.
- Verified both miss-path tests fail against the unmodified `sw.js`.

## Risks

Low risk. The change only affects the fetch-rejection path; successful
fetches and cache hits behave exactly as before. `Response.error()`
mirrors what the browser would produce for an unhandled failed no-cors
fetch.

## Model Used

- Claude (Anthropic), Claude Fable 5 (`claude-fable-5`), via Claude Code
CLI with tool use (code search, edit, 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
- [ ] 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 11:23:18 -07:00
Dotta 1a377424db
fix(ui): add mobile blocker actions (#11282)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators use task properties to inspect and change task
relationships.
> - A blocked-by chip linked directly to the blocking task.
> - Its remove control appeared only on hover, so touch users could not
reach it.
> - This pull request opens a small action menu when a user taps the
chip on mobile.
> - The menu lets the user visit the task or start the existing
blocker-removal confirmation.
> - The benefit is that touch users can manage blockers without changing
the fast desktop flow.

## Linked Issues or Issue Description

**What happened?**

On a phone-width layout, a tap on a blocked-by chip opened the blocking
task immediately. The remove control appeared only on hover, so a touch
user could not remove the blocker.

**Expected behavior**

A tap on a blocked-by chip on mobile opens a menu. The menu offers
`Visit task` and `Remove blocker` actions.

**Steps to reproduce**

1. Open a task that has a blocker.
2. Use a viewport below the mobile breakpoint.
3. Open the task properties.
4. Tap the blocked-by chip.

**Paperclip version or commit**

Reproduced on `e5a7fd7038` from `master`.

**Deployment mode**

Built from source with the local development workflow.

## What Changed

- Added a mobile-only action menu to blocked-by chips.
- Kept the direct task link and hover/focus remove control on desktop.
- Reused the existing removal confirmation before the relation update.
- Added focused regression coverage for the mobile visit and remove
choices.
- Added a phone-width Storybook state with the action menu open.

## Verification

- `pnpm --filter @paperclipai/ui exec vitest run
src/components/IssueProperties.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm check:token-gates`
- `pnpm build-storybook`
- Opened the new Storybook state in Playwright Chromium with a Pixel 5
viewport. Confirmed that both actions are visible and fit in the
viewport.

## Risks

- Low risk. The behavior change is limited to the existing mobile
breakpoint.
- Desktop navigation and blocker removal keep their current behavior.
- The menu uses the shared dropdown and dialog primitives.

> 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 `gpt-5.6-sol`, xhigh reasoning. The Codex CLI managed the
context window for this run. The model used repository tools, code
execution, tests, and browser automation.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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-12 12:12:55 -04:00
Nicky Leach e5a7fd7038
Add sandbox device-login for the Codex adapter (#11237)
## Thinking Path

> - Paperclip helps people manage AI agents for work.
> - Agent adapters connect Paperclip to tools such as the Codex command
line tool.
> - A sandboxed Codex agent may start without a credential.
> - The operator needs a safe sign-in flow that does not expose
credentials to the shared package or the sandbox.
> - This pull request adds a company-scoped device-login flow with a
temporary Daytona sandbox.
> - The flow promotes the credential only after readiness checks pass
and removes the temporary sandbox after use.
> - The result lets an operator sign in to a sandboxed Codex agent from
the agent form.

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting (multiple of the above)

**Problem or motivation**

A Codex adapter that runs in a sandbox cannot authenticate when the
company has no pre-provisioned Codex credential.

**Proposed solution**

Add a company-scoped device-login session. Start a temporary sandbox,
run `codex login --device-auth`, stream the code and URL, verify
readiness, promote the credential, and delete the sandbox.

**Alternatives considered**

Pre-provisioning a credential does not support first-time sandbox login.
Keeping the credential in the login sandbox does not provide a durable
company credential.

**Roadmap alignment**

This supports the roadmap item for cloud and sandbox agents.

**Additional context**

The flow uses a five-minute cleanup reaper, compare-and-set status
changes, and a PostgreSQL advisory lock to protect promotion and
cleanup.

## What Changed

- Add the adapter login-session contract, database table, and migration.
- Add company-scoped server routes and a service for sandbox device
login.
- Add credential promotion, readiness checks, and cleanup after login.
- Add restart-safe cleanup for abandoned login sandboxes.
- Add sandbox login controls to the agent creation and edit forms.
- Keep device-login and vendor identifiers out of public shared and
adapter UI symbols.

## Verification

- `pnpm --filter @paperclipai/adapter-codex-local exec vitest run`
passed with 310 tests at the submitted commit.
- The server login route, service, and reaper tests passed with 45 tests
at the submitted commit.
- The agent form render tests passed with 26 tests at the submitted
commit.
- The public-symbol leak check passed at the submitted commit.
- A live Daytona sign-in flow still requires confirmation by a user with
a live sandbox.

## Risks

The migration adds a new company-scoped table. A promotion or cleanup
race could remove a credential or leave a sandbox active, so the service
uses claims, compare-and-set transitions, and an advisory lock. The live
Daytona flow needs operator confirmation because local tests do not
provide a real browser sign-in.

## Model Used

OpenAI Codex, GPT-5, tool use and code execution, extended reasoning.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` /
`Closes: #` / `Refs: #` OR (b) described the issue in-PR following the
relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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-12 08:58:25 -07:00