Files
PaperClipAI/server/src/routes
DottaandClaude Opus 5.5 267f54ef72 fix(chat): make task-not-found, unreachable-MCP and board denials final for agents (#20208)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - People talk to their agents in Agent Chat, and the agents act
through the Paperclip API
> - A board-only denial now tells the agent that it is final and where
the person acts (#20151, #20182). Other refusals and errors still give
no sign that they are final
> - So the agent keeps trying until the reply deadline, and the person
gets no answer. In chat evaluations this happened on a task lookup that
returned 404, on an MCP server that Paperclip could not reach, on a
model question, and after a final budget denial
> - This pull request makes those answers say that they are final and
what the person must do, and it tightens the chat guidance after a
board-only denial
> - The benefit is a prompt, correct reply instead of a long search or
forbidden workarounds

## Linked Issues or Issue Description

Refs #20151, #20182 (same remedy for board-only denials).

**What happened**
In four Agent Chat evaluation cases on a slower model, the agent did not
reply before its deadline:
- Asked to open a task from an old link, the agent got `404
{"error":"Issue not found"}`. It quoted the private-task guidance, then
searched archived projects, activity and the old host for 3 more
minutes.
- Asked to connect an MCP server at an unreachable URL, the agent got
`502 {"error":"fetch failed","code":"runtime_error"}` from the health
check. It retried, then used `curl` on the host and gateway IP addresses
to look for the server.
- Asked which coding models an agent can use, the agent got the
runtime's model list in its first minute. The catalog did not say how to
list OpenRouter models or that these lists answer the question, so it
enumerated OpenRouter with the shell until the deadline.
- Asked to raise a budget, the agent got the final board-only denial.
The chat guidance said "Complete other permitted work", so it probed
approvals and other routes and replied too late.

**Expected behavior**
The answer the agent receives says whether it is final and what the
person must do, with a link when the page is known. The agent replies at
once. It does not search other routes, retry, or contact other network
addresses.

**Steps to reproduce**
1. Open Agent Chat with a CEO agent.
2. Ask it to open a deleted task by its old link, or to connect an MCP
server at a closed local port.
3. The agent calls `GET /api/issues/{id}` (404) or `POST
/api/tool-connections/{id}/health-check` (502 "fetch failed").
4. The body says nothing about finality. The agent keeps searching or
probing and replies late or not at all.

## What Changed

- `GET /api/issues/:id`: for an agent, the 404 body adds `code:
"issue_not_visible"` and a `remediation`. It says that the lookup is
final, that the agent must not search other routes or open a similarly
named task, and that it should reply now and offer sharing or pasting. A
missing task, a private task and another company's task get the same
body, so the body reveals nothing. People and other routes keep the
plain body.
- MCP health check and catalog refresh: when Paperclip cannot reach the
server (connection refused, DNS, network, reset, timeout or TLS), an
agent's error adds a `remediation`. It says the person must correct the
URL or start the server, that the agent must not contact other hosts,
ports or network addresses, and it links Connectors (`/<prefix>/apps`).
Status, `error` and `code` do not change.
- `AGENT_CHAT_DIRECTIVE`: after a board-only denial, the agent replies
now instead of making more calls. "Complete other permitted work" is
removed.
- API catalog: `POST /api/companies/{companyId}/tools/connections` says
it saves without a check and points MCP-by-URL setup at
`tools/apps/connect`, which checks the server first and saves nothing
for a fresh setup that cannot reach it. `GET
/api/companies/{companyId}/adapters/{type}/models` says which lists
answer a model question (the runtime's list with `provider`, `acpxAgent`
for ACPX agents and `environmentId`, and `opencode_local` with
`provider=openrouter` for OpenRouter keys), that the OpenRouter list is
not a qualification, and that with those inputs these calls answer the
question. `openrouter` is added to the documented `provider` values.
- API catalog authorization: budget policy upsert, budget incident
resolution, company and agent budget updates, and approval
approve/reject are now listed as board-only. Their routes already call
`assertBoard`, but the catalog said `board_or_agent`, so an agent
explored them as its own action before it got the 403.
- Tests: new `agent-refusal-finality.test.ts` (including the catalog
actor for the budget and approval routes); the private-task access test
now checks that a private and a missing task give an agent the same 404
body; the guidance test checks the new wording.

## Verification

- `cd server && npx vitest run
src/__tests__/agent-refusal-finality.test.ts
src/__tests__/board-access-denial.test.ts
src/__tests__/issue-access-grants-routes.test.ts
src/__tests__/authz-existence-oracle-guard.test.ts`: all pass (embedded
Postgres included).
- `cd server && npx vitest run src/__tests__/error-handler.test.ts
src/__tests__/generic-mcp-connection.test.ts
src/__tests__/openapi-routes.test.ts
src/__tests__/remote-http-rebinding.test.ts
src/__tests__/cloud-portfolio-error.test.ts
src/__tests__/database-backup-catalog-guidance.test.ts`: 183 tests pass.
- `npx tsc --noEmit -p server`: passes.
- Real chat journeys on the chat evaluation branch (runner-opencode):
the results will follow in a comment.

## Risks

- Low risk. HTTP status, `error` and `code` stay the same for each
changed error. Agents get extra top-level fields (`code` on the task
404, `remediation`). A client that compares a whole agent 404 body for
`GET /api/issues/:id` exactly will see a change.
- The task 404 body is the same for missing, private and other-company
tasks, so it adds no existence oracle. Other issue routes keep their
plain body.
- The guidance change can make an agent stop after a board-only denial
in a request that had other, permitted parts. The agent still reports
what it finished.

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), 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 <noreply@anthropic.com>
2026-11-09 04:30:19 -06:00
..