fix(issues): recover agent attribution when bearer token is absent#4441
fix(issues): recover agent attribution when bearer token is absent#4441supertaz wants to merge 4 commits intopaperclipai:masterfrom
Conversation
When an agent sends `x-paperclip-run-id` but omits `Authorization: Bearer`, the server in `local_trusted` mode resolves the actor as board, causing: 1. Issue comments are attributed to the board user instead of the authoring agent (`author_agent_id = NULL`, `author_user_id = 'local-board'`). 2. Self-wake suppression on the comment route is bypassed because `actor.actorType !== "agent"`, triggering a repeated wake cascade until the runaway detector auto-pauses the agent. This fix addresses both issues: - In `issueRoutes` (PATCH and POST comment handlers): when the resolved actor is not an agent but a `runId` is present, look up the run record and recover the true `agentId`. Use the recovered identity for comment attribution and self-wake suppression. - In `heartbeatService.wakeup`: add belt-and-suspenders self-wake suppression. The first check handles the normal agent-actor path. The second handles the board-actor path by looking up whether the triggering comment's `created_by_run_id` belongs to the agent being woken.
Greptile SummaryThis PR fixes agent comment misattribution and self-wake cascade in
Confidence Score: 3/5Partially safe — the primary attribution fix is correct, but a missed self-skip guard in the PATCH handler leaves the @-mention wake-cascade path incompletely fixed. One P1 finding (missed mentions self-skip in PATCH handler) pulls the score to the P1 ceiling of 4; the partially incomplete fix combined with the PR template violations brings it to 3. server/src/routes/issues.ts — specifically the @-mention loop in the PATCH handler (line 2366) and all wakeup requestedByActor fields that still pass the unresolved actor. Important Files Changed
|
Ensure the run looked up by runId belongs to the same company as the issue being commented on before using its agentId for attribution. Prevents cross-company agentId injection via a crafted run-id header.
…odo check Greptile review: !resolvedActorIsAgent incorrectly passed board actor ID as userId for board actors with no runId. Revert to actor.actorType === "user" which returns undefined for board actors, as the original code did. Also pass resolved actor type/id to shouldImplicitlyMoveCommentedIssueToTodoForAgent so bearer-less agent requests correctly trigger the move-to-todo behavior.
|
@greptile recheck |
Address review feedback while preserving the literal actorType-based guard. The recovery path now writes only the resolved agentId, leaving userId undefined; for genuine board users (no resolved agent), userId continues to be set to actor.actorId via the original predicate.
|
@greptile recheck |
Problem
When an agent sends
x-paperclip-run-idbut omitsAuthorization: Bearer, the server inlocal_trusteddeployment mode resolves the request actor as board rather than agent. This causes two compounding bugs:Comment misattribution —
addCommentwritesauthor_user_id = 'local-board'andauthor_agent_id = NULL, so the UI shows the board user's avatar as the comment author instead of the agent that actually wrote it.Wake cascade — Because
actor.actorType !== "agent", the self-wake suppression check (selfComment = actorIsAgent && actor.actorId === assigneeId) evaluates tofalse. Every comment the agent posts triggers a new wakeup for itself. Each wakeup has a uniquewakeCommentId, so the existing dedup gate also misses. The agent is repeatedly woken until the runaway detector auto-pauses it.Fix
Route handlers (
PATCH /issues/:idandPOST /issues/:id/comments):When the resolved actor is not an agent but a
runIdis present, look up the run record and recover the true agent identity. Use the recoveredagentIdfor comment attribution and self-wake suppression — without expanding auth scope.heartbeatService.wakeup(belt-and-suspenders):Add self-wake suppression at the execution layer so the cascade is stopped even if the route-level check is somehow bypassed. Two paths covered:
requestedByActorType === "agent" && requestedByActorId === agentIdcreated_by_run_idand check whether that run belongs to the agent being woken.Scope
Only attribution metadata and self-wake suppression are affected. The
local_trustedfallback actor type, access control, and all other authz checks are unchanged.