Skip to content

Record a Bot's decline only against a Bot the caller may reach - #382

Merged
davidmckayv merged 2 commits into
CopilotKit:mainfrom
kevin9327:declined-checks-the-bot
Sep 5, 2026
Merged

Record a Bot's decline only against a Bot the caller may reach#382
davidmckayv merged 2 commits into
CopilotKit:mainfrom
kevin9327:declined-checks-the-bot

Conversation

@kevin9327

Copy link
Copy Markdown
Contributor

What this changes

POST /api/agents/:agentId/declined writes a bot.declined row, marked reportedBy: the Bot itself, for whatever id is in the path. Every other route on a Bot in agents/routes.ts asks the store first and answers 404 for a Bot the caller cannot reach (store.get runs findAccessibleProfile). This one did not.

The Bot reports through the person's session (report_refusal in computer-tools.tsx posts to /api/agents/${bot.current}/declined), so the trail's only basis for calling the row the Bot's own word is that the person could have been talking to that Bot. Without the check, any signed-in person can write a decline, in any words, against any agent id: a coworker they cannot see, a system-owned one, or an id that does not exist. An administrator reading the trail sees The Bot declined with the reason the person typed. The comment on the route already says this row is evidence rather than enforcement; this makes the evidence at least come from somebody who could reach the Bot.

The route now calls store.get(actor, agentId) before recording and answers Agent not found. (404, the same wording the other routes use, so the check does not confirm which ids exist), writing nothing. A legitimate report is unchanged: the Bot's own id is one the person can reach.

Where it runs

  • New state that outlives a request? None.
  • What happens on the second replica? Nothing new: one read on the agents table, the same one every other route on a Bot does.
  • Anything serialised? No.
  • Anything fanned out to a browser? No.
  • New listener, port, or schedule? No.

Boundary and audit

  • Every acting call still goes through the gateway: this route acts on nothing, it only records.
  • New refusals and new failures each write a row: a report against a Bot the caller cannot reach is not a Bot's refusal, it is a request that is not found, so it writes nothing, as a refused change writes nothing already expects for the other routes.
  • Nothing new is trusted from the client: the id in the path is now checked against the store rather than recorded as-is.

Changelog

A line under Unreleased.

Proof

bot-lifecycle-audit.test.ts gains two tests: a decline against a reachable Bot records bot.declined with reportedBy: the Bot itself; a decline against an id the store does not return answers 404 and writes nothing. The fake store in that file gains a get, which the route did not call before.

bun test server/tests/bot-lifecycle-audit.test.ts server/tests/agent-routes.test.ts
 70 pass, 0 fail

bunx tsc --noEmit in server and bunx biome check on the changed files are clean.

The declined route wrote a bot.declined row, marked "reportedBy: the Bot
itself", for whatever agent id was in the path. Every other route on a
Bot asks the store first and answers not found for one the caller cannot
reach; this one did not, so any signed-in person could put a decline in
any words against any coworker, including one they cannot see, and the
trail would present it as the Bot's own word.

Ask the store the same question before recording, and answer not found
for a Bot the caller cannot reach, writing nothing.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@davidmckayv davidmckayv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed diff against current main; resolved CHANGELOG keep-both; validated locally (format + composed test run). CI green.

@davidmckayv
davidmckayv merged commit 2468ca4 into CopilotKit:main Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants