## Thinking Path
> - Paperclip lets people manage agent work from tasks.
> - Agents can request app access in a task card.
> - Some apps first require Paperclip Cloud enrollment.
> - Enrollment could leave the task, and OAuth could lose the task
interaction ID.
> - This PR keeps enrollment in a separate window and retains the
interaction ID through OAuth.
> - The task can then recognize the connection and continue
automatically.
## Linked Issues or Issue Description
**What happened?**
A first Gmail connection could leave the task dialog during Cloud
enrollment. Setup resumed on the Apps page. Gmail connected, but the
task card could remain pending because OAuth did not retain its
interaction ID.
**Expected behavior**
Keep the task open and preserve its access choices. Resolve the card
after the server verifies connection access. Continue the agent
automatically.
**Steps to reproduce**
1. Start a fresh source test-drive instance without Cloud enrollment.
2. Ask an agent to read Gmail.
3. Open Connect on the task card.
4. Complete Cloud enrollment and Gmail authorization.
5. Check whether the task card updates without selecting the connection
again.
**Paperclip version or commit**
Reproduced on 35fdc0c66b. This branch
applies the fix to current master.
**Deployment mode**
Source test-drive in local-trusted mode. The shared setup code also
serves authenticated instances; live authenticated acceptance was not
performed.
Related PRs: #13058 introduced task connections. #12943 repaired expired
enrollment links. #12906 concerns Composio service matching and does not
fix this OAuth handoff.
## What Changed
- Open task enrollment in a reserved window, with a new-tab fallback.
- Refresh server enrollment status and the provider catalog while
keeping the task dialog and access choices.
- Return the enrollment callback to the verified task when available.
- Retain the interaction ID when OAuth resumes from the page host.
- Clear the server capability cache after enrollment and reject stale
cache writes.
- Close reserved popups on enrollment errors and invalid authorization
URLs.
- Add callback and setup regression tests, including blocked popups.
Update connection-intent documentation.
## Verification
- All 176 focused connector, enrollment, callback, OAuth, and setup
tests pass.
- Greptile gives commit `e58da6662` a 5/5 score. Both review threads are
resolved.
- `pnpm check:token-gates`, `pnpm build`, and `pnpm -r typecheck` pass.
- All CI checks pass for `e58da6662`, including the full test matrix,
browser tests, Runner verification, release registry, and canary dry
run.
- The serial local `pnpm test:run` was stopped after the complete CI
test matrix passed. It is not counted as a full local pass.
- Live browser test: fresh instance, native Codex runner, Gmail
read-only access, and a real Google account.
- Enrollment preserved the task dialog. The card changed to connected
and the agent called Gmail search and message-read tools without another
message or Run click.
- The connected card persisted after refresh. Google reused existing
consent during this attempt.
- The runner displayed only its completion summary. Full answer delivery
is a separate issue and is outside this PR.
## Risks
- Browsers can block or isolate authorization windows. The new-tab
fallback remains available, and the parent checks server state.
- Enrollment completion is only a prerequisite. It does not grant access
or resolve the task card by itself.
- No database migration, runner lifecycle change, or recovery UI is
included.
## Model Used
OpenAI Codex (GPT-6), with code editing, shell tools, and browser
automation. The session does not expose an exact runtime 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>
9.3 KiB
Connection intents
Connection intents let an agent ask the responsible user for a known service connection without leaving the task thread. The user can reuse an eligible connection or run the normal provider setup in a dialog. A successful resolution grants and installs the connection for the requesting agent, then wakes the task assignee in a fresh run.
Shared setup flow
ui/src/features/connections/ConnectionSetupFlow.tsx is the only connection setup implementation. It owns provider selection, method and identity choices, provider fields, validation, OAuth, access, catalog setup, installs, retry states, and completion. It has two presentation hosts:
ui/src/pages/apps/AppsConnect.tsxsupplies full-page routing and breadcrumbs.ui/src/features/connections/ConnectionIntentInteractionBody.tsxsupplies the task dialog, intent resolution, query invalidation, and focus return.
Provider-specific setup must stay in the shared feature and AppDefinition metadata. Do not add provider forms or connection mutations to either host.
When a task connection needs Paperclip Cloud enrollment, the shared dialog opens enrollment in a separate window. The task keeps its access selection and interaction ID. A new-tab link is available if the window does not open. The dialog reads server enrollment status and refreshes the provider catalog after approval; enrollment alone does not mark the app connected. OAuth retains the interaction ID even if setup resumes in the page host, so the verified callback can resolve the task card and queue its continuation.
Agent tools
Every active heartbeat with a responsible user receives two run-bound tools:
connections_search({ query })searches catalog names and descriptions, plus authorized configured MCP connections and indexed tool descriptions and returnsready,needs_user_action,available, orunavailablefrom the requesting agent's perspective.connection_request({ service })returns immediately when the service is already usable. Otherwise it creates or reuses aconnection_intentand instructs the agent to finish independent work, then yield pending continuation.
The native Paperclip Runner advertises both tools through its server-owned tool authority even with an empty MCP assignment. It captures the current responsible identity at each call. Legacy Claude and Codex receive the tools through a managed MCP server. Local/process adapters receive PAPERCLIP_RUNTIME_TOOLS_* environment variables and CLI guidance. Cloud, HTTP, gateway, and external adapters receive the typed runtime descriptor in their invocation context; compatible adapters may also project it into their remote environment.
Legacy delivery uses the same intent service, setup card, and fresh-session resolution wake. Environment and descriptor delivery require the receiving harness to consume them; they do not establish support in every third-party runtime. The default legacy prompt includes the canonical discovery guidance. A custom promptTemplate replaces that default and should retain the connection guidance if proactive discovery is desired.
The equivalent CLI helpers are:
paperclipai connections search notion
paperclipai connections request notion
The manually configured Paperclip MCP server also advertises connections_search and connection_request. Both helper surfaces require the narrow runtime token and fail outside an active heartbeat.
Security and lifecycle
- Company, agent, run, task, and responsible user come only from the signed legacy runtime token or the native server-owned binding and stored execution identity.
- Tokens are scoped to connection intents, expire after one hour, and are rejected when the heartbeat is no longer running.
- The thread payload contains only service identity, requesting-agent identity, and a safe phase. It never contains credentials or authorization URLs.
- OAuth state is linked to the interaction. The same-origin callback finalizes the existing connection pipeline, posts only interaction ID/outcome to its opener, and redirects back to the task if there is no opener.
- Personal OAuth defaults to the addressed user and creates an explicit delegation to the requesting agent. Reuse and installs are additive.
- Task-hosted setup locks install reach to the requesting agent; the store host retains its normal broader access choices.
- The intent resolves only after the connection, grant/delegation, profile access, and install succeed. Failures remain pending with
needs_retry. - Closing or reassigning the task expires pending intents and deletes linked OAuth state. Ordinary comments and later runs preserve the pending card. Requests reuse the same task, requester, addressed user and service; a different addressed user supersedes an older request for that service.
- Success and explicit decline atomically persist a continuation delivery with resolution. A leased startup/periodic worker dispatches through heartbeat with a unique
connection-intent:<interaction>:<outcome>wake key. It checks assignment, status, membership and current executable access, retries paused/suppressed delivery, and recovers a crash after enqueue without creating a second wake. The continuation forces a fresh provider session; heartbeat queues it behind active execution.
Legacy request_confirmation.payload.connectionAuthorization interactions remain readable and resolvable. New agent requests use connection_intent exclusively.
Model evaluations
The companion paperclip-evals repository owns the connection cases in
evals/runner-api-tools/connection-cases.json. They use the existing real-server
API-tool eval controller and scripts/runner-api-eval-worker.ts, with this
checkout's production tool definitions, connection guidance, and authority.
Natural prompts measure discovery and request selection; explicit contract probes
measure deduplication, readiness, and denied targets. The fixture helper supplies
isolated company records and retains initial and final connection interaction
state. Scoring requires observed calls/results and persisted state, not an
assistant's claim of having connected.
These tool evals do not perform live provider OAuth or establish browser quality. The native and legacy connection browser suites separately cover setup and continuation, while the live-provider journey report records actual authorization and data-read coverage.
Custom targets, readiness and recovery
Catalog slugs remain stable. Search also returns connection:<uuid> for configured custom connections whose active identity grants authorize the responsible person, their company, or the requesting agent. Identifiers are never interpreted as URLs. Configured metadata and tool descriptions, including catalog-provider descriptions, are read only after the company and identity audience checks. Setup choices expose only display and selection metadata; they never include connection configuration, transport settings, or credential fields. Discovery reads the stored index without refreshing providers. Search is ranked with exact provider matches first and capped at 20 results.
ready requires an installed, enabled, healthy executable connection, permitted catalog tools, and a usable runtime identity. An installed connection with denied actions is administrative denial rather than a request to reauthenticate. Runtime calls continue enforcing access after a historical card resolves. If access is ready but the native provider's pinned tool snapshot is older, connection_request queues a fresh session without another authorization card.
Task setup defaults to personal identity when supported and the requesting agent's install reach. Existing installs are additive. An OAuth callback from a task prepares the catalog without adding access. Intent completion validates the current task and identity, then adds the requesting agent’s binding and install in the resolution transaction. Callback messages do not establish authorization: the card reloads the durable server result. A blocked popup offers a new-tab fallback; closing or declining provider sign-in keeps the request retryable. Only the card's Not now action declines the request. Decline continuations are told to pursue alternatives and cannot immediately request the same service again.
Verification
Service and native-authority tests cover discovery, current identity, company boundaries, cross-run deduplication, additive grants and installs, permission denial, resolution atomicity, restart delivery and stale assignment. tests/e2e/in-feed-native/playwright.config.ts starts source test-drive instances with fresh data directories and a deterministic fake Codex provider plus MCP server. It exercises the real native runner and gateway; it is fixture proof, not live Notion or GitHub proof. Run with pnpm exec playwright test -c tests/e2e/in-feed-native/playwright.config.ts.
Offline Storybook examples live in ui/storybook/stories/in-feed-connections.stories.tsx. Build with pnpm --filter @paperclipai/ui build-storybook, then run pnpm exec playwright test -c tests/storybook-visual/in-feed-connections.config.ts. The suite checks every independently addressable story in both themes, catches play-function failures, and saves screenshots. Live provider acceptance additionally requires a model credential and a test workspace/account; do not describe fixture results or local-trusted testing as authenticated/cloud acceptance.