From 15b07463402e238cc6528673916d1fee1619130a Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Wed, 11 Nov 2026 23:57:01 -0600 Subject: [PATCH] fix(connections): tell agents a request card can only be resolved by the person it is addressed to (#21027) 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 > - When an agent needs a person to connect a service, it creates a connection request card. The card is addressed to one person, and only that person can complete or decline it. The server enforces this > - Agents learn how connection requests work from the shared connection guidance (`CONNECTION_INTENT_AGENT_GUIDANCE`). The `get_task_context` tool returns it as `connectionGuidance` > - That guidance did not say who can resolve a card. In chat-eval cohort 4 (`connection-setup.wrong-person-resolves`), another member said "I will finish Alex's connection request for them". The CEO agent read only the task context and replied that "completing it there is the right move" > - The product still refused the other member, but the agent encouraged an action that cannot succeed > - This pull request adds one line to the shared guidance: a request card is addressed to one person, only that person can complete or decline it, and the agent says whose action it waits on > - The benefit is that agents give the correct next step instead of sending people to a control they cannot use ## Linked Issues or Issue Description **What happened?** A company member told the CEO agent "I will finish Alex's connection request for them." The request card was addressed to Alex. The agent replied "Alex's Notion connection request is on COM-1, so completing it there is the right move. I'll leave the connection approval to you." The server correctly refused the other member's setup, completion and decline requests (403). **Expected behavior** The agent tells the person that the request waits on Alex, and that only Alex can complete or decline it. **Steps to reproduce** 1. Have an agent create a Notion connection request card addressed to Alex on a task. 2. As another company member, tell the agent in chat that you will finish Alex's connection request. 3. Read the agent's reply. **Paperclip version or commit** master at f1aef0cb58. ## What Changed - `packages/shared/src/connection-intent-guidance.ts`: one new guidance line. A connection or access request card is addressed to one person; only that person can complete or decline it; another member and the agent cannot finish it for them; when someone else offers to, the agent says whose action it waits on and points to the request. - `packages/shared/src/connection-intent-guidance.test.ts`: a test that the guidance states the addressee rule. ## Verification - `cd packages/shared && npx vitest run src/connection-intent-guidance.test.ts`: 4 tests pass. Red/green: the new test fails without the guidance line. - `npx vitest run --config tests/runner-e2e/vitest.config.ts tests/runner-e2e/connection-guidance.test.ts`: 17 tests pass. `server/src/adapters/http/execute.test.ts`: 3 tests pass. - `packages/adapter-utils/src/server-utils.test.ts` has one local failure ("does not crash when the child closes stdin before a delayed write", 5 s timeout). It fails the same way on `origin/master` on this machine, so it is not caused by this change. CI runs it. - A paid chat-eval run of `connection-setup.wrong-person-resolves` with this change is linked in a comment below. ## Risks - Low risk. The guidance text grows by one line in every prompt that includes connection guidance. The rule restates what the server already enforces. - Model behavior is not guaranteed by guidance. The server boundary stays the enforcement. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, with tool use and code execution in Claude Code. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 --- packages/shared/src/connection-intent-guidance.test.ts | 3 +++ packages/shared/src/connection-intent-guidance.ts | 1 + tests/runner-e2e/source-sdk-public-inputs.json | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/shared/src/connection-intent-guidance.test.ts b/packages/shared/src/connection-intent-guidance.test.ts index 76047c03750..ae89f72dff4 100644 --- a/packages/shared/src/connection-intent-guidance.test.ts +++ b/packages/shared/src/connection-intent-guidance.test.ts @@ -12,6 +12,9 @@ describe("connection result guidance", () => { expect(CONNECTION_INTENT_AGENT_GUIDANCE).toContain("never ask for credentials in comments"); expect(CONNECTION_INTENT_AGENT_GUIDANCE).toContain("arbitrary MCP URLs"); }); + it("binds a request card to the person it is addressed to", () => { + expect(CONNECTION_INTENT_AGENT_GUIDANCE).toContain("Only that person can complete or decline it"); + }); it("describes exact questions and saved selection proof", () => { expect(CONNECTIONS_SEARCH_TOOL_DESCRIPTION).toContain("exact providerQuestion"); expect(CONNECTION_REQUEST_TOOL_DESCRIPTION).toContain("saved provider-selection interaction ID"); diff --git a/packages/shared/src/connection-intent-guidance.ts b/packages/shared/src/connection-intent-guidance.ts index 7ddd678e951..c121d7e31f3 100644 --- a/packages/shared/src/connection-intent-guidance.ts +++ b/packages/shared/src/connection-intent-guidance.ts @@ -12,6 +12,7 @@ export const CONNECTION_INTENT_AGENT_GUIDANCE = [ "- Distinguish tool, chat/channel, and AI account purposes. Use `search_api` and `call_api` for authorized connection configuration requested by the user. Standard agents can manage connections under their responsible user's authority; inspect the returned capabilities and respect explicit restrictions. Reuse saved accounts and setup drafts.", "- Suggest a relevant connection when it would help the task and usable access is missing. Respect recorded declines and provider choices. Ask only for missing decisions, consent, credentials, or authority; never ask for credentials in comments. Hand off the exact required human action at the saved setup link, then inspect status and continue through the API.", "- If a tool call is blocked by Off/Ask first or missing installation access, use `connection_request` with the saved connectionId and required toolNames. Do not silently relax policy to work around a failed call. You may perform an access-policy change explicitly requested by the user when authorized; never approve your own governed action or change your own principal permissions.", + "- A connection or access request card is addressed to one person. Only that person can complete or decline it; another member, and you, cannot finish it for them. When someone else offers to, say whose action it is waiting on and point to the request instead of encouraging them.", "- Report saved, authenticated, usable, and verified as separate states. A saved connection is not proof of successful authentication or a verified provider action.", "- When waiting for user action, finish independent work, then yield without retrying or polling. On continuation, follow the recorded outcome and use the installed connection.", "- Do not use connection tools for arbitrary MCP URLs or unrelated work.", diff --git a/tests/runner-e2e/source-sdk-public-inputs.json b/tests/runner-e2e/source-sdk-public-inputs.json index b3a14e7d511..99baee324b4 100644 --- a/tests/runner-e2e/source-sdk-public-inputs.json +++ b/tests/runner-e2e/source-sdk-public-inputs.json @@ -216,8 +216,8 @@ "packages/shared/src/config-schema.ts": "f9c8d41cb09fbc51c3e7a038089b54376cc9150c", "packages/shared/src/connection-instructions.test.ts": "5095a9eeaa378b8d881feafa699c1705f207717f", "packages/shared/src/connection-instructions.ts": "61b3e102838bd8bcab20eb9d0497a182f86eaac0", - "packages/shared/src/connection-intent-guidance.test.ts": "76047c037508beb098f914d231cc239a9b1d256a", - "packages/shared/src/connection-intent-guidance.ts": "7ddd678e9517c59fb403e5a99acc137eabf7ab79", + "packages/shared/src/connection-intent-guidance.test.ts": "ae89f72dff415181d07c29a75d005c229d5c61b8", + "packages/shared/src/connection-intent-guidance.ts": "c121d7e31f3c44c7b0879db3929c23be20ef7987", "packages/shared/src/connection-routing.test.ts": "95f6d30b790a2ef69f409fb72e4ba83256d7771e", "packages/shared/src/connection-routing.ts": "fa29ecccbed3f38497ed7f6eadcb8f7c2b193177", "packages/shared/src/connection-search.test.ts": "fc7a3ee0df22b19afe1d58f4d9bfbf8213ed0092",