Commit Graph
2 Commits
Author SHA1 Message Date
Devin FoleyandPaperclip 49b3804830 fix(connections): load permissions and show AI account identity (#18653)
## Thinking Path

> - Paperclip manages AI agents and their access to saved connections.
> - The Permissions page loads account details and permitted agent
choices.
> - Company-wide AI accounts repeat retained installation checks for
every candidate agent.
> - This makes target checks grow with the square of the roster size.
> - The browser waits without a deadline, and connection names do not
identify the provider login.
> - This pull request batches direct AI checks and bounds loading with
recovery controls.
> - Account details load separately and show a provider email or an
explicit profile-permission limit.

## Linked Issues or Issue Description

**What happened?**

Opening a saved AI account can leave Permissions on “Loading AI
account…” for minutes. The account itself can be connected. The existing
capability batch excludes AI connections, so a company-wide install
repeats checks for every installed agent for every candidate. Saved
names and Paperclip ownership also do not establish which provider login
a credential uses.

**Expected behavior**

Direct AI accounts reuse retained-target checks within one descriptive
request. The page shows loaded account controls or a bounded failure
with Retry. Permissions shows the provider email and organization when
the selected credential permits profile access. An unavailable profile
must not block maintenance or disconnect a working inference credential.
Writes still authorize current permissions.

**Steps to reproduce**

1. Save an AI account in a company with several agents.
2. Install the account company-wide.
3. Open its Permissions page.
4. Delay the account-list or grants response to exercise the loading
failure.

The maintained benchmark reproduces database work with synthetic local
data. Refs #17945, which added batching for non-AI connections. Searches
found no open PR for this AI-account loading fix.

## What Changed

- Include direct AI accounts in the existing board capability batch.
Referenced pool members, delegated actors, complex selectors, and
mutation authorization keep their existing evaluator.
- Add opt-in per-caller GET deadlines to the API client. A timed-out
caller detaches the stuck coalesced request so Retry starts a fresh
fetch. Other callers retain their own cancellation and results.
- Apply a 15-second deadline to AI account lists and grants reads. The
permissions view retries transient failures once and reports other
failures immediately.
- Share the parent Permissions grants query with AI account details.
Keep loaded controls visible after a background error and offer Retry.
- Add a grant-scoped identity GET and OpenAPI contract. The service
checks current membership and credential audience before it reads the
selected credential. It projects only the provider email and
organization; provider IDs, tokens, and raw errors stay private.
- Show account details independently of maintenance controls. Anthropic
profile denial has an explicit message and preserves connected status.
Transient profile failures have a separate Retry action. Observations
are cached for five minutes under the company, connection, grant, and
grant revision.
- Extend real PostgreSQL and authenticated HTTP coverage,
client/component regressions, map guidance, and the existing benchmark's
AI mode.
- Address the Apex file-size finding by moving gallery identity lookup
into the existing app display helper module. `AppDetail.tsx` is 984
lines; lookup behavior is unchanged.

## Verification

Local source checkout on macOS, Node 25.6.1, pnpm 9.15.4, and disposable
PostgreSQL 18.1. Provider calls use fixtures. No hosted deployment or
provider qualification is claimed.

- Passed `pnpm -r typecheck` and `pnpm build`.
- Passed all 87 tests in `connection-management.integration.test.ts` and
all 33 tests in `connection-install-capabilities.test.ts`.
- Passed 196 AI connection, identity-provider, and OpenAPI tests:
`ai-connections.test.ts`, `ai-account-identity.test.ts`, and
`openapi-routes.test.ts`.
- Passed 107 focused UI/client tests: `client.test.ts`,
`ManagedAiConnectionDetails.test.tsx`,
`AiConnectionAccountDetails.test.tsx`, and `AppDetail.test.tsx`.
- After the Apex extraction, passed UI typecheck/build and 106 component
tests across `AppDetail.test.tsx`, `AppNotConnected.test.tsx`,
`ManagedAiConnectionDetails.test.tsx`, and
`AiConnectionAccountDetails.test.tsx`.
- The authenticated HTTP tests compare complete grants responses across
human roles, for tool and AI connections. They verify a real install
mutation succeeds before revocation and fails afterward.
- A company-wide personal AI fixture checks 20 targets instead of 400.
Pool-member permission loss still denies the complete pool description.
- UI states covered: shared cached grants, stuck account/grants fetches,
one automatic retry, terminal failure, manual recovery, authorization
failure, background failure with retained controls, and unmount
cancellation. Client tests also cover stalled response bodies and
independent coalesced callers.
- Identity coverage includes actual bearer-key/JWT reads, private-user
and cross-company denial before provider access, revoked credentials,
provider 401/403/429/500, and grant-change isolation. A real local HTTP
fixture stalls the response body, verifies the shared 15-second
deadline, and keeps unrelated HTTP requests responsive. The test
shortens the native deadline to 100 ms. Profile permission denial leaves
the saved account connected.
- Passed `pnpm check:token-gates`, `node scripts/check-maps.mjs`, `node
scripts/check-module-boundaries.mjs`, and `git diff --check`.
- The full local `pnpm test:run` started with the loading-only change.
All six chat shards passed. It recorded one failure in the unchanged
`heartbeat-retry-scheduling.test.ts` quota-rejection case. That exact
case passed a focused rerun on the final head. The initial failure cause
remains unconfirmed and its local log is retained; this is not a full
local-suite pass. The redundant local run was stopped with SIGINT after
final-head CI completed successfully.
- [Final-head
CI](https://github.com/paperclipai/paperclip/actions/runs/41515556725)
passed on `ead8d276d4a2023614fff54b3e9a858f28c3178b`: typecheck, build,
all 12 server shards, all 3 chat shards, workspace suites, all 9
serialized suites, all 8 end-to-end shards, Runner checks, and the
aggregate verify gate. Security checks passed. Optional Storybook checks
were skipped by workflow policy. GitHub reports the branch mergeable
without conflicts.
- Apex reviewed head `ead8d276d4a2023614fff54b3e9a858f28c3178b` at 5/5,
confirmed the extraction resolves its earlier file-size finding, and has
no unresolved review threads.

The existing benchmark ran for the loading-only change now recorded in
commit `5f725b36fa52002545241a5a8b7a10602f0cc0ce`, with `--purpose=ai
--baseline-ref=026fd9e3452`. The identity follow-up leaves that batch
source unchanged. Two warmups and five alternating samples exercise each
of eight fixture shapes. Complete positional capabilities, reasons, and
operations match the baseline in every sample.

| AI fixture | SQL calls before → after | Target reads before → after |
| --- | ---: | ---: |
| 1 agent, company install | 9 → 9 | 1 → 1 |
| 32 agents, company install | 5,248 → 164 | 1,024 → 32 |
| 100 agents, company install | 50,400 → 504 | 10,000 → 100 |
| 100 agents, partial install | 13,175 → 503 | 2,575 → 100 |

These counts measure the real service over local PostgreSQL. They do not
predict hosted page latency. To reproduce the standalone benchmark, use
a checkout of commit `5f725b36fa52002545241a5a8b7a10602f0cc0ce` and run
the command below. Its isolation check intentionally rejects the later
identity follow-up's additional runtime changes.

```sh
node --import ./server/node_modules/tsx/dist/esm/index.mjs scripts/benchmark-connection-install-capabilities.ts --purpose=ai --baseline-ref=026fd9e3452
```

Run the final-head regression suites from the repository root:

```sh
cd server
pnpm exec vitest run src/__tests__/connection-management.integration.test.ts src/__tests__/connection-install-capabilities.test.ts src/__tests__/ai-connections.test.ts src/services/ai-account-identity.test.ts src/__tests__/openapi-routes.test.ts
cd ../ui
pnpm exec vitest run src/api/client.test.ts src/components/ai-connections/ManagedAiConnectionDetails.test.tsx src/components/ai-connections/AiConnectionAccountDetails.test.tsx src/pages/apps/AppDetail.test.tsx
```

## Risks

Descriptive permissions reuse retained facts within one request. A
concurrent change can leave a displayed choice stale; the next request
and every write still authorize fresh. Pools with referenced members
retain their existing cost.

The account and grants clients now bound each caller's wait. A browser
abort does not cancel an authorization read already admitted by the
server. The independent profile request uses a fixed Anthropic origin, a
bounded body, no redirects, and a 15-second deadline. Inference-only
credentials can return no email; that is reported without guessing from
a Paperclip owner or changing connection health. Profiles are fetched
only for the caller's authorized selected grant. No ambient CLI
identity, persistent identity cache, background polling, or
billing-identity change is added. Other API reads keep their existing
behavior. No migration is required. Staging has not been redeployed.

## Model Used

OpenAI `gpt-6.1-sol` through the Codex harness in T3 Code, with xhigh
reasoning, code editing, execution, and test tools. The host does not
expose the context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change 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-11-02 12:29:03 -08:00
DottaandPaperclip 2cd747d1b6 perf(server): batch connection install capability checks (#17945)
## Thinking Path

> - Paperclip manages AI agents and their access to external
connections.
> - The connection grants screen describes which agents can receive an
installation.
> - Each candidate currently repeats authorization for every retained
installation.
> - A company installation makes this work grow with the square of the
roster size.
> - This pull request checks retained targets once inside the existing
service owner.
> - The benefit is fewer authorization calls with the same scoped
capability results.

## Linked Issues or Issue Description

**What happened?**

The grants GET repeats connection preparation and retained-target checks
for each candidate agent. A local 100-agent company-install fixture made
50,300 SQL calls and 10,000 target lookups in one capability-description
operation. Open PR searches found no matching capability fan-out fix.

**Expected behavior**

The description must preserve retained-installation obligations and each
candidate's permission. It should evaluate retained targets once per
description. Writes must still authorize fresh.

**Steps to reproduce**

Run the committed benchmark with
`--baseline-ref=f6bc653f7f745adaafa26393852fb2f5a99256a4`. It creates
disposable synthetic PostgreSQL fixtures and loads the actual pre-fix
service from Git. It compares every positional capability, reason, and
operation.

**Paperclip version or commit**

Baseline `f6bc653f7f745adaafa26393852fb2f5a99256a4`.

**Deployment mode**

Built from source. Local real PostgreSQL proof. No hosted rollout.

## What Changed

- Add `describeAgentInstallCapabilities` to connection management.
Ordinary authorization and the batch share preparation, target checks,
and fallback completion.
- Use the batch only for direct saved non-AI connections and board
actors. Complex resources, delegated actors, and unusual saved target
IDs keep the ordinary evaluator. Fallback consumes the first preparation
once.
- Replace only the grants route's editable-agent loop. Keep
configure/use checks, identity filtering, audience disclosure, and write
authority on their existing paths.
- Add scoped-grant, retained-target, error-order, freshness, fallback,
full HTTP response, and real mutation regression coverage. Add a
reproducible benchmark and evidence notes.

## Verification

Real PostgreSQL 18.6 Linux Docker, Node 26.4.0. Two warmups and five
measured samples per arm, in alternating order. These are
service-component measurements with the same SQL-count instrumentation
in both arms. They do not measure the whole hosted endpoint.

| Fixture | Old median | Batch median | SQL calls old → batch | Target
lookups old → batch |
| --- | ---: | ---: | ---: | ---: |
| 100 agents, company install | 24.753 s | 246.39 ms | 50,300 → 503 |
10,000 → 100 |
| 100 agents, partial install | 6.339 s | 236.37 ms | 13,075 → 502 |
2,575 → 100 |
| 1 agent, company install | 5.13 ms | 4.82 ms | 8 → 8 | 1 → 1 |

All eight fixture shapes and every measured iteration had complete
positional equality. One-agent timing ranges overlap. Full ranges,
database settings, and limits are in
`doc/plans/2026-10-27-connection-grants-batch-verification.md`.

- **Passed:** `pnpm -r typecheck` and `pnpm build`, using pinned pnpm
9.15.4.
- **Passed:** 116 focused tests: all 83 connection-management
integration cases use real PostgreSQL and HTTP; 33 batch regressions
execute real policy over synthetic query transport. The integration
cases keep existing database creation, migrations, clients, services,
and assembled HTTP server, with disposable Docker PostgreSQL.
- **Passed:** complete grants GET parity across six human-role fixtures;
real install PUT succeeds before revocation, then fresh GET and actual
PUT deny after revocation without changing saved installs.
- **Passed:** a real PostgreSQL red/green regression for empty saved
target selectors, including candidate-specific decisions and later
reached infrastructure faults.
- **Passed:** independent old/new evaluator comparisons, including
recursive and delegated fallback, run-context reuse, continuation,
failure order, and principal/company isolation.
- **Local full-suite limit:** `pnpm test:run` was attempted and stopped
after native PostgreSQL fixtures could not initialize. An isolated
failed native case reproduced that startup error. The host cannot
allocate PostgreSQL SysV semaphores, including outside the sandbox. The
focused suites use an existing constructor seam to start disposable
Docker PostgreSQL. This is not a complete local full-suite pass.
- **Passed:** all 16 native-runtime cases that failed to start
PostgreSQL on the host, rerun with the disposable Docker provider. The
full local workflow remains incomplete.
- `git diff --check` passes. Greptile is 5/5 with no actionable findings
on final head `41dcf3ae029b6c60999ca6208136399b0af663d0`. All final-head
CI gates passed, including the full PR workflow. The two Storybook
checks are expected skips because this change has no UI changes.

Normal reproduction:

```sh
node --import ./server/node_modules/tsx/dist/esm/index.mjs scripts/benchmark-connection-install-capabilities.ts --baseline-ref=f6bc653f7f745adaafa26393852fb2f5a99256a4
cd server
pnpm exec vitest run src/__tests__/connection-install-capabilities.test.ts src/__tests__/connection-management.integration.test.ts
```

## Risks

Retained facts are observed once during one descriptive call. A mid-call
revoke or deletion can leave a stale displayed choice. The next
description and every mutation or human continuation read fresh
authority. The batch returns no reusable authorization context.

The bound applies to target evaluations in the eligible shape. Scoped
grant payloads and hierarchy reads can still grow inside each policy
decision. Complex resources and agent callers keep existing costs.
Hosted latency improvement remains unverified. There is no migration,
provider protocol change, new worker, persistent cache, or UI contract
change.

## Model Used

OpenAI Codex with code editing, execution, and test tools. Design, test,
and independent review agents used exact model IDs `gpt-6.1-sol` and
`gpt-6-astra` with xhigh reasoning. The host did not expose the primary
session's exact model ID or context-window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 20:59:01 -05:00