Commit Graph
5194 Commits
Author SHA1 Message Date
DottaandPaperclip 3ab022d7df test: align merged directory and continuation fixtures
Remove a duplicate service import, register the warm remote fixture leases required by the cleanup ownership guard, and assert the server-owned bounded continuation for an unauthorized unfinished response wait. Keep runtime implementation and qualified inputs unchanged.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-01 14:48:54 -05:00
DottaandPaperclip 9e0ede4cbe test(runner-e2e): retain Pi steering presentation evidence
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-01 11:21:03 -05:00
DottaandPaperclip b97f3ce7c7 test(runner): preserve idempotent receiver result retries
Verify identical tool-result retries emit one sidecar resolution while changed payloads, operations, and error status remain rejected.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-01 11:20:58 -05:00
Dotta e12b25f2b4 test(runner-e2e): qualify Pi active Stop and steering 2026-10-01 10:44:04 -05:00
Dotta f6406e7e55 Merge frozen master into the rich ACP production candidate
Preserve incremental Codex checkpoints and ACP unchanged-directory ownership as separate warm-session paths. Combine cancellation commit fencing, terminal outcome recovery, and both native fixture catalogs. Keep all candidate qualification states and provider profile identities unchanged.

Validation: 629 controller unit checks, 5 heartbeat cancellation checks, 210 runner/profile/sidecar checks, 44 catalog checks; token gates, Rust source formatting, and generated protocol manifest pass. Database tests and builds intentionally deferred. Source-aliased no-emit checking is blocked only by the borrowed ACPX SessionRecord declaration lacking the already-patched cursor_prompt_usage field.
2026-10-01 10:41:38 -05:00
DottaandPaperclip 4ac374103f fix(connections): repair Asana MCP and add shared-app sign-in (#14756)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Connections let agents use provider tools through the permission
gateway.
> - Asana provides an official remote MCP server, but its v2 server
requires a registered MCP OAuth app.
> - Setup can discover retired v1 endpoints and send a callback that
differs from the displayed URL.
> - This pull request repairs custom app setup and adds sign-in through
Paperclip's shared app.
> - Users can choose their own app without enrolling with Paperclip
Cloud.
> - Agents can use Asana tools after the user connects their account and
sets action permissions.

## Linked Issues or Issue Description

Related: #14739 supplies the personal credential repair used by resumed
Asana setup. No duplicate Asana authentication PR was found.

**What happened?**

Asana setup failed even with a user-created app. Root discovery metadata
still points at v1. MCP v2 uses the Asana OAuth issuer and requires an
MCP app with a client secret. Local setup also displayed a localhost
callback while an Origin header could make authorization use a numeric
loopback callback.

**Expected behavior**

Sign in with Paperclip's app when its broker profile is available. Keep
custom MCP app setup available without Cloud enrollment. Use the correct
issuer, callback, client credentials, and resource throughout setup.

**Steps to reproduce**

1. Open Asana in the connection catalog.
2. Supply an Asana MCP app's client ID and secret.
3. Start OAuth on a local instance opened with a numeric loopback
address, or resume a draft that cached v1 metadata.
4. Observe the wrong discovery endpoint or callback mismatch.

**Paperclip version or commit**

Reproduced from b54b2dc35c. Rebased onto
master at `0829d94af` after the single-screen setup change in #14811.

**Deployment mode**

Local development from source. The managed path also supports enrolled
self-hosted instances.

## What Changed

- Add the `asana.mcp` managed profile and the default Sign in with Asana
method.
- Use Asana's reviewed v2 protected-resource metadata before cached
endpoints.
- Require a custom MCP app's client secret and retain saved credentials
during setup or reconnect. Repair only the known Asana v1
issuer/resource binding, retaining company and callback checks.
- Expose a boolean for the acting user's saved client secret. The form
offers secret reuse only when that user has an active grant with the
required reference.
- Let users select their own app from the enrollment and
shared-app-unavailable screens, or from Advanced on the single-screen
setup page.
- Canonicalize HTTP loopback callbacks even when the request includes an
Origin header.
- Extend signed broker claims and provider URL validation for Asana.
Require refresh credentials on managed authorization.
- Document setup, distribution, and shared-app rollout requirements.
- Resolve permission-profile name collisions when finishing another
account. The live staging test found this after renaming the first Asana
connection; OAuth succeeded but profile finalization failed.

## Verification

- Final live staging proof used app commit
`c6053157c4e42ac017727117b754ae77fa5c45fa` and the real Cloud broker at
`767b63835170f664542afd0df99a76615e204b62`. In the embedded browser,
default shared sign-in required no client credentials, returned through
the central Cloud callback to the tenant, and discovered 39 actions. Get
me succeeded through the gateway as the selected QA agent (2.1 seconds).
The custom-app connection also returned a real result on this final
build (0.9 seconds).
- Retried the shared draft that failed during the first staging test. It
completed after the profile-name fix, retained the selected agent, and
kept the existing custom connection intact. Two database regressions
reproduced the collision before the fix and passed afterward. The
updated transaction rollback test also passes.
- Shared reconnect returned to the same staging connection with 39
actions. Earlier staging checks verified the custom-app fallback when
the shared profile was unavailable, saved-secret reuse on reconnect, and
Off blocking the action test. Allowed was restored after that check.
- Local live-provider checks also repaired a saved Asana v1
issuer/resource binding without reentering the secret and verified that
numeric-loopback setup uses the displayed localhost callback. Expiring
the local managed access-token timestamp triggered a real Asana refresh
and a successful Get me call. These early local broker tests used
enrollment/authentication and storage fixtures; the final staging proof
used deployed Cloud identity and persistent storage.
- The new production app is registered and configured, but production
sign-in has not been deployed or verified. Live provider revocation was
not run because the existing staging test app is shared with other
connections.

- After rebasing onto the single-screen setup flow, full `pnpm -r
typecheck`, `pnpm build`, and `pnpm check:token-gates` pass. Focused
verification passes 365 service and 36 broker-client tests. Broader
checks pass all 833 shared-package tests and all 530 connector-page
tests. The shared suite uses `TMPDIR=/private/tmp` to avoid macOS
temporary-directory symlinks in its canonical-path tests. The UI tests
verify the shared-app default and switching to a custom app with its
required client secret.
- Embedded-browser smoke on the current rebased build verified the
shared sign-in default, Advanced → custom app (client ID and secret
required), and switching back to Paperclip. Both existing Asana
connections remained connected after restart. No new provider
authorization was performed during this smoke.
- The full local `pnpm test:run` was interrupted when the execution
session restarted. Before interruption, it reported one runtime-slot
restart test failure. That test passed on an isolated retry after
clearing two unused PostgreSQL shared-memory segments. The full local
suite did not complete; CI must pass on the current head before merge.
- All 52 CI and security checks pass on
`9318fd4e9b616cdc3de12f40cdb9bd32d865af4c` (CI run `36882064080`),
including all eight browser shards, nine serialized-server shards,
build, typecheck, and canary dry run. Two optional Storybook checks were
skipped. Greptile review 4 reports 5/5 on this exact commit, with all
review threads resolved. Its updated summary identifies the current SHA;
this comment-triggered review did not publish a separate GitHub check
run.
- Provider revocation is unit-tested in the companion broker. Live
provider revocation was not run because the existing test app is shared
with other connections.

## Risks

- The shared Paperclip Asana MCP app has been registered with its
production callback and Any workspace distribution. Its secret is
provisioned in the production secret store, and the runtime client ID
and secret reference are configured. The production profile is enabled
in the saved deployment configuration. The companion broker has merged
and passed staging deployment; production sign-in still requires a
production deployment and live verification. Custom setup remains
available.
- Asana MCP uses the provider's fixed `default` grant. Paperclip action
policies limit agent tool use; they do not narrow provider consent.
- The callback correction affects HTTP loopback OAuth flows. Public
HTTPS callbacks retain their existing behavior.
- Reviewed discovery URLs now override stale cached endpoints. Tests
cover the Asana v1-to-v2 repair.
- No schema migration. Connection removal retains the existing
local-only revocation behavior.

## Model Used

OpenAI GPT-6 through Codex, with code execution, browser testing, and
GitHub tooling. The exact model variant and context window are not
exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-01 10:24:44 -05:00
dependabot[bot] 8458c31915 chore(deps): bump @assistant-ui/react from 0.15.16 to 0.15.21 (#13477)
Bumps
[@assistant-ui/react](https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react)
from 0.15.16 to 0.15.21.
<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.21</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/7692">#7692</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/dbb1496e4bc28c8e0fd8d25aeba2dfd079c69116"><code>dbb1496</code></a>
- fix: resume bottom following when auto-scroll is enabled at runtime
(<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/7370">#7370</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/b7f9a960dda7c7548ac1ebdf3bae368fe28bcbfc"><code>b7f9a96</code></a>
- chore: update dependencies (<a
href="https://github.com/Yonom"><code>@​Yonom</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/7518">#7518</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/d99c80e437ce7b480501e3f74a90d391b26f62a3"><code>d99c80e</code></a>
- fix: cancel the selection toolbar's pending animation frame on
teardown and between selection events (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="https://github.com/assistant-ui/assistant-ui/commit/43b587d9bc15adf624437950c270e50b749602d0"><code>43b587d</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/5428610760ed57e90577fddd459ca9f86adc397b"><code>5428610</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/562e495139605d5279e9bd39abc223ef52b79a94"><code>562e495</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/ddb720192d22a7b97572318eb527e5e55d62c413"><code>ddb7201</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/69c3d0f171e5bb61fd3d45db093cf69ba224eb5e"><code>69c3d0f</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/c046153b0cd5e0e6f9c3e894722b707efc559ffc"><code>c046153</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/4788b61eb9f6e9b8481e3b85348a95ea8ad7c4ba"><code>4788b61</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/99c9988951b5c469b2706bc3c85116a65660836a"><code>99c9988</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/37a5a955d4d51a1b7013232a358e5e7461879d28"><code>37a5a95</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/2caa1cebe9ef7db666496e6d109813caee708ee4"><code>2caa1ce</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/bd77c46263d295d3fca6a57de37b44614189d689"><code>bd77c46</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e8cf372a3ba08f937149dc924e807228324b45f7"><code>e8cf372</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/4e08ba680a4adb66fb39043d93f46377be0f861a"><code>4e08ba6</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/b7f9a960dda7c7548ac1ebdf3bae368fe28bcbfc"><code>b7f9a96</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/50d65c04a37255111206d038b9dfb34d3e0ba6e4"><code>50d65c0</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/11969a219201f49eb42a76d05e9f3cc787c5f025"><code>11969a2</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/408d5f43a69baa9df723b395eaafba7a501f8884"><code>408d5f4</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/bc842502b68a0dcc4c3728e6f6ea542e5a9bcbc5"><code>bc84250</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f513bc7cbdede455e81652004b8142c05e323353"><code>f513bc7</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/b712ee83bde9a89fce2812968f951a5742d757b9"><code>b712ee8</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e02bf06e88c76e21ba3f303559d65269010c0269"><code>e02bf06</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/5c5522271e67eade40482a555c836b9bf7301429"><code>5c55222</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/479d6a3a363bcf9362421e44a834865c0c152808"><code>479d6a3</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/44248e03036ffd89c3a278041f8715dbc3f1b587"><code>44248e0</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/70b633f378deff6c693f2720ceb9cbb5b8677d8c"><code>70b633f</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.20</li>
<li>assistant-stream@0.3.44</li>
<li>assistant-cloud@0.2.2</li>
<li>safe-content-frame@0.0.31</li>
<li><code>@​assistant-ui/store</code><a
href="https://github.com/0"><code>@​0</code></a>.3.14</li>
<li><code>@​assistant-ui/tap</code><a
href="https://github.com/0"><code>@​0</code></a>.9.18</li>
</ul>
</li>
</ul>
<h2><code>@​assistant-ui/react</code><a
href="https://github.com/0"><code>@​0</code></a>.15.20</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/7414">#7414</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/00cff0bc9c475e2acc6cb258b5f96c28eb3292a2"><code>00cff0b</code></a>
- feat: add an opt-in cache to <code>convertExternalMessages</code> so a
source message that has not changed keeps its <code>ThreadMessage</code>
object across calls (<code>createExternalMessageConversionCache</code>,
exported as <code>unstable_createExternalMessageConversionCache</code>
from react and react-native); react-langchain uses it for subagent
transcripts, so a streamed token no longer rebuilds every message of the
nested transcript (<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/7185">#7185</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/2c22f5d7fdeb45f10891a0ab2457d046ace668fa"><code>2c22f5d</code></a>
- fix: settle pending frame tool calls when cancellation fails (<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/7359">#7359</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/75b3bd36e0d580a32d35e15a54ef2c42fb6ba61a"><code>75b3bd3</code></a>
- feat: derive thread.tasks from tool calls that carry nested
conversations, with a task scope (<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/7213">#7213</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/cd42d1caab1f910841741b738cc2ca8691f71b9c"><code>cd42d1c</code></a>
- fix: keep notifying thread viewport scroll listeners after a listener
fails (<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/7351">#7351</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f6b7ba98cbd88154e5e7b8e51de5729d2b950c4e"><code>f6b7ba9</code></a>
- feat: mark voice transcript messages with metadata.modality (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="https://github.com/assistant-ui/assistant-ui/commit/628df887b9cfc9e0381cf53139a32dd0e75bbc67"><code>628df88</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/ed77e956811a161243e6c9faf13320846db30a8a"><code>ed77e95</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/00cff0bc9c475e2acc6cb258b5f96c28eb3292a2"><code>00cff0b</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/b82150660dcf2ca6902b3b987c34440a2ff0af46"><code>b821506</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/79c221977f9c2fe9da4374bd45132ed28d02cae4"><code>79c2219</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f0bbcecdee210c5d73ca4ec39ce31abd5c139cf3"><code>f0bbcec</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/c41d93a84231a54256e0e1fe6f64951603a039d7"><code>c41d93a</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/4cc817d4cb0d49c1704352845731b82f8591b623"><code>4cc817d</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/46193357fb581d8440c40b6e2fbf3e79560d6870"><code>4619335</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/4e5fde6c2d09909c5b286fee098c9950615a26d3"><code>4e5fde6</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e54bf9aacd73a2431e3194aaced86dfdff67ed2d"><code>e54bf9a</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e54bf9aacd73a2431e3194aaced86dfdff67ed2d"><code>e54bf9a</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/c083236df07705cae1109e4342c08f6c8bcd7c3b"><code>c083236</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/3cd77005ede1d8e3a30345991e6567e28bd8e75f"><code>3cd7700</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/617bbf9277dac2bda46e3cf526c54dd64cbccc79"><code>617bbf9</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/5fb3235b68a96dc02695aeb28f7d5720ec893302"><code>5fb3235</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/83ede73e8d3f0eadcb3969fae62d41fb7f253f1b"><code>83ede73</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/49ee689f7ec5540aabc38f293090901f246978a9"><code>49ee689</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/49ee689f7ec5540aabc38f293090901f246978a9"><code>49ee689</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/a8e0ff741611714aa56b9917439b4f603715723f"><code>a8e0ff7</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/050d915daa2caf4d791f7254a8e42d31fc59e6da"><code>050d915</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/d49ff906869981c4d2f3f2443089bf0b2f4a3c42"><code>d49ff90</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e7bdef5df7201663f2d5c269d035ab3643cacde7"><code>e7bdef5</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/69cbf47bfa24d1d588cdb7461c42b2f9887075e3"><code>69cbf47</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/37bd6763d2e2a1799f8f52ae62afc9bf026f6984"><code>37bd676</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/be818db8e1cc97c3398948e5b4ae5ae8e70c672f"><code>be818db</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/ff1c692d67eda7e07878b2a8d7cc2bf1b3d90476"><code>ff1c692</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/b4b00813ef30a37c36df3fd8acf3be0a5cbd498c"><code>b4b0081</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/6b29e7de829bef7e51297d3d66cd9e97175f3fc5"><code>6b29e7d</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/de6d6b7667ca5c778409fc9a81b8d2b6ca07ca48"><code>de6d6b7</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f6f52cde1814bac7bca41c5da163bb50021921ff"><code>f6f52cd</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/ab97a410a4f67e097ddcb186e12fd4a637790876"><code>ab97a41</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/790217b85d9130116ac1a06c46a7902a2552ed07"><code>790217b</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/8530b17b8aee50413c5cbbe628832ad039a6d584"><code>8530b17</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/063b9ec8c92098c51b6924d49b1a6c3cc80eec45"><code>063b9ec</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/063b9ec8c92098c51b6924d49b1a6c3cc80eec45"><code>063b9ec</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/2c22f5d7fdeb45f10891a0ab2457d046ace668fa"><code>2c22f5d</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/d7aa090819e7f36c04e9fe5ecdc60651b52c83a5"><code>d7aa090</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/dacfcecf633340250e38f6055eeea3c9e01a978d"><code>dacfcec</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/7af910126ecf03c68a9870917363f264c3ac14fa"><code>7af9101</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/63ae5b8917898f7403bee81b439f2dc9c574976d"><code>63ae5b8</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/97bd4b39fce83163354c9ec8d9d4fb2c9bd1aac7"><code>97bd4b3</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/c0d615046cbbbdfee1a183428f51e9c547564a8c"><code>c0d6150</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/275eeb91d0be1d43e98b7b48a53a9609e87419c4"><code>275eeb9</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e57c33f956b28d1c504ea6a1eadbc9e3092e4931"><code>e57c33f</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e63d2e440239a8b9add59c74cfa2044567f0b362"><code>e63d2e4</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/75b3bd36e0d580a32d35e15a54ef2c42fb6ba61a"><code>75b3bd3</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f03be0ced78dc2b60b7c698919592005a0ce7532"><code>f03be0c</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/06bdf1f9e4d8796ff12b91379a4175625f3a75e8"><code>06bdf1f</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e533f6b2790d4f8ffcc75c256d3753c95f801a9e"><code>e533f6b</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/28691a5ef6f7f0a333fa910220f29b0b2a0fae8c"><code>28691a5</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/3cfddfdc9282a87186a3a26b74558d6cf8cdc204"><code>3cfddfd</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f6b7ba98cbd88154e5e7b8e51de5729d2b950c4e"><code>f6b7ba9</code></a>]:</p>
<ul>
<li>assistant-stream@0.3.43</li>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.19</li>
<li>assistant-cloud@0.2.1</li>
</ul>
</li>
</ul>
</blockquote>
</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.21</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/7692">#7692</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/dbb1496e4bc28c8e0fd8d25aeba2dfd079c69116"><code>dbb1496</code></a>
- fix: resume bottom following when auto-scroll is enabled at runtime
(<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/7370">#7370</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/b7f9a960dda7c7548ac1ebdf3bae368fe28bcbfc"><code>b7f9a96</code></a>
- chore: update dependencies (<a
href="https://github.com/Yonom"><code>@​Yonom</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/7518">#7518</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/d99c80e437ce7b480501e3f74a90d391b26f62a3"><code>d99c80e</code></a>
- fix: cancel the selection toolbar's pending animation frame on
teardown and between selection events (<a
href="https://github.com/Kinfe123"><code>@​Kinfe123</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="https://github.com/assistant-ui/assistant-ui/commit/43b587d9bc15adf624437950c270e50b749602d0"><code>43b587d</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/5428610760ed57e90577fddd459ca9f86adc397b"><code>5428610</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/562e495139605d5279e9bd39abc223ef52b79a94"><code>562e495</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/ddb720192d22a7b97572318eb527e5e55d62c413"><code>ddb7201</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/69c3d0f171e5bb61fd3d45db093cf69ba224eb5e"><code>69c3d0f</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/c046153b0cd5e0e6f9c3e894722b707efc559ffc"><code>c046153</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/4788b61eb9f6e9b8481e3b85348a95ea8ad7c4ba"><code>4788b61</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/99c9988951b5c469b2706bc3c85116a65660836a"><code>99c9988</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/37a5a955d4d51a1b7013232a358e5e7461879d28"><code>37a5a95</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/2caa1cebe9ef7db666496e6d109813caee708ee4"><code>2caa1ce</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/bd77c46263d295d3fca6a57de37b44614189d689"><code>bd77c46</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e8cf372a3ba08f937149dc924e807228324b45f7"><code>e8cf372</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/4e08ba680a4adb66fb39043d93f46377be0f861a"><code>4e08ba6</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/b7f9a960dda7c7548ac1ebdf3bae368fe28bcbfc"><code>b7f9a96</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/50d65c04a37255111206d038b9dfb34d3e0ba6e4"><code>50d65c0</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/11969a219201f49eb42a76d05e9f3cc787c5f025"><code>11969a2</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/408d5f43a69baa9df723b395eaafba7a501f8884"><code>408d5f4</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/bc842502b68a0dcc4c3728e6f6ea542e5a9bcbc5"><code>bc84250</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f513bc7cbdede455e81652004b8142c05e323353"><code>f513bc7</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/b712ee83bde9a89fce2812968f951a5742d757b9"><code>b712ee8</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e02bf06e88c76e21ba3f303559d65269010c0269"><code>e02bf06</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/5c5522271e67eade40482a555c836b9bf7301429"><code>5c55222</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/479d6a3a363bcf9362421e44a834865c0c152808"><code>479d6a3</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/44248e03036ffd89c3a278041f8715dbc3f1b587"><code>44248e0</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/70b633f378deff6c693f2720ceb9cbb5b8677d8c"><code>70b633f</code></a>]:</p>
<ul>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.20</li>
<li>assistant-stream@0.3.44</li>
<li>assistant-cloud@0.2.2</li>
<li>safe-content-frame@0.0.31</li>
<li><code>@​assistant-ui/store</code><a
href="https://github.com/0"><code>@​0</code></a>.3.14</li>
<li><code>@​assistant-ui/tap</code><a
href="https://github.com/0"><code>@​0</code></a>.9.18</li>
</ul>
</li>
</ul>
<h2>0.15.20</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/7414">#7414</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/00cff0bc9c475e2acc6cb258b5f96c28eb3292a2"><code>00cff0b</code></a>
- feat: add an opt-in cache to <code>convertExternalMessages</code> so a
source message that has not changed keeps its <code>ThreadMessage</code>
object across calls (<code>createExternalMessageConversionCache</code>,
exported as <code>unstable_createExternalMessageConversionCache</code>
from react and react-native); react-langchain uses it for subagent
transcripts, so a streamed token no longer rebuilds every message of the
nested transcript (<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/7185">#7185</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/2c22f5d7fdeb45f10891a0ab2457d046ace668fa"><code>2c22f5d</code></a>
- fix: settle pending frame tool calls when cancellation fails (<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/7359">#7359</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/75b3bd36e0d580a32d35e15a54ef2c42fb6ba61a"><code>75b3bd3</code></a>
- feat: derive thread.tasks from tool calls that carry nested
conversations, with a task scope (<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/7213">#7213</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/cd42d1caab1f910841741b738cc2ca8691f71b9c"><code>cd42d1c</code></a>
- fix: keep notifying thread viewport scroll listeners after a listener
fails (<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/7351">#7351</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f6b7ba98cbd88154e5e7b8e51de5729d2b950c4e"><code>f6b7ba9</code></a>
- feat: mark voice transcript messages with metadata.modality (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="https://github.com/assistant-ui/assistant-ui/commit/628df887b9cfc9e0381cf53139a32dd0e75bbc67"><code>628df88</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/ed77e956811a161243e6c9faf13320846db30a8a"><code>ed77e95</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/00cff0bc9c475e2acc6cb258b5f96c28eb3292a2"><code>00cff0b</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/b82150660dcf2ca6902b3b987c34440a2ff0af46"><code>b821506</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/79c221977f9c2fe9da4374bd45132ed28d02cae4"><code>79c2219</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f0bbcecdee210c5d73ca4ec39ce31abd5c139cf3"><code>f0bbcec</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/c41d93a84231a54256e0e1fe6f64951603a039d7"><code>c41d93a</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/4cc817d4cb0d49c1704352845731b82f8591b623"><code>4cc817d</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/46193357fb581d8440c40b6e2fbf3e79560d6870"><code>4619335</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/4e5fde6c2d09909c5b286fee098c9950615a26d3"><code>4e5fde6</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e54bf9aacd73a2431e3194aaced86dfdff67ed2d"><code>e54bf9a</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e54bf9aacd73a2431e3194aaced86dfdff67ed2d"><code>e54bf9a</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/c083236df07705cae1109e4342c08f6c8bcd7c3b"><code>c083236</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/3cd77005ede1d8e3a30345991e6567e28bd8e75f"><code>3cd7700</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/617bbf9277dac2bda46e3cf526c54dd64cbccc79"><code>617bbf9</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/5fb3235b68a96dc02695aeb28f7d5720ec893302"><code>5fb3235</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/83ede73e8d3f0eadcb3969fae62d41fb7f253f1b"><code>83ede73</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/49ee689f7ec5540aabc38f293090901f246978a9"><code>49ee689</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/49ee689f7ec5540aabc38f293090901f246978a9"><code>49ee689</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/a8e0ff741611714aa56b9917439b4f603715723f"><code>a8e0ff7</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/050d915daa2caf4d791f7254a8e42d31fc59e6da"><code>050d915</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/d49ff906869981c4d2f3f2443089bf0b2f4a3c42"><code>d49ff90</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e7bdef5df7201663f2d5c269d035ab3643cacde7"><code>e7bdef5</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/69cbf47bfa24d1d588cdb7461c42b2f9887075e3"><code>69cbf47</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/37bd6763d2e2a1799f8f52ae62afc9bf026f6984"><code>37bd676</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/be818db8e1cc97c3398948e5b4ae5ae8e70c672f"><code>be818db</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/ff1c692d67eda7e07878b2a8d7cc2bf1b3d90476"><code>ff1c692</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/b4b00813ef30a37c36df3fd8acf3be0a5cbd498c"><code>b4b0081</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/6b29e7de829bef7e51297d3d66cd9e97175f3fc5"><code>6b29e7d</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/de6d6b7667ca5c778409fc9a81b8d2b6ca07ca48"><code>de6d6b7</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f6f52cde1814bac7bca41c5da163bb50021921ff"><code>f6f52cd</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/ab97a410a4f67e097ddcb186e12fd4a637790876"><code>ab97a41</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/790217b85d9130116ac1a06c46a7902a2552ed07"><code>790217b</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/8530b17b8aee50413c5cbbe628832ad039a6d584"><code>8530b17</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/063b9ec8c92098c51b6924d49b1a6c3cc80eec45"><code>063b9ec</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/063b9ec8c92098c51b6924d49b1a6c3cc80eec45"><code>063b9ec</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/2c22f5d7fdeb45f10891a0ab2457d046ace668fa"><code>2c22f5d</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/d7aa090819e7f36c04e9fe5ecdc60651b52c83a5"><code>d7aa090</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/dacfcecf633340250e38f6055eeea3c9e01a978d"><code>dacfcec</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/7af910126ecf03c68a9870917363f264c3ac14fa"><code>7af9101</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/63ae5b8917898f7403bee81b439f2dc9c574976d"><code>63ae5b8</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/97bd4b39fce83163354c9ec8d9d4fb2c9bd1aac7"><code>97bd4b3</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/c0d615046cbbbdfee1a183428f51e9c547564a8c"><code>c0d6150</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/275eeb91d0be1d43e98b7b48a53a9609e87419c4"><code>275eeb9</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e57c33f956b28d1c504ea6a1eadbc9e3092e4931"><code>e57c33f</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e63d2e440239a8b9add59c74cfa2044567f0b362"><code>e63d2e4</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/75b3bd36e0d580a32d35e15a54ef2c42fb6ba61a"><code>75b3bd3</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f03be0ced78dc2b60b7c698919592005a0ce7532"><code>f03be0c</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/06bdf1f9e4d8796ff12b91379a4175625f3a75e8"><code>06bdf1f</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/e533f6b2790d4f8ffcc75c256d3753c95f801a9e"><code>e533f6b</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/28691a5ef6f7f0a333fa910220f29b0b2a0fae8c"><code>28691a5</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/3cfddfdc9282a87186a3a26b74558d6cf8cdc204"><code>3cfddfd</code></a>,
<a
href="https://github.com/assistant-ui/assistant-ui/commit/f6b7ba98cbd88154e5e7b8e51de5729d2b950c4e"><code>f6b7ba9</code></a>]:</p>
<ul>
<li>assistant-stream@0.3.43</li>
<li><code>@​assistant-ui/core</code><a
href="https://github.com/0"><code>@​0</code></a>.3.19</li>
<li>assistant-cloud@0.2.1</li>
</ul>
</li>
</ul>
<h2>0.15.19</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/6971">#6971</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/9247ae3fe1c05181f6975bc2fdbd74491eed494d"><code>9247ae3</code></a>
- fix: honor message part text render elements with an explicit
component. (<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/6738">#6738</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/3c17a5f08cacc55a5eaa5c6eb7016664847a80a6"><code>3c17a5f</code></a>
- fix: prevent queued live completion requests from starting after
unmount (<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/7115">#7115</a>
<a
href="https://github.com/assistant-ui/assistant-ui/commit/4767a923d818cc2a4a7b0e50ce12d2abbe83bccb"><code>4767a92</code></a>
- feat: align the cloud SDK with Assistant Cloud 0.2 (<a
href="https://github.com/okisdev"><code>@​okisdev</code></a>)</p>
<!-- raw HTML omitted -->
<ul>
<li>run reports now carry <code>provider</code>,
<code>outcome_type</code> (<code>aborted</code>,
<code>disconnected</code>, <code>length</code>,
<code>content_filter</code>), <code>error_code</code> and
<code>error</code>, <code>message_id</code>,
<code>first_token_ms</code>, <code>duration_ms</code>, a
<code>finish_reason</code> per step from <code>useCloudChat</code> and
<code>trace_id</code>, plus <code>environment</code>,
<code>release</code> and <code>tags</code> from the
<code>telemetry</code> config; one <code>createRunReport</code> builder
in <code>assistant-cloud</code> assembles the body for the assistant-ui
runtime and for <code>@assistant-ui/cloud-ai-sdk</code>, and
<code>provider_type</code> and <code>metadata</code> stay on the wire
for older self hosted clouds</li>
<li><code>assistant-cloud/telemetry</code> (server side):
<code>createAssistantCloudTraceExporter</code>,
<code>createAssistantCloudSpanProcessor</code>,
<code>assistantCloudTraceMetadata</code> and
<code>withAssistantCloudTraceMetadata</code> send AI SDK GenAI spans to
<code>POST /v1/traces</code> and hand the trace id to the browser, so a
client report and its server spans merge into one run; the OpenTelemetry
packages are optional peers of the subpath only</li>
<li>engagement events: sends, edits, stops, regenerates, copies, branch
switches, suggestions, attachments, thread switches, speech, voice and
shown errors are batched to <code>POST /v1/events</code> without any
message content; <code>telemetry.events: false</code> opts out</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/assistant-ui/assistant-ui/commit/f6bcca7cd0c901ae5a0a58a9ad3ce75fcf07bb09"><code>f6bcca7</code></a>
chore: update versions (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/7471">#7471</a>)</li>
<li><a
href="https://github.com/assistant-ui/assistant-ui/commit/dbb1496e4bc28c8e0fd8d25aeba2dfd079c69116"><code>dbb1496</code></a>
fix(react): resume dynamically enabled auto-scroll (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/7692">#7692</a>)</li>
<li><a
href="https://github.com/assistant-ui/assistant-ui/commit/b7f9a960dda7c7548ac1ebdf3bae368fe28bcbfc"><code>b7f9a96</code></a>
chore: update dependencies (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/7370">#7370</a>)</li>
<li><a
href="https://github.com/assistant-ui/assistant-ui/commit/9163e3d6adca8e194c93f267be2f0d89ed9cdaa2"><code>9163e3d</code></a>
ci: typecheck changed workspaces with a turbo typecheck task (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/7562">#7562</a>)</li>
<li><a
href="https://github.com/assistant-ui/assistant-ui/commit/108c6520567cb0f59b5cd5f1e961a2daf94a3874"><code>108c652</code></a>
test(react): make the test files typecheck (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/7550">#7550</a>)</li>
<li><a
href="https://github.com/assistant-ui/assistant-ui/commit/d99c80e437ce7b480501e3f74a90d391b26f62a3"><code>d99c80e</code></a>
fix(react): cancel the selection toolbar's pending animation frame (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/7518">#7518</a>)</li>
<li><a
href="https://github.com/assistant-ui/assistant-ui/commit/6cd9226241d7e3f1a89e93f3160baa4f63157f6f"><code>6cd9226</code></a>
chore: update versions (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/7192">#7192</a>)</li>
<li><a
href="https://github.com/assistant-ui/assistant-ui/commit/00cff0bc9c475e2acc6cb258b5f96c28eb3292a2"><code>00cff0b</code></a>
feat: add a conversion cache so nested transcripts keep message identity
(<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/7414">#7414</a>)</li>
<li><a
href="https://github.com/assistant-ui/assistant-ui/commit/75b3bd36e0d580a32d35e15a54ef2c42fb6ba61a"><code>75b3bd3</code></a>
feat(core): derive thread.tasks from tool calls that carry nested
conversatio...</li>
<li><a
href="https://github.com/assistant-ui/assistant-ui/commit/f6b7ba98cbd88154e5e7b8e51de5729d2b950c4e"><code>f6b7ba9</code></a>
feat(core): mark voice transcript messages with metadata.modality (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/7351">#7351</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/assistant-ui/assistant-ui/commits/@assistant-ui/react@0.15.21/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-10-01 08:20:39 -07:00
dependabot[bot]andPriya Raman 8b4aa06920 build(deps): bump multer from 2.2.0 to 2.4.0 (#14493)
Bumps [multer](https://github.com/expressjs/multer) from 2.2.0 to 2.4.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/expressjs/multer/releases">multer's
releases</a>.</em></p>
<blockquote>
<h2>v2.4.0</h2>
<h2>Highlights</h2>
<p><strong>multer finally supports Google Cloud Functions and Firebase
🎉</strong></p>
<p>These platforms read the request body before your code runs, so
multer's classic <code>req.pipe(busboy)</code> received nothing: empty
<code>req.body</code>, empty <code>req.files</code>, and nearly a decade
of duplicated issues.</p>
<p>The new <code>streamHandler</code> option closes that gap: you decide
how the body reaches the parser, so the pre-read <code>rawBody</code>
just works (see image).</p>
<pre lang="js"><code>const multer = require('multer')
<p>const upload = multer({<br />
storage: multer.memoryStorage(),<br />
streamHandler: (req, busboy) =&gt; {<br />
// Cloud Functions / Firebase expose the pre-read body here<br />
if (req.rawBody) busboy.end(req.rawBody)<br />
else req.pipe(busboy)<br />
}<br />
})</p>
<p>app.post('/upload', upload.single('file'), (req, res) =&gt; {<br />
res.json({ name: req.file.originalname, size: req.file.size })<br />
})<br />
</code></pre></p>
<p>This landed thanks to community PRs going back to 2017; their authors
are credited as co-authors in the release.</p>
<h2>Important: Security</h2>
<ul>
<li>Fix <a
href="https://www.cve.org/CVERecord?id=CVE-2026-88932">CVE-2026-88932</a>
(<a
href="https://github.com/expressjs/multer/security/advisories/GHSA-3pph-fpjx-jg34">GHSA-3pph-fpjx-jg34</a>)</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>docs: remove README translations by <a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1463">expressjs/multer#1463</a></li>
<li>ci: add macOS to the test matrix by <a
href="https://github.com/kilisamemarisaaa"><code>@​kilisamemarisaaa</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1464">expressjs/multer#1464</a></li>
<li>feat. improve wording for LIMIT_UNEXPECTED_FILE error code by <a
href="https://github.com/flashbag"><code>@​flashbag</code></a> in <a
href="https://redirect.github.com/expressjs/multer/pull/426">expressjs/multer#426</a></li>
<li>feat: add filename to file errors by <a
href="https://github.com/UjjwalKumar239"><code>@​UjjwalKumar239</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1416">expressjs/multer#1416</a></li>
<li>refactor: remove concat-stream dependency by <a
href="https://github.com/Phillip9587"><code>@​Phillip9587</code></a> in
<a
href="https://redirect.github.com/expressjs/multer/pull/1356">expressjs/multer#1356</a></li>
<li>fix: reject non-integer fileSize limits by <a
href="https://github.com/abhu85"><code>@​abhu85</code></a> in <a
href="https://redirect.github.com/expressjs/multer/pull/1395">expressjs/multer#1395</a></li>
<li>fix: allow exactly limits.parts parts by <a
href="https://github.com/deepakganesh78"><code>@​deepakganesh78</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1446">expressjs/multer#1446</a></li>
<li>fix: do not consume maxCount for files skipped by fileFilter by <a
href="https://github.com/Sagargupta16"><code>@​Sagargupta16</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1426">expressjs/multer#1426</a></li>
<li>fix: validate all limits at construction time by <a
href="https://github.com/ShubhamOulkar"><code>@​ShubhamOulkar</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1335">expressjs/multer#1335</a></li>
<li>test: cover storage engine _removeFile invocation semantics by <a
href="https://github.com/kilisamemarisaaa"><code>@​kilisamemarisaaa</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1460">expressjs/multer#1460</a></li>
<li>feat: accept a function for limits by <a
href="https://github.com/hossein-zare"><code>@​hossein-zare</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1133">expressjs/multer#1133</a></li>
<li>fix: add flush option to disk storage to fsync files before
completion by <a
href="https://github.com/kilisamemarisaaa"><code>@​kilisamemarisaaa</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1458">expressjs/multer#1458</a></li>
<li>feat: expose busboy defCharset, highWaterMark and fileHwm options by
<a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1465">expressjs/multer#1465</a></li>
<li>docs: describe the file stream contract for storage engines by <a
href="https://github.com/UlisesGascon"><code>@​UlisesGascon</code></a>
in <a
href="https://redirect.github.com/expressjs/multer/pull/1468">expressjs/multer#1468</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/expressjs/multer/blob/main/CHANGELOG.md">multer's
changelog</a>.</em></p>
<blockquote>
<h2>2.4.0</h2>
<ul>
<li>Fix <a
href="https://www.cve.org/CVERecord?id=CVE-2026-88932">CVE-2026-88932</a>
(<a
href="https://github.com/expressjs/multer/security/advisories/GHSA-3pph-fpjx-jg34">GHSA-3pph-fpjx-jg34</a>)</li>
<li>Add <code>filename</code> to <code>LIMIT_FILE_SIZE</code> and
<code>LIMIT_UNEXPECTED_FILE</code> errors (<a
href="https://redirect.github.com/expressjs/multer/pull/1416">#1416</a>)</li>
<li>Accept a function for <code>limits</code>, called with the request,
to set limits per request (<a
href="https://redirect.github.com/expressjs/multer/pull/1133">#1133</a>)</li>
<li>Add opt-in <code>flush</code> option to <code>DiskStorage</code> to
fsync files before the callback runs (<a
href="https://redirect.github.com/expressjs/multer/pull/1458">#1458</a>)</li>
<li>Expose busboy's <code>defCharset</code>, <code>highWaterMark</code>
and <code>fileHwm</code> options (<a
href="https://redirect.github.com/expressjs/multer/pull/1465">#1465</a>)</li>
<li>Add <code>streamHandler</code> option to feed busboy from
pre-consumed bodies (Google Cloud Functions, Firebase) (<a
href="https://redirect.github.com/expressjs/multer/pull/1466">#1466</a>)</li>
<li>Allow <code>multer.diskStorage()</code> to be called without options
(<a
href="https://redirect.github.com/expressjs/multer/pull/1471">#1471</a>)</li>
<li>Decode WHATWG-escaped characters (<code>%0A</code>,
<code>%0D</code>, <code>%22</code>) in field names, matching
<code>file.originalname</code> since 2.3.0: <code>req.body</code> keys,
<code>file.fieldname</code> and <code>err.field</code> now carry the
real name. If you matched the escaped spelling as a workaround, use the
real name now (<a
href="https://redirect.github.com/expressjs/multer/pull/1473">#1473</a>)</li>
<li>Report the decoded filename in <code>err.filename</code> on
<code>LIMIT_FILE_SIZE</code> errors, matching
<code>file.originalname</code> (<a
href="https://redirect.github.com/expressjs/multer/pull/1478">#1478</a>)</li>
<li>Reject non-integer or negative <code>limits</code> values at
construction time; a float limit silently disabled the check (<a
href="https://redirect.github.com/expressjs/multer/pull/1395">#1395</a>,
<a
href="https://redirect.github.com/expressjs/multer/pull/1335">#1335</a>)</li>
<li>Accept requests with exactly <code>limits.parts</code> parts;
<code>LIMIT_PART_COUNT</code> now fires only when the limit is exceeded.
If you set <code>parts</code> one higher to work around this, you can
drop the extra one (<a
href="https://redirect.github.com/expressjs/multer/pull/1446">#1446</a>)</li>
<li>Files skipped by <code>fileFilter</code> no longer count towards
<code>maxCount</code> (<a
href="https://redirect.github.com/expressjs/multer/pull/1426">#1426</a>)</li>
<li>Change the <code>LIMIT_UNEXPECTED_FILE</code> message to
&quot;Unexpected file field&quot; (<a
href="https://redirect.github.com/expressjs/multer/pull/426">#426</a>)</li>
<li>Remove the <code>concat-stream</code> dependency (<a
href="https://redirect.github.com/expressjs/multer/pull/1356">#1356</a>)</li>
<li>Docs: add JSDoc to the public API and document the storage engine
stream contract (<a
href="https://redirect.github.com/expressjs/multer/pull/1467">#1467</a>,
<a
href="https://redirect.github.com/expressjs/multer/pull/1468">#1468</a>)</li>
<li>Docs: add FormData upload examples (<a
href="https://redirect.github.com/expressjs/multer/pull/896">#896</a>)</li>
<li>Docs: remove the translated READMEs (<a
href="https://redirect.github.com/expressjs/multer/pull/1463">#1463</a>)</li>
<li>Internal: run the test suite on macOS (<a
href="https://redirect.github.com/expressjs/multer/pull/1464">#1464</a>)</li>
</ul>
<h2>2.3.0</h2>
<ul>
<li>Fix <a
href="https://www.cve.org/CVERecord?id=CVE-2026-77078">CVE-2026-77078</a>
(<a
href="https://github.com/expressjs/multer/security/advisories/GHSA-wc9g-mqfw-jrwm">GHSA-wc9g-mqfw-jrwm</a>)</li>
<li>Fix <a
href="https://www.cve.org/CVERecord?id=CVE-2026-77037">CVE-2026-77037</a>
(<a
href="https://github.com/expressjs/multer/security/advisories/GHSA-qfvm-cv95-jqjf">GHSA-qfvm-cv95-jqjf</a>)</li>
<li>Fix <a
href="https://www.cve.org/CVERecord?id=CVE-2026-77063">CVE-2026-77063</a>
(<a
href="https://github.com/expressjs/multer/security/advisories/GHSA-qvfw-j98x-7q72">GHSA-qvfw-j98x-7q72</a>)</li>
<li>Fix <a
href="https://www.cve.org/CVERecord?id=CVE-2026-82333">CVE-2026-82333</a>
(<a
href="https://github.com/expressjs/multer/security/advisories/GHSA-535w-7cp7-47q4">GHSA-535w-7cp7-47q4</a>)</li>
<li>Add <code>MulterError</code> codes <code>INVALID_FIELD_NAME</code>
and <code>STREAM_DESTROYED</code></li>
<li>Add opt-in <code>limits.fieldArrayIndexLimit</code> to bound numeric
array indexes in field names (<a
href="https://redirect.github.com/expressjs/multer/pull/1438">#1438</a>)</li>
<li>Accept files whose size is exactly <code>limits.fileSize</code> (<a
href="https://redirect.github.com/expressjs/multer/pull/1407">#1407</a>)</li>
<li>Preserve the caller's async context (<code>AsyncLocalStorage</code>)
when calling <code>next()</code> (<a
href="https://redirect.github.com/expressjs/multer/pull/1124">#1124</a>)</li>
<li>Decode WHATWG-escaped characters (<code>%0A</code>,
<code>%0D</code>, <code>%22</code>) in <code>file.originalname</code>
(<a
href="https://redirect.github.com/expressjs/multer/pull/1421">#1421</a>)</li>
<li>Do not crash when <code>fileFilter</code> invokes its callback more
than once (<a
href="https://redirect.github.com/expressjs/multer/pull/1427">#1427</a>)</li>
<li>Use a fallback message for <code>MulterError</code> codes without a
mapping (<a
href="https://redirect.github.com/expressjs/multer/pull/1448">#1448</a>)</li>
<li>Docs: clarify <code>preservePath</code> and <code>parts</code>, use
<code>crypto.randomBytes</code> in the <code>DiskStorage</code> example
(<a
href="https://redirect.github.com/expressjs/multer/pull/1414">#1414</a>,
<a
href="https://redirect.github.com/expressjs/multer/pull/1430">#1430</a>,
<a
href="https://redirect.github.com/expressjs/multer/pull/1436">#1436</a>)</li>
<li>Docs: add Indonesian, Japanese and Tamil translations and refresh
all translations from the current README (<a
href="https://redirect.github.com/expressjs/multer/pull/1431">#1431</a>,
<a
href="https://redirect.github.com/expressjs/multer/pull/1354">#1354</a>,
<a
href="https://redirect.github.com/expressjs/multer/pull/1462">#1462</a>)</li>
<li>Internal: run the test suite on Windows (<a
href="https://redirect.github.com/expressjs/multer/pull/1334">#1334</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/expressjs/multer/commit/35979e5afbb814bdb4b750ce028b125eb84c53af"><code>35979e5</code></a>
2.4.0 (<a
href="https://redirect.github.com/expressjs/multer/issues/1469">#1469</a>)</li>
<li><a
href="https://github.com/expressjs/multer/commit/b888532fe2e10ceb13da44286448cb2bb4ce9720"><code>b888532</code></a>
chore(deps): bump github/codeql-action/upload-sarif to 4.37.9 (<a
href="https://redirect.github.com/expressjs/multer/issues/1474">#1474</a>)</li>
<li><a
href="https://github.com/expressjs/multer/commit/e6bcd7db69315714fb9a38b1cd1e0f582cbce65a"><code>e6bcd7d</code></a>
chore(deps): bump github/codeql-action/analyze from 4.37.4 to 4.37.9 (<a
href="https://redirect.github.com/expressjs/multer/issues/1475">#1475</a>)</li>
<li><a
href="https://github.com/expressjs/multer/commit/00dec43b394c5bfaf4efb6d0fe8467bad05525ed"><code>00dec43</code></a>
chore(deps): bump github/codeql-action/init from 4.37.4 to 4.37.9 (<a
href="https://redirect.github.com/expressjs/multer/issues/1476">#1476</a>)</li>
<li><a
href="https://github.com/expressjs/multer/commit/8d5c3b72e430e7edaa2749e96dcb75bf18d84733"><code>8d5c3b7</code></a>
feat: allow diskStorage without options (<a
href="https://redirect.github.com/expressjs/multer/issues/1471">#1471</a>)</li>
<li><a
href="https://github.com/expressjs/multer/commit/02f6e8265b6bf6b6921a819db3b84276efa03ed2"><code>02f6e82</code></a>
fix: report the decoded filename on LIMIT_FILE_SIZE (<a
href="https://redirect.github.com/expressjs/multer/issues/1478">#1478</a>)</li>
<li><a
href="https://github.com/expressjs/multer/commit/bc3f72d5edaa19b993771348e3fb47b366316a88"><code>bc3f72d</code></a>
fix: decode escaped field names, not just filenames (<a
href="https://redirect.github.com/expressjs/multer/issues/1473">#1473</a>)</li>
<li><a
href="https://github.com/expressjs/multer/commit/2661325ba8ca2a72b7fb554b63d2df51da9290c4"><code>2661325</code></a>
docs: add JSDoc to the public API (<a
href="https://redirect.github.com/expressjs/multer/issues/1467">#1467</a>)</li>
<li><a
href="https://github.com/expressjs/multer/commit/53337f9713619ef3381ee6b4e541f926dbaac305"><code>53337f9</code></a>
fix: remove late-completing uploads aborted before the engine names
them</li>
<li><a
href="https://github.com/expressjs/multer/commit/7f2c9ab5f35c0478a7b3bb0f5e1af9b536780132"><code>7f2c9ab</code></a>
feat: add streamHandler option to feed busboy from pre-consumed bodies
(<a
href="https://redirect.github.com/expressjs/multer/issues/1466">#1466</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/expressjs/multer/compare/v2.2.0...v2.4.0">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for multer 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 <noreply@paperclip.ing>
canary/v2026.1001.0-canary.2
2026-10-01 15:00:48 +00:00
DottaandPaperclip 0829d94af2 fix(auth): derive low-trust human direction from existing execution records (#14775)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Low-trust review contains work that may include hostile input.
> - Its default intake boundary currently blocks direct human chat and
tasks outside that boundary.
> - Human direction should authorize the assigned work while preserving
containment.
> - Existing conversations and execution requests already identify
direct human instructions.
> - This pull request derives exact-task authority from those records
and the current assignee.
> - The agent can perform that work without gaining access to unrelated
tasks or privileged tools.

## Linked Issues or Issue Description

**What happened?**

A low-trust agent with a project boundary rejects its owner's direct
Agent Chat before provider execution. Human-assigned tasks outside that
project fail the same check.

**Expected behavior**

An authorized human can talk to the agent or assign it a task. The exact
task runs with its existing sandbox, credential, and tool restrictions.

**Steps to reproduce**

1. Enable Agent Chat and isolated workspaces. Configure a sandbox agent
with low-trust review scoped to an intake project.
2. Send the agent a direct board chat message, or assign it a
projectless task.
3. Observe `low_trust_boundary_mismatch` before execution.

Related: #14766 adds private task directories for repo-free low-trust
execution. It is now merged into master and included in the branch base,
so CI and staging verify the combined behavior.

## What Changed

- Derive owner-chat access from existing conversation identity.
- Derive exact-task access from the existing human requester and
server-owned request origin, including coalesced requests. Plugin and
external sender attribution do not authorize work.
- Follow existing `retryOfRunId` database links for automatic
continuations, checking company, agent, and task throughout; cancelled
ancestors cannot grant authority.
- Require a live run and current assignment. Preserve sandbox,
credential, privileged-tool, responsible-user, and quarantined-output
checks.
- Retain board backlog assignments in existing request records without
starting execution. Reassignment cancels old human requests in the
common service transaction, including plugin writes; late settlement
cannot revive them.
- Add real database and HTTP coverage for request provenance, retry
ancestry, cancelled runs, concurrent reassignment, spoofing, and
containment. Document the rule.
- Preserve legacy board assignment requests through their existing
source, reason, and human requester.
- Use the existing wrapped-error helper for concurrent chat-question
idempotency; a deterministic race test reproduces the CI failure before
the fix and passes after it.
- No new schema, migrations, or user-identity fields. Existing requester
columns hold attribution.

## Verification

- Passed the focused database, policy-retention, HTTP, and reassignment
tests locally. The HTTP test creates a task through the real board route
and checks the resulting persisted wakeup before exercising agent reads,
comments, mutations, and review handoff.
- Database tests hold a reassignment transaction open to verify coherent
authorization before and after commit, with a two-connection pool. They
cover retries, coalesced requests, cancelled ancestry, invalid
cross-company/agent/task links, cycles, and forged attribution.
- Full local `pnpm -r typecheck` and `pnpm build` passed on the final
commit (`d107c26df`). [Latest-head
CI](https://github.com/paperclipai/paperclip/actions/runs/36815589542)
passed: 54 successful checks, two expected skips, including all eight
browser-test shards. Greptile is 5/5 on this exact commit with no
unresolved threads. Local tests were targeted; the full test suite ran
through CI’s test matrix.
- The revised HTTP suite passed all 13 tests; database authorization
tests passed all 11, including legacy compatibility and late watchdog
settlement; the backlog route contract passed all 3 tests. Another 102
tests covering durable chat admission, wake queues, and Cursor execution
passed.
- All 90 interaction-service tests passed with both create calls
deliberately held until their optimistic reads complete, forcing
duplicate-key recovery. That forced race failed before switching to the
shared wrapped-error helper.
- Previous staging proof covered owner chat and projectless task
persistence. The simplified revision has not been redeployed; that
earlier proof is not claimed for the new implementation.

## Risks

- This is an authorization change: only the live run's exact task
qualifies, and normal responsible-user restrictions still apply.
- Existing request and retry records are authoritative. Merely naming a
responsible/originating user or an external connector sender does not
qualify.
- Reassignment invalidates existing human request records
transactionally. A cancelled run or request cannot regain authority when
the task is assigned back.
- Ordinary task exceptions require server-owned origin or the legacy
board assignment source/reason/actor combination. Existing owner chats
use conversation identity.

## Model Used

OpenAI GPT-6 in Codex, with reasoning, repository tools, code execution,
and browser testing. The runtime does not expose a more specific model
ID or context-window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-01 09:45:18 -05:00
467125fafb feat(connections): one-screen connector setup with stated defaults (#14811)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents use Connections (the Apps catalog) to act in services like
Notion, GitHub, Google Workspace and Railway
> - Each connector asked the user to answer setup questions before it
went to the provider. Most of the questions already had the correct
answer selected
> - ROADMAP.md lists "simpler setup" for Apps and Connections as ongoing
work. This change continues that work
> - This pull request removes the questions that Paperclip can answer
itself. It states the defaults in one line and moves the choices behind
"Change" and onto the Permissions tab
> - The benefit is that most connectors take one click in Paperclip and
then the provider's own consent screen

## Linked Issues or Issue Description

No public issue exists. This is the description, from the enhancement
template.

**What existing behavior does this improve?**
The setup flow for tool connectors in the Apps catalog.

**Subsystem affected**
Apps and Connections: `ui/src/features/connections`,
`ui/src/pages/apps`, the `packages/shared` app definitions, and the
OAuth routes in `server/src/routes/tool-access.ts`.

**Current behavior**
Every connector opened with an Access step. The step asked who can use
the connection and which agents get it, and both answers were already
selected. 18 connectors also asked "How do you want to connect?" when
Paperclip could rank the methods. The Google apps and Postman also asked
"What should Paperclip be able to do?" before sign-in. The four gateway
connectors (Zapier, Arcade, Composio, Executor) used a separate two-step
wizard. Asana was pinned to a customer-owned OAuth app, so the user had
to register an app in Asana's developer console. The "Set all" control
on the Permissions tab changed only one action. After the user approved
access, Railway's consent page showed "you can close this window" and
did not return to Paperclip.

**Proposed behavior**
One screen per connector, with one primary button. The screen states the
defaults in one sentence, for example "Connects for everyone in your
organization, available to all agents". A "Change" link opens one
Advanced panel. When the provider's metadata allows dynamic client
registration, Paperclip registers a client itself. Connecting lands on
the Permissions tab. On that tab, "Set all" changes every action in the
group.

**Reason and benefit**
The user makes fewer decisions before the connection exists. Most
choices are easier to make after the connection, on the Permissions tab,
where a change has an immediate effect.

**Breaking changes**
None. No schema or API change. Existing connections keep their settings.

## What Changed

- **No Access step.** `ConnectionSetupFlow` no longer has the Access
step. The flow shows the resolved default above the primary button and
on the completion screen. The access controls moved into one Advanced
panel. The panel opens automatically only when a setting in it is
required.
- **A default method for every app.** The flow always picks the ranked
default method. Alternate methods are in the Advanced panel. The Google
and Postman capability choice is not asked before sign-in. The
write-capable method is the default.
- **Gateway connectors.** `RemoteMcpProductionSetup` (Zapier, Arcade,
Composio, Executor) no longer has its own Access step. Its commit path
and the main commit path use one helper, `askFirstCatalogEntryIdsFor`,
for server-suggested defaults.
- **Dynamic registration from live metadata.**
`canRegisterOAuthClientDynamically` now allows registration when the
provider advertises a registration endpoint, even if the catalog entry
lists only customer-owned clients. The Asana and Linear definitions and
catalog text match live probes. Asana issues clients for loopback
callbacks only, so a hosted deployment still needs an Asana app.
- **Connection setup states.** New
`packages/shared/src/connection-setup-state.ts` sorts each method into
`instant`, `authorize`, `paste` or `register`. The gallery card verb
("Connect" or "Add key") comes from this resolver and the instance's
ownership availability.
- **Generic MCP.** The generic path no longer asks "Does it need a key?"
first. A credential challenge from the server shows the key field.
- **Permissions tab.** Each action row shows its risk level. Each group
has a "Set all" control. The control sends one change for the whole
group. Before, each row's save started from the same render, so the
saves overwrote each other. The Zapier/Arcade/Composio/Executor setup
screen had the same defect.
- **OAuth callback interstitial.** A cross-site browser navigation to
`/api/tools/oauth/callback` gets a small same-origin "Finishing your
connection…" page. That page repeats the request, and the repeat does
the code exchange. Railway's consent page replaces itself after about
two seconds, and the code exchange plus tool discovery takes longer than
that. The interstitial uses only a meta refresh, because the OAuth code
is single-use. Requests without `Sec-Fetch-Site: cross-site` take the
old path.
- **Linear registers through its MCP server.** Linear pins the console
endpoints at `linear.app`. Pinned endpoints now replace discovery only
when the method cannot register, or when the connection has an
operator-entered client. So a Linear connection now finds the
registration endpoint at `mcp.linear.app`.
- **Own-OAuth-app recovery stays on the one-click screen.** When the
method also accepts a customer-owned client, the client fields are in
the Advanced panel. The panel opens after a failed sign-in. "Try again"
resumes the draft with the operator's client.
- **E2E specs** follow the one-screen flow. The Access-step clicks are
removed, the specs open **Change** before they pick agents, and they
expect GitHub's **Add key** verb.
- **Default permissions do not change.** New connections still allow
every action. The user can set actions to Ask first or Off on the
Permissions tab.

## Verification

- `cd ui && npx vitest run src/pages/apps src/features/connections
--no-file-parallelism`
- `cd packages/shared && npx vitest run src/app-definitions.test.ts
src/connection-setup-state.test.ts`
- `cd server && npx vitest run src/__tests__/tool-access-service.test.ts
src/__tests__/remote-mcp-connectors.test.ts`
- `pnpm check:token-gates`
- New tests:
- `PermissionsPanel.group.test.tsx` checks that "Set all" sends one
change for the whole group. It fails on the old code.
  - `action-permissions.test.ts` checks the group update.
  - `connection-setup-state.test.ts` checks the four setup states.
- A server test checks that a cross-site callback gets the interstitial
and does not use the OAuth state, and that the same-origin repeat
completes the connection.
- Manual check on a hosted staging deployment. GitHub, Google Drive,
Composio, Notion, PostHog and Railway each connected from one screen and
returned to the Permissions tab. On Railway, "Set all" changed all 65
write actions, and the change remained after a reload.
- Visual changes: snapshot baselines are intentionally not updated. See
the `doc/design/DECISION-SHEET.md` entry "Per-change snapshot
verification demoted to dormant (Jul 13 2026)".

## Risks

- **Fewer confirmation clicks.** Organization-wide access is the
default, and the user does not confirm it on a separate step. This was
already the preselected answer. The flow shows the default before the
user clicks and again after the connection.
- **Google write scope.** Google apps now request the write-capable
scope by default. A narrower scope needs a new sign-in.
- **Dynamic registration from live metadata.** A provider can advertise
registration and then reject a redirect URI. Asana rejects hosted
callbacks, for example. In that case registration fails, and the
customer-owned client path remains available for recovery.
- **Callback interstitial.** The OAuth callback adds one same-origin
step for cross-site browser navigations. Browsers without `Sec-Fetch-*`
headers use the old direct path.
- Chat and bot connectors (Discord, Telegram, Microsoft Teams, iMessage)
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

- Claude Opus 5.5 (Anthropic), model ID `claude-opus-5-5`, used through
Claude Code with tool use (shell, file editing, browser automation) and
extended thinking. It wrote the code, the tests and this description. A
human product owner directed the work and tested it by hand.

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

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

---------

Co-authored-by: scotttong <squadbot000@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
canary/v2026.1001.0-canary.1 nightly/v2026.1001.0-nightly.0 beta/v2026.1002.0-beta.0 v2026.1005.0
2026-09-30 23:13:47 -07:00
Dotta 22c78242a4 fix(runner): require receiver acknowledgment for semantic receipt 2026-10-01 00:14:49 -05:00
DottaandPaperclip 2e4e06d7f0 fix(runner): commit receipts only for admitted sidecar frames
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 23:34:59 -05:00
DottaandPaperclip 3e1dc7dabc test(runner-e2e): join normalized Copilot completion receipts
Keep raw invocation provenance separate from the validated proposal digest. Advance the semantic evidence contract to v2 and Copilot suite to 8; preserve denial and attached-operation invariants.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 23:28:02 -05:00
DottaandPaperclip 85b88f24b4 test(runner-e2e): assert native permission cancellation response
Use session/request_permission for the native permission fixture and verify that the real pending-request mapper settles its callback exactly once with action cancel.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 23:28:02 -05:00
Dotta 3665daf9dc fix(runner): bind normalized semantic receipt inputs 2026-09-30 23:28:02 -05:00
DottaandPaperclip 266b92ea69 fix(runner-e2e): match Product cancellation evidence
Require the normalized pending-request closure and cancelled terminal with the existing caller-owned Stop and native evidence guards. Exercise the real Product projection and version the changed settlement oracle.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 23:03:45 -05:00
DottaandPaperclip 0a67197e14 test(runner): allow bounded native shim fixture setup
The Copilot shim test copies and hashes the complete host Node closure before launching the bootstrap and owned shim. Two CI runs stopped at Vitest's default 5s limit (jobs 110198660276 and 110200639147), while identical code passed in 1081ms in job 110197353844. Use the adjacent real-Node closure test's 30s envelope; preserve all assertions, product deadlines, and retry behavior.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 22:21:48 -05:00
DottaandPaperclip f6f23961da fix(runner): scope Cursor identity policy to its sidecar profile
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 22:21:48 -05:00
DottaandPaperclip 5adbe07365 test(runner-e2e): bind semantic acceptance and complete shell reads
Keep bridge call identity separate from canonical result item identity. Require one complete shell-read lifecycle tied to the started command and reject extra or partial reads.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 22:15:39 -05:00
DottaandPaperclip 4a876efef5 test(runner-e2e): require correlated accepted Copilot completion
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 22:15:39 -05:00
DottaandPaperclip 35fbc123a7 test(runner-e2e): honor provider permission evidence order
Generate both input orders through the real Cursor and Copilot projectors. Preserve Cursor's deferred permission evidence invariant while allowing Copilot's immediate permission notice before its exact tool origin; retain all pre-Stop and settlement bindings.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 22:14:28 -05:00
DottaandPaperclip 90a4d86417 fix(runner-e2e): bind active-stop evidence before dispatch
Accept either canonical permission/tool arrival order while binding the exact native-origin and tool-start rows before Stop and through cancellation. Version the pending receipt and suite; retain strict identity, no-effects, unanswered-request, and settlement checks.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 22:14:28 -05:00
DottaandPaperclip a06fa49483 chore(runner): integrate pending v10 correlation for qualification only
Preserve both reviewed source and historical qualification evidence. CI-only branch; do not merge.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 22:14:15 -05:00
DottaandPaperclip 041d84760f fix(runner): align Cursor control IDs and retain native receipt evidence
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 22:11:14 -05:00
DottaandPaperclip 8ec354f609 test(runner): consume ordered permission notifications in forwarding fixture
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 21:57:02 -05:00
DottaandPaperclip 1a941e85e5 fix(runner): preserve event order before native input dispatch
Queue bounded internal request callbacks alongside notifications so the Codex driver maps earlier tool activity before exposing permission cards. Dispatch without awaiting human answers and discard stale callbacks after closure, replacement, or durable settlement.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 21:55:48 -05:00
DottaandPaperclip e5ef415eef fix(runner): version rich tool correlation profiles
Bind Cursor permission identity and Copilot semantic receipt sources across TypeScript, native admission, provider packaging, and recovery. Preserve historical v9 fixtures and pending qualification status.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 21:54:21 -05:00
DottaandPaperclip 4eca3e8a02 fix(runner): correlate Copilot semantic tool results with bounded receipts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 21:51:20 -05:00
DottaandPaperclip ae0c0f9f71 fix(runner): preserve Cursor permission tool identity
Reuse the existing provider tool identity transform across native evidence, permission details, and canonical activity without changing the provider request or option binding.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 21:51:20 -05:00
DottaandPaperclip 7bbac082ef fix(runner): retain reviewed Cursor projection failure reasons
Apply the two provider evidence files from 2cbf72ec96 (#14802). The separate Product E2E changes remain on their existing branch.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 21:51:12 -05:00
DottaandPaperclip ef06355ad4 test(runner): verify permission forwarding through transport handler
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 21:17:47 -05:00
DottaandPaperclip 6ab85a3733 fix(runner): preserve native permission tool correlation
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 21:04:20 -05:00
DottaandPaperclip 64dd389ebe fix(runner): retain strict active-stop evidence diagnostics
Accept matching PRP v1/v2 session envelopes without weakening pending-operation identity or cancellation checks. Record closed Cursor projection failure codes while keeping external error text private and incomplete evidence disqualifying.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 21:04:20 -05:00
DottaandPaperclip d46931a522 fix(runner): preserve native permission tool correlation
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 21:02:08 -05:00
Devin FoleyandPaperclip 0d3e7bf6ac fix(daytona): keep commands alive after log stream closure (#14799)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Sandbox providers run agent processes and send their output to the
host.
> - The Daytona SDK can close a log socket while the remote command
still runs.
> - The driver treated a clean socket close as completion before it had
a command exit code.
> - This pull request recovers log observation for the same command and
waits for a recorded exit.
> - The host keeps receiving new output without a second command
dispatch.

## Linked Issues or Issue Description

**What happened?**

A clean close of the Daytona session log WebSocket resolves the SDK
callback promise. If the command still runs, the driver returned
`exitCode: null` with `timedOut: false` after a short status check. A
streamed ACP bridge can then report a process disconnect.

**Expected behavior**

A log socket close must not complete a running command. Recovery must
preserve new output, the caller's lifetime controls, and one command
dispatch.

**Steps to reproduce**

Use the callback form of `getSessionCommandLogs`. Let that promise
resolve while `getSessionCommand` still has no exit code. Keep the
command running, then expose its final logs and exit code. The new
regressions exercise this sequence, including streams that run longer
than the provider operation timeout.

Related: #11021, #11049. #14485 covers input delivery retries, which are
a separate transport path.

## What Changed

- Require a recorded command exit after a clean log-stream close.
Reconnect once, then read status and full log snapshots at most once per
second.
- Forward new output during recovery. Remove replayed prefixes and
reconcile a final snapshot so bytes written after socket close are
retained.
- Hold a trailing UTF-8 replacement suffix until replay or completion
resolves it. This handles the SDK decoder flush when a socket closes in
the middle of a character.
- Preserve healthy initial and reconnected stream lifetimes. Bound each
recovery read. Preserve the existing fallback timeout budget after
rejected stream attempts.
- Retain partial output on timeout. A log-observation timeout reports an
unconfirmed result and preserves any observed exit code in metadata; it
does not synthesize successful completion.

## Verification

- `pnpm exec vitest run --config
packages/plugins/sandbox-providers/daytona/vitest.config.ts`: 335
passed, 14 gated tests skipped.
- `pnpm exec vitest run --project @paperclipai/plugin-daytona`: 335
passed, 14 gated tests skipped.
- `pnpm exec tsc --noEmit -p
packages/plugins/sandbox-providers/daytona/tsconfig.json`: passed.
- `pnpm exec tsc -p
packages/plugins/sandbox-providers/daytona/tsconfig.json`: passed.
- `pnpm --workspace-concurrency=1 -r typecheck`: passed.
- `CARGO_BUILD_JOBS=2 pnpm --workspace-concurrency=1 -r build`: passed.
- `pnpm test:run`: exited with failure after 845.77 seconds. The server
phase reported 43 failed files, 529 passed, and 163 skipped; 14 failed
tests, 9,122 passed, and 5,599 skipped. All failure entries were traced
to local PostgreSQL startup/cleanup errors or ten macOS skill-cache
rename errors. The package helper restored 17 missing PostgreSQL library
links; the four sequencing/migration tests and fourteen
native-workspace-finalizer tests then passed. The ten cache failures
match clean-base evidence with identical source/test blobs. This is not
a full-suite pass; later local test groups did not run. PR CI supplies
the complete check result.
- Regressions cover clean and rejected stream recovery, two hour-long
streams with a five-minute operation budget, live fallback output, one
dispatch, delayed final output, stale snapshots, SDK UTF-8 decoding,
bounded observation, and timer cleanup.
- `git diff --check` and a local diff scan for secrets and private
identifiers passed.
- Greptile reviewed head `293c4dbe67` at 5/5. Both prior findings are
fixed, both threads are resolved, and no new actionable findings remain.

## Risks

- The SDK returns full snapshots with no offset API. After both stream
attempts end, polling bandwidth grows with retained output. The
one-second cadence limits request frequency.
- The SDK callback stream has no cancellation handle. Existing caller
stop logic and provider/session teardown still own its lifetime. Late
callbacks from a settled stream are ignored.
- A successful status read with no exit code keeps recovery active under
the existing caller guard. A failed read stops recovery; it is not
retried indefinitely.
- No command replay, provider API change, schema migration, or runtime
timeout policy change is included.

## Model Used

OpenAI GPT-6 through Codex, with tool use and independent code review.
The exact serving model identifier 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
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-30 18:37:54 -07:00
Devin FoleyandPaperclip 0dc8d80eea Clarify worktree seed source setup failures (#14795)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Managed worktrees can prepare an isolated Paperclip development
instance.
> - The built-in provisioner requires a canonical registered seed source
config.
> - A missing source currently has the same message as a rejected
symlink or non-regular file.
> - This pull request separates those messages and names the supported
setup choices.
> - Operators can choose the intended setup without changing the
source-validation guards.

## Linked Issues or Issue Description

**What happened?**

A plain repository checkout can select the control-plane instance as its
seed source. If that instance runs with environment-only configuration,
the source config file can be unavailable. The provisioner stops with a
message that also covers noncanonical files and gives no repair
guidance.

**Expected behavior**

The error should identify the selected source and distinguish an
unavailable prerequisite from a rejected file. It should explain that a
seeded development instance needs a canonical registered source. It
should describe the explicit no-op only for a checkout-only worktree.

**Steps to reproduce**

1. Use a plain base checkout with no repository-local config.
2. Leave the control-plane instance config file absent.
3. Run the built-in worktree provisioner against an isolated checkout.

**Paperclip version or commit**

Base commit `c8f874311c`.

**Deployment mode**

Managed local worktrees, including servers configured only through
environment variables.

Related: #11733 adds deeper source-readiness checks. #11735 changes
runtime and seed lifecycle handling. This change only improves the
existing shell guard's diagnostics.

## What Changed

- Distinguish unavailable source configs from symlinks and non-regular
files.
- Identify whether the selected source belongs to the base workspace or
control-plane instance.
- Explain seeded-instance prerequisites and the explicit checkout-only
setup choice.
- Verify failure still precedes target-state creation and CLI
invocation.
- Document the setup choice and its runtime-readiness limit.

## Verification

- `node --test scripts/__tests__/provision-worktree-self-heal.test.mjs`:
21 passed; one platform-gated test skipped because macOS lacks `flock`.
- `bash -n scripts/provision-worktree.sh` and `git diff --check`:
passed.
- `pnpm -r typecheck`: passed.
- `pnpm exec vitest run server/src/__tests__/ai-connections.test.ts`: 50
passed after running the installed PostgreSQL package's own symlink
hydration script in this worktree.
- `pnpm test:run`: attempted, then stopped after unrelated database
suites failed at startup. The offline install had omitted PostgreSQL
native library symlinks. The focused database rerun above verifies the
local repair; the complete suite is delegated to CI.
- `pnpm build`: passed.

- [Required PR
CI](https://github.com/paperclipai/paperclip/actions/runs/36797650741)
passed on `0a3ba63e12`: 50 successful checks and two intentional
Storybook skips. Greptile scored that exact commit 5/5; there are zero
unresolved review threads and no merge conflicts.

## Risks

- Diagnostics only. This does not supply a source config or repair an
existing blocked task.
- The failure predicates and exit status stay unchanged. Symlink and
non-regular-file errors do not recommend skipping setup.
- The checkout-only no-op requires an explicit policy choice. It does
not grant runtime or seed readiness.
- No schema, migration, tenant policy, deployment, or Sentry reporting
change.

## Model Used

OpenAI GPT-6-based Codex, with reasoning, shell tools, and code
execution. The exact serving model ID and context-window size are not
exposed by this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [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-09-30 18:35:33 -07:00
Devin FoleyandPaperclip 4b9a6000f7 Add bounded evidence for directory lock timeouts (#14787)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent files use directory locks during collection and cleanup.
> - A lock timeout can fail finalization after the model turn completes.
> - The timeout currently identifies no owner state or waiting
operation.
> - This pull request adds bounded evidence to the existing run failure
report.
> - Operators can distinguish a known local holder from a possible old
lock without changing lock safety.

## Linked Issues or Issue Description

**What happened?**

A directory lock timeout does not distinguish active local work from an
owner record left by an earlier process. The stored execution stage can
also precede the cleanup operation that failed.

**Expected behavior**

The failure report should identify the waiting operation and expose
bounded ownership clues. It must preserve the timeout and keep unknown
ownership protected.

**Steps to reproduce**

Hold a directory merge lock while a second caller reaches its
acquisition deadline. The regression tests exercise a live holder and an
older owner record with a live PID.

Related: #9667 proposes stale-lock recovery under a single-server
assumption. This change only adds evidence and does not adopt that
assumption. #14575 and #14665 add other run failure diagnostics.

## What Changed

- Record lock owner state, capped age and wait duration, same-process
and process-age comparisons, and whether this module holds the lock.
- Label agent-directory release, collection, checkpoint, and warm
handoff timeouts with a fixed operation code.
- Validate each field before the existing event-local Sentry report
accepts it. Exclude owner records, PIDs, paths, and absolute timestamps.
- Limit the extra diagnostic owner read to 100 ms with best-effort
abort; malformed JSON is `invalid` and unreadable owner records remain
`unknown`.
- Document the diagnostic limits and verify that contenders never
reclaim protected locks.

## Verification

- Focused lock, diagnostic, real Sentry SDK, and database-backed
agent-directory tests: 126 passed, including stalled-read and
malformed/missing/unreadable-owner regression coverage.
- Final revision `0691613dcc`: all 54 reported checks successful, with
two intentionally skipped Storybook checks. Greptile: 5/5, zero
unresolved review threads; no merge conflicts.
- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- `pnpm test:run`: complete suite coverage ran with the existing
repository shard flags: four general-server shards, four serialized
shards, two general-workspaces-a shards, and general-workspaces-b. The
full run is not green because of the base failures below.
- The broad run found 13 failures in the unchanged macOS skill-cache
tests. All 13 reproduce on the clean base revision. Open PR #14290
covers that existing failure.
- Two unchanged CLI archive tests hit their five-second limits during
the broad run; all 17 tests in that file pass on recheck. A CLI auth
socket error also cleared on recheck (19 tests), and its full serialized
shard passed on rerun.

## Risks

This is a diagnostic change, not a stale-lock fix. Owner observations
can race with release. Wall-clock shifts can affect the age comparison.
A local-holder flag covers only this module instance. None of these
fields authorizes reclamation or proves a file save. Lock acquisition,
release, retries, task status, and recovery guards retain their current
behavior. No schema change or deployment action is required.

## Model Used

OpenAI Codex, based on GPT-6, with code execution and repository tools.
The exact model build and context window were not exposed to this agent.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-30 18:35:12 -07:00
842efe0181 fix(ui): stack project field save indicator below its label (#14765)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The web UI has a project detail page. A properties panel on that
page lets a user edit the project name, description, and other fields.
> - Each field shows a "Saving...", "Saved", or "Failed" indicator while
an edit is in progress.
> - The indicator was rendered next to the label text in a fixed 80px
label column. The "Description" label almost fills that column, so the
indicator spilled into the value column and covered the description
text.
> - A user cannot read the description while the indicator is visible.
This looks broken and it hides content.
> - This pull request stacks the indicator directly below the label
text, so it stays inside the label column.
> - The benefit is that the indicator never covers the field value, for
the description and for every other labelled field.

## Linked Issues or Issue Description

No public GitHub issue exists for this bug. The issue is described here.

**What happened?**

When a user edits a project description in the project properties panel,
the "Saving..." and "Saved" indicator appears next to the "Description"
label. The label column is 80px wide. The indicator does not fit, so it
overflows into the value column and overlaps the description text.

**Expected behavior**

The "Saving..." / "Saved" / "Failed" indicator must appear directly
below the "Description" label. It must not overlap the description text
or any other field value.

**Steps to reproduce**

1. Open a project in the Paperclip web UI.
2. Click the Description field in the properties panel and change the
text.
3. Click outside the field to save.
4. Look at the "Description" label while the "Saving..." and then
"Saved" indicator is visible. The indicator overlaps the description
text.

**Paperclip version or commit**

master at `5edf55d7350c7f08c9dd132c7e0f1421fa0bf2fb`.

**Deployment mode**

Local development (`pnpm dev`). The bug is in the UI layout, so it
applies to every deployment mode.

## What Changed

- `ui/src/components/ProjectProperties.tsx`: `FieldLabel` now renders
the label text and the `SaveIndicator` in a vertical flex column
(`flex-col`) instead of a horizontal row. The indicator sits directly
below the label and stays inside the 80px label column. This applies to
every labelled property row (Name, Description, Env, and so on), so no
label can overflow.
- `ui/src/components/ProjectProperties.save-indicator.test.tsx`: new
regression test. It asserts that the indicator is a stacked sibling
under the Description label for the `saving` and `saved` states, and
that no indicator renders for the `idle` state.

## Verification

- Run `pnpm --filter @paperclipai/ui exec vitest run
src/components/ProjectProperties` from the repo root. All
ProjectProperties tests pass, including the new save-indicator test.
- The new test fails against the previous inline layout (2 of 3 cases
fail) and passes with this change (3 of 3 cases pass).
- The existing `ProjectProperties.concurrency`,
`ProjectProperties.managed-sandbox`, and `ProjectDetail` tests pass (18
tests).
- `tsc -b` in `ui/` reports no errors in the changed files.
- Manual check: open a project, edit the description, and save. The
"Saving..." and "Saved" indicator now appears below the "Description"
label and does not cover the description text.

## Risks

- Low risk. The change is a single flex-direction swap on the label
wrapper in one component.
- Every labelled row in the project properties panel gets a slightly
taller label cell while an indicator is visible. This is intentional and
it matches the requested layout.
- No data, API, or migration changes.

## Model Used

- Claude Fable 5.1 (Anthropic), model id `claude-fable-5-1`, with
extended thinking and tool use, run through Claude Code inside a
Paperclip agent session. A human reviewed the change and the pull
request text.

## Checklist

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

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

Co-authored-by: Bender (Fable) <bender@paperclip.local>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 18:34:41 -07:00
Devin FoleyandPaperclip 98d8a6ccac Stop replaying ambiguous database disconnects (#14773)
## Thinking Path

> - Paperclip stores agent work and control state in PostgreSQL.
> - Its database client must not repeat a mutation after an uncertain
result.
> - The global retry wrapper treated `write CONNECTION_CLOSED` as proof
that PostgreSQL never received a statement.
> - postgres.js also uses that message when the connection closes after
statement delivery.
> - This pull request removes that global replay and tests the actual
driver over a local wire connection.
> - Callers retain control of retries when they can prove the complete
operation is idempotent.

## Linked Issues or Issue Description

Follow-up to #13417. Preserve the transaction disconnect handling from
#13643 and the explicit actor synchronization retries introduced in
#12773. Searched open and closed issues and PRs for database retries,
disconnects, and `CONNECTION_CLOSED`. The open circuit-breaker proposal
#11142 addresses outage queue growth; it does not establish whether an
already-sent statement can be replayed.

**What happened?**
The database wrapper replayed an arbitrary statement up to three times
after `write CONNECTION_CLOSED`. The driver adds `write ` to
connection-close errors even after the peer receives the statement. A
local protocol peer receives the same submitted INSERT three times when
it drops each response. A committed write could therefore execute more
than once.

**Expected behavior**
An ambiguous statement result must fail without automatic replay. A
subsequent operation must be able to reconnect.

**Steps to reproduce**
Run the new wire regression against the parent commit. The six Simple
Query cases and the parameterized Drizzle case receive three executions
instead of one. The named prepared-client case was already safe and
stays covered. The peer reads the entire statement and then closes the
connection. This demonstrates repeated delivery with the real driver; it
does not claim that a historical incident duplicated a committed write.

**Paperclip version or commit**
Reproduced on source commit `018993140f` with the patched postgres.js
3.4.9 dependency.

**Deployment mode**
Built from source with a local PostgreSQL protocol peer. No live
provider or customer database is used.

## What Changed

- Pass the original postgres.js client to Drizzle and remove the global
statement replay wrapper.
- Add eight wire regressions: six Simple Query cases for INSERT,
side-effect-capable SELECT, and a data-changing CTE, plus parameterized
Drizzle and named prepared-client cases. The extended peer processes
Parse, Describe, Bind, and Execute, verifies bound parameters, and drops
the response only after Execute. Each case checks one delivery and
recovery on a fresh query.
- Document ambiguous outcomes and the retry compatibility tradeoff. Keep
explicit idempotent actor-sync retries and disconnected-transaction
handling unchanged.

## Verification

- Before the fix: the six Simple Query cases and the parameterized
Drizzle case failed with three executions instead of one. The named
prepared-client case was already safe. All eight wire cases pass on this
branch.
- Final focused client, pool teardown, configuration, and actor-sync
retry checks: 28 tests passed. `pnpm --filter @paperclipai/db typecheck`
also passed after the test-only follow-up.
- First implementation head, `pnpm exec vitest run --project
@paperclipai/db`: all 158 tests passed across 45 files, including real
PostgreSQL transaction/reserved-connection recovery. The local embedded
dependency's symlinks were hydrated before this run.
- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- The complete local `pnpm test:run` did not finish; no complete local
suite pass is claimed. All CI test, typecheck, and build gates passed on
the first implementation head `11f8b22d90`. Final-head CI is pending
after the test-only follow-up.
- `git diff --check`: passed. Reviewed the diff for secrets, personal
data, generated output, and run artifacts.

## Risks

Some transient statement failures that the global wrapper previously
replayed now reach the caller. Operation owners must retry only when
they have an idempotency guarantee or a durable receipt that prevents
duplicate effects. A connection error is not proof that a write failed
to commit. There is no SQL-text retry heuristic, new suppression, schema
change, or migration. This change prevents unsafe replay; it does not
prevent network disconnects.

## Model Used

OpenAI Codex / GPT-6, with reasoning, repository inspection, code
execution, and local protocol tests. The exact backend model ID and
context-window size are not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (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


Final verification (September30): every final-head CI check passed at
`3004c5bda39c985c3557547ec45e33870ce5d010`. Greptile scored5/5 on this
head, all review threads are resolved, and the branch is mergeable.
Eight real-wire regressions cover simple, parameterized Drizzle, and
named prepared queries. Full local suite did not produce a completed
result; the complete CI matrix passed. This public PR remains open for
maintainer merge.

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1001.0-canary.0
2026-09-30 18:34:00 -07:00
DottaandPaperclip 8ffecf0fc1 fix(runner-e2e): admit explicit native active-stop candidates
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 20:11:36 -05:00
DottaandPaperclip 2ea54f2c3f fix(runner): bind transitive Copilot permission policy sources
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 20:11:36 -05:00
DottaandPaperclip eeb48425ee fix(runner): bind transitive Copilot permission policy sources
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 20:09:09 -05:00
DottaandPaperclip 66f61521a1 fix(runner-e2e): preserve negation in async bootstrap task
State each bootstrap prohibition separately so the production file-delivery classifier does not treat an isolated clause as a requested output. Keep immediate completion stress, private marker evidence and all settlement assertions unchanged; version the authored Copilot protection definition.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 20:03:36 -05:00
DottaandPaperclip fcf1866928 fix(runner): bind Copilot permission grants to visible targets
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 19:59:38 -05:00
DottaandPaperclip a9cec40001 test(cli): retain target Postgres fixture failure logs
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 19:59:19 -05:00
DottaandPaperclip fa8642d1eb test(runner): gate installed package staging regressions
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 19:59:19 -05:00
DottaandPaperclip 9661dd6473 fix(runner): stage installed dependency pack inputs
Preserve non-bundled package contents outside the pnpm tree before npm packs them. Keep bundled and root-package behavior and clean-consumer assertions unchanged.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 19:59:19 -05:00
DottaandPaperclip 3eeaf69a96 test(runner): capture warm attach evidence before epoch rotation
Observe the retired authority at its durable prepared commit rather than the later attach observer. Add a forced post-activation lost-ACK observer regression while preserving event, ACK, identity, process, and cleanup assertions.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 19:59:19 -05:00
DottaandPaperclip 03090216b7 fix(runner): bind Copilot permission grants to visible targets
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-30 19:54:27 -05:00
DottaandPaperclip c8f874311c fix(ui): hide Google connectors only on the Connections page (#14774)
## Thinking Path

> - Paperclip helps people manage AI agents for work.
> - The Connections page lists the apps and saved accounts that agents
can use.
> - Google Workspace verification is still pending.
> - Google entries must be temporarily hidden from this page without
removing their implementations.
> - This PR filters the final page rows, including saved Google
accounts, after the page resolves their provider.
> - Definitions, direct setup routes, OAuth profiles, credentials, and
runtime access stay intact.
> - Review instances can keep the prior UI by staying on their pinned
app release.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Temporary provider visibility on the Connections landing page.

**Current behavior**

The page can show Google Workspace catalog entries and saved accounts
while verification is pending.

**Proposed behavior**

Hide all nine Google Workspace rows on this page. Keep every other
connector and all Google integration code unchanged. Use an existing
release pin for review instances instead of a hostname exception in the
app.

**Reason and benefit**

Pause public discovery without disabling existing runtime tools or
removing the implementation needed for verification and later
re-enablement.

**Breaking changes**

Google accounts are no longer visible on this landing page. Direct setup
and management routes remain available. This is not an access-control
restriction.

Related completed work: #13551 used catalog-level visibility. This
change is deliberately limited to the landing page and also covers saved
account rows. #14740 reduced Google scopes; this change leaves those
scopes unchanged. No duplicate open PR or matching open issue was found.

## What Changed

- Derive the Google app slugs from the existing Workspace profile
registry.
- Filter the combined catalog and saved-account rows only inside
`Browse`.
- Cover all nine Google entries, active/draft/disabled accounts, legacy
connection metadata, mixed-provider rows, and independently identified
non-Google connectors in regression tests.
- Document the display-only hold, pinned review builds, and how to
restore visibility after approval.

## Verification

- Passed: `pnpm exec vitest run ui/src/pages/apps/Browse.test.tsx
ui/src/pages/apps/AppsConnect.test.tsx` (199 tests, including the latest
master changes).
- Passed: `pnpm check:token-gates`.
- Passed: `pnpm build`.
- Passed: `pnpm -r typecheck` and `pnpm build` after merging the latest
master. An earlier overlapping run hit a local runner codesign race;
sequential checks passed.
- Passed again after the final custom-provider fix: `pnpm --filter
@paperclipai/ui typecheck` and `pnpm --filter @paperclipai/ui build`.
- The full local `pnpm test:run` was started, then stopped after the
full remote CI suite passed to avoid continuing duplicate long-running
work on the developer machine. It is not claimed as a completed local
pass.
- All 54 latest-head CI checks passed. Two non-applicable Storybook jobs
were skipped. One serialized server job lost its self-hosted runner
connection; its single retry passed.
- Greptile: 5/5 on `aeda167bf4494feed6ee0de2585960511fb02918`, with no
unresolved review threads.
- Confirmed in the existing review instance that all nine Google entries
still appear after its current release was pinned. No new app release
was deployed to that instance.
- Reviewer steps: open Connections on this branch with Google catalog
entries and saved Google accounts. None should appear. Non-Google
connectors must remain. Direct Google setup routes must still load.

## Risks

- Existing Google accounts cannot be found on this page during the hold.
Their data and runtime access remain unchanged.
- This is a UI-only filter, not an authorization gate. Direct routes and
API access still work by design.
- Review instances must not receive this UI build until the hold is
removed. Their existing release pin excludes fleet app upgrades; an
explicit targeted upgrade must still be avoided.
- No migrations, backend changes, broker changes, or credential changes.

## Model Used

OpenAI Codex (GPT-5-based coding agent), with reasoning, tool use, code
execution, and browser inspection. The exact deployment model ID and
context window are not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#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>
canary/v2026.930.0-canary.17
2026-09-30 18:22:25 -05:00