Files
PaperClipAI/maps/feature-map/app-permissions.md
T
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

10 KiB
Raw Blame History

navigation
navigation
section sectionOrder order
Apps, channels, and extensions 50 20

App access and action permissions

After connecting an app, an operator or authorized agent decides which agents may use it and whether each action is off, requires approval, or is allowed. Activity and action tests help verify the resulting access.

Feature ID: app-permissions.

Implementation: app detail, permissions, activity.

Agent access: companion.

Sub-features

  • agent-access: app-permissions/agent-access — grant the connection to all or selected eligible agents.
  • action-policy: app-permissions/action-policy — set off/ask/allowed independently for available actions.
  • catalog-change: app-permissions/catalog-change — review newly discovered/quarantined actions before enabling them.
  • test-action: app-permissions/test-action — choose an actor and inputs and inspect a real action result.
  • account-maintenance: app-permissions/account-maintenance — inspect accounts, reconnect/revoke, and review connection activity.

Changing the connection's agent list preserves profile assignments for agents that remain installed. Saving the list must not restore a removed app-profile assignment. New installations retain the current app-profile defaults. Non-empty saves also remove legacy connection-wide include rules, including unchanged lists and removals with no new targets.

How to get to it (user POV)

permissions

Open an app at /apps/:connectionId/permissions; this is the default connected-app view.

test-and-maintain

Use the action test control in Permissions, then the connection’s Activity and available account/advanced controls.

delegated-permissions

Ask an agent to change access or action policy for a named connection through the existing APIs. The responsible person's current authority, the agent's management grants, resource scope and all affected targets bound the request. An explicitly requested, authorized policy change may target that same agent.

Driving it

Preconditions: follow the baseline. Prepare a connection to a fixture or test provider, two agents, and a harmless action. Record provider and credential ownership.

permissions

Automated: app detail covers agent/action controls, quarantined actions, and navigation using mocked requests.

Installation permission regressions exercise the real install API with a local operator and PostgreSQL. They check that adding, removing, or retaining agents preserves removed app-profile grants and denies write tools for agents with read-only profiles. They also check company installations, new-agent defaults, legacy connection-wide include cleanup, and access-extension audit records. These fixtures do not qualify an agent-authenticated request or a live provider.

Runtime assignment tests use PostgreSQL to verify committed assignment notifications, bounded snapshots, rollback, and concurrent AI adoption. The owning lifecycle plugin can read standing assignments through ctx.agents.getRuntime; this does not grant tool execution access or replace task-specific authorization.

Tool-definition runtime tests cover catalog invalidation, scoped policy/profile-entry recipients, ignored bookkeeping, profile-entry edits concurrent with setup finalization, and profile replacement concurrent with application deletion using PostgreSQL. They do not qualify live provider access.

Connector access runtime tests cover grants, delegation, membership cleanup, referenced vault changes, and agent-scoped secret bindings. OAuth and Railway tests exercise the corresponding credential lock order. Slack search and membership cleanup tests cover lock contention and complete transaction retries. Provider calls use fixtures; no live credentials are qualified by this coverage.

Manual: Grant one agent access, leave another excluded, and exercise off, ask, and allowed states for a harmless action. Reload between changes. Verify actual denied calls stay denied and ask-first creates a decision rather than executing early.

For Braintrust, distinguish reviewed action risk from effective policy: S4 methods retain the shared Allowed defaults. Explicit Off and Ask-first choices must survive refresh and reconnect. Exercise the read, write, and destructive classifications with a fixture, including billable tests classified as writes and dataset row edits/automation activation classified as destructive. After initial discovery, newly added or changed tools remain quarantined until reviewed. Use synthetic data for provider calls; see Braintrust. Live US OAuth proof covers bounded reads, a synthetic dataset insert/readback through actual process-agent gateway calls, and denial after local removal. Off, Ask-first, and changed-tool denial use fixture evidence; the other three methods still need live account qualification.

test-and-maintain

Automated: app detail tests cover test-dialog/UI contracts. Real provider execution and revocation require the selected provider’s live check.

AI account details share the Permissions page's grants query. Account and grants reads have a 15-second deadline. The AI panel retries a transient failure once, then shows an error with Retry. A background failure keeps loaded controls visible. AI account loading tests exercise stalled account/grants reads, cancellation, retry recovery and cached grants.

The account-details section shows the provider email and organization when available, beside the sign-in method and Paperclip ownership. Anthropic profile permission denial shows an explicit unavailable message without changing the connection's connected state. Account identity loads independently of maintenance controls, has a bounded wait and a separate retry for temporary failures, and is refreshed after a credential grant changes. Account-details tests cover these states and prevent one grant's email from appearing for another.

Manual: Run a read-only action with explicit inputs and inspect returned data and activity. Refresh discovery and review new actions. Revoke or disconnect only the disposable credential, then verify subsequent use fails with an actionable reconnect state.

For Daytona, opening an older saved catalog discovers the current native contract. Removed diagnostic actions disappear from Permissions and agent discovery; new or changed actions require review. Existing Off/Allowed choices remain in place. Daytona regressions cover shared/personal catalog upgrades, failed refresh, permission preservation, run-bound file transfers, and revocation with fixture provider responses.

Unikraft actions follow the same review rules: deletion, stop/suspend/detach and sandbox command execution are destructive, and catalog updates quarantine new or changed actions. Unikraft regressions cover classification, Off policies, revocation and stale contracts with fixture provider responses.

delegated-permissions

Automated: production-authenticated integration covers actual key/JWT policy edits, retained targets, caller/target test policies, human approval, scope and revocation using PostgreSQL and provider fixtures.

Manual: Request a bounded access or policy change through an actual agent, reload Permissions, and verify the saved audience, action choice and agent audit actor. Include an explicitly requested self-targeted policy edit. Repeat with missing management grants, an excluded target/resource, and Ask or Plan mode; writes must be denied. A denied tool call must not cause an unsolicited policy relaxation. An Ask-first action still requires the authorized human's decision; the agent cannot approve its own governed action or edit its own principal permissions. Verify caller and target policies both apply when testing as another agent.

Gotchas

  • Connected does not imply an agent has access or an action is allowed.
  • Credential-only connections may replace tool-action controls with account-specific controls.
  • See connection setup for auth and questions and approvals for pending action review.

Storybook verification

catalog-change

Capability ID: app-permissions/catalog-change.

Scope: ui Storybook: Mount PermissionsPanel/AppDetail with quarantined actions, selection/review, refresh pending, empty and failed discovery states. Existing auto-allowed NewToolsAllowed examples are not quarantine review. Production: Exercise catalog refresh and new-tool review with permitted/denied actors; unreviewed actions stay quarantined, prior Off/Ask choices survive refresh, and approved review persists. Sources: Source: PermissionsPanel.tsx, AppDetail.tsx, tool-access.ts. Tests: AppDetail.test.tsx, tool-access-service.test.ts, tool-access-policy-service.test.ts

delegated-configuration

See the canonical delegated configuration guidance and acceptance evidence.