From 0c9d10d695f772a311d596bb43837864be366387 Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Tue, 27 Oct 2026 08:21:02 -0500 Subject: [PATCH] feat(connections): complete saved setup guidance and coverage maps (#17739) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Product and agent access maps describe the same capability IDs. > - Agent connection management changes the callable operations and permission boundaries. > - Reviewers need an operation inventory that distinguishes API access, human handoffs, and fixture evidence. > - This pull request adds saved setup screens and updates the maps for #17737. > - The benefit is that users and contributors can inspect the coverage and remaining boundaries. ## Linked Issues or Issue Description **Subsystem affected** Product and permission documentation, saved authentication setup UI and consent metadata, and product/agent access maps. **Problem or motivation** The current maps describe board-only management paths. They need to match delegated connection management and distinguish administration from ordinary tool execution. **Proposed solution** Record route operations, required grants, responsible-user limits, transport, saved authentication handoffs, provider-fixture evidence, and explicit local-operator boundaries for every connection family. **Roadmap alignment** This is the documentation companion to #17737. It covers existing Connected Apps, governed MCP gateways, chat channels, and personal/shared AI accounts. It uses existing server setup operations and adds their saved handoffs to the existing UI. ## What Changed - Show saved AI sign-in and connection setup handoffs in the existing UI. - Open GitHub import and rotation links at the required human step. Resume a committed rotation after reload without asking for another secret. - Restore saved Google service choices for the addressed user while preserving newer consent. Successful direct/cloud consent records its own timestamp, independently of refresh or lease writes; existing refresh paths preserve it. - Update product, implementation, and permission-default documentation. - Reconcile app permissions, connection setup, chat channels, profiles/gateways, and model account coverage. - Use matching capability IDs in the product and agent access maps. - Label source-reviewed operations separately from fixture-tested operations and live provider setup. - Preserve catalog creator attribution under the responsible user, with the actual agent still recorded. This follow-up moved from the implementation to keep both reviews below 100 files. This companion now changes 38 files, including the preserved Vercel personal-credential and OAuth callback attribution follow-ups alongside consent and catalog ownership. - Name the addressed-human browser-session and consent step for Slack managed authorization and installation. Preserve source-only evidence for those individual operations. ## Verification - Current companion head: `04d40cdd605452d91cf4931476eb292a210825af`, including implementation `2ecd6451d0` and master `ea727dc3c2`, 38 files. All intended companion files survive: 35 are byte-identical to the previous companion; two chat maps and qualification add current evidence. Exact-head Superagent passed and Paperclip Review supplied an actual neutral5/5 assessment with no findings and explicit no-independent-runtime/live/current-CI verification limits. Exact-head Greptile also completed at5/5 with no findings; both formerly failed shards and their verification aggregate passed on attempt2; full exact-head Linux CI workflow40446969603 completed SUCCESS. All current checks and statuses are terminal clean, with no unresolved findings. This PR remains dependent on #17737 and needs fresh checks after its required post-squash replay. The preceding 605 head’s reviews are historical after this update. Older b0ea Linux workflow success after its single rerun also remains historical. - Earlier preserved-stack verification passed **2,165 tests**: 70 assembled-server actual-auth scenarios, 27 OpenAPI contracts, 2,022 native API tests and 46 saved GitHub wizard cases in fresh PostgreSQL. The parent also passed **141 existing gateway/policy tests**. Workspace typecheck/build, maps and token gates passed. The initial typecheck IPC denial and all material intermediate regressions remain in the qualification record; no full local sweep or live provider setup is claimed. - Operation maps now cover issuer/target gateway policy intersection and post-approval revocation, bounded activity pagination through private-only windows, and native local-login encrypted receipts. The companion preserves the Vercel OAuth callback ownership change with its existing credential ownership follow-up. - Parent CI has one retained connection-intent e2e failure from a periodic-recovery timing race. GitHub refused the first job-rerun attempt while its workflow was nonterminal. Once completed, the single failed-job rerun was accepted as attempt 2 and passed, including the full parent Linux workflow. - Companion Linux server shard 4/12 retained a cleanup-idleness timeout in `login-cleanup-work.test.ts` (active work was still 1 after its five-second wait). The same unchanged cleanup suite passed all 16 cases in the parent Linux job 121950185096 and all 16 cases locally against a fresh disposable PostgreSQL database. Relevant cleanup/reaper/scheduling runtime code is unchanged. The single unchanged-code failed-job rerun was accepted as **attempt 2** on the previous `b0ea3c940f` head (workflow 40431671705). The failed shard and complete Linux workflow passed on attempt 2; no additional unchanged-code rerun was used for this failure. The first local attempt failed the existing dedicated `_test` database-name guard before running tests; the correctly named fixture passed without weakening the guard. - **Earlier 82 GitHub UI tests passed**. All three new replacement cases were red before the fix. A confirmed unchanged generation preserves the earlier copy; a changed or unreadable generation blocks copying and reconnecting. An explicit status check can restore the copy when it is still current. All earlier import, rotation, verification, and retry states remain covered. - The complete stack passed **82 API/OpenAPI tests** in fresh PostgreSQL, including 55 scenarios with real agent keys, run JWTs, human sessions, production defaults, persistence, and deterministic providers. The parent also passed **60 Copilot installation tests**, preserving published v62 and binding combined guidance as v63. - **Earlier 10 focused Google/OAuth tests passed**: eight picker cases and two local-HTTP protocol cases proving consent metadata survives concurrent token refresh. The refresh and legacy UI cases were red before the fix. These protocol fixtures use the existing injected board actor and do not count as agent-authenticated proof. - Workspace typecheck/build passed after the consent change; normal migration generation reported no SQL changes for the additive JSON metadata. The parent's latest caller-authorization correction passed server typecheck/build. Map validation and token gates passed. Maps cover 35 areas and 192 capability IDs. The specification names migration `0380_broken_aqueduct.sql`. - The test-call operation maps now record the full caller/target tool access decision (profiles, policies, and explicit grants), credential identity for every auth label, approval, and rate checks. The profile-only precondition was removed after the new permitted-access cases failed; those cases and the no-auth identity regression now pass. Saved test status requires its originating principal and current linked-task access; actual key/JWT privacy-denial evidence is recorded. - Tool-connection activity is now operation-verified: 100 events over two tasks require two capability decisions and at most two run captures (previously 100/102), preserve private-event filtering, and deny the next request after audit revocation. The regression was red before the fix. - Cloud enrollment creation and callback entries now name instance-administrator and company/principal checks. The callback is a browser return with HTML or a 303 redirect. Status reads retain their separate board-actor rule. - Operation maps identify the API, transport, authorization, evidence, and human/operator boundaries. Structural checks do not establish live provider behavior. Earlier failures and fixture corrections remain in `doc/plans/2026-10-13-connection-api-parity.md`. - An earlier companion head (`26edbe17`) received an actual Paperclip Review assessment of 5/5 with no actionable findings, published with neutral status. That review did not run tests or confirm CI, and it is historical for this new head. The prior incomplete execution result is not counted as a passed assessment. Fresh review remains pending. The duplicate enrollment findings have explanatory replies and are resolved. - The latest implementation correction passed all 55 actual-auth scenarios and 27 OpenAPI contracts (separate commands). It repairs unavailable old credential material, preserves fresh import versus configured reconnect links, and distinguishes credential field sets/completion modes in handoff keys. The new fresh-link and collision cases were red before the fix. All 41 GitHub wizard tests now pass, including filling the fresh saved handoff and asserting App import rather than reconnect; UI typecheck, maps and token gates also pass. Qualification retains the earlier failures and an incorrect OpenAPI filename that selected only the integration module before the correct separate contract run. - Configured GitHub credential-entry links carry canonical requested fields and all/any mode. The form displays and submits those values through human reconnect while retaining other saved credentials. Two webhook/client-secret cases were red before the fix; combined all/any and legacy-link cases also pass. The current UI fixes the old bf760912 baseline referenced by the duplicate main review finding. - After merging master baec3abd67 and regenerating migration0380, the complete stack passed **128 tests** (55 actual-auth, 27 OpenAPI and 46 GitHub wizard cases), workspace typecheck/build, maps and token gates. Eight current Google UI cases also passed. The two local-HTTP OAuth cases could not initialize embedded PostgreSQL in this fresh attempt, so that attempt is not a protocol pass; the earlier ten-case evidence is historical and fresh Linux CI remains required. - The reproduced Linux automatic-Slack challenge failure is fixed by retaining verification only for the same signing fingerprint and callback URL; all eight neighboring provider compatibility cases pass. Qualification also retains the uncommitted GitHub field variant that failed initial-secret compatibility before the extension was narrowed to reconnect, and the first unmigrated external-database diagnostic. Upstream 0379 remains intact; normal generation/pruning produced additive/idempotent 0380, with repeated application verified. - Both PRs must ship together. The user authorized merging when checks pass. Merge #17737 first, replay only this companion's changes onto master, retarget this PR, and require fresh current-head CI and review before its squash merge. - The updated maps distinguish authorized bot-token/API-key setup from human verification-key entry and forbid generic channel secret-reference replacement. Actual key/JWT security baselines were red before both guards; all 82 API/OpenAPI cases, eight native receipt protections, and 49 policy-service tests now pass. The qualification record preserves the initial lifecycle/fixture/union-assertion diagnostics and the preceding Linux CI assertion failures. Maps pass on this updated stack. Fresh exact-head CI and reviews remain required. - The parent now preserves a per-field credential-change boundary before retiring saved credential-entry cards. Old health, unchanged entry, unrelated repair and retries cannot bypass it. Current authority is checked before and inside credential persistence; a real provider-time revocation regression was red before the fix. Final 82 API/OpenAPI tests, four neighboring lifecycle cases, and server typecheck/build pass. Maps pass, and qualification retains the hidden-metadata fixture failure and the measured minute-boundary rate-test failure. Production rate behavior is unchanged; only the relevant test Date clock is fixed. - After the current master/parent refresh, all **341 affected UI cases**, combined workspace typecheck/build, maps and token gates pass. The updated parent also passed **97 actual-auth/OpenAPI cases**, **141 gateway/policy cases** and **61 profile installation cases**. Published upstream Copilotv62/v63 remain unchanged; combined guidance is now v64 (`sha256:22e31f95ef972bcead98a4a707c9f33433558fd111e390e027a2bbb4346e112a`). Qualification retains the initial source-identity failure and fixture port/migration setup failures. No full local sweep, fresh local OAuth protocol run or live acceptance is claimed; fresh Linux CI must qualify this successor. - Latest review follow-up: the parent fixes caller-policy bypass in Slack tests/invitations and premature identity-handoff completion. The maps now name caller/target Block/Ask/rate intersection, one charge for a shared bucket, and the addressed human’s exact current identity link. Parent verification passes all **99 actual-auth/OpenAPI tests**, typecheck and build. The preserved combined companion passes the four focused tests (including twelve Slack policy/recipient combinations and the actual human OAuth confirmation path), workspace typecheck/build and maps. The 50 embedded-PG continuation cases were skipped locally because their database could not start; fresh Linux CI remains required. All material red/intermediate results and corrected-fixture evidence are retained in the qualification record. - Current `04d40cdd60` CI workflow40446969603 retained two new failures: server1/12 `workspace-runtime.test.ts` could not reach a fixture subprocess’s health endpoint within its ten-second readiness window (job122001926052), and chat2/3 `wakes Slack session sync from publication commit and leaves settled action queues quiet` observed one select after its idle predicate (job122001925936). Both same cases passed in parent `2ecd6451d0` Linux jobs122001920494/122001920581. The runtime and queue implementations are unchanged by this companion. Both failing cases also pass unchanged locally: two real-subprocess runtime readiness/reuse tests and the exact Slack quietness assertion against a fresh migrated PostgreSQL database. Logs: `/tmp/parity-04d-server1-failure.log`, `/tmp/parity-04d-chat2-failure.log`, `/tmp/parity-2ecd-server1-green.log`, `/tmp/parity-2ecd-chat2-green.log`, `/tmp/parity-04d-runtime-qualified.log`, `/tmp/parity-04d-chat-quiet-qualified.log`. The single standard unchanged-code failed-job rerun was **accepted as attempt2** on the unchanged exact head at `2026-10-27T04:24:30.513973+00:00` after the workflow completed. Both new failure retry allowances are now **consumed**; both failed shards and the complete Linux workflow have now passed on attempt2, including the verification aggregate. No additional unchanged-code rerun will be requested for either failure. No timing, assertion, idle-work guard or policy was weakened. If either case fails again, investigate/fix rather than rerun again. The older 5b/b0ea rerun allowances remain consumed and separate. ## Risks - Saved handoff links depend on the API changes in #17737, including the nonsecret credential-generation metadata. Both PRs must ship together. - A map can overstate operation coverage. Entries identify their evidence and retain human authentication and trusted-host limits. - Structural map checks do not prove live provider behavior. ## Model Used - OpenAI Codex, GPT-6 family, with reasoning, repository tools, and code execution. The runtime does not expose a more specific deployment 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 — focused UI/guidance and map checks - [x] I have added or updated tests where applicable — saved setup UI tests and map validation apply - [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 — exact `04d40cdd60` full Linux CI completed SUCCESS on attempt2; all current checks/statuses are terminal clean, with an actual neutral5/5 Paperclip assessment - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups — exact `04d40cdd60` review completed with no findings - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip --- doc/PRODUCT.md | 10 + doc/SPEC-implementation.md | 10 + doc/SPEC.md | 10 + doc/agent-permission-defaults.md | 10 + doc/plans/2026-10-13-connection-api-parity.md | 294 ++++++ maps/agent-access-map/app-permissions.md | 554 ++++++++--- maps/agent-access-map/chat-channels.md | 864 ++++++++++++++++-- maps/agent-access-map/connection-setup.md | 512 ++++++----- maps/agent-access-map/gateways-profiles.md | 410 ++++++--- maps/agent-access-map/runs-adapters.md | 297 +++++- maps/feature-map/app-permissions.md | 3 + maps/feature-map/chat-channels.md | 10 + maps/feature-map/connection-setup.md | 15 + maps/feature-map/gateways-profiles.md | 5 +- maps/feature-map/runs-adapters.md | 5 + packages/db/src/schema/tool_access.ts | 2 + packages/shared/src/types/chat-channels.ts | 3 + packages/shared/src/types/tool-access.ts | 2 + ...-access-oauth-protocol.integration.test.ts | 4 + .../tool-access/catalog/default-access.ts | 13 +- .../connections/organization-grant.ts | 3 +- .../tool-access/oauth/cloud-callback.ts | 1 + .../tool-access/oauth/direct-callback.ts | 1 + .../tool-access/oauth/vercel-callback.ts | 5 +- .../tool-access/vercel-credentials.ts | 3 +- ui/src/api/ai-connections.ts | 1 + ui/src/api/chatEndpoints.ts | 2 + .../ConnectionIntentInteractionBody.test.tsx | 14 + .../ConnectionIntentInteractionBody.tsx | 7 +- .../GoogleWorkspaceServices.test.tsx | 38 +- .../connections/GoogleWorkspaceServices.tsx | 16 +- ui/src/pages/apps/AppsConnect.test.tsx | 22 + ui/src/pages/apps/AppsConnect.tsx | 14 + ui/src/pages/apps/app-connect-policy.test.ts | 6 + ui/src/pages/apps/app-connect-policy.ts | 1 + .../pages/apps/chat/GitHubChatSetup.test.tsx | 168 ++++ ui/src/pages/apps/chat/GitHubChatSetup.tsx | 20 +- .../apps/chat/GitHubConnectionRecovery.tsx | 96 +- 38 files changed, 2824 insertions(+), 627 deletions(-) create mode 100644 doc/plans/2026-10-13-connection-api-parity.md diff --git a/doc/PRODUCT.md b/doc/PRODUCT.md index 48086411897..70f5184cdf2 100644 --- a/doc/PRODUCT.md +++ b/doc/PRODUCT.md @@ -391,3 +391,13 @@ operation logs retain their app authorization. Workspace `_issuePrivacySources` operation privacy metadata protect app-record disclosure; they do not grant or deny filesystem execution or task-bound file browsing. Current trust and sandbox policy, publication receipts, binding checks, and lifecycle locks remain authoritative. + +## Agent connection administration (2026-10-13) + +Standard agents can configure connections through the same domain APIs used by the app, bounded by their grants and their responsible user's current authority. Fresh standard agents for default Operators can manage app/tool connections, chat/email channels, AI accounts and pools, profiles and gateways without additional grants. Existing grants are not backfilled; explicit restrictions persist. Gateway administration retains the responsible human's administrative permission and does not grant agents blanket `tools:admin`. + +The agent performs authorized setup, reuses saved accounts and drafts, and hands off only required human authentication, consent, credential entry or identity confirmation. Saved handoffs resume through existing task continuation after current authority is rechecked. Management, tool execution and approval remain separate. Private accounts, host commands and stored credentials retain their boundaries; agent-issued gateway clients preserve issuer and responsible-user restrictions at use time. + +Authorized policy administration includes user-requested changes that loosen the acting agent’s own tool controls. These writes remain bounded by management grants, resource scope and the responsible user’s current authority; they do not need an additional approval solely because the agent is a target. Subsequent tool calls obey the resulting policy. Agents cannot approve their own governed action requests or edit their own principal permission grants. + +The implementation, operation inventory, permission matrix and fixture qualification are recorded in [Agent API parity for connection configuration](plans/2026-10-13-connection-api-parity.md). Provider fixtures establish API behavior; they do not constitute live provider setup or an empirical agent-success claim. diff --git a/doc/SPEC-implementation.md b/doc/SPEC-implementation.md index 948d3fd20c2..ee9d93c0f9c 100644 --- a/doc/SPEC-implementation.md +++ b/doc/SPEC-implementation.md @@ -2612,3 +2612,13 @@ Company-managed [Prompt Injection Prevention](prompt-injection-prevention.md) optionally classifies existing tool-result boundaries through a shared OpenRouter connection. Settings → Connections is permanent; valid model verdicts take precedence and dependency failures use the original built-in detector. + +## Agent connection administration (2026-10-13) + +Standard agents can configure connections through the same domain APIs used by the app, bounded by their grants and their responsible user's current authority. Fresh standard agents for default Operators can manage app/tool connections, chat/email channels, AI accounts and pools, profiles and gateways without additional grants. Existing grants are not backfilled; explicit restrictions persist. Gateway administration retains the responsible human's administrative permission and does not grant agents blanket `tools:admin`. + +The agent performs authorized setup, reuses saved accounts and drafts, and hands off only required human authentication, consent, credential entry or identity confirmation. Saved handoffs resume through existing task continuation after current authority is rechecked. Management, tool execution and approval remain separate. Private accounts, host commands and stored credentials retain their boundaries; agent-issued gateway clients preserve issuer and responsible-user restrictions at use time. + +Authorized policy administration includes user-requested changes that loosen the acting agent’s own tool controls. These writes remain bounded by management grants, resource scope and the responsible user’s current authority; they do not need an additional approval solely because the agent is a target. Subsequent tool calls obey the resulting policy. Agents cannot approve their own governed action requests or edit their own principal permission grants. + +The implementation, operation inventory, permission matrix and fixture qualification are recorded in [Agent API parity for connection configuration](plans/2026-10-13-connection-api-parity.md). Provider fixtures establish API behavior; they do not constitute live provider setup or an empirical agent-success claim. diff --git a/doc/SPEC.md b/doc/SPEC.md index 556d3d70717..f8e55dc3f03 100644 --- a/doc/SPEC.md +++ b/doc/SPEC.md @@ -864,3 +864,13 @@ Company-managed [Prompt Injection Prevention](prompt-injection-prevention.md) optionally classifies existing tool-result boundaries through a shared OpenRouter connection. Settings → Connections is permanent; valid model verdicts take precedence and dependency failures use the original built-in detector. + +## Agent connection administration (2026-10-13) + +Standard agents can configure connections through the same domain APIs used by the app, bounded by their grants and their responsible user's current authority. Fresh standard agents for default Operators can manage app/tool connections, chat/email channels, AI accounts and pools, profiles and gateways without additional grants. Existing grants are not backfilled; explicit restrictions persist. Gateway administration retains the responsible human's administrative permission and does not grant agents blanket `tools:admin`. + +The agent performs authorized setup, reuses saved accounts and drafts, and hands off only required human authentication, consent, credential entry or identity confirmation. Saved handoffs resume through existing task continuation after current authority is rechecked. Management, tool execution and approval remain separate. Private accounts, host commands and stored credentials retain their boundaries; agent-issued gateway clients preserve issuer and responsible-user restrictions at use time. + +Authorized policy administration includes user-requested changes that loosen the acting agent’s own tool controls. These writes remain bounded by management grants, resource scope and the responsible user’s current authority; they do not need an additional approval solely because the agent is a target. Subsequent tool calls obey the resulting policy. Agents cannot approve their own governed action requests or edit their own principal permission grants. + +The implementation, operation inventory, permission matrix and fixture qualification are recorded in [Agent API parity for connection configuration](plans/2026-10-13-connection-api-parity.md). Provider fixtures establish API behavior; they do not constitute live provider setup or an empirical agent-success claim. diff --git a/doc/agent-permission-defaults.md b/doc/agent-permission-defaults.md index 54113802773..8dcd1ac4c45 100644 --- a/doc/agent-permission-defaults.md +++ b/doc/agent-permission-defaults.md @@ -29,3 +29,13 @@ Agent-authenticated callers cannot set process adapter configuration or switch a The new direct grants are withheld from low trust agents and managed built-in agents. Low trust run or project policies can also deny privileged actions even if an agent has a grant. There is no permission backfill or upgrade migration for existing agents. A standard pending new hire receives the new default set when approved and activated. Invitation approval retains the set when it replaces a new agent's grants and preserves any explicitly scoped default grant. Existing scoped grants stay unchanged. A grant removed by an operator stays removed; ordinary updates and startup do not reapply it. The standard agent configuration default supports the agent setup in the linked runner task. Enabling a warm runtime can still depend on the target agent's adapter and runtime configuration, provider availability, and any responsible-user or approval checks. + +## Connection administration (2026-10-13) + +The five default tool grants are usable through the existing connection APIs and default native `search_api`/`call_api`. A fresh standard agent for a normally provisioned Operator can configure app/tool, chat/email and AI connections, profiles and gateways without additional agent grants. Gateway management uses the agent's `tools:manage_connections` grant and still requires `tools:admin` on the responsible human. It does not grant blanket agent administration. + +Connection management always intersects the current human's authority, including in responsible-user shadow mode. Personal self-service remains available where the human setup surface permits it; sharing and assignments require authority over their targets. Current run/work-mode/trust restrictions, scoped grants and explicit grant removal remain effective. No existing-agent permissions are backfilled. Agent-issued gateway clients preserve this delegation at every use, even after the setup run ends. + +See [the authorization and qualification record](plans/2026-10-13-connection-api-parity.md) and the operation-reviewed [agent access maps](../maps/agent-access-map/connection-setup.md). Human consent, credential entry and identity confirmation remain bounded handoffs to saved setup. + +Authorized policy administration includes user-requested changes that loosen the acting agent’s own tool controls. These writes remain bounded by management grants, resource scope and the responsible user’s current authority; they do not need an additional approval solely because the agent is a target. Subsequent tool calls obey the resulting policy. Agents cannot approve their own governed action requests or edit their own principal permission grants. diff --git a/doc/plans/2026-10-13-connection-api-parity.md b/doc/plans/2026-10-13-connection-api-parity.md new file mode 100644 index 00000000000..900f493a519 --- /dev/null +++ b/doc/plans/2026-10-13-connection-api-parity.md @@ -0,0 +1,294 @@ +# Agent API parity for connection configuration + +Dated 2026-10-13. Implementation and qualification record for the connection-management change based on `e43af9770a36ff92e14cf0e31ee16fa238310f76`. + +## Outcome + +A newly created standard agent can administer connections using its existing default grants and its responsible user's authority. The default Operator has the human permissions needed for these operations. API reachability includes app/tool connections, chat/email endpoints, GitHub and Slack setup, AI accounts and pools, profiles, named gateways and gateway clients. Human authentication and trusted-host operations retain their explicit boundaries. + +No agent grant backfill is performed. An existing missing or scoped grant remains missing or scoped. An operator can explicitly change those grants through the existing permission-management surface. Agents do not acquire `tools:admin` by default and cannot edit their own principal permissions or approve their own governed requests. + +## Authorization contract + +`server/src/services/connection-management.ts` resolves authenticated management context and invokes the existing authorization evaluator. `server/src/routes/connection-management-authz.ts` resolves route resources and all referenced targets, then supplies that context to services. Capability reporting calls the same evaluator; every mutation checks authority again. + +| Operation | Agent grant | Responsible-user ceiling | +| --- | --- | --- | +| Configure connections, channels and AI accounts | `tools:manage_connections` | Existing personal/creator authority or connection management | +| Profiles, assignments and action policies | `tools:manage_profiles` | Corresponding profile and target-agent authority; changes to connection audience also require connection management | +| Named gateways and client tokens | `tools:manage_connections` | `tools:admin`; bindings also require profile and target authority | +| Audit | `tools:view_audit` | Audit and resource visibility | +| Start/stop runtimes | `tools:manage_runtime` | Runtime and resource authority | +| Test actions | `tools:use` | Credential access, target-agent authority and ordinary tool/action policy | + +The acting principal, responsible user, credential owner and target agent remain distinct. Personal credentials belong to the responsible human. Activity records identify the actual agent, responsible user, run or API key, resource and authorization reason. API parameters cannot select a responsible user or forge ownership attribution. + +Agent keys resolve their saved responsible user. Run JWTs require current run identity and assignment. Both fail closed on absent user context, inactive membership, foreign companies, unavailable personal credentials, invalid scopes or low trust. Connection authorization enforces the human ceiling even when general responsible-user authorization runs in shadow mode. `skill_test` and `task_bridge` cannot administer connections. Ask and Plan contexts cannot mutate configuration. Connection/profile/gateway/provider/purpose/operation grant scopes apply to resolved resources, including plural policy selectors and affected targets. + +Services recheck management context after asynchronous provider work before sensitive persistence. Existing revisions, transaction locks, credential leases, vault storage, duplicate prevention and cleanup stay authoritative. Reads filter private accounts, referenced profile/pool resources and task activity. Configuration authority is separate from use: authorized policy changes, including self-targeted changes, take effect for subsequent calls. Pending governed action requests remain separate and cannot be self-approved. + +Authorized policy administration includes user-requested changes that loosen the acting agent’s own tool controls. These writes remain bounded by management grants, resource scope and the responsible user’s current authority; they do not need an additional approval solely because the agent is a target. Subsequent tool calls obey the resulting policy. Agents cannot approve their own governed action requests or edit their own principal permission grants. + +Binding and audience authorization checks field presence, including `null`, `false` and empty arrays. Clearing an assignment or reducing an audience remains a profile-management operation after an explicit grant revocation. Gateway binding changes check both the saved and replacement profile/project/task contexts; reauthorization reloads the saved side after asynchronous work. Referenced resources require each applicable connection and profile permission, including the replacement side of a project binding. + +## Saved authentication and delegation + +Generic chat configure/reconnect treats signing/webhook secrets, private keys, client secrets and project secrets as human credential entry. A delegated request returns the saved endpoint's addressed handoff before provider work or vault mutation. Verified provider provisioning can still save provider-selected credentials. Authorized bot-token and API-key entry remains delegated. Generic tool-connection updates cannot replace channel credential references and bypass the provider setup boundary. + +Credential-entry intents capture opaque generations for the requested fields. Vault persistence changes a field's generation only when its value changes, and commits those markers plus the active-endpoint verification fence with the new references. Retries preserve the original baseline. Old health, unrelated bot-token repair and unchanged key entry cannot satisfy a replacement request. Changed signing/webhook keys also require renewed webhook verification, and continuation rechecks task and current authority. Credential-entry cards without a completion boundary fail closed; ordinary authentication handoffs are unchanged. Saved credential links open the reconnect form. + +Agent setup responses retain their browser fields and add `requiredUserAction` where a human step is needed. It identifies the addressed user, saved resource, setup destination, reason and continuation. Chat handoffs reuse the saved endpoint and existing connection-intent delivery. AI login handoffs reopen the saved sign-in intent in the existing credential UI. Completion is based on server state; the UI does not ask a human to certify setup success. + +Generic app OAuth returns the saved setup destination without creating agent-bound provider state; the human browser starts the existing session-bound authorization flow. Its `expiresAt` is null until that human step creates an expiring provider session. Slack/GitHub provider callbacks preserve human subject and state checks. Agent-created GitHub setup binds its browser session when the addressed human opens it. Identity callbacks validate the current human and session before consuming state, and revalidate authority before saving identity. Wrong-user callbacks leave the rightful user's flow intact. Continuation rechecks task state and current authority after a yielding run; cancelled tasks and revoked access cannot be revived by a late provider result. + +GitHub webhook signing-secret rotation is a human credential step. Agents receive the saved endpoint handoff without rotating or reading the secret; direct delegated service generation is rejected. Human sessions retain the existing rotation and vault persistence path. + +Agent-issued durable gateway clients store `responsible_user_id` alongside `created_by_agent_id`. Token use reevaluates issuer/user authority, gateway/profile scope, target constraints, credential access and action restrictions. The responsible-user marker remains after deletion clears the issuer foreign key, so an orphaned delegated token fails closed instead of becoming a system client. It does not inherit the lifetime of the setup run. Heartbeat tokens remain bound to their authenticated run. Legacy agent-issued clients without a responsible user fail closed; existing board-issued clients retain their behavior. Migration `0380_broken_aqueduct.sql` is additive and idempotent; it does not infer legacy identity or modify agent grants. + +## Discovery and guidance + +OpenAPI carries explicit REST transport and connection-management authorization metadata. JSON authorization initiation and status are callable through default `search_api`/`call_api`; OAuth callbacks, redirects, MCP and WebSocket sessions retain their protocols. There is no new administration tool family. + +The shared connection guidance is delivered by the runtime prompt, search tools and existing adapter/CLI consumers. It tells agents to distinguish tool/channel/AI purposes, configure what the user requested when authorized, suggest relevant missing connections, reuse saved drafts and hand off only missing decisions, credentials, consent or authority. It prohibits silently relaxing policy after failed execution and distinguishes saved, authenticated, usable and verified states. Native API setup secrets and gateway credentials use protected replay receipts and redacted history. + +## Operation inventory + +The existing maps are the checklist, not a second runtime registry: + +| Inventory | Coverage | +| --- | --- | +| [Connection setup](../../maps/agent-access-map/connection-setup.md) | Catalog/method discovery, preflight, drafts, authentication, connection intents and host boundaries | +| [App permissions](../../maps/agent-access-map/app-permissions.md) | Account lifecycle, installations/grants, policy, catalog, action tests and activity | +| [Chat channels](../../maps/agent-access-map/chat-channels.md) | Endpoint/provider setup, settings, defaults, destination access, GitHub/Slack registration and identity, email, tests and lifecycle | +| [Gateways and profiles](../../maps/agent-access-map/gateways-profiles.md) | Profiles/bindings, gateway configuration, client tokens, runtime controls, protocol and audit | +| [Model accounts](../../maps/agent-access-map/runs-adapters.md#model-accounts) | AI discovery, key/login setup, reconnect/defaults, managed agent bindings, pools and lifecycle | + +The route inventory was reconciled against the production `tools`, `chatEndpoints`, `email`, `ai-connections`, `ai-connection-pools` and relevant `agents` UI API clients. Human-only operations remain individually classified: browser callback/identity confirmation and credential entry, principal approvals, arbitrary local configuration import/host-template authoring, cloud instance enrollment, and manual chat publication/replay that does not run through the governed agent tool path. Approved template use and ordinary authorized remote configuration remain delegable. Catalog/provider availability gates still apply. + +## Qualification + +`server/src/__tests__/connection-management.integration.test.ts` runs the assembled server, production bearer authentication, real database/vault persistence and deterministic provider responses. It creates each standard agent with an actual human-authenticated `POST /companies/:companyId/agents`; it provisions the human using normal Operator role defaults. It adds no special agent grants and does not inject `req.actor`. Only fixture sign-in throttling is disabled. GitHub/Slack/OpenAI/AgentMail responses are fixtures, not live provider proof. + +The suite exercises: + +- Fresh default app connections, profiles, chat settings, AI account/binding/pool and named gateway/token creation; no default `tools:admin` grant. +- Actual persistent API keys and run JWTs, native discovery/calls, and delivery of the new built-in guidance. +- Correct human ownership and actual-agent audit attribution, including personal grant delegation and revocation; persisted settings and optimistic revision conflicts. +- Slack configured credentials and test messages under ordinary action policy; repeated request IDs do not resend. +- GitHub app import, repository/verification configuration and real-session identity handoff; wrong-user and replay rejection. +- Saved chat handoff reuse and existing task continuation; late credential promotion after yield, cancellation and user revocation. +- User restrictions with legitimate personal self-service, missing/scoped agent grants, foreign companies/accounts, forged identity, invalid responsible users, bridge keys, low trust and work-mode restrictions. +- Durable gateway use after the setup run finishes, purpose-scoped grants, denial after membership or issuer-grant revocation and in the persisted cleared-issuer state, encrypted native token receipts, redacted history and retry without a second token. + +Focused neighboring tests cover existing human routes, callback behavior, provider setup, device/setup-token sign-in, native API policy, secret redaction, saved setup UI and connection-intent behavior. Maps distinguish individually executed fixture operations from source-reviewed operations. Scripted evidence proves reachability and behavior, not an empirical LLM success rate. + +Required final checks are `node scripts/check-maps.mjs`, `pnpm -r typecheck`, `pnpm test:run`, `pnpm build`, database migration validation and UI token gates. The final handoff records their actual results. Live provider setup, a new chatbot skill and redesigned setup workflows are outside this delivery. + +## Historical validation record (2026-10-13) + +- Focused connection, AI hiring, native API and OpenAPI suites: **2,533 passed**, including **16 assembled-server acceptance scenarios** using production authentication and default agent creation. +- Final fresh-process connection qualification: **618 passed across five suites**, including **all 18 assembled-server acceptance scenarios**, 418 tool-access tests, 87 gateway acceptance tests, 73 authorization tests and 22 decision-model tests. This covers binding/audience revocation, both sides of profile/project scope changes, current membership authority and gateway-token ownership metadata. +- Chat compatibility regression selection: **49 passed** (Slack tests, resource auditing, manager boundaries and Teams activity/projections). +- Saved setup UI, canonical guidance and native history redaction: passed in the neighboring focused run; its three outdated API transport/security assertions were corrected and included in the 2,533-test passing run. +- `pnpm -r typecheck`: passed. +- `pnpm build`: passed. +- Database migration numbering/safety validation: passed through typecheck/build; existing historical safety findings remain covered by the repository baseline. +- `node scripts/check-maps.mjs`: passed, 35 areas / 176 capability IDs; structural consistency only. +- UI token gates: passed. +- Broad UI run: 8,210 passed / 5 failed under contention; both failing suites passed serially (82 tests). +- Shared contracts and catalog: 941 passed. Adapter utilities: 1,740 passed / 5 failed under contention; all five failing suites passed serially (217 tests). +- Remaining source-only adapter/plugin projects: 2,087 passed / 1 macOS temporary-path assertion failed; that suite passed with the repository runner’s canonical temporary directory (14 tests). +- Serialized route/auth suites without embedded PostgreSQL: 1,849 passed / 22 failures in two empty-database fixtures; after adding the resource/membership fixture rows, both suites passed (25 tests). Their authentication, isolation and provider-error assertions remain exercised. +- Email address/provider regression suite: 10 passed after supplying resource and membership rows to its old empty-database fixture. HTTP log redaction: 66 passed in a fresh process after a sweep timeout. +- CLI suites without embedded PostgreSQL: 462 passed across 63 files. The three database-backed suites produced 62 passes and three embedded-PostgreSQL initialization failures in `worktree.test.ts`. The host was at its 32/32 shared-memory segment limit; these worktree tests need additional concurrent clusters. Other chats' databases and system limits were not modified. +- Database package: 194 passed across 50 files. +- Database-backed serialized routes: 1,208 passed / two failed / 73 skipped. Seven suites hit their 20-second database startup limit; the failures were an outdated AI-pool role fixture and a routine test timeout. All affected suites passed fresh-process reruns. +- Final isolated gateway/service/route reruns: **320 passed across 13 suites**, including the startup failures, skill cleanup deadlock, AI-pool membership fixture and routine timeout. The stale-lock route suite initially skipped its 14 tests after a failed database availability probe; a final isolated retry executed and passed all 14. +- Final assembled-server acceptance after the gateway scope and orphaned-token fixes: **18 passed**. The orphaned-token case models the token foreign key's persisted `ON DELETE SET NULL` state without deleting protected historical audit references; token use still traverses the real public gateway endpoint. +- Final workspace typecheck/build, migration checks, maps, token gates and `git diff --check`: passed. The generated migration retains exactly the five newest schema snapshots. +- `pnpm test:run`: the general server sweep finished with 19,282 passed / 15 failed / 381 skipped across 914 files (898 passed, 10 failed, 6 skipped). Four suites could not start their databases; other failures included two gateway tests loaded against earlier transformed authorization code, outdated role/resource fixtures, a cleanup deadlock, an HTTP timeout and a socket hang-up. All reported server failures and blocked startup suites subsequently passed fresh reruns. Workspace and serialized groups were executed separately after the runner's fail-fast exit. The original sweep is not represented as a clean pass. + +As of October 13, qualification was complete for the connection feature and its provider fixtures. The repository-wide test result at that time remained **not fully green** because the three CLI worktree tests could not initialize their additional PostgreSQL clusters at the host's shared-memory limit. They require rerunning on a host with spare PostgreSQL capacity. Live provider verification remains outside this delivery. + +## PR qualification (2026-10-26) + +The implementation was rebased onto `a1f77821b9cd5c9c846fdf7a0fa5cc620ad2b6bb`. Connection-service changes were ported into the current extracted modules. The historical checks above describe the October 13 snapshot; fresh PR qualification is recorded separately below. + +- Fresh Node 24.22.1 / pnpm 9.15.4 workspace typecheck and build passed. Migration safety, module boundaries, and UI token gates passed. +- Initial embedded-database acceptance could not run: the first install lacked native library links; after package setup, a direct `initdb` reproduction reported the host semaphore limit (`semget`: No space left on device). No shared databases or kernel limits were changed. The broad local test attempt is not a clean pass. +- First Linux CI exposed a duplicate user fixture, a lost viewer restriction, a host-template visibility gap, an integration fixture race with production harness verification, and the prompt-bound Copilot identity. These are tracked and fixed separately from the local host limit. Current-head CI remains the completion gate. +- Harness verification uses a deterministic lifecycle driver fixture; agent creation, grant defaults, bearer authentication, authorization, and persistence remain production paths. + +- Fresh isolated PostgreSQL 18 qualification: **31 acceptance scenarios passed** through the assembled server and actual bearer/session middleware. The database is a task-owned container; no shared service or kernel setting was changed. CI uses its normal embedded database path. +- Review regressions now prove nested finish audience authorization, both management grants, all/current target limits, task/project execution policy, actual actor/run attribution, another authorized target agent, human approval after a successfully yielded run, user/agent revocation, task cancellation, and replay without a second provider call. +- The local MCP fixture initially lacked cached healthy catalog state; the next fixture pass exposed equal policy-priority assumptions and a legitimate closed-task 409. Those fixture expectations were corrected. A reused external test database also caused a Slack identity collision; the passing run used a fresh database. None of those attempts is represented as a passing run. +- Saved setup UI and shared guidance: **278 passed**. Native API/OpenAPI regression selection: **2,041 passed**. Prompt-bound Copilot identity: **57 passed**. + +- Further review regressions cover bounded selector expansion before resource reads, duplicate-selector lookup cost, foreign-resource non-disclosure, one run capture per management listing, provider scope on existing and proposed configuration, and grant revocation during remote endpoint validation before persistence. Commit-time checks resolve current saved selectors and affected targets again. +- A one-call rate limit permits the admitted test and denies the next test without a second provider invocation. The final policy check carries the invocation's recorded admission. The first new test assertion used an incorrect response shape; the corrected assertion checks the existing `decision`/`error` contract and actual provider call count. +- Current workspace typecheck and production build passed after these changes. The focused Slack retry authorization cases passed (**3 tests**) after sorting both ID reads. Their first local attempt raced the database package build's migration-directory replacement; after that build completed, normal migrations and all three cases passed. +- The preceding CI head had several runner-shutdown cancellations and a browser strict-locator failure involving duplicate chat replies. The chat integration failure was an unordered ID comparison, corrected above. These failures remain part of the qualification history; only fresh current-head CI can establish the final check result. + +- A subsequent route review found a board-only owner helper left behind in the sandbox device-login path. It now uses the shared connection authority while keeping the responsible user as immutable session owner. A focused assembled-server case passed for actual key/JWT reads, cancellation, strict start validation, and another-owner denial. This fixture does not launch a sandbox/provider process; existing login service fixtures cover that contract. +- Map review also corrected unsupported MCP GET handlers (405, POST required), restored private voice/phone consent boundaries and existing chat provenance notes, and distinguished saved device-session HTTP evidence from provider authentication. + +- Final assembled-server acceptance after the device-login fix: **32 scenarios passed** in a fresh PostgreSQL database. +- Review found that the top-level Apps connect route rejected an `aiLogin`-only handoff before rendering its saved login panel. The new route-policy test failed before the fix; the route now admits a non-empty saved-login ID, leaving owner/session validation to the existing server and panel. Empty IDs remain rejected. The prior component-only tests did not cover this entry gate. + +- Saved-login route policy and Apps connect components: **236 tests passed** after the entry fix; the added route-policy case was red before the fix. Token gates and map validation passed. +- Master advanced during PR qualification. The implementation now merges `5823e9a664`; its already published Copilot v60 identity is preserved byte-for-byte, and the combined connection guidance is declared as v61. Generated TypeScript/Rust identity checks and all **58 Copilot installation tests passed**. These checks do not imply a new live provider qualification. + +- Latest authorization qualification: **36 assembled-server scenarios passed**. Additional cases cover application-provider spoofing and proposed metadata changes, real agent action approval with its reserved rate slot, distinct subsequent-call denial, concurrent run-authenticated finish/reorder/update operations, issue/routine/gateway binding targets and retained gateway profiles, private GitHub review list/detail filtering, and rate-limited Slack test messages/invitations. Exact action retries retain their saved receipt and do not consume a second provider call. +- Generic caller-labelled application/connection creation cannot establish a provider-scoped grant. Provider-scoped setup uses the catalog operation whose provider the server resolves; default unscoped management remains available. Existing and proposed application metadata are checked independently. +- Authorization runs before profile/policy transaction locks in finish and reorder. The concurrency fixture allows a stale reorder's documented 422 while requiring the other writes to complete without lock-order errors. Approval admission preserves only the recorded unchanged policy generation and does not grant approval or waive current blocks/grants. +- The preceding Linux CI heads passed their browser shards but exposed scheduling flakes in `workspace-recovery`, `environment-cleanup-work`, and `login-cleanup-work`; their actual assertion failures remain recorded separately from the connection qualification. Fresh latest-head CI remains required. Earlier new fixture attempts also used an invalid application type and omitted a profile key; the corrected fixtures use production schemas. A profile already assigned to a gateway is already bound to that gateway's agent and cannot serve as an unrelated positive control. + +- Master subsequently advanced to `125ddcabe5`. Its published Copilot v61 identity is preserved; the combined prompt is now v62. Generated profile checks and **59 Copilot installation tests passed**. Project bindings now enumerate every company agent that could run project work; the focused retained-target case passed with that restriction. +- The previously failing login-cleanup and free-workspace-export scheduling cases both passed in isolated local runs against dedicated PostgreSQL test databases. This does not erase their CI failures or establish their cause. +- Later review found that policy-audience expansion included generic resource selectors and denied otherwise permitted narrow writes. Actual-auth regressions now cover narrow create/update/reorder/delete and denial of broader targets. Deferred Google authorization also persists the addressed user's selected scopes with the saved connection. The browser restores that request only until newer consent is saved. All **six service-picker tests passed**; the new draft and newer-pending regressions failed before the UI fix. An initial stale-consent fixture used incomplete Drive scopes and was corrected to use the shared canonical selection. +- The complete acceptance suite now passes **38 scenarios** against a fresh isolated PostgreSQL database, including both new review regressions. Provider setup remains fixture evidence, not live-provider qualification. +- An outside-diff review finding exposed AI collection projections that checked only the responsible user's account access. Both returned accounts and selectable pools now pass the shared agent resource evaluator; pool checks include every member. The new actual-key/JWT regression failed before the fix and passed after it. The full acceptance suite then passed **39 scenarios** in a fresh isolated database. +- Both PRs' complete GitHub Actions workflows passed at implementation `9aa8fbe34a` and companion `6f2a14d2d7`; both received current-head Greptile 5/5 reviews. The companion also received Paperclip 5/5. The implementation's Paperclip reviewer is held by a prior `execution_finalization_deadline_exceeded` recovery boundary, which requires stopped-provider and uncertain-action reconciliation; an ordinary retry did not clear it. +- The separate Entire gate requires linked Changes and independent approval with self-approval disabled. Both existing branches were linked through its supported API. Its base-freshness requirement also required merging master `a67a18c171` into the implementation and then into the companion. This merge carries upstream task-history UI fixes; fresh current-head CI is required again. No review or gate settings were weakened. +- A subsequent profile review found that saved application/catalog selectors did not resolve their underlying connections. Authorization now covers every supported selector, the application scope used by new-tool review, empty managed installation profiles, proposed broad entries, and effective-profile tools/installations. The new actual-key/JWT regression failed before the fix and also exposed the empty managed-profile label; the corrected shared evaluator protects both. All **40 assembled-server scenarios passed** afterward in a fresh database, and server typecheck/module boundaries passed. Host-template map entries now explicitly name their board-user and `tools:admin` requirements; MCP import preview retains its separate board/company boundary. + +- Effective-profile projection now authorizes each distinct returned connection once across tools and installations. The new 200-tool regression measured **201 checks before the fix and one afterward**, preserving all returned tools and privacy denials. The complete assembled-server suite then passed **41 scenarios** in a fresh database. AI pool writes explicitly document both management grants; AI setup and profile mutation entries document their conditional grants. + +- A security regression confirmed that delegated setup-secret requests returned the GitHub webhook signing key. Agents now receive a saved human handoff, and the provider service rejects delegated secret generation. Actual key/JWT requests preserve credential refs, while a human session can still rotate them. The regression failed before the fix; all **41 scenarios passed** afterward in a fresh database, and server typecheck passed. +- CI on implementation `271740cc19` failed a browser shard when run cancellation hit a database lock timeout. Companion `4ad17e94d2` failed the Runner static suite when its real Claude descriptor fixture reported an unverified surviving process group; that exact test passed in an isolated local run. Both failures remain recorded; fresh current-head CI is required after the security fix. + +- The OpenAPI setup-secret response now distinguishes the agent credential-entry handoff (200) from human-only secret generation (201). All **27 OpenAPI contract tests passed**, the focused actual-auth GitHub flow passed, and server typecheck passed after this contract update. + +- The maintainer confirmed authorized policy administration, including self-targeted changes. The new actual-auth scenario changes an agent’s own Ask-first rule through an authorized policy write, proves the next call follows the new policy, preserves self-approval denial, and rejects further policy writes after grant revocation. All **42 assembled-server scenarios passed** in fresh PostgreSQL databases. +- Existing-App import now hands off to the human credential screen; agents cannot persist a caller-chosen signing key. Rotation requests record a separate saved objective and do not complete from old connection health. The rotation generation and cleared webhook verification commit atomically with new vault references; completion requires a newer generation and restored verified health. The regression was red before the fix. No best-effort audit or clock comparison establishes completion. +- Saved import and rotation links open the matching credential screen. The rotation pane exposes a one-time copy action, waits for explicit human generation, and preserves retry behavior through generation/reconnect failures. Both new UI regressions were red before the fix; all **35 GitHub wizard tests passed**, and token gates passed. The provider verification state in the assembled-server test is a deterministic fixture; live GitHub rotation remains unqualified. +- Implementation `35585448ef` hit a 30-second module-load hook timeout in `company-skills-routes`; that suite passed in a focused local run. The combined route/contract selection passed **90 tests**, followed by all **27 OpenAPI contract tests** after the final server contract update. The companion’s previous complete CI was green at `d84538f8a3`. Fresh CI on the credential-handoff changes remains required. Entire was removed from the expected gates by the maintainer and is no longer a completion dependency. + +- Final review found that the rotation UI tied reconnect to the one-time plaintext. Saved links now bind an opaque, nonsecret generation baseline, and the endpoint projection exposes only the committed generation ID. Reloading resumes that rotation without generating another secret; an unchanged generation remains blocked. Both new resume cases were red before the UI fix. All **38 GitHub wizard tests** and **69 API/OpenAPI tests** passed; token gates passed. Prior full CI passed at implementation `f99e8aa6d5` and companion `e22d2a0eba`; their final successor checks must complete independently. + +- Current-head review found an AgentMail collection scope leak and agent-keyed Arcade discovery ownership. Both new actual-auth regressions were red before the fixes. Email accounts now pass the shared resource evaluator before metadata or credential discovery; aggregator settings, credentials, inventory, and sync leases use the responsible user, while audit and secret-access records preserve the actual agent. The Arcade fixture initially created a draft and omitted provider authorization metadata; it was corrected to represent an active provider account. Its guarded HTTP transport also required an explicit deterministic fixture; a prior real transport attempt returned 401. No provider authentication was weakened. +- A reused test database retained a Slack bot identity and caused a 409 during the full suite. In a fresh PostgreSQL database, all **70 API/OpenAPI tests passed**, including 43 assembled-server cases and binding the human-only secret response to its committed nonsecret generation. Server typecheck passed. Earlier fixture and contract-update failures remain accounted for here. +- Failed GitHub secret replacements retain the prior copy only after its committed generation is confirmed unchanged. A changed or unreadable generation blocks copying and reconnecting; an explicit status check can restore the earlier copy when the server confirms it. All three new replacement cases were red before the fix; all **82 GitHub UI tests passed** afterward. Cloud enrollment map entries now name instance-admin creation/callback checks and distinguish the HTML/303 browser return. Maps and UI token gates passed. Fresh successor CI and reviews remain required before merging. + +- Master advanced to `c7c12dbc79` while the review fixes were being qualified. Its published Copilot v62 identity is preserved byte-for-byte. The combined delegated connection guidance uses v63 with digest `sha256:f29dc04c3217ad74aeaf96b2c466d5a1dc6c55942f5bd4db9c1cf52f2985d19e`; the shared identity generator and **60 Copilot installation tests passed**. The first root-level invocation selected no Runner tests; the package configuration ran the intended suite. Migration 0379 is unchanged by this merge. + +- A later security review found that activity reads authorized the entire endpoint conversation history before applying the activity limit. The real run-JWT regression measured **200 checks before the fix and one afterward** for the same one-item page. The query now selects only conversations referenced by the bounded activity candidates, deduplicates issue decisions, and reuses the request's run read context. The focused regression is green; a fresh complete suite and current-head CI remain required. + + +### 2026-10-26: test authority and saved Google consent review fixes + +- The exact-head review of `69bf11aef9` found private task results readable through test status and cross-agent test execution without the caller's connection access. Both initial actual-auth regressions failed in `/tmp/parity-test-boundaries-red.log`. Implementation `70b7c973c8` passes the authenticated actor into status reads, rechecks task authorization plus agent/responsible-user privacy, and binds agent reads to their originating principal. +- Cross-agent tests require a matching caller profile and the same credential identity, intersect caller and target action policies, and recheck both before execution including approved continuation. The ordinary human approval covers either Ask first policy; self-approval stays denied. Caller-only and shared rate admissions persist without charging one shared bucket twice. +- Intermediate focused runs exposed an expectation mismatch (normal policy denial returns Off), the need to check profile membership independently of broad `tools:use`, and result classification attempting to capture a target agent with the caller's run. The final implementation preserves the actual run identity while retaining the separate test target. Diagnostic failures remain in `/tmp/parity-test-boundaries-green.log`, `/tmp/parity-test-boundaries-green-2.log`, and `/tmp/parity-test-cross-agent-diagnostic.log`; they are superseded by the 16-case passing focused run `/tmp/parity-test-boundaries-green-3.log`. +- **78 API/OpenAPI tests passed** in fresh PostgreSQL (`parity_review_31`): 51 assembled-server actual-auth scenarios and 27 API contracts. Log: `/tmp/parity-test-final-full.log`. Final server typecheck/build and map validation passed (`/tmp/parity-test-final-types.log`, `/tmp/parity-test-final-build.log`, `/tmp/parity-test-final-maps.log`). These replace the earlier 71-test snapshot; no full local test-sweep pass is claimed. +- Companion review also found that token refresh advances `grant.updatedAt` and can erase a saved pending Google service selection. Both new component cases failed before the fix (`/tmp/parity-google-refresh-ui-red.log`). Successful direct/cloud consent now records additive `providerTenant.oauth.consentedAt` metadata; refresh preserves it. The picker compares the pending request against that consent timestamp, using grant creation for legacy metadata rather than treating refresh as new consent. +- **10 focused Google/OAuth tests passed**: eight service-picker cases (including newer/older consent and refresh/legacy behavior) plus two real local-HTTP OAuth protocol cases proving that successful consent is timestamped and concurrent token refresh preserves it. Log: `/tmp/parity-google-refresh-green.log`. The protocol fixture uses its existing board actor injection and does not count as actual-auth agent evidence. JSON metadata is additive; normal migration generation validates that no SQL change is required. + + +### 2026-10-26: preserve policy and explicit-grant tool access + +The review of `70b7c973c8` correctly identified that requiring a caller profile rejects legitimate access through an allow policy or explicit scoped `tools:use` grant. Both actual run-JWT cases failed before the correction (`/tmp/parity-caller-grants-red.log`). The caller's full access decision remains intersected with the target's decision; the narrower profile-only precondition is removed. The negative case now uses a caller-specific Block policy with the target still permitted, proving that another target cannot bypass the caller's actual action restriction. Default broad `tools:use` remains governed by the ordinary evaluator rather than being reinterpreted by this API. + +A further real-JWT regression found that the `authKind: none` shortcut could skip comparison of dedicated grant identities (`/tmp/parity-credential-kind-red.log`). Credential identity comparison now applies for every auth label. Both API-key and no-auth-labelled dedicated grants deny before provider invocation when their identities differ. The same responsible-user and target authority, approval, revocation, and rate checks remain in force. + +Implementation `52a87697f0` passed **81 API/OpenAPI tests** (54 assembled-server actual-auth scenarios plus 27 contracts) in fresh PostgreSQL `parity_review_33`, log `/tmp/parity-caller-grants-full-green.log`. The earlier profile-only version is superseded by this evidence; its failures and recovery remain above. Final server typecheck and build passed (`/tmp/parity-caller-grants-final-types.log`, `/tmp/parity-caller-grants-final-build.log`). The earlier 18-case focused run passed before adding the no-auth-label regression (`/tmp/parity-caller-grants-green.log`); the final full suite includes all 19 delegated-test cases. Google consent, GitHub UI, and Copilot code are unchanged by this correction. + + +### 2026-10-26: bounded tool-connection activity authorization + +Security review found that tool-connection activity repeated a full run capture per returned event, unlike the already corrected chat activity path. The actual run-JWT regression over 100 persisted events (half linked to an inaccessible private task) measured **100 capability decisions and 102 run captures** before the fix and failed (`/tmp/parity-tool-activity-red.log`). + +Implementation `e5b8070d2f` creates one management evaluator, shares decisions by task within the bounded page, and passes the current request's read context into each probe. The passing regression proves two capability decisions and at most two run captures, the same 50 visible events, no private event content, and a fresh 403 after revoking audit permission (`/tmp/parity-tool-activity-green.log`). No authority is cached across requests. + +**82 API/OpenAPI tests passed** in fresh PostgreSQL `parity_review_35`: 55 assembled-server actual-auth scenarios and 27 contracts (`/tmp/parity-tool-activity-full-green.log`). Server typecheck/build passed (`/tmp/parity-tool-activity-types.log`, `/tmp/parity-tool-activity-build.log`). The earlier red result is retained as the baseline; no full local sweep, live provider verification, or empirical model success claim is added. + +### 2026-10-26: delegated verification-key entry and CI rate assertions + +Security review found that generic Slack reconnect accepted an agent-chosen signing key while verification reused the saved bot token. A clean actual-key regression reproduced HTTP 200 instead of a human handoff (`/tmp/parity-chat-signing-red-fresh.log`). Source review exposed a second path: actual-JWT generic tool-connection PATCH accepted a replacement signing-key secret reference (`/tmp/parity-chat-binding-red.log`). Both paths are now guarded before persistence. Configure/reconnect returns the saved addressed credential-entry handoff; the generic credential-reference update fails with 403. Tests confirm unchanged refs and the original vaulted signing value, human-session setup, permitted agent bot-token repair, and normal policy/rate checks on test messages. + +The first diagnostic used `configure` on an already configured endpoint and returned the expected lifecycle 409 rather than exercising reconnect (`/tmp/parity-chat-signing-red.log`). Reusing that database then hit the known Slack identity fixture collision (`/tmp/parity-chat-signing-red-reconnect.log`). Fresh databases established the two security baselines above. The first full post-fix run passed all 55 authenticated cases but failed an OpenAPI assertion that expected `anyOf` instead of the emitted `oneOf` (`/tmp/parity-chat-signing-full-green.log`). The final clean PostgreSQL `parity_review_41` run passed all **82 API/OpenAPI tests** (`/tmp/parity-chat-signing-final-full.log`). Eight focused native receipt protections also passed in `/tmp/parity-chat-signing-green.log`. Server typecheck/build passed (`/tmp/parity-chat-signing-types.log`, `/tmp/parity-chat-signing-build.log`). No live provider claim is added. + +Linux CI run `40415124314` failed two existing rate-admission assertions because persisted admissions now also record the bucket identity used by paired caller/target checks (`/tmp/parity-e5-linux-failure.log`). The local baseline reproduced both failures (`/tmp/parity-rate-bucket-red.log`). Updating the expected shape first used the wrong fixture field (`/tmp/parity-rate-bucket-green.log`); using the actual actor identity then passed all **49 policy service tests** (`/tmp/parity-rate-bucket-final-green.log`). No production rate behavior changed for this correction. The companion preserves the catalog creator-attribution follow-up so the implementation review stays at 99 changed files. Both PRs must ship together. Fresh current-head Linux CI and reviews remain required; no full local test-sweep pass is claimed. + +### 2026-10-26: credential-entry completion and in-flight revocation + +The `c8cc70ae30` review correctly found that an already active, healthy endpoint could satisfy the new credential-entry card without human entry. The actual-JWT Slack baseline failed with an available connection instead of `null` (`/tmp/parity-credential-completion-red.log`). Implementation `805d165bc3` persists opaque per-field generations only for changed protected values, fences old active state in the vault-reference transaction, saves the requested field baseline, and preserves it on retries. Completion requires the requested change, active healthy state, renewed signing verification when applicable, and current task/user/agent authority. No secret or value-derived hash is added to the intent. + +The expanded journey proves pending state before human entry, after unrelated bot-token repair, after re-entering the old signing key, after changed entry without verification, and across retries with the original baseline. New verification allows normal continuation; cancellation or responsible-user suspension still denies it. A later fixture assertion initially lost the hidden generation by copying the redacted API setup back into the database (`/tmp/parity-credential-completion-final.log`). The fixture now preserves the persisted setup. Two focused cases and four neighboring real-database Slack/GitHub provisioning, reconnect, rotation and concurrency cases passed (`/tmp/parity-credential-completion-green.log`, `/tmp/parity-credential-completion-neighbors.log`). + +An added actual-auth race then suspended the responsible user inside provider authentication. The request returned 403 but had replaced refs (`/tmp/parity-credential-commit-revocation-red.log`). Configuration now reauthorizes before persistence and inside the reference transaction, so revocation leaves the original refs intact. Final **82 API/OpenAPI tests passed** in fresh PostgreSQL `parity_review_48` (`/tmp/parity-credential-completion-qualified.log`), and final server typecheck/build passed (`/tmp/parity-credential-completion-commit-types.log`, `/tmp/parity-credential-completion-commit-build.log`). + +The preceding full run had one unrelated rate assertion fail as two calls crossed a minute boundary (`/tmp/parity-credential-completion-final-green.log`). The fixture database recorded consumed counters at 23:35 and 23:36 for the same policy and bucket (`/tmp/parity-credential-completion-rate-window-evidence.log`). Rate cases now freeze only `Date`; transport and asynchronous timers remain real. No production rate behavior changed. The final run above includes all rate cases. Companion Slack authorization/installation map entries now explicitly describe the required addressed-human browser-session and consent step; those individual operations retain source-only evidence. Fresh current-head CI and reviews remain required. + + +### 2026-10-26: unavailable credential repair and distinct fresh handoffs + +The exact `805d165bc3` review found three valid regressions. The actual-auth Slack/GitHub baseline reproduced all three in `/tmp/parity-repair-handoffs-red.log`: complete human replacements failed for disabled, deleted and unreadable old signing secrets; a fresh GitHub draft received `reconnect=1`; and a subsequent private-key-only request collided with the broader import request's idempotency key. The red Slack run also reached a consequential test-message failure because the unrepaired corrupt secret remained bound. That is retained as a consequence of failed repair, not a separate provider outage. + +Implementation `f70c1a05a3` compares each old protected reference separately. Known unavailable secret/version states and typed provider-client failures count as changed; unexpected errors and company/scope failures still propagate before reference writes. The actual human session repairs all three real persisted secret states. A controlled unexpected resolver fault returns 500 and leaves refs unchanged; this is fault-injected resolver evidence, not a reproduced database outage. Opaque generation and verification requirements remain unchanged. + +Saved credential links now use reconnect only for a configured endpoint; fresh GitHub imports retain their webhook-secret form. Idempotency keys include the canonical protected field set and completion mode; matching retries retain their interaction and original generation baseline. The actual run-JWT GitHub journey proves separate broad/private-key handoffs, retry reuse and continued human import. The companion wizard test also submits the fresh saved form and verifies App import rather than reconnect. + +Both focused journeys passed (`/tmp/parity-repair-handoffs-green.log`). All **55 actual-auth integration scenarios** then passed in fresh PostgreSQL `parity_review_51` (`/tmp/parity-repair-handoffs-full.log`). That invocation accidentally named a nonexistent OpenAPI test file, so its reported pass covered only the integration module. The correctly named OpenAPI route suite ran separately and all **27 contracts passed** (`/tmp/parity-repair-handoffs-openapi.log`): 82 tests across the two commands. Server typecheck/build passed (`/tmp/parity-repair-handoffs-types.log`, `/tmp/parity-repair-handoffs-build.log`). All **41 GitHub wizard component cases** and UI typecheck passed (`/tmp/parity-repair-handoffs-ui.log`, `/tmp/parity-repair-handoffs-ui-types.log`). Maps and token gates passed (`/tmp/parity-repair-handoffs-maps.log`, `/tmp/parity-repair-handoffs-tokens.log`). Fresh current-head CI and reviews remain required; no live provider or full local sweep claim is added. + + +### 2026-10-27: configured GitHub entry and provisioned Slack verification + +The exact `bf760912fa` companion review found that configured GitHub credential-entry links opened a saved-credential repair screen with no webhook-secret input. Two component regressions failed before the fix (`/tmp/parity-configured-entry-ui-red.log`). The saved form now displays the requested supported fields, submits only nonempty requested values through reconnect, and keeps stored credentials for the remaining fields. It supports private-key, webhook-secret and client-secret entry, all/any completion modes, and legacy links without field metadata. Rotation retains its separate generation/reconciliation flow. All **46 wizard cases**, UI typecheck, UI build and token gates passed (`/tmp/parity-configured-entry-ui-qualified.log`, `/tmp/parity-configured-entry-ui-types.log`, `/tmp/parity-configured-entry-ui-build.log`, `/tmp/parity-configured-entry-tokens.log`). + +The API baseline also showed missing field/completion metadata on the immediate and persisted-intent links, and rejected configured GitHub webhook replacement with 422 (`/tmp/parity-configured-entry-api-red.log`). Implementation `9ee6f19c3c` includes canonical credential field names and completion mode in both links. Additional GitHub verification keys are accepted only for reconnect and still require human entry; agents receive the addressed handoff before provider work or vault writes. The actual run-JWT/human-session journey proves that webhook-only entry preserves the private key and client secret, clears old verification, and completes only after new webhook verification and current authority. The first focused corrected run passed (`/tmp/parity-configured-entry-api-green.log`). An intermediate full suite passed 82 tests before narrowing the extension to reconnect (`/tmp/parity-configured-entry-full.log`). + +Compatibility review retained GitHub's existing initial-setup secret restriction. The first diagnostic used an unmigrated external database and failed on the absent companies table (`/tmp/parity-configured-entry-initial-boundary-red.log`); that harness requires the repository's normal migration workflow before use. A migrated run then exposed the overly broad initial variant as HTTP500 instead of the existing422, and reproduced the separate Linux Slack failure (`/tmp/parity-configured-entry-neighbors-red.log`). The final credential-key extension is reconnect-only; the existing first-setup restriction remains unchanged. + +Companion Linux CI run40423570884, chat2/3, failed automatic Slack provisioning because runtime credential persistence erased the earlier signed manifest-update challenge (`/tmp/parity-bf760-chat-ci-failure.log`). The migrated local regression reproduced the same missing verification timestamp. A provider-owned helper now preserves proof only when the current signing fingerprint and observed callback URL match those exact credentials. The generic vault transaction reads and locks the current setup before deciding whether verification survives; other changed keys retain the verification fence. This is service/provider-fixture evidence, separate from actual-agent authentication coverage. All **eight existing compatibility cases** passed, including automatic Slack challenge preservation, initial GitHub secret rejection, failed manifest-update recovery, reconnect, rotation and concurrency (`/tmp/parity-configured-entry-neighbors-green.log`). + +Final **82 API/OpenAPI tests passed** in fresh PostgreSQL `parity_review_56` (`/tmp/parity-configured-entry-final-full.log`), with final server typecheck/build (`/tmp/parity-configured-entry-final-types.log`, `/tmp/parity-configured-entry-final-build.log`). The Vercel personal-credential attribution follow-up is preserved in the companion with its other consent/ownership follow-ups so the implementation remains at 99 files after adding the provider-owned helper. Both PRs must ship together, and that file must be retained in post-squash replay. No broad local sweep or live provider pass is claimed. Fresh exact-head CI and reviews remain required. + + +### 2026-10-27: upstream migration reconciliation + +Master advanced to `baec3abd67` and took migration0379 for project Git authorization. The only merge conflicts were the generated379 snapshot and journal. Upstream `0379_white_giant_girl.sql` and its complete journal/snapshot were retained; normal `pnpm db:generate` regenerated the additive gateway-token column as `0380_broken_aqueduct.sql` and pruned snapshots to the newest five. Its `ADD COLUMN IF NOT EXISTS` also succeeded on two repeated applications to the already migrated fixture database (`/tmp/parity-0380-idempotence.log`). Migration numbering/safety checks passed (`/tmp/parity-configured-entry-0380-validation.log`); no legacy identity or agent grant is backfilled. The earlier379 names above remain historical evidence for prior revisions. + +The merged implementation passed workspace `pnpm -r typecheck` and `pnpm build` (`/tmp/parity-0380-types.log`, `/tmp/parity-0380-build.log`) and all82 actual-auth/OpenAPI cases in fresh PostgreSQL `parity_review_58` (`/tmp/parity-0380-final-api.log`). The published Copilotv62 fixture is byte-for-byte equal to upstream; guidancev63 is unchanged. The combined companion passed **128 tests** (55 actual-auth scenarios, 27 OpenAPI contracts and 46 GitHub wizard cases) in fresh PostgreSQL `parity_review_59` (`/tmp/parity-0380-stack-tests.log`), workspace typecheck/build (`/tmp/parity-0380-stack-types.log`, `/tmp/parity-0380-stack-build.log`), maps and token gates (`/tmp/parity-0380-maps.log`, `/tmp/parity-0380-tokens.log`). Eight Google UI cases passed in `/tmp/parity-0380-google-oauth.log`; its two local-HTTP OAuth cases could not start embedded PostgreSQL and were skipped after suite initialization failed. This fresh attempt does not count as a protocol pass; the earlier ten-case evidence remains historical and fresh Linux CI must run the protocol cases. No full local test-sweep pass is claimed. Full Linux CI and current-head reviews remain required. + + +### 2026-10-27: gateway issuer policy, private activity pagination and native setup receipts + +The exact `e7aba614fb` review reproduced two gaps. Agent-issued gateway clients selecting another agent checked only that target's tool policy. Four actual-key gateway cases were red for issuer Block, Ask first, post-request Block and rate policy (`/tmp/parity-gateway-issuer-red-qualified.log`); the initial underscore-name fixture mismatch in `/tmp/parity-gateway-issuer-red.log` was diagnostic and did not prove the bypass. Private-only activity windows also ended pagination early (`/tmp/parity-private-pagination-red.log`). + +Gateway sessions now retain their actual issuing agent independently from the target. Every tool decision intersects the existing policy evaluator's issuer and target decisions; either Block or Ask first governs the call. Separate admissions are persisted, common rate buckets consume one slot, and approved continuation rechecks current token scope, issuer/user authority, both policies and credential identity before provider dispatch. Durable invocation rows retain the token ID and a server-authored issuer marker, including fail-closed behavior when the token is deleted. A red approval recheck exposed that recordInvocation previously dropped gatewayTokenId (`/tmp/parity-gateway-issuer-green.log`, `/tmp/parity-gateway-issuer-approval-diagnostic.log`); persisting that attribution closed the continuation bypass. Board-issued tokens retain their existing behavior; heartbeat tokens remain independently run-bound. + +The expanded gateway journey covers 13 cases: issuer/target Block, ordinary and rate-bounded Ask first, current policy/grant/token revocation after requesting approval, token deletion and narrowed action scope, independent/shared rates, credential identity and board-issued approval compatibility. Twelve cases passed before adding the board compatibility/attribution checks (`/tmp/parity-gateway-issuer-qualified.log`). The board baseline failed when incorrectly treated as delegated authority (`/tmp/parity-gateway-board-compat-red.log`), and the intermediate attribution assertions failed while requestedByAgentId still named the target (`/tmp/parity-gateway-issuer-board-qualified.log`). Correct attribution then exposed five review-context mismatches (`/tmp/parity-gateway-pagination-final-full.log`). The review transaction now checks the acting principal; a 96-case actual-auth/OpenAPI run passed afterward (`/tmp/parity-gateway-pagination-qualified.log`). The existing gateway suite subsequently exposed implicit legacy session actors being compared without the policy evaluator's normalization: four failures and two consequential unhandled rejections (`/tmp/parity-gateway-pagination-neighbors-final.log`). Approval session checks now use that canonical actor normalization. + +Activity cursors advance across a bounded examined window even when no rows are visible. The actual-JWT regression traverses 27 private events to reach three older readable events, including tied timestamps, without exposing private event details or scanning all history. The existing page-bounded authorization regression remains green (`/tmp/parity-private-pagination-green.log`). + +Superagent then found local subscription checks returning device URL/code into ordinary native mutation receipts. A production-native call_api regression reproduced the unencrypted receipt (`/tmp/parity-local-check-receipt-red.log`). Local check and test-call responses now use the existing sensitive-response path; the real-auth local-login fixture verifies encrypted persistence, exact replay, redacted history and no response asset. Only the browser process is a deterministic fixture; production routes, native bindings, authentication and database persistence remain real. No ambient CLI credentials or provider consent is used. + +The final combined API/native run passed **2,119 tests**: 70 assembled-server actual-auth scenarios, 27 OpenAPI contracts and 2,022 native API tests (`/tmp/parity-gateway-pagination-receipts-qualified.log`). Server typecheck/build passed (`/tmp/parity-gateway-pagination-receipts-types.log`, `/tmp/parity-gateway-pagination-receipts-build.log`). Focused existing acceptance suites use separate migrated disposable PostgreSQL URLs through test-only PAPERCLIP_TOOL_GATEWAY_TEST_DATABASE_URL and PAPERCLIP_TOOL_POLICY_TEST_DATABASE_URL; normal Linux embedded execution remains unchanged. All **141 existing gateway/policy tests passed** after the canonical-actor correction (92 gateway and 49 policy-service cases, `/tmp/parity-gateway-pagination-neighbors-qualified.log`). The Vercel OAuth callback ownership follow-up travels with the companion's existing Vercel credential ownership changes to keep the implementation below 100 files. Fresh exact-head Linux CI and reviews remain required; no broad local sweep, live provider setup or empirical LLM success rate is claimed. + +The fresh workspace typecheck initially stopped at the migration checker because the sandbox denied tsx its local IPC pipe (`EPERM`, `/tmp/parity-gateway-pagination-stack-types.log`); this is a local execution-permission failure, not a migration or TypeScript failure. The authorized rerun and final stack checks are recorded separately. + +Final preserved-stack verification passed **2,165 tests** (70 actual-auth integration scenarios, 27 OpenAPI contracts, 2,022 native API tests and 46 saved GitHub wizard cases) in fresh PostgreSQL `parity_review_73` (`/tmp/parity-gateway-pagination-stack-qualified.log`). Workspace `pnpm -r typecheck` passed with local IPC enabled (`/tmp/parity-gateway-pagination-stack-types-qualified.log`), as did `pnpm build`, maps and UI token gates (`/tmp/parity-gateway-pagination-stack-build.log`, `-maps.log`, `-tokens.log`). The earlier IPC failure remains documented above. These focused suites do not replace the full Linux CI matrix or establish a full local pnpm test:run sweep. + + +### Master refresh and published profile preservation (2026-10-27) + +Master advanced to `ea727dc3c2` after the preceding exact-head Linux CI workflows passed. The implementation now includes it in `f9e9661a11342dff41a07603c6d28f90d9d35768`; its diff stays at99files. The conflicts were the Copilot manifest, TypeScript/Rust generated admission, installation regression and v63 attestation. Published upstream v62 and v63 fixtures remain byte-for-byte unchanged. The combined delegated guidance now uses v64 with digest `sha256:22e31f95ef972bcead98a4a707c9f33433558fd111e390e027a2bbb4346e112a`. The earlier guidance-v63 digest above is historical. Current master GitHub replay and chat continuation repairs are retained. + +The existing installation source-identity assertion failed against unchanged published v63 because the delegated runtime guidance source differed (`/tmp/parity-ea727-profile-preservation-red.log`:59pass,1fail). The normal identity generator produced the new v64 attestation and shared TypeScript/Rust data; its check and all61installation cases pass (`/tmp/parity-ea727-profile-green.log`). The added case pins published v63 bytes and rejects its historical identity for current launch. + +The refreshed implementation passed all97 actual-auth/OpenAPI cases (70+27, `/tmp/parity-ea727-auth-contracts.log`), all141gateway/policy cases (`/tmp/parity-ea727-neighbors-qualified.log`), workspace typecheck and build (`/tmp/parity-ea727-typecheck.log`, `/tmp/parity-ea727-build.log`). Test setup initially found an occupied fixed localhost port; Docker then assigned a fresh localhost port. The first neighbor-suite migration command could not resolve the root `tsx` package and ran against an uninitialized fixture schema; those setup failures are retained in `/tmp/parity-ea727-neighbors-migrate.log` and `/tmp/parity-ea727-neighbors.log`. Using the built database package initialized the separate disposable test database and the unchanged141cases passed. No schema guard, assertion, policy or provider receipt was weakened. The fixture container was stopped and removed after verification. Full local test-sweep success is not claimed; fresh exact-head Linux CI and reviews are required after both pushes. + +The companion merge preserves both upstream chat-recovery recipes and the delegated configuration/voice access entries. All38own companion files and their complete patch were backed up before refreshing the stack. Consent/catalog/Vercel credential ownership and callback, UI and docs remain part of this companion. + +The refreshed combined stack passed all341cases in the five affected connection intent, Google Workspace, AppsConnect, policy and GitHub setup UI suites (`/tmp/parity-ea727-stack-ui.log`). Workspace typecheck/build, map structure and token gates also pass on the preserved combined source (`/tmp/parity-ea727-stack-typecheck.log`, `/tmp/parity-ea727-stack-build.log`, `/tmp/parity-ea727-maps.log`, `/tmp/parity-ea727-tokens.log`). No fresh local OAuth protocol pass or live provider acceptance is claimed by those UI/static results; fresh Linux CI must qualify this successor head. + +### Slack caller policy and human identity handoff completion (2026-10-27) + +Greptile's review of `f9e9661a11` found two valid gaps. Slack test sends selected only the endpoint's assigned agent for tool policy, so a blocked caller could use another allowed agent's endpoint. A `confirm_identity` card also treated healthy App credentials as sufficient without a current link for its addressed human. Actual bearer-authenticated regression cases reproduced both linked-account messages and identity invitations sending under a caller Block, and a healthy unconfirmed GitHub handoff satisfying early (`/tmp/parity-slack-identity-qualified-red.log`: three failing tests). The first diagnostic reused one bot token across endpoint fixtures and correctly hit the existing credential-identity conflict; assigning unique fixture tokens/workspaces exposed the invitation bypass without changing the guard (`/tmp/parity-slack-identity-red.log`). + +Implementation `2ecd6451d0` evaluates both caller and target with the existing tool-policy service, preserves actual-caller audit attribution, denies either Block or Ask first decision before dispatch, applies independent rates and shares only identical versioned rate buckets. Identity handoffs require a linked identity for the exact company, endpoint, addressed user, provider and provider account, in addition to the existing health and current-management checks. The existing human GitHub OAuth journey now proves that the callback alone is insufficient and explicit human confirmation unlocks the identity requirement. Missing, pending, revoked, expired, wrong-user and wrong-account links leave the handoff blocked. A worker sweep with no link leaves the card pending and creates no continuation delivery. + +The focused run passed four test cases, including twelve Slack caller/target/recipient combinations (`/tmp/parity-slack-identity-qualified-green.log`). The intermediate repaired-code run failed only the new audit assertion's incorrect top-level agentId projection; the legacy audit stores it in details.agentId, which the corrected assertion now verifies (`/tmp/parity-slack-identity-green.log`). A fresh PostgreSQL database then passed all **99 API/OpenAPI tests** (72 actual-auth scenarios and27 contracts) in `/tmp/parity-slack-identity-suite.log`. The same command selected the existing continuation suite, but its50cases were skipped because embedded PostgreSQL could not start on this host; they are not claimed as passing and remain a fresh Linux CI obligation. Actual-auth fixtures exercise the changed identity finder and sweep against disposable real PostgreSQL. Workspace typecheck and build passed (`/tmp/parity-slack-identity-typecheck.log`, `/tmp/parity-slack-identity-build.log`). No full local sweep or live provider consent is claimed. All38companion files and their patch were backed up under `/tmp/parity-pre-slack-identity/` before carrying these parent repairs into the companion. + +The preserved combined companion source also passes the four focused real-auth Slack/GitHub/continuation tests in another fresh PostgreSQL database (`/tmp/parity-slack-identity-stack-qualified.log`), workspace typecheck/build (`/tmp/parity-slack-identity-stack-typecheck.log`, `/tmp/parity-slack-identity-stack-build.log`) and map validation (`/tmp/parity-slack-identity-maps.log`). All38own files remain;35retain identical bytes and only the two chat maps and this qualification record add evidence. The addressed findings PRRT_kwDORclPyM6wijEE and PRRT_kwDORclPyM6wijEG have evidence replies4384927765 and4384927895 and are resolved. Fresh exact-head CI and all reviewer gates are still required; resolving threads does not supply a new Greptile score or external assessment. diff --git a/maps/agent-access-map/app-permissions.md b/maps/agent-access-map/app-permissions.md index 7183dcb59bf..716fbb8a8c7 100644 --- a/maps/agent-access-map/app-permissions.md +++ b/maps/agent-access-map/app-permissions.md @@ -1,191 +1,499 @@ # App access, action permissions, and testing — agent access -Product map: [App access, action permissions, and -testing](../feature-map/app-permissions.md). +Product map: [app-permissions](../feature-map/app-permissions.md). Feature ID: `app-permissions`. -Review date: 2026-10-12. Source commit: `f981fa3cc10cb8663a87dec60345fbff46d54d0a`. +Review date: 2026-10-26. Source base: `a1f77821b9cd5c9c846fdf7a0fa5cc620ad2b6bb`; implementation: [#17737](https://github.com/paperclipai/paperclip/pull/17737). -This is a source-reviewed baseline of representative operations and known gaps. -Each capability needs operation-level review before its entire scope can be -qualified. No live agent execution is claimed. Follow the [map contract](../README.md) -for format, permission checks, evidence and maintenance. +Connection operations reviewed in this working tree. The inventory was reconciled +against production UI clients, REST registrations and service entry points; it is +a review checklist, not a runtime registry. [Authorization and evidence contract](../../doc/plans/2026-10-13-connection-api-parity.md). +Fixture evidence is separate from live provider qualification and from empirical +LLM success. Source-only entries have a reviewed agent path or an explicit boundary, +but do not claim an individually executed HTTP test. ## `agent-access` Capability ID: `app-permissions/agent-access`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-tool-connections-grants` -- `PUT /api/tool-connections/{connectionId}/installs`. +API: `present` — `GET /api/tool-connections/{connectionId}/grants`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `restricted` — Board management API; agents use their resulting assigned -app tools. +### `put-tool-connections-grants-members` -Policy: Connection/profile/policy management routes require a board user; tool use -separately evaluates agent grants and action policies. +API: `present` — `PUT /api/tool-connections/{connectionId}/grants/{grantId}/members`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles` and `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Evidence: `source-only` — [implementation](../../server/src/routes/tool-access.ts#L517) -at the reviewed commit above. +### `post-tool-connections-grants-installations` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `POST /api/tool-connections/{connectionId}/grants/installations`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles` and `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Gaps: Granting access is distinct from using access. No equivalent agent management path -exists in these routes. +### `delete-tool-connections-grants` -The owning lifecycle plugin can read bounded standing connection and active -profile assignments with `ctx.agents.getRuntime`. It requires the existing -`agents.lifecycle.manage` capability and agent ownership checks. Committed -assignment changes use the opt-in `agent_runtime` stream. These inputs contain -no grant credentials and do not authorize a task's tool calls. See the -[runtime contract](../../doc/plugins/AGENT_RUNTIME_SYNC.md#standing-connection-assignments). +API: `present` — `DELETE /api/tool-connections/{connectionId}/grants/{grantId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. +### `post-tool-connections-grants-delegations` + +API: `present` — `POST /api/tool-connections/{connectionId}/grants/{grantId}/delegations`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles` and `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [authenticated integration fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed through production authentication and database persistence. +Verification: `passed` — deterministic local fixture; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `delete-tool-connections-grants-delegations` + +API: `present` — `DELETE /api/tool-connections/{connectionId}/grants/{grantId}/delegations/{delegationId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles` and `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [authenticated integration fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed through production authentication and database persistence. +Verification: `passed` — deterministic local fixture; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `get-tool-connections-installs` + +API: `present` — `GET /api/tool-connections/{connectionId}/installs`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `put-tool-connections-installs` + +API: `present` — `PUT /api/tool-connections/{connectionId}/installs`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles` and `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. ## `action-policy` Capability ID: `app-permissions/action-policy`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-companies-tools-trust-rules` -- `POST /api/tool-profiles/{profileId}/entries`. +API: `present` — `GET /api/companies/{companyId}/tools/trust-rules`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `restricted` — Board management API; agent execution follows -off/ask/allowed. +### `get-companies-tools-policies` -Policy: Connection/profile/policy management routes require a board user; tool use -separately evaluates agent grants and action policies. +API: `present` — `GET /api/companies/{companyId}/tools/policies`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Evidence: `source-only` — [implementation](../../server/src/routes/tool-access.ts#L517) -at the reviewed commit above. +### `post-companies-tools-policies-reorder` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `POST /api/companies/{companyId}/tools/policies/reorder`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Gaps: An agent cannot authorize itself by editing the policy. Decide whether any -explicitly delegated administration should be supported. +### `post-companies-tools-policies` -Braintrust's reviewed classifier treats retrieval as read, dataset row edits and -automation-status changes as destructive, and other mutations, billable -execution, and unknown tools as write. Provider hints cannot lower a reviewed -mutation's risk. The classifier grants no authority: shared Allowed defaults -and explicit Off/Ask-first policies still govern each agent's actual run-bound -call. See [Braintrust](../../doc/connections/BRAINTRUST.md#actions-and-governance). -Live US OAuth bounded reads and a synthetic dataset insert/readback passed -through actual process-agent runs and run-scoped gateway sessions; local removal -then denied further use. This does not establish live Off/Ask-first policy -behavior, which is covered with provider fixtures. US API-key and both EU -methods remain `not-run` against real accounts; board Test calls do not -establish agent execution evidence. +API: `present` — `POST /api/companies/{companyId}/tools/policies`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. +### `post-companies-tools-policies-duplicate` + +API: `present` — `POST /api/companies/{companyId}/tools/policies/{policyId}/duplicate`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `patch-companies-tools-policies` + +API: `present` — `PATCH /api/companies/{companyId}/tools/policies/{policyId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `delete-companies-tools-policies` + +API: `present` — `DELETE /api/companies/{companyId}/tools/policies/{policyId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-tools-trust-rules-revoke` + +API: `present` — `POST /api/companies/{companyId}/tools/trust-rules/{policyId}/revoke`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-tools-policy-test` + +API: `present` — `POST /api/companies/{companyId}/tools/policy/test`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. ## `catalog-change` Capability ID: `app-permissions/catalog-change`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `post-tool-connections-catalog-refresh` -- `POST /api/tool-profiles/{profileId}/new-tools/review`. +API: `present` — `POST /api/tool-connections/{connectionId}/catalog/refresh`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `restricted` — Board catalog-review API; agents can discover authorized -tools. +### `get-tool-connections-catalog` -Policy: Connection/profile/policy management routes require a board user; tool use -separately evaluates agent grants and action policies. +API: `present` — `GET /api/tool-connections/{connectionId}/catalog`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Evidence: `source-only` — [implementation](../../server/src/routes/tool-access.ts#L517) -at the reviewed commit above. +### `get-tool-profiles-new-tools` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `GET /api/tool-profiles/{profileId}/new-tools`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) exercise saved application/catalog/tool-name/risk selectors, allow-all profiles, managed empty profiles, personal-account visibility, and scoped grants through actual keys and run JWTs. +Verification: `passed` — local assembled server and real database; inaccessible profile resources are omitted from lists and denied before new-tool or effective-profile data is returned. Proposed broad entries and installed connections use the same authority checks. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Gaps: Catalog refresh/review and quarantined-action enablement are management -operations. - -Braintrust enables changed-tool quarantine after initial discovery and preserves -it through reconnect and refresh. Agents cannot approve newly quarantined tools -or bypass a changed schema by reusing its old name. Verify the quarantined call -denial through a real run-scoped gateway separately from board catalog review; -the [provider qualification record](../../doc/connections/BRAINTRUST.md#qualification-record) -records live US OAuth catalog refresh and reconnect preserving the regional -method and quarantine setting. Live changed-tool denial remains outstanding; -fixtures cover that denial and policy preservation across refresh/reconnect. -US API-key and both EU methods have no account-bound qualification. Live token -expiry/refresh, remote OAuth revocation, and native CLI automatic capability -delivery also remain unqualified. - -Daytona also checks its deployed native contract on the board's -`GET /api/tool-connections/{connectionId}/catalog`. A changed saved catalog -refreshes under the viewing human's credential context. Retired tools are -hidden even if refresh fails. Agent discovery and dispatch reject retired names -and stale same-name contracts before refresh; new/changed tools stay quarantined until reviewed. The -[database-backed Daytona fixtures](../../server/src/__tests__/daytona-connection.test.ts) -exercise this boundary through real run-bound gateway sessions with fixture -provider responses. They do not qualify a live Daytona account or agent model. - -Unikraft uses the same native-catalog contract: stale same-name saved entries are -rejected before refresh and new or changed actions stay quarantined. Its -[database-backed fixtures](../../server/src/__tests__/unikraft-connection.test.ts) -cover this through real run-bound gateway sessions with fixture provider -responses, not a live Unikraft account. +### `post-tool-profiles-new-tools-review` +API: `present` — `POST /api/tool-profiles/{profileId}/new-tools/review`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; mutations whose entry selectors resolve connections also require `tools:manage_connections`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. ## `test-action` Capability ID: `app-permissions/test-action`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-companies-tools-action-requests` -- `POST /api/tool-connections/{connectionId}/test-calls`. +API: `present` — `GET /api/companies/{companyId}/tools/action-requests`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `restricted` — Board test-as-agent API; agent executes its own assigned -tools normally. +### `get-tool-connections-test-agents` -Policy: Connection/profile/policy management routes require a board user; tool use -separately evaluates agent grants and action policies. +API: `present` — `GET /api/tool-connections/{connectionId}/test-agents`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Evidence: `source-only` — [implementation](../../server/src/routes/tool-access.ts#L517) -at the reviewed commit above. +### `get-tool-connections-test-agents-access` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `GET /api/tool-connections/{connectionId}/test-agents/{agentId}/access`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Gaps: A board test performed as an agent is not proof the agent can manage or invoke the -testing endpoint. +### `post-tool-connections-test-calls` +API: `present` — `POST /api/tool-connections/{connectionId}/test-calls`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:use`, responsible-user credential access and target-agent authority. Testing another agent also requires the caller’s full tool access decision (effective profiles, explicit grants, and action policies) and both principals to select the same credential identity, regardless of the connection’s auth label. Caller and target action policies apply before dispatch and after approval; either Ask first decision requires ordinary human approval. Shared rate buckets consume one slot per test. A successful yielded run may continue only while current user, agent, task, and grant authority remain valid. +Evidence: `verified` — [assembled-server integration](../../server/src/__tests__/connection-management.integration.test.ts), actual bearer/session authentication, persisted PostgreSQL state, and deterministic local MCP/provider fixtures. +Verification: `passed` — 55-scenario fixture suite on a fresh isolated PostgreSQL database, including permitted policy/explicit grants without a caller profile, denied caller policies, dedicated credentials (including no-auth labels), caller-only approvals and rate limits, shared rates, private status reads, approved continuation, revocation, cancellation and replay. No live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `get-tool-connections-test-calls` + +API: `present` — `GET /api/tool-connections/{connectionId}/test-calls/{actionRequestId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:use`; shared management policy, originating-principal binding, and current linked-task read authorization. Agent and responsible-user task privacy checks apply even in shadow mode before exposing saved arguments or provider results. +Evidence: `verified` — [assembled-server integration](../../server/src/__tests__/connection-management.integration.test.ts), actual persistent key/run JWT authentication and saved PostgreSQL state. +Verification: `passed` — own visible status returns 200; moving its invocation to an inaccessible private task causes 403 with both agent authentication paths. This regression was red before the fix. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `post-companies-tools-action-requests-trust-rule` + +API: `present` — `POST /api/companies/{companyId}/tools/action-requests/{actionRequestId}/trust-rule`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-tool-gateway-action-requests-approve` + +API: `present` — `POST /api/tool-gateway/action-requests/{id}/approve`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-tool-gateway-action-requests-decline` + +API: `present` — `POST /api/tool-gateway/action-requests/{id}/decline`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. ## `account-maintenance` Capability ID: `app-permissions/account-maintenance`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-companies-tools-apps-attention` -- `POST /api/tool-connections/{connectionId}/reconnect`. -- `GET /api/tool-connections/{connectionId}/activity`. +API: `present` — `GET /api/companies/{companyId}/tools/apps/attention`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `restricted` — Board account management; native -connections_search/connection_request can request human setup. +### `get-companies-tools-applications` -Policy: Connection/profile/policy management routes require a board user; tool use -separately evaluates agent grants and action policies. +API: `present` — `GET /api/companies/{companyId}/tools/applications`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Evidence: `source-only` — [implementation](../../server/src/routes/tool-access.ts#L517) -at the reviewed commit above. +### `post-companies-tools-applications` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `POST /api/companies/{companyId}/tools/applications`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Gaps: Agent requests and continued tool use exist; reconnect/revoke/audit management are -human paths. +### `patch-tool-applications` + +API: `present` — `PATCH /api/tool-applications/{applicationId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `delete-tool-applications` + +API: `present` — `DELETE /api/tool-applications/{applicationId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-tools-connections` + +API: `present` — `GET /api/companies/{companyId}/tools/connections`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `get-tool-connections` + +API: `present` — `GET /api/tool-connections/{connectionId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `post-tool-connections-railway-ssh` + +API: `present` — `POST /api/tool-connections/{connectionId}/railway/ssh`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-tool-connections-usage` + +API: `present` — `GET /api/tool-connections/{connectionId}/usage`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-tool-connections-aggregator-apps` + +API: `present` — `GET /api/tool-connections/{connectionId}/aggregator/apps`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-tool-connections-aggregator-apps-sync` + +API: `present` — `POST /api/tool-connections/{connectionId}/aggregator/apps/sync`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-tool-connections-aggregator-apps-refresh` + +API: `present` — `POST /api/tool-connections/{connectionId}/aggregator/apps/refresh`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `put-tool-connections-aggregator-discovery` + +API: `present` — `PUT /api/tool-connections/{connectionId}/aggregator/discovery`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-tool-connections-composio-apps` + +API: `present` — `GET /api/tool-connections/{connectionId}/composio/apps`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-tool-connections-composio-apps-sync` + +API: `present` — `POST /api/tool-connections/{connectionId}/composio/apps/sync`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-tool-connections-composio-apps-refresh` + +API: `present` — `POST /api/tool-connections/{connectionId}/composio/apps/refresh`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-tool-connections-composio-apps-accounts` + +API: `present` — `POST /api/tool-connections/{connectionId}/composio/apps/{toolkit}/accounts`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-tool-connections-composio-apps-setup` + +API: `present` — `POST /api/tool-connections/{connectionId}/composio/apps/{toolkit}/setup`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `patch-tool-connections` + +API: `present` — `PATCH /api/tool-connections/{connectionId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. Agents cannot replace channel credential references through this generic operation; provider setup owns credential validation and human verification-key entry. +Evidence: `verified` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server, including actual-JWT denial of a Slack signing-key reference replacement. Ordinary connection configuration remains permitted. No live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `delete-tool-connections` + +API: `present` — `DELETE /api/tool-connections/{connectionId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-tool-connections-health-check` + +API: `present` — `POST /api/tool-connections/{connectionId}/health-check`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-tool-connections-activity` + +API: `present` — `GET /api/tool-connections/{connectionId}/activity`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:view_audit`, shared management authority, and linked-task visibility. Pages are capped at 100 events; each distinct task is authorized once and the request's run capture is reused. Decisions are not retained across requests. +Evidence: `verified` — [assembled-server integration](../../server/src/__tests__/connection-management.integration.test.ts), actual run JWT authentication and PostgreSQL event persistence. +Verification: `passed` — a 100-event page over two tasks returns its 50 visible events, with two capability decisions and at most two run captures (previously 100 and 102). Private events remain hidden; revoking audit permission makes the next request return 403. +Gaps: This internal audit-read proof does not qualify live provider setup or actions. diff --git a/maps/agent-access-map/chat-channels.md b/maps/agent-access-map/chat-channels.md index b2d708898b3..458fe3745f4 100644 --- a/maps/agent-access-map/chat-channels.md +++ b/maps/agent-access-map/chat-channels.md @@ -1,100 +1,582 @@ # External chat and email conversations — agent access -Product map: [External chat and email conversations](../feature-map/chat-channels.md). +Product map: [chat-channels](../feature-map/chat-channels.md). Feature ID: `chat-channels`. -Review date: 2026-10-12. Source commit: `58e16dbddfc9c8d0cf60867f9d9847f0feda055b`. -Native REST transport reconciled in this change; see the -[shared transport contract](README.md#native-rest-transport). +Review date: 2026-10-26. Source base: `a1f77821b9cd5c9c846fdf7a0fa5cc620ad2b6bb`; implementation: [#17737](https://github.com/paperclipai/paperclip/pull/17737). -This is a source-reviewed baseline of representative operations and known gaps. -Each capability needs operation-level review before its entire scope can be -qualified. No live agent execution is claimed. Follow the [map contract](../README.md) -for format, permission checks, evidence and maintenance. +Connection operations reviewed in this working tree. The inventory was reconciled +against production UI clients, REST registrations and service entry points; it is +a review checklist, not a runtime registry. [Authorization and evidence contract](../../doc/plans/2026-10-13-connection-api-parity.md). +Fixture evidence is separate from live provider qualification and from empirical +LLM success. Source-only entries have a reviewed agent path or an explicit boundary, +but do not claim an individually executed HTTP test. ## `providers` Capability ID: `chat-channels/providers`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-companies-chat-endpoints` -- `POST /api/companies/{companyId}/chat-endpoints`. +API: `present` — `GET /api/companies/{companyId}/chat-endpoints`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `restricted` — Board setup APIs; native connection request can ask for -setup. +### `post-companies-chat-endpoints` -Policy: Endpoint management/identity linking requires a board user; delivery is -server-owned and agent tools use scoped assignments. +API: `present` — `POST /api/companies/{companyId}/chat-endpoints`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Evidence: `source-only` — [implementation](../../server/src/routes/chat-channels.ts#L75) -at the reviewed commit above. +### `get-chat-endpoints` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `GET /api/chat-endpoints/{endpointId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Gaps: Provider installation and account enrollment are not available to an ordinary -agent actor. +### `get-companies-chat-slack-setup-options` -Speko UI reconciliation (2026-10-13): experimental voice setup uses the same -board-managed endpoint boundary. Source review follows the merged voice runtime -and the UI integration; live provider qualification is still separate. +API: `present` — `GET /api/companies/{companyId}/chat-slack/setup-options`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. +### `post-chat-endpoints-slack-managed-authorize` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/slack/managed/authorize`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api` return a saved `requiredUserAction` handoff. The responsible user must open that setup, bind their browser session, and consent to Slack access. An agent call does not start provider consent. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Current agent and responsible-user authority bound the request. The addressed human starts the existing session-bound Slack authorization flow; callback state and subject checks remain enforced. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-chat-endpoints-slack-managed-provision` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/slack/managed/provision`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `delete-companies-chat-slack-manager-grants` + +API: `present` — `DELETE /api/companies/{companyId}/chat-slack/manager-grants/{grantId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-chat-slack-managed-oauth-callback` + +API: `present` — `GET /api/chat-slack/managed/oauth/callback`; browser/provider/MCP protocol. +Agent access: `restricted` — browser authentication return. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-chat-endpoints-slack-registration` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/slack/registration`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-chat-endpoints-slack-install` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/slack/install`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api` return a saved `requiredUserAction` handoff. The responsible user must open that setup, bind their browser session, and consent to the Slack installation. An agent call does not start installation consent. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Current agent and responsible-user authority bound the request. Only the addressed human starts session-bound installation; callback state and subject checks remain enforced. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-chat-slack-oauth-callback` + +API: `present` — `GET /api/chat-slack/oauth/callback`; browser/provider/MCP protocol. +Agent access: `restricted` — browser authentication return. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-chat-endpoints-github-configuration` + +API: `present` — `GET /api/chat-endpoints/{endpointId}/github/configuration`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `put-chat-endpoints-github-configuration` + +API: `present` — `PUT /api/chat-endpoints/{endpointId}/github/configuration`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `post-chat-endpoints-github-verify` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/github/verify`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `put-chat-endpoints-github-progress` + +API: `present` — `PUT /api/chat-endpoints/{endpointId}/github/progress`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-chat-endpoints-github-personal-connections` + +API: `present` — `GET /api/chat-endpoints/{endpointId}/github/personal-connections`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-chat-endpoints-github-people-lookup` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/github/people/lookup`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-chat-endpoints-github-registration` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/github/registration`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `post-chat-endpoints-github-registration-restart` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/github/registration/restart`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `put-chat-endpoints-github-draft` + +API: `present` — `PUT /api/chat-endpoints/{endpointId}/github/draft`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-chat-endpoints-github-setup` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/github/setup`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-chat-endpoints-github-app` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/github/app`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api` return a saved human credential-entry handoff; agents may send an empty body. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). A human session imports the App credentials and signing key. Delegated service import is rejected, and agent requests persist no caller-chosen key. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts), [provider service](../../server/src/services/chat-channels/github/app-setup.ts), and [actual-auth fixture](../../server/src/__tests__/connection-management.integration.test.ts). Agent key/JWT handoffs leave vault refs unchanged. Fresh drafts open the saved import form without the reconnect flag; its component test submits App import with the required webhook secret. Human import persists credentials, then agent repository discovery and verification continue. +Verification: `passed` — deterministic provider fixtures, assembled server and [saved-screen tests](../../ui/src/pages/apps/chat/GitHubChatSetup.test.tsx). +Gaps: Human credential entry is intentional; no live provider qualification. + +### `post-chat-endpoints-github-repositories-refresh` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/github/repositories/refresh`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `get-chat-endpoints-github-repositories` + +API: `present` — `GET /api/chat-endpoints/{endpointId}/github/repositories`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `put-chat-endpoints-github-repositories-access` + +API: `present` — `PUT /api/chat-endpoints/{endpointId}/github/repositories/access`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `patch-chat-endpoints` + +API: `present` — `PATCH /api/chat-endpoints/{endpointId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `post-chat-endpoints-photon-inspect` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/photon/inspect`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-chat-endpoints-setup` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/setup`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. Agent-supplied signing/webhook secrets, private keys, client secrets, and project secrets return a saved human credential-entry handoff. Authorized write-only bot-token and API-key setup remains delegated. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). The service rejects delegated verification-key entry before provider work or vault writes. Human sessions and verified provider handoffs retain credential entry; agent configuration uses the responsible user's current authority. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — actual key/JWT configure/reconnect requests return the addressed handoff with unchanged secret refs and vault signing value. The saved card stays pending through old health, bot-token repair, unchanged signing-key entry and retries. It completes only after a protected value change, renewed webhook verification and current authority; cancellation or user revocation still denies continuation. Credential-write revocation during provider authentication leaves refs unchanged. Complete human replacements repair disabled, deleted or unreadable old signing material, while unexpected resolver failures abort without changing refs. Different credential field sets/completion modes save separate handoffs; matching retries preserve the original baseline. Immediate and saved-intent links carry those field names and completion mode. Configured GitHub webhook/client-secret replacements use human reconnect while first-setup secret restrictions remain; the saved form submits only requested values and retains other vaulted credentials. An earlier provisioned Slack challenge survives only for the same signing fingerprint and callback URL. A human session configures Slack; an agent can repair its bot token and send a policy-governed test message. Native setup receipts are protected. No live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `post-chat-endpoints-setup-secret` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/setup-secret`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api` return the saved endpoint's `requiredUserAction` handoff. Only a human session can rotate and receive the signing secret. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Agents cannot generate or export the GitHub webhook signing secret; the service also rejects delegated generation. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts), [provider service](../../server/src/services/chat-channels/github/setup-secret.ts), and [authenticated regression](../../server/src/__tests__/connection-management.integration.test.ts). Actual key/JWT requests return a handoff without changing credential refs; a human session can rotate and persist new refs. +Verification: `passed` — deterministic provider fixtures / local assembled server. The regression failed before the fix; all 42 assembled-server scenarios passed afterward. Rotation remains pending through old health and generation/reconnect failures. A completion marker commits with new vault refs and cleared webhook verification; restored verified health is also required. Saved links open the one-time human rotation screen. +Gaps: No live GitHub rotation qualification; plaintext signing-secret setup intentionally remains a human step. + +### `get-chat-endpoints-resources` + +API: `present` — `GET /api/chat-endpoints/{endpointId}/resources`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `put-chat-endpoints-resources` + +API: `present` — `PUT /api/chat-endpoints/{endpointId}/resources`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-email-connections` + +API: `present` — `GET /api/companies/{companyId}/email/connections`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `post-companies-email-connections` + +API: `present` — `POST /api/companies/{companyId}/email/connections`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `post-companies-email-connections-inspect` + +API: `present` — `POST /api/companies/{companyId}/email/connections/{connectionId}/inspect`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-email-connections-check-address` + +API: `present` — `POST /api/companies/{companyId}/email/connections/{connectionId}/check-address`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-email-inboxes` + +API: `present` — `GET /api/companies/{companyId}/email/inboxes`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-email-inspect` + +API: `present` — `POST /api/companies/{companyId}/email/inspect`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-email-inboxes` + +API: `present` — `POST /api/companies/{companyId}/email/inboxes`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-email-deliveries` + +API: `present` — `GET /api/companies/{companyId}/email/deliveries/{publicationId}`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. ## `identity` Capability ID: `chat-channels/identity`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `post-chat-endpoints-github-identity` -- `POST /api/chat-identity-links/confirm`. +API: `present` — `POST /api/chat-endpoints/{endpointId}/github/identity`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. -Agent access: `restricted` — Human identity-link flow. +### `post-chat-endpoints-github-identity-start` -Policy: Endpoint management/identity linking requires a board user; delivery is -server-owned and agent tools use scoped assignments. +API: `present` — `POST /api/chat-endpoints/{endpointId}/github/identity/start`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — actual run-JWT handoff and human-session OAuth/confirmation routes against deterministic providers. Healthy credentials and an OAuth callback alone leave the request pending with no continuation delivery. Missing, pending, revoked, expired, wrong-user and wrong-provider-account links do not satisfy it; the addressed user's current link for this endpoint does. Current membership and management authority still apply. No live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Evidence: `source-only` — [implementation](../../server/src/routes/chat-channels.ts#L75) -at the reviewed commit above. +### `post-chat-endpoints-github-identity-confirm` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `POST /api/chat-endpoints/{endpointId}/github/identity/confirm`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `verified` — [actual human-session OAuth/confirmation fixture](../../server/src/__tests__/connection-management.integration.test.ts) with production session middleware and database identity persistence. +Verification: `passed` — the addressed human completes OAuth and explicitly confirms the observed GitHub identity; only then can the saved identity handoff satisfy its identity requirement. Provider responses are fixtures and do not prove live consent. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. -Gaps: Identity linking requires a signed-in board user, rollout admission and -provider checks at the HTTP route. Native `call_api` preserves these permissions. +### `get-chat-endpoints-principals` +API: `present` — `GET /api/chat-endpoints/{endpointId}/principals`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-chat-endpoints-principals-link-intent` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/principals/{principalId}/link-intent`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `delete-chat-endpoints-principals-link` + +API: `present` — `DELETE /api/chat-endpoints/{endpointId}/principals/{principalId}/link`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-chat-identity-links-confirm` + +API: `present` — `POST /api/chat-identity-links/confirm`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-chat-identity-links-request-access` + +API: `present` — `POST /api/chat-identity-links/request-access`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-chat-identity-links-preview` + +API: `present` — `GET /api/chat-identity-links/preview`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-api-chat-github-manifest-callback` + +API: `present` — `GET /api/chat-github/manifest/callback`; browser/provider/MCP protocol. +Agent access: `restricted` — browser authentication return. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `source-only` — [route](../../server/src/app.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-api-chat-github-cloud-callback` + +API: `present` — `GET /api/chat-github/cloud/callback`; browser/provider/MCP protocol. +Agent access: `restricted` — browser authentication return. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `source-only` — [route](../../server/src/app.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-api-chat-github-identity-callback` + +API: `present` — `GET /api/chat-github/identity/callback`; browser/provider/MCP protocol. +Agent access: `restricted` — browser authentication return. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `verified` — [route](../../server/src/app.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. ## `conversation` Capability ID: `chat-channels/conversation`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-chat-endpoints-github-review-detail` -- `POST /api/chat-endpoints/{endpointId}/conversations/{conversationId}/publications`. +API: `present` — `GET /api/chat-endpoints/{endpointId}/github/reviews/{reviewId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. Linked task and project visibility apply to every review row in addition to endpoint and credential visibility. +Evidence: `verified` — [production-authenticated integration fixture](../../server/src/__tests__/connection-management.integration.test.ts) with real database persistence and deterministic provider responses where required. +Verification: `passed` — Actual agent-key reads return the visible review and omit or return 404 for a review linked to an inaccessible private task. Real database persistence; no live GitHub call. +Gaps: Fixture evidence does not qualify live provider behavior or an empirical model success rate. -Agent access: `partial` — Assigned-agent reply pipeline; native completion; provider -tools where assigned. +### `get-chat-endpoints-github-reviews` -Policy: Endpoint management/identity linking requires a board user; delivery is -server-owned and agent tools use scoped assignments. +API: `present` — `GET /api/chat-endpoints/{endpointId}/github/reviews`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. Linked task and project visibility apply to every review row in addition to endpoint and credential visibility. +Evidence: `verified` — [production-authenticated integration fixture](../../server/src/__tests__/connection-management.integration.test.ts) with real database persistence and deterministic provider responses where required. +Verification: `passed` — Actual agent-key reads return the visible review and omit or return 404 for a review linked to an inaccessible private task. Real database persistence; no live GitHub call. +Gaps: Fixture evidence does not qualify live provider behavior or an empirical model success rate. -Evidence: `source-only` — [implementation](../../server/src/routes/chat-channels.ts#L75) -at the reviewed commit above. +### `get-chat-endpoints-conversations` -Verification: `not-run` — exercise a listed permitted operation with actual agent -credentials/run context; inspect its result and applicable persistence/attribution. -Assert the relevant denied actor, owner, company or mode from Policy. Review the -remaining operations before qualifying the whole capability. +API: `present` — `GET /api/chat-endpoints/{endpointId}/conversations`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Gaps: Normal agent replies work through server delivery. Explicit endpoint publication -APIs are board management paths. +### `post-chat-endpoints-conversations-publications` +API: `present` — `POST /api/chat-endpoints/{endpointId}/conversations/{conversationId}/publications`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-chat-endpoints-conversations-publications-status` + +API: `present` — `GET /api/chat-endpoints/{endpointId}/conversations/{conversationId}/publications/{publicationId}/status`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:use`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-issues-chat-binding` + +API: `present` — `GET /api/issues/{issueId}/chat-binding`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`, responsible-user ceiling, task privacy, endpoint resource scope and target-agent visibility. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-api-chat-webhooks` + +API: `present` — `POST /api/chat-webhooks/{publicId}/{provider}`; provider webhook ingress. +Agent access: `not-applicable` — provider-signed inbound events; agent keys and gateway tokens cannot authenticate this route. +Policy: The provider verifies the payload signature and binds the endpoint/company. The request has no agent principal. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Provider ingress has no agent invocation path. Provider signature validation remains required. + +### `post-companies-email-send` + +API: `present` — `POST /api/companies/{companyId}/email/send`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-email-tasks` + +API: `present` — `GET /api/companies/{companyId}/email/tasks/{issueId}`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-api-chat-webhooks-agentmail` + +API: `present` — `POST /api/chat-webhooks/agentmail/{publicId}`; provider webhook ingress. +Agent access: `not-applicable` — provider-signed inbound events; agent keys and gateway tokens cannot authenticate this route. +Policy: The provider verifies the payload signature and binds the endpoint/company. The request has no agent principal. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Provider ingress has no agent invocation path. Provider signature validation remains required. Monitor publication lineage uses the admitted wake and its source run's matching `set_task_monitor` receipt, including company, task, immutable agent, requester, and scheduled time. A source ID in context cannot grant publication. The normal @@ -222,70 +704,187 @@ An existing conversation keeps its task binding. See [creation service](../../server/src/services/chat-channels.ts) and [defaults contract](../../packages/shared/src/types/chat-channels.ts). +### `post-voice-sessions` + +API: `present` — `POST /api/companies/{companyId}/voice-sessions`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `get-voice-session` + +API: `present` — `GET /api/companies/{companyId}/voice-sessions/{sessionId}`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `get-voice-notification` + +API: `present` — `GET /api/companies/{companyId}/voice-sessions/{sessionId}/notification`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `get-voice-report` + +API: `present` — `GET /api/companies/{companyId}/voice-sessions/{sessionId}/report`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + ## `attachments` Capability ID: `chat-channels/attachments`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-issues-attachments` -- `GET /api/issues/{id}/attachments`. -- `POST /api/companies/{companyId}/issues/{issueId}/attachments`. +API: `present` — `GET /api/issues/{id}/attachments`; JSON REST. +Agent access: `available` — agent REST and native `search_api` / `call_api` for the attachment list. +Policy: Current company membership and task read/privacy authority. Connection management grants do not replace task access. +Evidence: `source-only` — [asset routes](../../server/src/routes/assets.ts). +Verification: `not-run` — list attachments with actual agent authentication and deny a foreign or private task. +Gaps: This operation has source review, not current-head HTTP verification. -Agent access: `partial` — Native register_deliverable / attachment tools when admitted; -agent REST; server-owned delivery. +### `post-companies-issues-attachments` -Policy: Endpoint management/identity linking requires a board user; delivery is -server-owned and agent tools use scoped assignments. +API: `present` — `POST /api/companies/{companyId}/issues/{issueId}/attachments`; multipart upload. +Agent access: `partial` — agent REST upload; native attachment tools and `register_deliverable` use their admitted resource paths and server-owned chat delivery. JSON-only `call_api` does not upload multipart content. +Policy: Task mutation/privacy authority, company/resource bounds, storage limits, and admitted native tool scope. Server-owned delivery does not grant endpoint administration. +Evidence: `source-only` — [asset routes](../../server/src/routes/assets.ts) and [native authority](../../server/src/services/native-runtime/paperclip-runner-tool-authority.ts). +Verification: `not-run` — upload through an admitted agent path, inspect the stored task attachment and outbound delivery, and deny a foreign task. +Gaps: Multipart upload is not a JSON native API call. Live chat file delivery remains unqualified by this connection setup audit. -Evidence: `source-only` — [implementation](../../server/src/routes/chat-channels.ts#L75) -at the reviewed commit above. +### `post-chat-endpoints-publications-replay` -Verification: `not-run` — exercise a listed permitted operation with actual agent -credentials/run context; inspect its result and applicable persistence/attribution. -Assert the relevant denied actor, owner, company or mode from Policy. Review the -remaining operations before qualifying the whole capability. +API: `present` — `POST /api/chat-endpoints/{endpointId}/publications/{publicationId}/replay`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. -Gaps: Uploading/registering is supported. Provider delivery status and board replay are -separate operations. +### `post-chat-endpoints-publications-resolve` +API: `present` — `POST /api/chat-endpoints/{endpointId}/publications/{publicationId}/resolve`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-chat-endpoints-actions-resolve` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/actions/{actionId}/resolve`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-companies-email-deliveries-resolve` + +API: `present` — `POST /api/companies/{companyId}/email/deliveries/{publicationId}/resolve`; JSON REST. +Agent access: `restricted` — human connection manager records the outcome of an uncertain external delivery. Agents use normal governed send/status operations. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. ## `lifecycle` Capability ID: `chat-channels/lifecycle`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `post-chat-endpoints-slack-test-message` -- `GET /api/chat-endpoints/{endpointId}/activity`. -- `PATCH /api/chat-endpoints/{endpointId}`. -- `PUT /api/chat-endpoints/{endpointId}/resources` configures destination defaults. +API: `present` — `POST /api/chat-endpoints/{endpointId}/slack/test-message`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:use`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. Test messages and identity-link invitations intersect caller and endpoint-agent Block/Ask first/rate policies before dispatch. Independent rate buckets apply separately, identical shared buckets consume once, and saved request receipts prevent duplicate sends. +Evidence: `verified` — [production-authenticated integration fixture](../../server/src/__tests__/connection-management.integration.test.ts) with real database persistence and deterministic provider responses where required. +Verification: `passed` — actual agent-key/run-JWT requests cover caller Block/Ask first/rate rules, target Block/rate rules and one shared quota for both linked-account test messages and identity invitations. Denied requests make no additional provider send, caller audit retains the actual acting agent, and saved requests replay without another send. Provider responses are fixtures. +Gaps: Fixture evidence does not qualify live provider behavior or an empirical model success rate. -Agent access: `restricted` — Board lifecycle APIs; agent requests repair when blocked. +### `post-chat-endpoints-slack-resume` -Policy: Endpoint management/identity linking requires a board user; delivery is -server-owned and agent tools use scoped assignments. +API: `present` — `POST /api/chat-endpoints/{endpointId}/slack/resume`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Evidence: `source-only` — [implementation](../../server/src/routes/chat-channels.ts#L75) -at the reviewed commit above. +### `post-chat-endpoints-test` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `POST /api/chat-endpoints/{endpointId}/test`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Gaps: Activity/repair/replay/disable use board endpoint access, not the agent's ability -to reply. +### `post-chat-endpoints-finish` -Endpoint/destination execution defaults use the same board-only management -boundary. Agents select an authorized workspace for their current task through -the [workspace APIs](workspaces.md), rather than editing channel configuration. +API: `present` — `POST /api/chat-endpoints/{endpointId}/finish`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Speko callback preferences, incoming phone configuration/approval, and call -history use the company-scoped `voice-callbacks`, `voice-phone`, and -`voice-history` routes. Their UI controls do not grant agents board authority. -See [voice routes](../../server/src/routes/voice-sessions.ts); service-level -caller/session permissions and real provider delivery need separate verification. +### `get-chat-endpoints-test-status` + +API: `present` — `GET /api/chat-endpoints/{endpointId}/test-status`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-chat-endpoints-activity` + +API: `present` — `GET /api/chat-endpoints/{endpointId}/activity`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:view_audit`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Current task visibility applies to every returned event. Cursors advance over bounded examined candidates, including private-only windows; private event details remain filtered. +Evidence: `verified` — [actual-JWT regression](../../server/src/__tests__/connection-management.integration.test.ts) through the assembled server and real database. +Verification: `passed` — a private-only first page retains a cursor; the next reaches three readable older events after 27 private events, including tied timestamps. The page-bounded per-task authorization regression remains green. +Gaps: Deterministic database/provider fixtures do not qualify live provider setup or availability. + +### `post-chat-endpoints-deliveries-replay` + +API: `present` — `POST /api/chat-endpoints/{endpointId}/deliveries/{deliveryId}/replay`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-email-inboxes-control` + +API: `present` — `POST /api/email/inboxes/{endpointId}/control`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-email-inboxes-reconnect` + +API: `present` — `POST /api/email/inboxes/{endpointId}/reconnect`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/email.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. GitHub review summary headings use the bot display name. Checks remain independently scoped by GitHub App and endpoint/PR/commit identity. @@ -311,6 +910,87 @@ older/different-run review, pending publication and revoked connection cannot supply it. This grants no new publication/monitor authority and leaves Done policy unchanged; explicit malformed input is not coerced. +### `post-voice-end` + +API: `present` — `POST /api/companies/{companyId}/voice-sessions/{sessionId}/end`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `get-voice-callbacks` + +API: `present` — `GET /api/companies/{companyId}/voice-callbacks/{endpointId}`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `put-voice-callbacks` + +API: `present` — `PUT /api/companies/{companyId}/voice-callbacks/{endpointId}`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `get-voice-phone` + +API: `present` — `GET /api/companies/{companyId}/voice-phone/{endpointId}`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `put-voice-phone` + +API: `present` — `PUT /api/companies/{companyId}/voice-phone/{endpointId}`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `get-voice-incoming` + +API: `present` — `GET /api/companies/{companyId}/voice-phone/{endpointId}/incoming`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `post-voice-incoming-decision` + +API: `present` — `POST /api/companies/{companyId}/voice-phone/{endpointId}/incoming/{callId}`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `get-voice-phone-history` + +API: `present` — `GET /api/companies/{companyId}/voice-phone/{endpointId}/history`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + +### `get-voice-history` + +API: `present` — `GET /api/companies/{companyId}/voice-history/{endpointId}`; JSON REST. +Agent access: `operator-only` — the private voice caller must be an authenticated board user (or local operator); agent bearer credentials are rejected. +Policy: Company access and the service's current caller/session/endpoint authority apply. Browser start, notification, and callback preferences also require experimental chat connectors. Private microphone access, callback preferences, and incoming-call approval remain human identity/consent steps; connection-management grants do not impersonate that caller. +Evidence: `source-only` — [voice routes](../../server/src/routes/voice-sessions.ts), reviewed in this working tree. +Verification: `not-run` — verify the board caller's own state and reject agent, wrong-user, and foreign-company calls. No live telephone or browser audio qualification is claimed. +Gaps: This is an explicit human boundary. Authorized agents configure the underlying connection through its endpoint API; the human controls private calling and incoming-call consent. + ## `outbound-email` Capability ID: `chat-channels/outbound-email`. diff --git a/maps/agent-access-map/connection-setup.md b/maps/agent-access-map/connection-setup.md index b3bab180519..beb87d8923b 100644 --- a/maps/agent-access-map/connection-setup.md +++ b/maps/agent-access-map/connection-setup.md @@ -1,15 +1,17 @@ # Connection setup — agent access -Product map: [Connection setup](../feature-map/connection-setup.md). +Product map: [connection-setup](../feature-map/connection-setup.md). Feature ID: `connection-setup`. -Review date: 2026-10-12. Source commit: `f981fa3cc10cb8663a87dec60345fbff46d54d0a`. +Review date: 2026-10-26. Source base: `a1f77821b9cd5c9c846fdf7a0fa5cc620ad2b6bb`; implementation: [#17737](https://github.com/paperclipai/paperclip/pull/17737). -This is a source-reviewed baseline of representative operations and known gaps. -Each capability needs operation-level review before its entire scope can be -qualified. No live agent execution is claimed. Follow the [map contract](../README.md) -for format, permission checks, evidence and maintenance. +Connection operations reviewed in this working tree. The inventory was reconciled +against production UI clients, REST registrations and service entry points; it is +a review checklist, not a runtime registry. [Authorization and evidence contract](../../doc/plans/2026-10-13-connection-api-parity.md). +Fixture evidence is separate from live provider qualification and from empirical +LLM success. Source-only entries have a reviewed agent path or an explicit boundary, +but do not claim an individually executed HTTP test. Prompt Injection Prevention reuses shared OpenRouter API-key setup directly from Settings → Connections. It adds no Apps provider or transport. Credential ownership @@ -19,298 +21,370 @@ does not activate the service. See [service configuration](../../doc/prompt-inje 1Password adds a service-account catalog method. Agents request access through the existing connection tools and invoke its reviewed item actions through the run-scoped gateway. The service-account token stays in the selected user/company -vault; vault boundaries are enforced by 1Password. Setup remains board-owned. +vault; vault boundaries are enforced by 1Password. Authorized API setup uses the shared connection-management policy; credential entry remains a human step when needed. Evidence and limits: [1Password connector](../../doc/connections/1PASSWORD.md). ## `catalog` Capability ID: `connection-setup/catalog`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-companies-tools-gallery` -- `POST /runtime-tools/connections/search`. +API: `present` — `GET /api/companies/{companyId}/tools/gallery`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `available` — Native connections_search / run-bound runtime-tools MCP. +### `get-companies-tools-apps-preflight` -Policy: Setup and credentials belong to the addressed authorized human; active-run tools -can search/request access. +API: `present` — `GET /api/companies/{companyId}/tools/apps/{galleryKey}/preflight`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Evidence: `source-only` — -[implementation](../../server/src/routes/connection-intents.ts#L130) at the reviewed -commit above. +### `get-companies-tools-examples` -Verification: `not-run` — exercise a listed permitted operation with actual agent -credentials/run context; inspect its result and applicable persistence/attribution. -Assert the relevant denied actor, owner, company or mode from Policy. Review the -remaining operations before qualifying the whole capability. +API: `present` — `GET /api/companies/{companyId}/tools/examples`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Gaps: Runtime tool broker searches available services without granting connection -administration. +### `post-companies-tools-examples-install` -Notebook is internally gated by `enablePaperclipNotebook` (default off, no settings toggle). Disabled instances hide setup/discovery and deny curated Notebook dispatch and publishing; local removal and grant revocation remain available. +API: `present` — `POST /api/companies/{companyId}/tools/examples/{id}/install`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -The connector catalog declares the feature key. Shared availability checks use -the saved application identity. Notebook owns publication and file uploads; -Paperclip uses its existing MCP tools and OAuth flow. +### `post-companies-tools-examples-smoke` -Notebook source addition: `paperclip-notebook` is discoverable through -`connections_search` and requestable through `connection_request`. Its standard -personal authorization requests read, write, share, and delete scopes. Notebook -page ownership remains enforced by the remote service; a client-supplied agent -label grants no authority. The direct MCP connection requires no Cloud instance -enrollment. Notebook fixture evidence: `verified` against disposable PostgreSQL 18 -with controlled MCP responses — [Notebook integration](../../doc/connections/PAPERCLIP-NOTEBOOK.md) -and [generic OAuth fixture](../../server/src/__tests__/generic-mcp-connection.test.ts). -Ordinary MCP calls retain assignment and Allowed / Ask first / Off policy. -Live deployed publication and hosted/self-hosted OAuth remain release checks. +API: `present` — `POST /api/companies/{companyId}/tools/examples/{id}/smoke`; JSON REST. +Agent access: `operator-only` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. +### `get-companies-tools-stdio-templates` +API: `present` — `GET /api/companies/{companyId}/tools/stdio-templates`; JSON REST. +Agent access: `operator-only` — a board user reads host command templates. Agents select approved provider templates through the catalog. +Policy: board actor and `tools:admin`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-companies-tools-stdio-templates` + +API: `present` — `POST /api/companies/{companyId}/tools/stdio-templates`; JSON REST. +Agent access: `operator-only` — a board user creates host command templates; agents select approved templates through the catalog. +Policy: Board actor, company access, and `tools:admin`. Connection-management grants do not authorize host command template administration. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-companies-tools-stdio-templates-disable` + +API: `present` — `POST /api/companies/{companyId}/tools/stdio-templates/{templateId}/disable`; JSON REST. +Agent access: `operator-only` — a board user disables host command templates; agents select approved templates through the catalog. +Policy: Board actor, company access, and `tools:admin`. Connection-management grants do not authorize host command template administration. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-companies-tools-mcp-import-json` + +API: `present` — `POST /api/companies/{companyId}/tools/mcp/import-json`; JSON REST. +Agent access: `operator-only` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Board actor and company access. This operation previews parsed MCP configuration; it does not execute host commands or require the host-template `tools:admin` grant. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-runtime-tools-connections-search` + +API: `present` — `POST /runtime-tools/connections/search`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/connection-intents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. ## `identity-and-access` Capability ID: `connection-setup/identity-and-access`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `post-companies-tools-apps-connect` -- `POST /api/companies/{companyId}/tools/connections`. +API: `present` — `POST /api/companies/{companyId}/tools/apps/connect`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `restricted` — Board setup API plus agent connection_request. - -Policy: Setup and credentials belong to the addressed authorized human; active-run tools -can search/request access. - -Evidence: `source-only` — -[implementation](../../server/src/routes/connection-intents.ts#L130) at the reviewed -commit above. - -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. - -Gaps: Selecting account ownership/eligible agents is management; an agent can request -the intended connection. +### `post-companies-tools-connections` +API: `present` — `POST /api/companies/{companyId}/tools/connections`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. ## `credentials` Capability ID: `connection-setup/credentials`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `post-agents-me-connections-start-authorization` -- `POST /api/agents/me/connections/{connectionId}/start-authorization`. +API: `present` — `POST /api/agents/me/connections/{connectionId}/start-authorization`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: `tools:manage_connections`, an active run, and the server-derived responsible user matching the authorization subject; shared scope/trust/user checks apply. Human authentication remains a handoff, with the actual agent retained for audit. JSON authorization initiation is explicitly REST-callable. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `partial` — Scoped start-authorization/request flow; human provider login. +### `post-agents-me-connections-token` -Policy: Setup and credentials belong to the addressed authorized human; active-run tools -can search/request access. +API: `present` — `POST /api/agents/me/connections/{connectionId}/token`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Evidence: `source-only` — -[implementation](../../server/src/routes/connection-intents.ts#L130) at the reviewed -commit above. +### `post-companies-tools-connections-start-authorization` -Verification: `not-run` — exercise a listed permitted operation with actual agent -credentials/run context; inspect its result and applicable persistence/attribution. -Assert the relevant denied actor, owner, company or mode from Policy. Review the -remaining operations before qualifying the whole capability. +API: `present` — `POST /api/companies/{companyId}/tools/connections/{connectionId}/start-authorization`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Gaps: An initiation endpoint exists; it is not authorization to complete the person's -credential flow. +### `post-tools-oauth-start` -Braintrust uses the existing setup/request path for US and EU OAuth and API-key -methods. OAuth requests only `mcp`; provider authorization follows the selected -account's permissions. Personal invocation credentials must belong to their -user, while shared identities use company credentials. Region and explicit -method survive resume and reconnect. Agents use assigned tools through the -run-scoped gateway and receive no Braintrust token. See -[Braintrust](../../doc/connections/BRAINTRUST.md) for method keys and the -qualification matrix. Separate live US OAuth proof passed browser authorization, -49-tool discovery, bounded read, synthetic dataset insert/readback through actual -process-agent runs and gateway sessions, catalog refresh, OAuth reconnect, local -removal, post-removal denial, and structural API/log redaction. US API-key and -both EU methods remain `not-run` against real accounts. Live token expiry/refresh, -remote OAuth revocation, and native CLI automatic capability delivery remain -unqualified. Public metadata probes performed no registration or account -authorization; fixtures do not establish live proof for the remaining methods. +API: `present` — `POST /api/tools/oauth/{connectionId}/start`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. Returns the saved setup without issuing agent-bound OAuth state; the human browser initiates provider consent. +Evidence: `verified` — [authenticated integration fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed through production authentication and database persistence, including saved Google service choices addressed to the responsible user. [Service-picker tests](../../ui/src/features/connections/GoogleWorkspaceServices.test.tsx) cover restoring only that user's request and retaining newer consent. +Verification: `passed` — deterministic local fixture; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Daytona native setup uses an organization API key in the selected personal or -company vault grant. It does not borrow sandbox-provider credentials or CLI -login tokens. Agents use assigned provider tools through the normal run-bound -gateway; they cannot export the key. [Database-backed fixtures](../../server/src/__tests__/daytona-connection.test.ts) -cover selected vault ownership and gateway reads/writes with provider responses -replaced by fixtures. Real provider authentication remains unqualified. - -Unikraft native setup uses an organization API token in the selected personal or -company vault grant. It does not borrow `unikraft login` state or `UKC_TOKEN`. -Agents use assigned provider tools through the run-bound gateway, limited to the -published metro hosts, and cannot export the token. [Database-backed fixtures](../../server/src/__tests__/unikraft-connection.test.ts) -cover vault ownership and gateway reads, writes and sandbox commands with -fixture provider responses. Real provider authentication remains unqualified. +### `post-runtime-tools-github-credentials` +API: `present` — `POST /runtime-tools/github/credentials`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/connection-intents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. ## `handoff` Capability ID: `connection-setup/handoff`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-tools-oauth-cloud-connector-enrollment` -- `POST /api/connection-intents/{interactionId}/complete`. +API: `present` — `GET /api/tools/oauth/cloud-connector/enrollment`; JSON REST. +Agent access: `operator-only` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Board actor (`assertBoard`) for instance enrollment status; agents cannot read this operator surface. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. -Agent access: `restricted` — Addressed-user completion; server wakes the agent with -updated access. +### `post-tools-oauth-cloud-connector-enrollment` -Policy: Setup and credentials belong to the addressed authorized human; active-run tools -can search/request access. +API: `present` — `POST /api/tools/oauth/cloud-connector/enrollment`; JSON REST. +Agent access: `operator-only` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Instance administrator (`assertInstanceAdmin`) and access to the requested company. The saved enrollment binds the initiating human principal and return destination. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. -Evidence: `source-only` — -[implementation](../../server/src/routes/connection-intents.ts#L130) at the reviewed -commit above. +### `get-tools-oauth-cloud-connector-enrollment-callback` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `GET /api/tools/oauth/cloud-connector/enrollment-callback`; browser return — HTML completion page or HTTP 303 redirect. +Agent access: `operator-only` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Instance administrator (`assertInstanceAdmin`), access to the saved company, and the same initiating principal. The saved enrollment ID, approval code, state, and expiry must validate before completion. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. -Gaps: OAuth/callback and addressed-user checks are deliberate separate boundaries. +### `get-tools-oauth-cloud-connector-callback` +API: `present` — `GET /api/tools/oauth/cloud-connector/callback`; browser/provider/MCP protocol. +Agent access: `restricted` — browser authentication return. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-tools-oauth-paperclip-id-callback` + +API: `present` — `GET /api/tools/oauth/paperclip-id/callback`; browser/provider/MCP protocol. +Agent access: `restricted` — browser authentication return. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-tools-vercel-connect-callback` + +API: `present` — `GET /api/tools/vercel-connect/callback`; browser/provider/MCP protocol. +Agent access: `restricted` — browser authentication return. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-tools-oauth-callback` + +API: `present` — `GET /api/tools/oauth/callback`; browser/provider/MCP protocol. +Agent access: `restricted` — browser authentication return. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-mcp-runtime-tools` + +API: `present` — `GET /mcp/runtime-tools`; browser/provider/MCP protocol. +Agent access: `restricted` — GET always returns HTTP 405 with `Allow: POST`. Use the corresponding POST MCP operation. +Policy: GET streaming is unsupported; this is a transport boundary independent of principal permissions. POST still requires the documented MCP authentication and resource policy. +Evidence: `source-only` — [route](../../server/src/routes/connection-intents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — source-reviewed fixed 405 handler. A protocol check should assert status 405 and `Allow: POST`; use POST to exercise authenticated MCP calls. +Gaps: No GET event stream is implemented. This entry does not advertise a usable GET client connection. + +### `post-mcp-runtime-tools` + +API: `present` — `POST /mcp/runtime-tools`; browser/provider/MCP protocol. +Agent access: `available` — existing authenticated MCP/provider protocol; not callable as JSON REST. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `source-only` — [route](../../server/src/routes/connection-intents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-connection-intents-setup-options` + +API: `present` — `GET /api/connection-intents/{interactionId}/setup-options`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/connection-intents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-connection-intents-phase` + +API: `present` — `POST /api/connection-intents/{interactionId}/phase`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/connection-intents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-connection-intents-complete` + +API: `present` — `POST /api/connection-intents/{interactionId}/complete`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/connection-intents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. ## `draft-recovery` Capability ID: `connection-setup/draft-recovery`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `post-companies-tools-apps-finalize-oauth-access` -- `POST /api/connection-intents/{interactionId}/phase`. -- `POST /api/tool-connections/{connectionId}/reconnect`. +API: `present` — `POST /api/companies/{companyId}/tools/apps/{connectionId}/finalize-oauth-access`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `restricted` — Human setup/resume APIs; agent request state and -continuation. +### `post-companies-tools-apps-finish` -Policy: Setup and credentials belong to the addressed authorized human; active-run tools -can search/request access. +API: `present` — `POST /api/companies/{companyId}/tools/apps/{connectionId}/finish`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections` and `tools:manage_profiles`; current and requested audience/target authority, including nested `access`, all agents, and retained bindings. +Evidence: `verified` — [assembled-server integration](../../server/src/__tests__/connection-management.integration.test.ts), actual bearer/session authentication, persisted PostgreSQL state, and deterministic local MCP/provider fixtures. +Verification: `passed` — 25-scenario fixture suite on a fresh isolated PostgreSQL database, including scoped denial, approved continuation, revocation, cancellation and replay. No live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Evidence: `source-only` — -[implementation](../../server/src/routes/connection-intents.ts#L130) at the reviewed -commit above. - -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. - -Gaps: Board setup recovery exists. Browser-only navigation state is not automatically an -agent API gap. +### `post-tool-connections-reconnect` +API: `present` — `POST /api/tool-connections/{connectionId}/reconnect`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. ## `task-request` Capability ID: `connection-setup/task-request`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `post-runtime-tools-connections-request` -- `POST /runtime-tools/connections/request`. +API: `present` — `POST /runtime-tools/connections/request`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/connection-intents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `available` — Native connection_request / run-bound runtime-tools MCP. - -Policy: Setup and credentials belong to the addressed authorized human; active-run tools -can search/request access. - -Evidence: `source-only` — -[implementation](../../server/src/routes/connection-intents.ts#L130) at the reviewed -commit above. - -Verification: `not-run` — exercise a listed permitted operation with actual agent -credentials/run context; inspect its result and applicable persistence/attribution. -Assert the relevant denied actor, owner, company or mode from Policy. Review the -remaining operations before qualifying the whole capability. - -Gaps: This is the existing agent bridge for human-required setup; do not invent a second -approval system. +### `post-connection-intents-decline` +API: `present` — `POST /api/connection-intents/{interactionId}/decline`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/connection-intents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. ## `channel-setup` Capability ID: `connection-setup/channel-setup`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: - -- `POST /api/companies/{companyId}/chat-endpoints`. - -Agent access: `restricted` — Board channel management; agent requests required setup. - -Policy: Setup and credentials belong to the addressed authorized human; active-run tools -can search/request access. - -Evidence: `source-only` — -[implementation](../../server/src/routes/connection-intents.ts#L130) at the reviewed -commit above. - -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. - -Gaps: Provider endpoint, represented agent and linked sender are operator choices today. - -Internal recovery: Telegram discovery walks bounded pages at startup. Configuration -commits only queue the changed connection; durable maintenance rows supply retry deadlines. Inactive subscription -repair is parked until an endpoint or connection change. This creates no agent -management permission or new API. Local database-backed provider-fixture tests in -[channel integration](../../server/src/__tests__/chat-channels.integration.test.ts) -cover recovery, rollback, retries and idle query silence; they do not establish -live provider or agent access. +### `post-companies-chat-endpoints` +API: `present` — `POST /api/companies/{companyId}/chat-endpoints`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/chat-channels.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. ## `failure` Capability ID: `connection-setup/failure`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `post-tool-connections-health-check` -- `POST /api/tool-connections/{connectionId}/health-check`. - -Agent access: `partial` — Agent observes connection tool errors; board repair APIs. - -Policy: Setup and credentials belong to the addressed authorized human; active-run tools -can search/request access. - -Evidence: `source-only` — -[implementation](../../server/src/routes/connection-intents.ts#L130) at the reviewed -commit above. - -Verification: `not-run` — exercise a listed permitted operation with actual agent -credentials/run context; inspect its result and applicable persistence/attribution. -Assert the relevant denied actor, owner, company or mode from Policy. Review the -remaining operations before qualifying the whole capability. - -Gaps: Error reporting and human repair exist; no generic agent permission to reconnect -accounts. - -Supabase and Vercel deployment methods (2026-10-22) reuse human-authorized setup -and assigned run-scoped tools. Native Supabase calls use the saved project; -native Vercel calls use the saved team and validate project ownership. Tokens -remain in the selected personal/company vault grant and cannot be exported by -`mintConnectionTokenForAgent`. Action policies, changed-schema quarantine, -responsible-user access and immediate removal apply to deployment/configuration -calls. Database/provider-fixture evidence lives in -[deployment connector governance](../../server/src/__tests__/deployment-connections.test.ts). -It does not establish authenticated provider deployment, OAuth consent or remote -revocation. See [Supabase](../../doc/connections/SUPABASE.md) and -[Vercel](../../doc/connections/vercel.md) for the remaining live-proof matrix. - -Native delivery (2026-10-23): reviewed REST connections are admitted in connection -readiness, native runtime snapshots, and runtime gateway preparation. The real -Codex walkthrough initially failed at all three despite a successful board test; -the database-backed lifecycle regression now covers ready search/request, -existing-card completion, assigned-tool search/call, and inactive/unrecognized -connection denial for both providers. After repair, a real Codex agent deployed -and configured both services. Supabase's Dashboard invocation returned the marker, -and Vercel's signed-in browser served the preview. The Vercel first-deployment -production exception remains recorded as a separate failed outcome; default -preview now requires an existing deployment and reports target mismatches for -reconciliation. See the provider documents for the external-deletion race limit. +API: `present` — `POST /api/tool-connections/{connectionId}/health-check`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. diff --git a/maps/agent-access-map/gateways-profiles.md b/maps/agent-access-map/gateways-profiles.md index e137b9a2a84..6ecd81d18fc 100644 --- a/maps/agent-access-map/gateways-profiles.md +++ b/maps/agent-access-map/gateways-profiles.md @@ -1,217 +1,337 @@ # Tool gateways and access profiles — agent access -Product map: [Tool gateways and access profiles](../feature-map/gateways-profiles.md). +Product map: [gateways-profiles](../feature-map/gateways-profiles.md). Feature ID: `gateways-profiles`. -Review date: 2026-10-12. Source commit: `58e16dbddfc9c8d0cf60867f9d9847f0feda055b`. -Native REST transport reconciled in this change; see the -[shared transport contract](README.md#native-rest-transport). +Review date: 2026-10-26. Source base: `a1f77821b9cd5c9c846fdf7a0fa5cc620ad2b6bb`; implementation: [#17737](https://github.com/paperclipai/paperclip/pull/17737). -This is a source-reviewed baseline of representative operations and known gaps. -Each capability needs operation-level review before its entire scope can be -qualified. No live agent execution is claimed. Follow the [map contract](../README.md) -for format, permission checks, evidence and maintenance. +Connection operations reviewed in this working tree. The inventory was reconciled +against production UI clients, REST registrations and service entry points; it is +a review checklist, not a runtime registry. [Authorization and evidence contract](../../doc/plans/2026-10-13-connection-api-parity.md). +Fixture evidence is separate from live provider qualification and from empirical +LLM success. Source-only entries have a reviewed agent path or an explicit boundary, +but do not claim an individually executed HTTP test. ## `gateway` Capability ID: `gateways-profiles/gateway`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-companies-tools-gateways` -- `POST /api/companies/{companyId}/tools/gateways`. +API: `present` — `GET /api/companies/{companyId}/tools/gateways`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections (responsible user: tools:admin)`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/tool-gateway.ts); [authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) inspect the saved gateway after a denied binding change. +Verification: `passed` — local assembled server and real database; gateway visibility and unchanged target assignment after profile-grant revocation. No live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Agent access: `restricted` — Board tools:admin REST; CLI connections/configuration where -supported. +### `post-companies-tools-gateways` -Policy: Named gateway/profile administration and credentials are board tools:admin; -agent sessions/tool use use separate scoped authority. +API: `present` — `POST /api/companies/{companyId}/tools/gateways`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections (responsible user: tools:admin)`; bindings also require `tools:manage_profiles`. [Shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Evidence: `source-only` — [implementation](../../server/src/routes/tool-gateway.ts#L353) -at the reviewed commit above. - -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. - -Gaps: No agent named-gateway management path; assigned tool use exists separately. +### `patch-tool-gateway-gateways` +API: `present` — `PATCH /api/tool-gateway/gateways/{gatewayId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections (responsible user: tools:admin)`; setting or clearing bindings also requires `tools:manage_profiles`. [Shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/tool-gateway.ts); [authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) exercise persisted binding changes and revoked/scoped authority. +Verification: `passed` — local assembled server and real database. Clearing a binding requires retained profile authority; replacement profile/project changes require access to both saved and replacement resources. Authorized changes persist. No live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. ## `profiles` Capability ID: `gateways-profiles/profiles`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-companies-tools-profiles` -- `POST /api/companies/{companyId}/tools/profiles`. -- `PATCH /api/tool-profiles/{profileId}`. +API: `present` — `GET /api/companies/{companyId}/tools/profiles`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) exercise saved application/catalog/tool-name/risk selectors, allow-all profiles, managed empty profiles, personal-account visibility, and scoped grants through actual keys and run JWTs. +Verification: `passed` — local assembled server and real database; inaccessible profile resources are omitted from lists and denied before new-tool or effective-profile data is returned. Proposed broad entries and installed connections use the same authority checks. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Agent access: `restricted` — Board management REST; agent receives effective tool -assignment. +### `post-companies-tools-profiles` -Policy: Named gateway/profile administration and credentials are board tools:admin; -agent sessions/tool use use separate scoped authority. +API: `present` — `POST /api/companies/{companyId}/tools/profiles`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; mutations whose entry selectors resolve connections also require `tools:manage_connections`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Evidence: `source-only` — [implementation](../../server/src/routes/tool-gateway.ts#L353) -at the reviewed commit above. +### `get-companies-tools-profiles-effective-agents` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `GET /api/companies/{companyId}/tools/profiles/effective/agents/{agentId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) exercise saved application/catalog/tool-name/risk selectors, allow-all profiles, managed empty profiles, personal-account visibility, and scoped grants through actual keys and run JWTs. +Verification: `passed` — local assembled server and real database; inaccessible profile resources are omitted from lists and denied before new-tool or effective-profile data is returned. Proposed broad entries and installed connections use the same authority checks. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Gaps: Agent access to tools does not permit profile editing. +### `patch-tool-profiles` -Backend evidence: [profile entry validation](../../server/src/__tests__/tool-profile-entry-validation.test.ts) -covers company-scoped selector references and bounded reads for a large immutable -runtime assignment. Batched validation retains the checks before profile and -entry writes. It grants no profile-management authority to agents. +API: `present` — `PATCH /api/tool-profiles/{profileId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; mutations whose entry selectors resolve connections also require `tools:manage_connections`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. +### `post-tool-profiles-duplicate` + +API: `present` — `POST /api/tool-profiles/{profileId}/duplicate`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; mutations whose entry selectors resolve connections also require `tools:manage_connections`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `delete-tool-profiles` + +API: `present` — `DELETE /api/tool-profiles/{profileId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; mutations whose entry selectors resolve connections also require `tools:manage_connections`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-tool-profiles-entries` + +API: `present` — `POST /api/tool-profiles/{profileId}/entries`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; mutations whose entry selectors resolve connections also require `tools:manage_connections`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `patch-tool-profile-entries` + +API: `present` — `PATCH /api/tool-profile-entries/{entryId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; mutations whose entry selectors resolve connections also require `tools:manage_connections`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `delete-tool-profile-entries` + +API: `present` — `DELETE /api/tool-profile-entries/{entryId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; mutations whose entry selectors resolve connections also require `tools:manage_connections`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-tools-profiles-bind` + +API: `present` — `POST /api/companies/{companyId}/tools/profiles/{profileId}/bind`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; mutations whose entry selectors resolve connections also require `tools:manage_connections`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. Resolve issue/routine/gateway targets, private parent/project access, and affected agents. Existing bindings and saved gateway profile references retain the same checks. +Evidence: `verified` — [production-authenticated integration fixture](../../server/src/__tests__/connection-management.integration.test.ts) with real database persistence and deterministic provider responses where required. +Verification: `passed` — Actual authenticated requests bind/unbind an authorized task and reject issue/routine/gateway targets outside agent configuration scope. Existing bindings and gateway profile references are rechecked on profile edits. +Gaps: Fixture evidence does not qualify live provider behavior or an empirical model success rate. + +### `post-companies-tools-profiles-unbind` + +API: `present` — `POST /api/companies/{companyId}/tools/profiles/{profileId}/unbind`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_profiles`; mutations whose entry selectors resolve connections also require `tools:manage_connections`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. Resolve issue/routine/gateway targets, private parent/project access, and affected agents. Existing bindings and saved gateway profile references retain the same checks. +Evidence: `verified` — [production-authenticated integration fixture](../../server/src/__tests__/connection-management.integration.test.ts) with real database persistence and deterministic provider responses where required. +Verification: `passed` — Actual authenticated requests bind/unbind an authorized task and reject issue/routine/gateway targets outside agent configuration scope. Existing bindings and gateway profile references are rechecked on profile edits. +Gaps: Fixture evidence does not qualify live provider behavior or an empirical model success rate. ## `client-connect` Capability ID: `gateways-profiles/client-connect`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-mcp-gateways` -- `GET /api/tool-gateway/gateways/{gatewayId}/mcp`. -- `POST /api/tool-gateway/sessions`. +API: `present` — `GET /mcp/gateways/{gatewayPublicId}`; browser/provider/MCP protocol. +Agent access: `restricted` — GET always returns HTTP 405 with `Allow: POST`. Use the corresponding POST MCP operation. +Policy: GET streaming is unsupported; this is a transport boundary independent of principal permissions. POST still requires the documented MCP authentication and resource policy. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — source-reviewed fixed 405 handler. A protocol check should assert status 405 and `Allow: POST`; use POST to exercise authenticated MCP calls. +Gaps: No GET event stream is implemented. This entry does not advertise a usable GET client connection. -Agent access: `partial` — Agent REST / native `call_api` for scoped session creation -and revocation; existing MCP client protocol and assigned native app tools. +### `post-mcp-gateways` -Policy: Named gateway/profile administration and credentials are board tools:admin; -agent sessions/tool use use separate scoped authority. +API: `present` — `POST /mcp/gateways/{gatewayPublicId}`; browser/provider/MCP protocol. +Agent access: `available` — existing authenticated MCP/provider protocol; not callable as JSON REST. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. Issuer and target Block/Ask first/rate decisions intersect using the existing evaluator; credentials must select the same identity. Human continuation rechecks current token actions, scope, grants and both policies. Approval attribution records the issuer independently of the target; deleted tokens fail closed. Board clients and run-bound heartbeat clients retain their separate behavior. +Evidence: `verified` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — local assembled server and actual gateway-token authentication. Durable clients survive setup-run completion, honor purpose scopes, and reject revoked user/issuer authority and a cleared issuer foreign key. Thirteen actual-auth cases additionally verify issuer/target Block, governed approval, current policy/grant/token revocation and deletion, narrowed actions, independent/shared rates, credential identity and board-issued approval compatibility. No live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Evidence: `source-only` — [implementation](../../server/src/routes/tool-gateway.ts#L353) -at the reviewed commit above. +### `get-tool-gateway-gateways-mcp` -Verification: `not-run` — exercise a listed permitted operation with actual agent -credentials/run context; inspect its result and applicable persistence/attribution. -Assert the relevant denied actor, owner, company or mode from Policy. Review the -remaining operations before qualifying the whole capability. +API: `present` — `GET /api/tool-gateway/gateways/{gatewayId}/mcp`; browser/provider/MCP protocol. +Agent access: `restricted` — GET always returns HTTP 405 with `Allow: POST`. Use the corresponding POST MCP operation. +Policy: GET streaming is unsupported; this is a transport boundary independent of principal permissions. POST still requires the documented MCP authentication and resource policy. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — source-reviewed fixed 405 handler. A protocol check should assert status 405 and `Allow: POST`; use POST to exercise authenticated MCP calls. +Gaps: No GET event stream is implemented. This entry does not advertise a usable GET client connection. -Gaps: Scoped sessions remain agent/run-bound. MCP requires its existing protocol -client; named gateways and gateway-token management retain board permissions. +### `post-tool-gateway-gateways-mcp` -Native assigned-tool results above 500,000 encoded bytes are stored as scoped response assets. -`read_tool_result` reads bounded JSON pages without repeating the upstream call, -including when generic `call_api` is disabled. -Each encoded page is at most 500,000 bytes and continues by byte offset until the -entire result has been read. Current run authority, same task -and agent, and asset/responsible-user access remain required. Provider content -stays untrusted. The -[response integration tests](../../server/src/services/native-runtime/runner-api.integration.test.ts) -cover full Unicode reconstruction after controller recreation, low-trust access, -foreign assets, paused agents, and one upstream execution. The -[native PRP regression](../../server/src/services/native-runtime/runner-tool-results-prp.integration.test.ts) -also verifies complete delivery of escape-heavy pages through native Runner, -including a text wrapper larger than the PRP command limit that is decoded before -command serialization. The -[opt-in live probe](../../server/src/__tests__/helpers/verify-assigned-tool-results-live.ts) -uses real Codex and Runner with a synthetic large upstream response; it does not -publish to an external service. +API: `present` — `POST /api/tool-gateway/gateways/{gatewayId}/mcp`; browser/provider/MCP protocol. +Agent access: `available` — existing authenticated MCP/provider protocol; not callable as JSON REST. +Policy: Provider state/signature or gateway-token verification, company and resource binding; browser returns require the configuring human. Agent-issued durable gateway tokens retain both issuer and responsible-user authority. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Discovery collapses identical descriptors with the same gateway name across -providers, including native assigned-tool startup and fresh searches. Conflicting -definitions remain an error. Distinct names and the normal invocation grants, -approvals and revocation checks remain independent. Regression coverage lives in -[gateway tests](../../server/src/__tests__/tool-gateway.test.ts) and -[assigned-tool tests](../../server/src/services/native-runtime/assigned-mcp-tools.test.ts); -these fixtures do not certify a live external-client journey. +### `get-tool-gateway-tools` -Native assigned tools return bounded input-schema repair guidance after argument -validation fails. Rejection creates no execution receipt and discloses no submitted -values or unknown keys. Corrected calls keep the same authorization checks. -[Runner regressions](../../packages/paperclip-runner/runner/crates/runner-core/src/tests/provider_assigned_tool_validation.rs) -cover discovery repair and unauthorized calls; the -[Codex transport fixture](../../packages/paperclip-runner/runner/crates/runner-core/tests/support/codex_assigned_tool_validation.rs) -covers rejection, corrected dispatch, and completion with a simulated provider. -This does not establish live model or GitHub access. +API: `present` — `GET /api/tool-gateway/tools`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Remote invocation regressions preserve access to authorized sibling tools after -a fetch failure, timeout, non-authentication HTTP error, or JSON-RPC rejection. -HTTP 401/403 and insufficient-scope errors keep their connection gates. The -gateway records each failed invocation and never automatically replays it. -This covers the gateway service with synthetic providers and a local HTTP -server, not a live external provider or all client surfaces. +### `post-tool-gateway-tools-call` +API: `present` — `POST /api/tool-gateway/tools/call`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. ## `tokens` Capability ID: `gateways-profiles/tokens`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `post-tool-gateway-gateways-tokens` -- `POST /api/tool-gateway/gateways/{gatewayId}/tokens`. -- `POST /api/tool-gateway/gateway-tokens/{tokenId}/revoke`. +API: `present` — `POST /api/tool-gateway/gateways/{gatewayId}/tokens`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections (responsible user: tools:admin)`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. -Agent access: `restricted` — Board tools:admin token management. +### `post-tool-gateway-gateway-tokens-revoke` -Policy: Named gateway/profile administration and credentials are board tools:admin; -agent sessions/tool use use separate scoped authority. +API: `present` — `POST /api/tool-gateway/gateway-tokens/{tokenId}/revoke`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections (responsible user: tools:admin)`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Evidence: `source-only` — [implementation](../../server/src/routes/tool-gateway.ts#L353) -at the reviewed commit above. +### `post-tool-gateway-sessions` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `POST /api/tool-gateway/sessions`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Gaps: Gateway-token management requires board tools:admin at the HTTP route. -A grant to use an app does not create this authority; native `call_api` preserves -the denial. +### `post-tool-gateway-sessions-revoke` +API: `present` — `POST /api/tool-gateway/sessions/{sessionId}/revoke`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. ## `activity` Capability ID: `gateways-profiles/activity`. -Review: `baseline`. +Review: `operation-reviewed`. -API: `present` — representative operation IDs: +### `get-companies-tools-runtime-slots` -- `GET /api/tool-gateway/audit`. +API: `present` — `GET /api/companies/{companyId}/tools/runtime-slots`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_runtime`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Agent access: `restricted` — Board tools:view_audit; agent receives its own tool -receipts/errors. +### `post-companies-tools-runtime-slots-stop` -Policy: Named gateway/profile administration and credentials are board tools:admin; -agent sessions/tool use use separate scoped authority. +API: `present` — `POST /api/companies/{companyId}/tools/runtime-slots/{id}/stop`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_runtime`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Evidence: `source-only` — [implementation](../../server/src/routes/tool-gateway.ts#L353) -at the reviewed commit above. +### `post-companies-tools-runtime-slots-restart` -Verification: `not-run` — assert the documented agent denial with an owned test fixture; -verify any listed agent request/result path separately. A delegated operation needs its -own policy and access review. +API: `present` — `POST /api/companies/{companyId}/tools/runtime-slots/{id}/restart`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_runtime`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Gaps: Audit management API exists but broad audit visibility is not an agent use-path -guarantee. +### `get-companies-tools-runtime-health` -### Execution-host stdio assignments +API: `present` — `GET /api/companies/{companyId}/tools/runtime-health`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_runtime`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. -Runner registers verified, session-local stdio servers from the execution host -with native Codex and through the shared ACPX runtime. ACPX clients must support -stdio MCP assignments. The transport uses each implementation's own schemas and -responses. Each binding requires a private Unix socket to identify the shared -resource whose calls are serialized. This is a separate binding alongside the -HTTP gateway; it does not create gateway/profile administration -authority. Tasks cannot supply these reserved environment inputs. Read-only -sessions omit them. -ACPX adapters use their applicable ACPX permission policy. Host-assigned native -Codex tools run without a new per-call prompt, matching existing execution-host shell -utilities. Explicit deny/prompt rules remain effective; requested approvals use -PRP with active thread/turn checks and one-time decisions. Preconfigured Claude -rejects interactive approval requests. See the [Runner contract](../../packages/paperclip-runner/README.md#assigned-local-mcp-servers). +### `get-companies-tools-runs-decisions` -Local fixture coverage: [binding validation](../../packages/paperclip-runner/src/drivers/stdio-mcp.test.ts), -[relay processes and cancellation](../../packages/paperclip-runner/runner/crates/runner-core/tests/stdio_mcp.rs), -and [approval projection](../../packages/paperclip-runner/src/drivers/codex/codex-question-adapter.test.ts). -Provider-client image and approval smoke tests used native Codex and Claude SDK; -that coverage is not an exhaustive adapter compatibility test. -Vendor-client evidence belongs in the PR. These checks do not -certify live external credentials, deployment isolation or gateway revocation. +API: `present` — `GET /api/companies/{companyId}/tools/runs/{runId}/decisions`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:view_audit`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-access.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-tool-gateway-runtime-slots` + +API: `present` — `GET /api/tool-gateway/runtime-slots`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_runtime`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-tool-gateway-runtime-slots-stop` + +API: `present` — `POST /api/tool-gateway/runtime-slots/{slotId}/stop`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_runtime`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-tool-gateway-runtime-slots-restart` + +API: `present` — `POST /api/tool-gateway/runtime-slots/{slotId}/restart`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_runtime`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-tool-gateway-audit` + +API: `present` — `GET /api/tool-gateway/audit`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:view_audit`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/tool-gateway.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. diff --git a/maps/agent-access-map/runs-adapters.md b/maps/agent-access-map/runs-adapters.md index 4c732929e7e..c0578bf5178 100644 --- a/maps/agent-access-map/runs-adapters.md +++ b/maps/agent-access-map/runs-adapters.md @@ -1,17 +1,17 @@ # Runs, harnesses, and model accounts — agent access -Product map: [Runs, harnesses, and model accounts](../feature-map/runs-adapters.md). +Product map: [runs-adapters](../feature-map/runs-adapters.md). Feature ID: `runs-adapters`. -Review date: 2026-10-12. Source commit: `58e16dbddfc9c8d0cf60867f9d9847f0feda055b`. -Native REST transport reconciled in this change; see the -[shared transport contract](README.md#native-rest-transport). +Review date: 2026-10-26. Source base: `a1f77821b9cd5c9c846fdf7a0fa5cc620ad2b6bb`; implementation: [#17737](https://github.com/paperclipai/paperclip/pull/17737). -This is a source-reviewed baseline of representative operations and known gaps. -Each capability needs operation-level review before its entire scope can be -qualified. No live agent execution is claimed. Follow the [map contract](../README.md) -for format, permission checks, evidence and maintenance. +Connection operations reviewed in this working tree. The inventory was reconciled +against production UI clients, REST registrations and service entry points; it is +a review checklist, not a runtime registry. [Authorization and evidence contract](../../doc/plans/2026-10-13-connection-api-parity.md). +Fixture evidence is separate from live provider qualification and from empirical +LLM success. Source-only entries have a reviewed agent path or an explicit boundary, +but do not claim an individually executed HTTP test. Scoped runner-default source review: 2026-10-12 at `eee0534154eaf5d0d3f2bbc97c1e24d86f34a5a5`, limited to runner discovery, @@ -35,7 +35,7 @@ API: `present` — representative operation IDs: - `GET /api/companies/{companyId}/adapters/{type}/models`. Agent access: `partial` — Agent company-model REST / native `call_api` discovery and -bound runtime context; adapter/account inventories remain board-only. +bound runtime context; instance adapter inventory remains board-only; model-account management is enumerated below. Policy: Run/cost visibility and agent configuration policy; adapter installation/account login/session control require board/admin. @@ -98,7 +98,7 @@ API: `present` — representative operation IDs: - `POST /api/companies/{companyId}/adapters/{type}/test-environment`. Agent access: `partial` — Permitted agent-creation validation through REST / native -`call_api` / CLI; account login requires its operator. +`call_api` / CLI; supported account-login initiation has the delegated path below. Policy: Run/cost visibility and agent configuration policy; adapter installation/account login/session control require board/admin. @@ -344,3 +344,280 @@ release. Unconfirmed termination stays blocked. Existing fake-peer and delayed read controls are source evidence; this changes no actor, company, account or session-reset permission. Operation-level verification remains `not-run` and the failed Test setup does not qualify successful live cleanup. + + +## `model-accounts` + +Capability ID: `runs-adapters/model-accounts`. + +Review: `operation-reviewed`. + +### `get-companies-ai-connection-pools` + +API: `present` — `GET /api/companies/{companyId}/ai-connection-pools`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-ai-connection-pools-inspection` + +API: `present` — `GET /api/companies/{companyId}/ai-connection-pools/{poolId}/inspection`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-ai-connection-pools` + +API: `present` — `POST /api/companies/{companyId}/ai-connection-pools`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections` and `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `delete-companies-ai-connection-pools` + +API: `present` — `DELETE /api/companies/{companyId}/ai-connection-pools/{poolId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections` and `tools:manage_profiles`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `post-companies-ai-connections-local-attempts` + +API: `present` — `POST /api/companies/{companyId}/ai-connections/local/attempts`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; explicitly supplying ownership, `agentIds`, or `allAgents` also requires `tools:manage_profiles`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-ai-connections-local-check` + +API: `present` — `POST /api/companies/{companyId}/ai-connections/local/check`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; explicitly supplying ownership, `agentIds`, or `allAgents` also requires `tools:manage_profiles`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/ai-connections.ts) and [actual-auth native receipt fixture](../../server/src/__tests__/connection-management.integration.test.ts), with real database persistence. +Verification: `passed` — production native call_api with actual run JWT and a deterministic browser-login process returns device URL/code inline, encrypts the mutation receipt, replays exactly, redacts history, and creates no runner response asset. Login start/check/cancel use the real service and persisted addressed-user attempt. +Gaps: The browser process is a fixture; no real provider consent, CLI credentials or live setup is qualified. + +### `delete-companies-ai-connections-local-attempts` + +API: `present` — `DELETE /api/companies/{companyId}/ai-connections/local/attempts/{sessionId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-ai-connections-local-attempts-code` + +API: `present` — `POST /api/companies/{companyId}/ai-connections/local/attempts/{sessionId}/code`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `get-companies-ai-connections` + +API: `present` — `GET /api/companies/{companyId}/ai-connections`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware, including account/pool filtering by agent connection scope with both persistent keys and run JWTs. Pool visibility checks every member account. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `get-companies-ai-connections-usage` + +API: `present` — `GET /api/companies/{companyId}/ai-connections/{connectionId}/usage`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-ai-connections-active-runs` + +API: `present` — `GET /api/companies/{companyId}/ai-connections/{connectionId}/active-runs`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-ai-connections` + +API: `present` — `POST /api/companies/{companyId}/ai-connections`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; explicitly supplying ownership, `agentIds`, or `allAgents` also requires `tools:manage_profiles`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. [Authenticated fixtures](../../server/src/__tests__/connection-management.integration.test.ts) passed with database persistence and actual bearer/session middleware. +Verification: `passed` — deterministic provider fixtures / local assembled server; no live provider qualification. +Gaps: Provider fixtures do not qualify live provider setup or availability. + +### `post-companies-ai-connections-local` + +API: `present` — `POST /api/companies/{companyId}/ai-connections/local`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; explicitly supplying ownership, `agentIds`, or `allAgents` also requires `tools:manage_profiles`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `put-companies-ai-connections-default` + +API: `present` — `PUT /api/companies/{companyId}/ai-connections/default`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-ai-connections-login-setup` + +API: `present` — `GET /api/companies/{companyId}/ai-connections/login/{sessionId}/setup`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-ai-connections-login` + +API: `present` — `GET /api/companies/{companyId}/ai-connections/login/{sessionId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/ai-connections.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-adapters-models` + +API: `present` — `GET /api/companies/{companyId}/adapters/{type}/models`; JSON REST. +Agent access: `available` — existing REST/native runtime tool or scoped gateway client, as documented by the linked route. +Policy: Existing run-bound tool, scoped credential lease, execution policy, or assigned-email-agent checks; connection-management grants do not bypass these controls. +Evidence: `source-only` — [route](../../server/src/routes/agents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-adapters-login-sessions` + +API: `present` — `POST /api/companies/{companyId}/adapters/{type}/login-sessions`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; explicitly supplying ownership, `agentIds`, or `allAgents` also requires `tools:manage_profiles`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/agents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-adapters-login-sessions-active` + +API: `present` — `GET /api/companies/{companyId}/adapters/{type}/login-sessions/active`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [production-authenticated integration fixture](../../server/src/__tests__/connection-management.integration.test.ts), with a saved database session and deterministic registered adapter capability. +Verification: `passed` — actual persistent-key/run-JWT management reads, cancellation, and denial for another saved owner. Session ownership is the responsible human; no device provider or sandbox process was launched in this fixture. +Gaps: Live device authentication remains unqualified. The start route retains strict managed-account, approved adapter command, sandbox, and provider availability checks; human consent completes authentication. + +### `get-companies-adapters-login-sessions` + +API: `present` — `GET /api/companies/{companyId}/adapters/{type}/login-sessions/{sessionId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [production-authenticated integration fixture](../../server/src/__tests__/connection-management.integration.test.ts), with a saved database session and deterministic registered adapter capability. +Verification: `passed` — actual persistent-key/run-JWT management reads, cancellation, and denial for another saved owner. Session ownership is the responsible human; no device provider or sandbox process was launched in this fixture. +Gaps: Live device authentication remains unqualified. The start route retains strict managed-account, approved adapter command, sandbox, and provider availability checks; human consent completes authentication. + +### `post-companies-adapters-login-sessions-cancel` + +API: `present` — `POST /api/companies/{companyId}/adapters/{type}/login-sessions/{sessionId}/cancel`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `verified` — [production-authenticated integration fixture](../../server/src/__tests__/connection-management.integration.test.ts), with a saved database session and deterministic registered adapter capability. +Verification: `passed` — actual persistent-key/run-JWT management reads, cancellation, and denial for another saved owner. Session ownership is the responsible human; no device provider or sandbox process was launched in this fixture. +Gaps: Live device authentication remains unqualified. The start route retains strict managed-account, approved adapter command, sandbox, and provider availability checks; human consent completes authentication. + +### `post-agents-claude-login` + +API: `present` — `POST /api/agents/{id}/claude-login`; JSON REST. +Agent access: `operator-only` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/agents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-companies-setup-token-login-sessions` + +API: `present` — `POST /api/companies/{companyId}/setup-token-login-sessions`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; explicitly supplying ownership, `agentIds`, or `allAgents` also requires `tools:manage_profiles`. [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/agents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-setup-token-login-sessions-active` + +API: `present` — `GET /api/companies/{companyId}/setup-token-login-sessions/active`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/agents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-setup-token-login-sessions` + +API: `present` — `GET /api/companies/{companyId}/setup-token-login-sessions/{sessionId}`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/agents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `get-companies-setup-token-login-sessions-prompt` + +API: `present` — `GET /api/companies/{companyId}/setup-token-login-sessions/{sessionId}/prompt`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/agents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-setup-token-login-sessions-code` + +API: `present` — `POST /api/companies/{companyId}/setup-token-login-sessions/{sessionId}/code`; JSON REST. +Agent access: `restricted` — existing human setup or approval surface; agent can identify the saved setup and inspect the result. +Policy: Human consent, identity confirmation, action approval, or trusted-host administration. This endpoint does not delegate human identity or self-approval to an agent. +Evidence: `source-only` — [route](../../server/src/routes/agents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: Agent invocation is denied by the stated policy. The authorized human or operator must perform this step; this entry does not claim delegated execution. + +### `post-companies-setup-token-login-sessions-completion` + +API: `present` — `POST /api/companies/{companyId}/setup-token-login-sessions/{sessionId}/completion`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/agents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `post-companies-setup-token-login-sessions-cancel` + +API: `present` — `POST /api/companies/{companyId}/setup-token-login-sessions/{sessionId}/cancel`; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: `tools:manage_connections`; [shared management policy](../../doc/plans/2026-10-13-connection-api-parity.md#authorization-contract). Resolved resources and every affected target are checked; personal ownership remains the responsible user. +Evidence: `source-only` — [route](../../server/src/routes/agents.ts); reviewed in the working tree based on the source commit above. +Verification: `not-run` — individual operation not executed in this audit; shared authorization and representative family flows are exercised by the authenticated integration suite. Follow the route schema, inspect persistence/audit, and assert owner, scope and revoked-authority denial. +Gaps: This individual operation has source review but no recorded HTTP verification. Provider availability and live setup remain unqualified. + +### `update-agent-ai-binding` + +API: `present` — `PATCH /api/agents/{id}` with a managed AI binding in runtime configuration; JSON REST. +Agent access: `available` — agent REST and default native `search_api` / `call_api`. +Policy: existing agent configuration permission plus `tools:manage_profiles`, current responsible-user authority, resolved account/pool access and target-agent authority. Pool members retain their credential audience. +Evidence: `verified` — the production-authenticated [integration suite](../../server/src/__tests__/connection-management.integration.test.ts) saves a personal account and binds it to the fresh standard agent; provider response is a fixture. +Verification: `passed` — binding persistence and loss of profile-management authority tested with actual agent credentials. Other adapter/provider combinations remain source-reviewed. +Gaps: live provider setup is outside this delivery. diff --git a/maps/feature-map/app-permissions.md b/maps/feature-map/app-permissions.md index 02705f8c62c..6404551a9c1 100644 --- a/maps/feature-map/app-permissions.md +++ b/maps/feature-map/app-permissions.md @@ -107,3 +107,6 @@ 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](../../ui/src/pages/apps/app-detail/PermissionsPanel.tsx), [AppDetail.tsx](../../ui/src/pages/apps/AppDetail.tsx), [tool-access.ts](../../server/src/routes/tool-access.ts). Tests: [AppDetail.test.tsx](../../ui/src/pages/apps/AppDetail.test.tsx), [tool-access-service.test.ts](../../server/src/__tests__/tool-access-service.test.ts), [tool-access-policy-service.test.ts](../../server/src/__tests__/tool-access-policy-service.test.ts) +### `delegated-configuration` + +See the canonical [delegated configuration guidance and acceptance evidence](connection-setup.md#delegated-configuration). diff --git a/maps/feature-map/chat-channels.md b/maps/feature-map/chat-channels.md index 087993e8536..4f182357314 100644 --- a/maps/feature-map/chat-channels.md +++ b/maps/feature-map/chat-channels.md @@ -29,8 +29,12 @@ Message the configured bot/address from its external provider and follow the cor ### `endpoint-management` +Delegated setup can save ordinary bot tokens and API keys. Provider verification keys require the addressed human credential-entry step or verified provider provisioning. Generic setup returns the saved handoff without changing those keys, and generic connection updates cannot replace channel credential references. A replacement card waits for an actual protected-value change and restored verification; old health, unchanged entry and unrelated bot-token repair do not complete it. Fresh GitHub handoffs open the import form; configured endpoints retain reconnect. Credential-entry field sets and completion modes stay distinct while matching retries reuse their saved baseline. Complete human replacements can repair disabled, deleted or unreadable old vault values. Saved links name the requested fields and completion mode; configured GitHub forms support targeted replacements while keeping other saved credentials. Provisioned Slack challenge proof survives runtime storage only for the same signing key and callback URL. The actual-auth Slack/GitHub fixtures cover these boundaries, retries, revocation and permitted bot-token repair; live provider repair remains separately qualified. + Open `/apps/chat/:endpointId/settings` and the endpoint’s other available tabs; identity confirmation uses `/chat-identity/confirm`. +For delegated Slack test messages and identity invitations, apply a Block or Ask first rule to the calling agent while allowing the endpoint's assigned agent. Verify no message is sent. Exercise caller, target and shared rate limits: one shared quota admits one send and the next distinct request is denied. For a saved GitHub identity handoff on an otherwise healthy endpoint, verify the card stays pending before identity confirmation and after an OAuth callback alone. Confirm the addressed user's identity through their human session, then verify continuation becomes available. Revoked, expired, wrong-user and wrong-installation links must keep the handoff blocked. These cases pass through the assembled API with real key/JWT/session middleware and deterministic providers; live Slack/GitHub consent remains separate evidence. + ### `outbound-agentmail` Use `paperclipai email inboxes --company-id `, then `email send --company-id --file ` or `email reply --company-id --file `. Inspect `email thread --company-id ` and `email delivery --company-id `. Agent mutations must provide --run-id or the active PAPERCLIP_RUN_ID. @@ -256,3 +260,9 @@ verified controller and environment cleanup; an unknown executor remains held. After successful admission, reload and verify that the prior skipped receipt does not strand a later real provider reply or tree resume. Provider fixtures support this behavior; live fixed-source runtime and destination proof remain separate. + +### `delegated-configuration` + +Endpoint activity hides private-task events while retaining a next-page cursor across private-only windows, so older readable history stays reachable. Actual-JWT fixtures verify this separately from live provider delivery. + +See the canonical [delegated configuration guidance and acceptance evidence](connection-setup.md#delegated-configuration). diff --git a/maps/feature-map/connection-setup.md b/maps/feature-map/connection-setup.md index d27fc36c657..0b034823075 100644 --- a/maps/feature-map/connection-setup.md +++ b/maps/feature-map/connection-setup.md @@ -260,3 +260,18 @@ admission at those boundaries. Verify a served deployment and provider settings, not only the catalog action count. Vercel preview requires an existing deployment; its provider makes the first deployment production, so an empty-project preview must fail before submission and explain the explicit production prerequisite. +### `delegated-configuration` + +A fresh standard agent can use the existing REST API and default native +`search_api` / `call_api` to perform requested configuration within its responsible +user's authority. Personal ownership stays with that user; audit attribution stays +with the acting agent. Existing agents retain their explicit grants and restrictions. +Human authentication and identity confirmation open the exact saved setup; local +host administration and action approval retain their own boundaries. See the +operation inventory in the companion and the [qualification record](../../doc/plans/2026-10-13-connection-api-parity.md). + +Automated: [agent-authenticated integration](../../server/src/__tests__/connection-management.integration.test.ts) +uses normal creation defaults, persistent agent keys, run JWTs, native tool bindings, +real database persistence, and deterministic provider fixtures. It separately checks +user/agent revocation, trust, company/ownership scope, work modes and attribution. +This is API reachability evidence, not a live provider setup or an LLM success rate. diff --git a/maps/feature-map/gateways-profiles.md b/maps/feature-map/gateways-profiles.md index 078661b1fad..94726ade27a 100644 --- a/maps/feature-map/gateways-profiles.md +++ b/maps/feature-map/gateways-profiles.md @@ -66,7 +66,7 @@ Manual: Create a bounded profile, choose its offered tools and options, save or ## Gotchas - A copied endpoint is not proof of client authentication or tool discovery. -- Gateway access cannot replace underlying connection/action permission checks. +- Gateway access cannot replace underlying connection/action permission checks. Agent-issued clients retain the issuer and responsible user; issuer and target Block, Ask first and rate policies intersect, including after approval. Revoked or narrowed token authority stops continuation. - Advanced tool routes require the appropriate admin gate; legacy tool URLs may redirect. ### Storybook verification @@ -79,3 +79,6 @@ Scope: ui Storybook: Mount current TokensPanel with issue controls, once-only token reveal, expiry/actions, revocation confirmation and failures. Old GatewaysTab fixtures and assistant OAuth revocation are insufficient. Production: Issue bounded token via POST /tool-gateway/gateways/{id}/tokens; call allowed and excluded tools; revoke via /tool-gateway/gateway-tokens/{id}/revoke and verify denial using same client. Sources: Source: [TokensPanel.tsx](../../ui/src/pages/apps/gateways/panels/TokensPanel.tsx), [tool-gateway.ts](../../server/src/routes/tool-gateway.ts), [tools.ts](../../ui/src/api/tools.ts). Tests: [TokensPanel.test.tsx](../../ui/src/pages/apps/gateways/panels/TokensPanel.test.tsx), [tool-gateway-service.test.ts](../../server/src/__tests__/tool-gateway-service.test.ts), [tool-gateway.test.ts](../../server/src/__tests__/tool-gateway.test.ts) +### `delegated-configuration` + +See the canonical [delegated configuration guidance and acceptance evidence](connection-setup.md#delegated-configuration). diff --git a/maps/feature-map/runs-adapters.md b/maps/feature-map/runs-adapters.md index 32c7e51ae6a..373fdb858fa 100644 --- a/maps/feature-map/runs-adapters.md +++ b/maps/feature-map/runs-adapters.md @@ -14,6 +14,7 @@ Agent access: [companion](../agent-access-map/runs-adapters.md). ## Sub-features +- `model-accounts`: `runs-adapters/model-accounts` — configure AI accounts, login attempts, defaults, bindings, usage and pools through delegated connection APIs. - `availability`: `runs-adapters/availability` — discover installed adapters and available models/accounts. - `validation`: `runs-adapters/validation` — test an adapter in the selected execution environment and surface authentication failures. - `runs`: `runs-adapters/runs` — inspect run status, events, output, costs, and the source task. @@ -245,3 +246,7 @@ The existing transport and ingress suites exercise these conditions without a provider. Repeat the actual managed setup and useful task journey separately; the literal `651a3de64fc943216cc414b64b1443809f9499da` Test attempt failed cleanup before hire and retains its unconfirmed recovery state. + +### `delegated-configuration` + +See the canonical [delegated configuration guidance and acceptance evidence](connection-setup.md#delegated-configuration). diff --git a/packages/db/src/schema/tool_access.ts b/packages/db/src/schema/tool_access.ts index 747828734e4..f4aede1b4cd 100644 --- a/packages/db/src/schema/tool_access.ts +++ b/packages/db/src/schema/tool_access.ts @@ -204,6 +204,8 @@ export const connectionGrants = pgTable( * per-grant because two users can authorize the same connection with different scopes. */ requestedScopes?: string[]; + /** Last successful consent; ordinary token refresh preserves this. */ + consentedAt?: string; tokenType?: string; refreshedAt?: string; refreshTokenExpiresAt?: string; diff --git a/packages/shared/src/types/chat-channels.ts b/packages/shared/src/types/chat-channels.ts index 4a2c5b0a211..103eb76c5c5 100644 --- a/packages/shared/src/types/chat-channels.ts +++ b/packages/shared/src/types/chat-channels.ts @@ -229,6 +229,8 @@ export interface ChatEndpointSetupState { testSkipped?: boolean; /** Set only after the provider has delivered a signed callback challenge. */ webhookVerifiedAt?: string | null; + /** Nonsecret generation used to resume a requested signing-secret rotation. */ + credentialRotationId?: string | null; authorizationUrl?: string | null; providerUrl?: string | null; command?: string | null; @@ -245,6 +247,7 @@ export interface ChatEndpointSetupState { export interface ChatEndpointSetupSecret { webhookSecret: string; + credentialRotationId?: string; } export type ChannelPublicationMode = "automatic" | "explicit"; diff --git a/packages/shared/src/types/tool-access.ts b/packages/shared/src/types/tool-access.ts index b85ef4b83fd..0fbd422fdad 100644 --- a/packages/shared/src/types/tool-access.ts +++ b/packages/shared/src/types/tool-access.ts @@ -233,6 +233,8 @@ export interface ConnectionGrant { * request, so this is the baseline its response is judged against. */ requestedScopes?: string[]; + /** Last successful consent; ordinary token refresh preserves this. */ + consentedAt?: string; tokenType?: string; refreshTokenExpiresAt?: string; refreshedAt?: string; diff --git a/server/src/__tests__/tool-access-oauth-protocol.integration.test.ts b/server/src/__tests__/tool-access-oauth-protocol.integration.test.ts index 9223c214346..daa783228e6 100644 --- a/server/src/__tests__/tool-access-oauth-protocol.integration.test.ts +++ b/server/src/__tests__/tool-access-oauth-protocol.integration.test.ts @@ -152,6 +152,9 @@ describe("tool OAuth over local HTTP", () => { expect(calls.filter((call) => call.path === "/register")).toHaveLength(1); const [grant] = await db.select().from(connectionGrants).where(eq(connectionGrants.connectionId, connection.id)); + const consentedAt = grant.providerTenant?.oauth?.consentedAt; + expect(consentedAt).toEqual(expect.any(String)); + expect(Number.isFinite(Date.parse(consentedAt!))).toBe(true); await db.update(connectionGrants).set({ providerTenant: { ...grant.providerTenant, oauth: { ...grant.providerTenant?.oauth, accessTokenExpiresAt: new Date(0).toISOString() }, @@ -174,6 +177,7 @@ describe("tool OAuth over local HTTP", () => { expect(exchanges[1].authorization).toBe(tokenCall.authorization); const [persistedGrant] = await db.select().from(connectionGrants).where(eq(connectionGrants.id, grant.id)); expect(persistedGrant.providerTenant?.oauth).not.toHaveProperty("refreshLease"); + expect(persistedGrant.providerTenant?.oauth?.consentedAt).toBe(consentedAt); expect(Date.parse(persistedGrant.providerTenant!.oauth!.accessTokenExpiresAt!)).toBeGreaterThan(Date.now()); expect(JSON.stringify(persistedGrant)).not.toMatch(/fixture-(access|refresh|secret)/); await first.checkHealth(connection.id, { actorType: "user", actorId: userId }); diff --git a/server/src/services/tool-access/catalog/default-access.ts b/server/src/services/tool-access/catalog/default-access.ts index c9a459dfafc..408a33d9b14 100644 --- a/server/src/services/tool-access/catalog/default-access.ts +++ b/server/src/services/tool-access/catalog/default-access.ts @@ -1,3 +1,4 @@ +import { connectionActorUserId } from "../actor.js"; import { lockManagedAppPolicies, lockToolAssignmentAgents } from "../assignment-locks.js"; import type { ActorInfo, ToolAccessMutationDb } from "../types.js"; import { and, eq, inArray } from "drizzle-orm"; @@ -111,9 +112,7 @@ export function createCatalogDefaultAccess(db: Db) { ? (input.actor.actorId ?? null) : null, createdByUserId: - input.actor?.actorType === "user" - ? (input.actor.actorId ?? null) - : null, + connectionActorUserId(input.actor), }); } else if (resetToRecommendedDefaults) { // Removing an app may retain its profile row when another record still @@ -163,9 +162,7 @@ export function createCatalogDefaultAccess(db: Db) { ? (input.actor.actorId ?? null) : null, createdByUserId: - input.actor?.actorType === "user" - ? (input.actor.actorId ?? null) - : null, + connectionActorUserId(input.actor), }); } @@ -299,9 +296,7 @@ export function createCatalogDefaultAccess(db: Db) { ? (input.actor.actorId ?? null) : null, createdByUserId: - input.actor?.actorType === "user" - ? (input.actor.actorId ?? null) - : null, + connectionActorUserId(input.actor), }) .returning(); results.push(toPolicy(created)); diff --git a/server/src/services/tool-access/connections/organization-grant.ts b/server/src/services/tool-access/connections/organization-grant.ts index 721398dd70d..e95505549a7 100644 --- a/server/src/services/tool-access/connections/organization-grant.ts +++ b/server/src/services/tool-access/connections/organization-grant.ts @@ -19,7 +19,7 @@ export function createOrganizationGrantStore(db: Db) { * so without this the grants API reads back a shared identity whose scope has no source * and no over-grant warning — while the connection record carries both. * - * Deliberately scope fields only. The shared grant takes its credential lifecycle from the + * Scope provenance includes its consent timestamp. The shared grant takes its credential lifecycle from the * connection, and writing `accessTokenExpiresAt` here would make an expired connection * look fresh to the refresh-due check and skip the reconnect prompt. */ @@ -42,6 +42,7 @@ export function createOrganizationGrantStore(db: Db) { scopeSource: oauthProvenance.scopeSource, unrequestedScopes: oauthProvenance.unrequestedScopes, requestedScopes: oauthProvenance.requestedScopes, + consentedAt: new Date().toISOString(), }, } : current; diff --git a/server/src/services/tool-access/oauth/cloud-callback.ts b/server/src/services/tool-access/oauth/cloud-callback.ts index 5c5a3902620..dd3c6feed87 100644 --- a/server/src/services/tool-access/oauth/cloud-callback.ts +++ b/server/src/services/tool-access/oauth/cloud-callback.ts @@ -308,6 +308,7 @@ export function createCloudOAuthCallback(db: Db, dependencies: Dependencies) { accessTokenExpiresAt: credentials.accessTokenExpiresAt, scopes: credentials.scopes, requestedScopes: stateRow.requestedScopes ?? undefined, + consentedAt: now().toISOString(), tokenType: credentials.tokenType, ...(credentials.refreshTokenExpiresAt ? { refreshTokenExpiresAt: credentials.refreshTokenExpiresAt } diff --git a/server/src/services/tool-access/oauth/direct-callback.ts b/server/src/services/tool-access/oauth/direct-callback.ts index a95f49c8a21..b46e6c8fd16 100644 --- a/server/src/services/tool-access/oauth/direct-callback.ts +++ b/server/src/services/tool-access/oauth/direct-callback.ts @@ -270,6 +270,7 @@ export function createDirectOAuthCallback(db: Db, dependencies: Dependencies) { // different scopes. The connection-level list is whichever callback ran last, // so it is the wrong baseline for anyone else's refresh. requestedScopes: authorizedScopes, + consentedAt: connectedAt.toISOString(), tokenType: token.tokenType, refreshedAt: connectedAt.toISOString(), }, diff --git a/server/src/services/tool-access/oauth/vercel-callback.ts b/server/src/services/tool-access/oauth/vercel-callback.ts index 87164abd425..452497f38ea 100644 --- a/server/src/services/tool-access/oauth/vercel-callback.ts +++ b/server/src/services/tool-access/oauth/vercel-callback.ts @@ -1,3 +1,4 @@ +import { connectionActorUserId } from "../actor.js"; import { vercelConnectHttpError, vercelCredentialFor, vercelConnectResourcesFor } from "../vercel-credentials.js"; import type { ActorInfo } from "../types.js"; import { connectionMethodForConnection, projectedConnectionHeaders } from "../connection-methods.js"; @@ -174,9 +175,7 @@ export function createVercelOAuthCallback(db: Db, dependencies: Dependencies) { subjectUserId: stateRow.subjectUserId, ...grantValues, createdByUserId: - input.actor?.actorType === "user" - ? (input.actor.actorId ?? null) - : null, + connectionActorUserId(input.actor), }); } const preActivationLifecycle = { diff --git a/server/src/services/tool-access/vercel-credentials.ts b/server/src/services/tool-access/vercel-credentials.ts index 10bb87628dc..643a19e4113 100644 --- a/server/src/services/tool-access/vercel-credentials.ts +++ b/server/src/services/tool-access/vercel-credentials.ts @@ -1,3 +1,4 @@ +import { connectionActorUserId } from "./actor.js"; import type { ActorInfo } from "./types.js"; import { remoteEndpoint } from "./connection-methods.js"; import { and, eq } from "drizzle-orm"; @@ -140,7 +141,7 @@ export function createVercelCredentialService(db: Db, now: () => Date, vercelCon actor?: ActorInfo, ) { const actorUserId = - actor?.actorType === "user" ? (actor.actorId ?? null) : null; + connectionActorUserId(actor); if (actorUserId) { const [personal] = await db .select() diff --git a/ui/src/api/ai-connections.ts b/ui/src/api/ai-connections.ts index cfe31188368..419e60ed011 100644 --- a/ui/src/api/ai-connections.ts +++ b/ui/src/api/ai-connections.ts @@ -1,6 +1,7 @@ import type { AiConnectionList, AiConnectionUsage, CreateAiConnection, AiConnectionLoginIntent, LocalAiLoginAttempt, LocalAiLoginStatus } from "@paperclipai/shared"; import { api } from "./client"; export const aiConnectionsApi = { + loginSetup: (companyId: string, sessionId: string) => api.get<{ sessionId: string; intent: AiConnectionLoginIntent; environmentId: string; status: string; connectionId: string | null; grantId: string | null }>(`/companies/${companyId}/ai-connections/login/${encodeURIComponent(sessionId)}/setup`), probeUsage: (companyId: string, connectionId: string, grantId?: string) => api.get(`/companies/${companyId}/ai-connections/${connectionId}/usage${grantId ? `?grantId=${encodeURIComponent(grantId)}` : ""}`), startLocalLogin: (companyId: string, input: AiConnectionLoginIntent & { restart?: boolean }) => api.post(`/companies/${companyId}/ai-connections/local/attempts`, input), checkLocalLogin: (companyId: string, input: AiConnectionLoginIntent & { localSessionId?: string }) => api.post(`/companies/${companyId}/ai-connections/local/check`, input), diff --git a/ui/src/api/chatEndpoints.ts b/ui/src/api/chatEndpoints.ts index d50a6756c3b..572eeacdc64 100644 --- a/ui/src/api/chatEndpoints.ts +++ b/ui/src/api/chatEndpoints.ts @@ -151,6 +151,7 @@ export interface ChatEndpoint { slackAccount?: SlackAccountState; slackOAuthCallbackUri?: string | null; webhookVerifiedAt?: string | null; + credentialRotationId?: string | null; webhookSecretConfigured?: boolean; callbackSurfaces?: { events: ChatCallbackSurfaceState; @@ -168,6 +169,7 @@ export interface ChatCallbackSurfaceState { export interface ChatEndpointSetupSecret { webhookSecret: string; + credentialRotationId?: string; } type ListResponse = diff --git a/ui/src/features/connections/ConnectionIntentInteractionBody.test.tsx b/ui/src/features/connections/ConnectionIntentInteractionBody.test.tsx index f9ac280ec45..d6dbe090427 100644 --- a/ui/src/features/connections/ConnectionIntentInteractionBody.test.tsx +++ b/ui/src/features/connections/ConnectionIntentInteractionBody.test.tsx @@ -237,6 +237,20 @@ afterEach(async () => { }); describe("ConnectionIntentInteractionBody states and audience", () => { + it("opens the saved chat authentication step without another setup form", async () => { + const interaction = { ...pendingConnectionIntentInteraction, payload: { ...pendingConnectionIntentInteraction.payload, + purpose: "channel" as const, serviceSlug: "slack", serviceName: "Slack", phase: "authorizing" as const, + savedSetup: { endpointId: "endpoint", connectionId: "connection" } } }; + setupOptionsMock.mockResolvedValue({ version: 1, interaction, requestedAgentId: interaction.payload.requestingAgentId, + existingConnections: [], service: { service: "slack", name: "Slack", methods: [], state: "needs_user_action" }, + chatSetup: { endpointId: "endpoint", connectionId: "connection", path: "/ACME/apps/chat/connect?provider=slack&resume=endpoint" } }); + renderBody(interaction); + await flush(); + expect(document.querySelector('a')?.getAttribute("href")).toBe("/ACME/apps/chat/connect?provider=slack&resume=endpoint"); + expect(document.querySelector('[data-testid="shared-connection-setup"]')).toBeNull(); + expect(completeMock).not.toHaveBeenCalled(); + }); + it.each([ [pendingConnectionIntentInteraction, "Connect"], [ diff --git a/ui/src/features/connections/ConnectionIntentInteractionBody.tsx b/ui/src/features/connections/ConnectionIntentInteractionBody.tsx index a87af21f786..e0d22dc6621 100644 --- a/ui/src/features/connections/ConnectionIntentInteractionBody.tsx +++ b/ui/src/features/connections/ConnectionIntentInteractionBody.tsx @@ -64,6 +64,7 @@ export function ConnectionIntentInteractionBody({ ); const isPending = interaction.status === "pending"; const isAi = interaction.payload.purpose === "ai"; + const isChatHandoff = Boolean(interaction.payload.savedSetup); const provider = isAi ? aiProviderSchema.safeParse(interaction.payload.serviceSlug).data : undefined; const serviceName = provider ? AI_CONNECTION_CAPABILITIES[provider].name : interaction.payload.serviceName; const isEmail = interaction.payload.purpose === "channel" && interaction.payload.serviceSlug === "agentmail"; @@ -472,6 +473,7 @@ export function ConnectionIntentInteractionBody({ ? `${interaction.payload.requestingAgentName} needs your own AI connection. Connect here and the task will resume automatically.` : interaction.payload.purpose === "ai" ? "This task can’t run until the agent has a valid AI connection. Connect here and the task will resume automatically." + : isChatHandoff ? "Complete the human sign-in step in the saved setup. Your task continues when the connection is ready." : isEmail ? "Connect AgentMail to create an email address for this agent." : "Connect your identity or reuse an eligible connection. Access is added only for this agent."}

@@ -486,7 +488,10 @@ export function ConnectionIntentInteractionBody({

) : null} - {isEmail ? setupQuery.isLoading || setupQuery.isError ? <> + {isChatHandoff ?
+ + {setupQuery.data?.chatSetup ? : setupContent} +
: isEmail ? setupQuery.isLoading || setupQuery.isError ? <> {setupContent} : { expect(onCancel).toHaveBeenCalledWith({}); expect(onContinue).not.toHaveBeenCalled(); }); + + it("restores only the addressed user's pending services on a saved draft", async () => { + listGrants.mockResolvedValue({ currentUserId: "me", grants: [] }); + await render({ ...existing, status: "draft", config: { oauth: { pendingWorkspaceServiceRequests: { + me: { requestedScopes: ["https://www.googleapis.com/auth/gmail.readonly"], requestedAt: "2026-10-26T10:00:00Z" }, + other: { requestedScopes: ["https://www.googleapis.com/auth/documents"], requestedAt: "2026-10-26T10:00:00Z" }, + } } } }); + await click("Continue"); + expect(listGrants).toHaveBeenCalledWith(existing.id); + expect(onContinue).toHaveBeenCalledWith({ gmail: "read" }); + }); + + it.each([true, false])("uses pending consent only when newer than the saved grant (newer: %s)", async newer => { + listGrants.mockResolvedValue({ currentUserId: "me", grants: [{ + kind: "user", subjectUserId: "me", updatedAt: "2026-10-26T10:00:00Z", + providerTenant: { oauth: { requestedScopes: googleWorkspaceSelectedScopes({ drive: "write" }), consentedAt: "2026-10-26T10:00:00Z" } }, + }] }); + await render({ ...existing, config: { oauth: { pendingWorkspaceServiceRequests: { + me: { requestedScopes: ["https://www.googleapis.com/auth/gmail.readonly"], requestedAt: newer ? "2026-10-26T11:00:00Z" : "2026-10-26T09:00:00Z" }, + } } } }); + await click("Continue"); + expect(onContinue).toHaveBeenCalledWith(newer ? { gmail: "read" } : { drive: "write" }); + }); + it.each([false, true])("preserves pending service choices through token refresh (legacy grant: %s)", async legacy => { + listGrants.mockResolvedValue({ currentUserId: "me", grants: [{ + kind: "user", subjectUserId: "me", createdAt: "2026-10-26T10:00:00Z", updatedAt: "2026-10-26T12:00:00Z", + providerTenant: { oauth: { requestedScopes: googleWorkspaceSelectedScopes({ drive: "write" }), + ...(legacy ? {} : { consentedAt: "2026-10-26T10:00:00Z" }), refreshedAt: "2026-10-26T12:00:00Z" } }, + }] }); + await render({ ...existing, config: { oauth: { pendingWorkspaceServiceRequests: { + me: { requestedScopes: ["https://www.googleapis.com/auth/gmail.readonly"], requestedAt: "2026-10-26T11:00:00Z" }, + } } } }); + await click("Continue"); + expect(onContinue).toHaveBeenCalledWith({ gmail: "read" }); + }); + }); diff --git a/ui/src/features/connections/GoogleWorkspaceServices.tsx b/ui/src/features/connections/GoogleWorkspaceServices.tsx index 70d1eb16931..b0a9c21e2ba 100644 --- a/ui/src/features/connections/GoogleWorkspaceServices.tsx +++ b/ui/src/features/connections/GoogleWorkspaceServices.tsx @@ -44,8 +44,11 @@ type DialogProps = { export function GoogleWorkspaceServicesDialog(props: DialogProps) { const existing = props.connection && props.connection.status !== "draft" ? props.connection : null; - const grants = useQuery({ queryKey: ["workspace-service-consent", existing?.id], queryFn: () => toolsApi.listConnectionGrants(existing!.id), enabled: Boolean(existing) }); - if (existing && !grants.data) return { if (!open) props.onCancel(); }}> + const oauth = props.connection?.config?.oauth as { pendingWorkspaceServiceRequests?: Record } | undefined; + const pendingRequests = oauth?.pendingWorkspaceServiceRequests; + const loadConnection = existing ?? (pendingRequests && Object.keys(pendingRequests).length ? props.connection : null); + const grants = useQuery({ queryKey: ["workspace-service-consent", loadConnection?.id], queryFn: () => toolsApi.listConnectionGrants(loadConnection!.id), enabled: Boolean(loadConnection) }); + if (loadConnection && !grants.data) return { if (!open) props.onCancel(); }}> Connect Google Workspace{grants.isError ? "Could not load your saved service access. Your connection has not changed." : "Loading your saved service access…"}
{grants.isError && }
; @@ -55,7 +58,14 @@ export function GoogleWorkspaceServicesDialog(props: DialogProps) { ? grant.kind === "agent" && grant.subjectAgentId === (existing.config?.oauth as { connectorSubjectAgentId?: string } | undefined)?.connectorSubjectAgentId : grant.kind === "organization"); const savedScopes = grant?.providerTenant?.oauth?.requestedScopes ?? grant?.providerTenant?.oauth?.scopes ?? []; - return ; + const pending = grants.data?.currentUserId ? pendingRequests?.[grants.data.currentUserId] : undefined; + const pendingTime = Date.parse(pending?.requestedAt ?? ""); + // Refresh and lease writes update the grant row without changing consent. + const consentTime = grant ? Date.parse(grant.providerTenant?.oauth?.consentedAt ?? String(grant.createdAt)) : NaN; + const grantTime = Number.isFinite(consentTime) ? consentTime : -Infinity; + const pendingScopes = Number.isFinite(pendingTime) && pendingTime > grantTime && Array.isArray(pending?.requestedScopes) && pending.requestedScopes.every(scope => typeof scope === "string") + ? pending.requestedScopes as string[] : undefined; + return ; } function ServicePicker({ onContinue, onCancel, initial = GOOGLE_WORKSPACE_DEFAULT_SELECTION }: DialogProps) { diff --git a/ui/src/pages/apps/AppsConnect.test.tsx b/ui/src/pages/apps/AppsConnect.test.tsx index 2c5fc57505a..23273b9550e 100644 --- a/ui/src/pages/apps/AppsConnect.test.tsx +++ b/ui/src/pages/apps/AppsConnect.test.tsx @@ -445,6 +445,28 @@ describe("AppsConnect — Connect with a link (M4 frame)", () => { expect(startOAuthMock).not.toHaveBeenCalled(); }); + it("opens the saved AI sign-in and reuses a completed account", async () => { + mockSearch.value = "aiLogin=saved-login"; + const setup = vi.spyOn(aiConnectionsApi, "loginSetup").mockResolvedValue({ + sessionId: "saved-login", environmentId: "local-env", status: "completed", connectionId: "saved-account", grantId: "saved-grant", + intent: { provider: "openai", method: "subscription", name: "Saved account", ownership: "personal", agentIds: ["agent-1"], allAgents: false }, + }); + await render(); + expect(setup).toHaveBeenCalledWith("company-1", "saved-login"); + expect(container.textContent).toContain("This account is connected."); + expect(connectAppMock).not.toHaveBeenCalled(); + await act(async () => buttonByText("View connection")?.click()); + expect(mockNavigate).toHaveBeenCalledWith("/apps/saved-account/permissions"); + }); + + it("shows an unavailable saved sign-in without creating a replacement", async () => { + mockSearch.value = "aiLogin=another-users-login"; + vi.spyOn(aiConnectionsApi, "loginSetup").mockRejectedValue(new Error("Sign-in attempt not found")); + await render(); + expect(container.querySelector('[role="alert"]')?.textContent).toBe("Sign-in attempt not found"); + expect(connectAppMock).not.toHaveBeenCalled(); + }); + it("keeps Honcho setup blocked until the provider workspace is supplied", async () => { const honcho = CONNECTABLE_APP_DEFINITIONS.find((app) => app.slug === "honcho")!; experimentalMock.mockResolvedValue({ enableMemoryConnectors: true }); diff --git a/ui/src/pages/apps/AppsConnect.tsx b/ui/src/pages/apps/AppsConnect.tsx index afa5e5ba887..55e95c99ae4 100644 --- a/ui/src/pages/apps/AppsConnect.tsx +++ b/ui/src/pages/apps/AppsConnect.tsx @@ -1,5 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { aiConnectionsApi } from "@/api/ai-connections"; +import { AiConnectionCredentialStep } from "@/components/ai-connections/AiConnectionCredentialStep"; import { AiProviderSetup } from "@/components/ai-connections/AiProviderSetup"; import { AiConnectionPoolConnector } from "@/components/ai-connections/AiConnectionPoolConnector"; import { useEffect } from "react"; @@ -27,6 +28,9 @@ export function AppsConnect({ byoOnly = false, credentialSource = "paperclip_vau const { appKey } = useParams<{ appKey?: string }>(); const source = searchParams.get("source") ?? appKey ?? searchParams.get("appKey"); const reconnectId = searchParams.get("reconnect"); + const aiLoginId = searchParams.get("aiLogin"); + const login = useQuery({ queryKey: ["ai-login-setup", selectedCompanyId, aiLoginId], + queryFn: () => aiConnectionsApi.loginSetup(selectedCompanyId!, aiLoginId!), enabled: Boolean(selectedCompanyId && aiLoginId), retry: false }); const preset = aiProviderSetupPreset(source); const aiReconnectRequested = Boolean(reconnectId && (preset || ["openai", "anthropic", "xai"].includes(source ?? ""))); const aiAccounts = useQuery({ queryKey: ["ai-connections", selectedCompanyId], queryFn: () => aiConnectionsApi.list(selectedCompanyId!), enabled: Boolean(selectedCompanyId && aiReconnectRequested) }); @@ -55,6 +59,16 @@ export function AppsConnect({ byoOnly = false, credentialSource = "paperclip_vau const path = selectedCompanyId && consumeSkillSourceReturn(selectedCompanyId); if (path) navigate(path); } + if (aiLoginId && selectedCompanyId) { + if (login.isPending) return

Loading saved sign-in…

; + if (!login.data) return

{login.error?.message ?? "This sign-in is unavailable."}

; + const { intent, environmentId, connectionId, grantId } = login.data; + if (connectionId && grantId) return

This account is connected.

; + return navigate("/apps")} + onComplete={binding => navigate(`/apps/${binding.connectionId}/permissions`)} />; + } if (aiReconnectRequested && aiAccounts.isPending) return

Loading connection…

; if (aiReconnectRequested && aiAccounts.isError) return

Could not load this connection. Refresh to try again.

; if (aiReconnectRequested && !aiReconnect) return

This connection is unavailable. Return to Connectors to choose an account.

; diff --git a/ui/src/pages/apps/app-connect-policy.test.ts b/ui/src/pages/apps/app-connect-policy.test.ts index 89d00279801..7baa492a09d 100644 --- a/ui/src/pages/apps/app-connect-policy.test.ts +++ b/ui/src/pages/apps/app-connect-policy.test.ts @@ -11,6 +11,12 @@ import { } from "./app-connect-policy"; describe("app connect policy", () => { + it("admits a saved AI login handoff without a catalog source", () => { + expect(canEnterAppsConnect(new URLSearchParams("aiLogin=saved-login"))).toBe(true); + expect(canEnterAppsConnect(new URLSearchParams("aiLogin="))).toBe(false); + expect(canEnterAppsConnect(new URLSearchParams("aiLogin=%20"))).toBe(false); + }); + it("derives automatic OAuth entry points from app capabilities", () => { expect(MCP_DIRECT_OAUTH_CONNECT_SLUGS).toEqual(expect.arrayContaining(["jira", "notion", "sentry"])); expect(isMcpDirectOAuthConnectSlug("notion")).toBe(true); diff --git a/ui/src/pages/apps/app-connect-policy.ts b/ui/src/pages/apps/app-connect-policy.ts index 20f147da509..ba96b4ab09b 100644 --- a/ui/src/pages/apps/app-connect-policy.ts +++ b/ui/src/pages/apps/app-connect-policy.ts @@ -57,6 +57,7 @@ export function canEnterAppsConnect( searchParams: URLSearchParams, { chatConnectorsEnabled = false, githubReviewBotsEnabled = false }: { chatConnectorsEnabled?: boolean; githubReviewBotsEnabled?: boolean } = {}, ): boolean { + if (searchParams.get("aiLogin")?.trim()) return true; if (searchParams.get("byo") === "1") { // The old BYO discovery page has moved to the Connectors list. Keep only // exact custom-connection reconnects using this legacy query contract. diff --git a/ui/src/pages/apps/chat/GitHubChatSetup.test.tsx b/ui/src/pages/apps/chat/GitHubChatSetup.test.tsx index e7486c3d546..4086c83caa8 100644 --- a/ui/src/pages/apps/chat/GitHubChatSetup.test.tsx +++ b/ui/src/pages/apps/chat/GitHubChatSetup.test.tsx @@ -72,6 +72,7 @@ vi.mock("@/api/chatEndpoints", () => ({ get: vi.fn(), listResources: vi.fn(), setup: vi.fn(), + generateSetupSecret: vi.fn(), }, })); vi.mock("@/api/projects", () => ({ projectsApi: { repositoryOptions: vi.fn() } })); @@ -257,6 +258,173 @@ describe("GitHub App wizard", () => { expect(container.textContent).not.toContain("GitHub connected"); expect(githubChatApi.registration).not.toHaveBeenCalled(); }); + it("completes a fresh credential-entry handoff through App import", async () => { + await render("resume=draft-1&stage=credentials"); + expect(container.querySelector("#github-app-id")).not.toBeNull(); + expect(container.querySelector("#github-webhook-secret")).not.toBeNull(); + expect(githubChatApi.advance).not.toHaveBeenCalled(); + expect(githubChatApi.registration).not.toHaveBeenCalled(); + const privateKey = "-----BEGIN PRIVATE KEY-----\nfixture\n-----END PRIVATE KEY-----"; + for (const [selector, value] of [["#github-app-id", "1234"], ["#github-webhook-secret", "human-webhook-secret"]]) { + await act(async () => { + const input = container.querySelector(selector)!; + Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")!.set!.call(input, value); + input.dispatchEvent(new Event("input", { bubbles: true })); + }); + } + const paste = new Event("paste", { bubbles: true, cancelable: true }); + Object.defineProperty(paste, "clipboardData", { value: { getData: () => privateKey } }); + await act(async () => container.querySelector("#github-private-key")!.dispatchEvent(paste)); + const connect = container.querySelector('[aria-label="Connect existing App"]')!; + expect(connect.disabled).toBe(false); + await act(async () => connect.click()); + expect(githubChatApi.connectApp).toHaveBeenCalledWith("draft-1", { appId: "1234", privateKey, webhookSecret: "human-webhook-secret" }); + expect(chatEndpointsApi.setup).not.toHaveBeenCalled(); + }); + it.each(["webhookSecret", "clientSecret"])("saves a configured %s handoff without replacing the private key", async (field) => { + fixture.endpoint.status = "active"; + fixture.endpoint.botExternalId = "1234"; + vi.mocked(chatEndpointsApi.setup).mockResolvedValue(fixture.endpoint); + await render(`resume=draft-1&stage=credentials&reconnect=1&credentialFields=${field}&credentialMode=all`); + const selector = field === "webhookSecret" ? "#github-webhook-secret" : "#github-client-secret"; + const input = container.querySelector(selector); + expect(input).not.toBeNull(); + expect(container.querySelector("#github-private-key")).toBeNull(); + expect(chatEndpointsApi.setup).not.toHaveBeenCalled(); + const submit = container.querySelector('[aria-label="Verify and reconnect"]')!; + expect(submit.disabled).toBe(true); + await act(async () => { + Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")!.set!.call(input, "human-replacement"); + input!.dispatchEvent(new Event("input", { bubbles: true })); + }); + expect(submit.disabled).toBe(false); + await act(async () => submit.click()); + expect(chatEndpointsApi.setup).toHaveBeenCalledWith("draft-1", { action: "reconnect", credentials: { [field]: "human-replacement" } }); + expect(githubChatApi.connectApp).not.toHaveBeenCalled(); + expect(chatEndpointsApi.generateSetupSecret).not.toHaveBeenCalled(); + }); + it.each(["all", "any"])("honors %s completion for combined credential entry", async (mode) => { + fixture.endpoint.status = "active"; + fixture.endpoint.botExternalId = "1234"; + vi.mocked(chatEndpointsApi.setup).mockResolvedValue(fixture.endpoint); + await render(`resume=draft-1&stage=credentials&reconnect=1&credentialFields=privateKey,webhookSecret&credentialMode=${mode}`); + const input = container.querySelector("#github-webhook-secret")!; + await act(async () => { + Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")!.set!.call(input, "human-webhook"); + input.dispatchEvent(new Event("input", { bubbles: true })); + }); + const submit = container.querySelector('[aria-label="Verify and reconnect"]')!; + expect(submit.disabled).toBe(mode === "all"); + const privateKey = "-----BEGIN PRIVATE KEY-----\nfixture\n-----END PRIVATE KEY-----"; + if (mode === "all") { + const paste = new Event("paste", { bubbles: true, cancelable: true }); + Object.defineProperty(paste, "clipboardData", { value: { getData: () => privateKey } }); + await act(async () => container.querySelector("#github-private-key")!.dispatchEvent(paste)); + } + expect(submit.disabled).toBe(false); + await act(async () => submit.click()); + expect(chatEndpointsApi.setup).toHaveBeenCalledWith("draft-1", { action: "reconnect", credentials: { + webhookSecret: "human-webhook", ...(mode === "all" ? { appId: "1234", privateKey } : {}), + } }); + }); + it("keeps legacy credential-entry links usable with only a webhook replacement", async () => { + fixture.endpoint.status = "active"; + fixture.endpoint.botExternalId = "1234"; + vi.mocked(chatEndpointsApi.setup).mockResolvedValue(fixture.endpoint); + await render("resume=draft-1&stage=credentials&reconnect=1"); + const input = container.querySelector("#github-webhook-secret")!; + await act(async () => { + Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")!.set!.call(input, "human-webhook"); + input.dispatchEvent(new Event("input", { bubbles: true })); + }); + await act(async () => container.querySelector('[aria-label="Verify and reconnect"]')!.click()); + expect(chatEndpointsApi.setup).toHaveBeenCalledWith("draft-1", { action: "reconnect", credentials: { webhookSecret: "human-webhook" } }); + }); + it("keeps a saved rotation handoff open through generation and reconnect failures", async () => { + fixture.endpoint.status = "active"; + fixture.endpoint.botExternalId = "1234"; + const secret = "a".repeat(64); + vi.mocked(githubChatApi.advance).mockResolvedValue({ endpointId: "draft-1", state: "connected" } as any); + vi.mocked(chatEndpointsApi.generateSetupSecret).mockRejectedValueOnce(new Error("Temporarily unavailable")).mockResolvedValue({ webhookSecret: secret }); + vi.mocked(chatEndpointsApi.setup).mockRejectedValueOnce(new Error("Waiting for the new signed webhook")).mockResolvedValue(fixture.endpoint); + await render("resume=draft-1&stage=credentials&reconnect=1&rotateSecret=1"); + const reconnect = () => [...container.querySelectorAll("button")].find(button => button.getAttribute("aria-label") === "Reconnect saved App")!; + const generate = () => [...container.querySelectorAll("button")].find(button => button.textContent === "Generate webhook secret")!; + expect(reconnect().disabled).toBe(true); + expect(chatEndpointsApi.generateSetupSecret).not.toHaveBeenCalled(); + expect(githubChatApi.advance).not.toHaveBeenCalled(); + await act(async () => generate().click()); + expect(container.querySelector('[role="alert"]')?.textContent).toContain("Temporarily unavailable"); + expect(reconnect().disabled).toBe(true); + await act(async () => generate().click()); + expect(chatEndpointsApi.generateSetupSecret).toHaveBeenLastCalledWith("draft-1"); + expect((container.querySelector("#github-rotation-secret") as HTMLInputElement).value).toBe(secret); + expect((container.querySelector("#github-rotation-secret") as HTMLInputElement).type).toBe("password"); + await act(async () => (container.querySelector('[aria-label="Copy webhook secret"]') as HTMLButtonElement).click()); + expect(copyTextToClipboard).toHaveBeenLastCalledWith(secret); + await act(async () => reconnect().click()); + expect(container.querySelector('[role="alert"]')?.textContent).toContain("Waiting for the new signed webhook"); + expect(reconnect().disabled).toBe(false); + await act(async () => reconnect().click()); + expect(chatEndpointsApi.setup).toHaveBeenLastCalledWith("draft-1", { action: "reconnect" }); + expect(githubChatApi.connectApp).not.toHaveBeenCalled(); + expect(chatEndpointsApi.create).not.toHaveBeenCalled(); + }); + it.each(["unchanged", "changed", "unavailable"])("reconciles a failed replacement without using an uncertain secret (%s)", async (outcome) => { + const previous = "11111111-1111-4111-8111-111111111111"; + const generated = "22222222-2222-4222-8222-222222222222"; + const replacement = "33333333-3333-4333-8333-333333333333"; + const secret = "b".repeat(64); + fixture.endpoint.status = "active"; + fixture.endpoint.botExternalId = "1234"; + fixture.endpoint.setup.credentialRotationId = previous; + vi.mocked(chatEndpointsApi.get).mockReset().mockImplementation(async () => fixture.endpoint); + vi.mocked(chatEndpointsApi.generateSetupSecret).mockReset().mockResolvedValueOnce({ webhookSecret: secret, credentialRotationId: generated }).mockRejectedValueOnce(new Error("Replacement request failed")); + await render(`resume=draft-1&stage=credentials&reconnect=1&rotateSecret=1&afterRotationId=${previous}`); + const button = (label: string) => [...container.querySelectorAll("button")].find(item => item.textContent === label)!; + const reconnect = () => [...container.querySelectorAll("button")].find(item => item.getAttribute("aria-label") === "Reconnect saved App")!; + await act(async () => button("Generate webhook secret").click()); + const before = vi.mocked(chatEndpointsApi.get).mock.calls.length; + if (outcome === "unavailable") vi.mocked(chatEndpointsApi.get).mockRejectedValueOnce(new Error("Status unavailable")); + else vi.mocked(chatEndpointsApi.get).mockResolvedValueOnce({ ...fixture.endpoint, setup: { ...fixture.endpoint.setup, credentialRotationId: outcome === "unchanged" ? generated : replacement } }); + await act(async () => button("Generate another secret").click()); + expect(chatEndpointsApi.get).toHaveBeenCalledTimes(before + 1); + if (outcome === "unchanged") { + expect((container.querySelector("#github-rotation-secret") as HTMLInputElement).value).toBe(secret); + expect(reconnect().disabled).toBe(false); + await act(async () => (container.querySelector('[aria-label="Copy webhook secret"]') as HTMLButtonElement).click()); + expect(copyTextToClipboard).toHaveBeenLastCalledWith(secret); + } else { + expect(container.querySelector('[aria-label="Copy webhook secret"]')).toBeNull(); + expect(reconnect().disabled).toBe(true); + if (outcome === "unavailable") { + vi.mocked(chatEndpointsApi.get).mockResolvedValueOnce({ ...fixture.endpoint, setup: { ...fixture.endpoint.setup, credentialRotationId: generated } }); + await act(async () => button("Check saved secret status").click()); + expect((container.querySelector("#github-rotation-secret") as HTMLInputElement).value).toBe(secret); + expect(reconnect().disabled).toBe(false); + } + } + }); + it.each([ + { generation: "22222222-2222-4222-8222-222222222222", verified: true, canResume: true }, + { generation: "22222222-2222-4222-8222-222222222222", verified: false, canResume: true }, + { generation: "11111111-1111-4111-8111-111111111111", verified: true, canResume: false }, + ])("resumes only the saved rotation generation without requiring its secret again ($verified, $canResume)", async ({ generation, verified, canResume }) => { + fixture.endpoint.status = "attention"; + fixture.endpoint.botExternalId = "1234"; + fixture.endpoint.setup.credentialRotationId = generation; + fixture.endpoint.setup.webhookVerifiedAt = verified ? new Date().toISOString() : null; + vi.mocked(chatEndpointsApi.setup).mockResolvedValue(fixture.endpoint); + await render("resume=draft-1&stage=credentials&reconnect=1&rotateSecret=1&afterRotationId=11111111-1111-4111-8111-111111111111"); + const reconnect = [...container.querySelectorAll("button")].find(button => button.getAttribute("aria-label") === "Reconnect saved App")!; + expect(reconnect.disabled).toBe(!canResume); + expect(container.querySelector("#github-rotation-secret")).toBeNull(); + expect(chatEndpointsApi.generateSetupSecret).not.toHaveBeenCalled(); + if (canResume) { + await act(async () => reconnect.click()); + expect(chatEndpointsApi.setup).toHaveBeenCalledWith("draft-1", { action: "reconnect" }); + } + }); it("reconnects the same App using vaulted credentials without requiring another paste", async () => { fixture.endpoint.status = "attention"; fixture.endpoint.botExternalId = "1234"; diff --git a/ui/src/pages/apps/chat/GitHubChatSetup.tsx b/ui/src/pages/apps/chat/GitHubChatSetup.tsx index 29e0c1c187f..c3af2d80b40 100644 --- a/ui/src/pages/apps/chat/GitHubChatSetup.tsx +++ b/ui/src/pages/apps/chat/GitHubChatSetup.tsx @@ -22,7 +22,7 @@ import { useBreadcrumbs } from "@/context/BreadcrumbContext"; import { useNavigate, useSearchParams, Link } from "@/lib/router"; import { buildPermissionsForTrustPreset } from "@/lib/trust-policy-ui"; import { queryKeys } from "@/lib/queryKeys"; -import { GitHubAppRecovery } from "./GitHubConnectionRecovery"; +import { GITHUB_APP_CREDENTIAL_FIELDS, GitHubAppRecovery } from "./GitHubConnectionRecovery"; import type { GitHubConnectionCompleteLocationState } from "./GitHubConnectionComplete"; /** Restrict native manifest submission to GitHub registration endpoints. */ @@ -80,7 +80,11 @@ export function GitHubChatSetup() { const queryClient = useQueryClient(); const resume = params.get("resume"); const identityOnly = params.get("stage") === "identity"; - const reconnect = params.get("reconnect") === "1"; + const rotateWebhookSecret = params.get("rotateSecret") === "1"; + const reconnect = params.get("reconnect") === "1" || rotateWebhookSecret; + const credentialFields = reconnect && !rotateWebhookSecret && params.get("stage") === "credentials" + ? GITHUB_APP_CREDENTIAL_FIELDS.filter(field => !params.has("credentialFields") || params.get("credentialFields")!.split(",").includes(field)) + : undefined; const [agentId, setAgentId] = useState(params.get("agentId") ?? ""); const [ownerType, setOwnerType] = useState<"personal" | "organization">( "personal", @@ -91,7 +95,7 @@ export function GitHubChatSetup() { const [busy, setBusy] = useState(false); const [submitManifest, setSubmitManifest] = useState(false); const [error, setError] = useState(""); - const [existing, setExisting] = useState(params.get("reconnect") === "1"); + const [existing, setExisting] = useState(reconnect || params.get("stage") === "credentials"); const [appNotCreated, setAppNotCreated] = useState(false); const [legacyAccount, setLegacyAccount] = useState(""); const [identityLinked, setIdentityLinked] = useState(false); @@ -375,15 +379,21 @@ export function GitHubChatSetup() { ) : existing ? ( { if (reconnect || ["active", "paused", "revoked"].includes(bot.status)) { await chatEndpointsApi.setup(bot.id, { action: "reconnect", - ...(credentials ? { credentials: { appId: credentials.appId.trim(), privateKey: credentials.privateKey } } : {}), + ...(credentials ? { credentials: Object.fromEntries(Object.entries(credentials).filter(([field, value]) => + value?.trim() && (!credentialFields || credentialFields.some(requested => requested === field) || (field === "appId" && !!credentials.privateKey.trim() && credentialFields.includes("privateKey"))), + ).map(([field, value]) => [field, field === "appId" ? value!.trim() : value])) } : {}), }); } else if (credentials) { await githubChatApi.connectApp(bot.id, credentials); diff --git a/ui/src/pages/apps/chat/GitHubConnectionRecovery.tsx b/ui/src/pages/apps/chat/GitHubConnectionRecovery.tsx index 2ee14e7d6d0..23709cef990 100644 --- a/ui/src/pages/apps/chat/GitHubConnectionRecovery.tsx +++ b/ui/src/pages/apps/chat/GitHubConnectionRecovery.tsx @@ -1,6 +1,8 @@ import { useState } from "react"; import { AlertTriangle, CircleHelp, ExternalLink, Loader2 } from "lucide-react"; import type { ChatEndpoint } from "@/api/chatEndpoints"; +import { chatEndpointsApi } from "@/api/chatEndpoints"; +import { CopyText } from "@/components/CopyText"; import { SetupWizardFooter } from "@/components/SetupWizard"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -29,7 +31,9 @@ export function GitHubConnectionRecoveryNotice({ endpoint }: { endpoint: ChatEnd ); } -export type GitHubAppCredentials = { appId: string; privateKey: string; webhookSecret: string }; +export const GITHUB_APP_CREDENTIAL_FIELDS = ["privateKey", "webhookSecret", "clientSecret"] as const; +export type GitHubAppCredentialField = typeof GITHUB_APP_CREDENTIAL_FIELDS[number]; +export type GitHubAppCredentials = { appId: string; privateKey: string; webhookSecret: string; clientSecret?: string }; function CredentialLabel({ id, label, help }: { id: string; label: string; help: string }) { return
@@ -40,24 +44,36 @@ function CredentialLabel({ id, label, help }: { id: string; label: string; help:
; } -export function GitHubAppRecovery({ endpoint, reconnect, onConnect, onExit }: { +export function GitHubAppRecovery({ endpoint, reconnect, credentialFields, requireAllCredentialFields = false, rotateWebhookSecret = false, rotationAlreadyCommitted = false, onConnect, onExit }: { endpoint: ChatEndpoint; reconnect: boolean; + credentialFields?: readonly GitHubAppCredentialField[]; + requireAllCredentialFields?: boolean; + rotateWebhookSecret?: boolean; + rotationAlreadyCommitted?: boolean; onConnect: (credentials?: GitHubAppCredentials) => Promise; onExit: () => void; }) { - const [replace, setReplace] = useState(!reconnect); + const [replace, setReplace] = useState(!!credentialFields || !reconnect); const [credentials, setCredentials] = useState({ appId: endpoint.botExternalId ?? "", privateKey: "", webhookSecret: "" }); const [privateKeyDraft, setPrivateKeyDraft] = useState(""); const [pending, setPending] = useState(false); const [error, setError] = useState(""); const [fileError, setFileError] = useState(""); - const needsWebhookSecret = !reconnect && !["active", "paused", "revoked"].includes(endpoint.status); + const [rotationSecret, setRotationSecret] = useState(""); + const [rotationGeneration, setRotationGeneration] = useState(null); + const [rotationUncertain, setRotationUncertain] = useState(false); + const needsPrivateKey = !credentialFields || credentialFields.includes("privateKey"); + const needsWebhookSecret = credentialFields ? credentialFields.includes("webhookSecret") : !reconnect && !["active", "paused", "revoked"].includes(endpoint.status); const appName = endpoint.botLabel ?? endpoint.setup?.github?.appName ?? "your existing App"; const appIdInvalid = !!credentials.appId.trim() && !/^\d+$/.test(credentials.appId.trim()); const keyInvalid = !!credentials.privateKey.trim() && !/-----BEGIN (?:RSA )?PRIVATE KEY-----/.test(credentials.privateKey); const keyLoaded = !!credentials.privateKey.trim() && !keyInvalid && !privateKeyDraft; - const incomplete = replace && (!credentials.appId.trim() || !credentials.privateKey.trim() || appIdInvalid || keyInvalid || (needsWebhookSecret && !credentials.webhookSecret.trim())); + const incomplete = credentialFields + ? !credentialFields.length || appIdInvalid || keyInvalid || (requireAllCredentialFields + ? credentialFields.some(field => !credentials[field]?.trim()) + : credentialFields.every(field => !credentials[field]?.trim())) + : replace && (!credentials.appId.trim() || !credentials.privateKey.trim() || appIdInvalid || keyInvalid || (needsWebhookSecret && !credentials.webhookSecret.trim())); const actionLabel = pending ? "Checking GitHub…" : replace ? (reconnect ? "Verify and reconnect" : "Connect existing App") : "Reconnect saved App"; async function connect() { setPending(true); @@ -66,17 +82,66 @@ export function GitHubAppRecovery({ endpoint, reconnect, onConnect, onExit }: { catch (cause) { setError(cause instanceof Error ? cause.message : "Could not reconnect GitHub. Try again."); } finally { setPending(false); } } + async function checkSavedRotation(expected: string | null | undefined) { + try { + const saved = await chatEndpointsApi.get(endpoint.id); + if (expected !== undefined && (saved.setup?.credentialRotationId ?? null) === expected) { + setRotationUncertain(false); + return true; + } + setRotationSecret(""); + setRotationGeneration(null); + setError("The webhook secret changed, but this page did not receive it. Generate another secret and update GitHub."); + } catch { + // A failed status read cannot establish that the earlier secret is current. + } + setRotationUncertain(true); + return false; + } + async function generateSecret() { + const previousGeneration = rotationGeneration ?? (rotationSecret ? undefined : endpoint.setup?.credentialRotationId ?? null); + setPending(true); + setError(""); + try { + const result = await chatEndpointsApi.generateSetupSecret(endpoint.id); + setRotationSecret(result.webhookSecret); + setRotationGeneration(result.credentialRotationId ?? null); + setRotationUncertain(false); + } catch (cause) { + setError(cause instanceof Error ? cause.message : "Could not generate the webhook secret. Try again."); + setRotationUncertain(true); + await checkSavedRotation(previousGeneration); + } + finally { setPending(false); } + } return
{reconnect &&

Restore {appName} using its saved credentials. Paperclip will check the App and its installation, then repair event delivery.

Your agent, repository choices, review settings, and task history stay attached to this connection. You don’t need to create another GitHub App.

} + {rotateWebhookSecret &&
+

Rotate the webhook secret

+

{rotationAlreadyCommitted ? "The rotated secret is saved. Reconnect using that value; Paperclip will check GitHub’s signed delivery." : <>Generate a new secret, then replace the webhook secret in GitHub App settings. GitHub must verify the new secret before you reconnect.}

+

Generating immediately invalidates the previous secret. This value is shown only here; keep this page open until you have copied it.

+ + {rotationUncertain &&

The active secret is not confirmed. Check its saved status before using an earlier value.

} + {rotationUncertain && rotationSecret && } + {rotationSecret && !rotationUncertain &&
+ + + {!pending && Copy webhook secret} +
} +
} {error &&
-

{replace ? "Could not reconnect this App" : "Could not reconnect the saved App"}

+

{rotateWebhookSecret ? "Could not finish secret rotation" : replace ? "Could not reconnect this App" : "Could not reconnect the saved App"}

{error}

-

Try again if the service is temporarily unavailable. If GitHub rejected the saved key, replace the credentials for this same App below.

+

{rotateWebhookSecret ? "Use the newest secret in GitHub App settings, then retry reconnecting after GitHub verifies delivery." : "Try again if the service is temporarily unavailable. If GitHub rejected the saved key, replace the credentials for this same App below."}

} - {reconnect && } {replace &&
@@ -84,14 +149,15 @@ export function GitHubAppRecovery({ endpoint, reconnect, onConnect, onExit }: {

{reconnect ? "Replace credentials for the same App" : "Connect your existing GitHub App"}

Open GitHub App settings and choose {appName}.

If it’s owned by an organization, open that organization’s Settings → Developer settings → GitHub Apps → Edit. You’ll need permission to manage the App.

+ {credentialFields &&

Enter the requested credentials below. Other saved credentials stay in place.

}
-
+ {needsPrivateKey &&

Copy App ID from the App’s settings page. {reconnect && "The saved ID is filled in when available."}

setCredentials({ ...credentials, appId: event.target.value })} /> {appIdInvalid &&

Use the numeric App ID, not the Client ID.

} -
-
+
} + {needsPrivateKey &&
  1. In the same App’s settings, open Credentials → Key pairs → New key. On the older settings page, use Private keys → Generate a private key.
  2. @@ -115,15 +181,19 @@ export function GitHubAppRecovery({ endpoint, reconnect, onConnect, onExit }: { {keyInvalid &&

    Use a PEM private key, including its BEGIN and END lines. A token or client secret won’t work here.

    } {fileError &&

    {fileError}

    } GitHub’s private key instructions -
+
} {needsWebhookSecret &&

Use the secret saved when you configured this App’s webhook. If you no longer have it, change the webhook secret in GitHub App settings and enter the same value here.

setCredentials({ ...credentials, webhookSecret: event.target.value })} />
} + {credentialFields?.includes("clientSecret") &&
+ + setCredentials({ ...credentials, clientSecret: event.target.value })} /> +
}
} -