MUL-6658 fix(cli): tell a rejected task token to stop, not to sign in again (#7522) (#7532)
* fix(cli): tell a revoked task token the task is over, not to sign in again The CLI's generic 401 copy reads "Your session has expired or you are not signed in. Run `multica login` to sign in again. On a self-hosted or non-OAuth setup, ask your administrator for valid credentials." That is correct advice for a person and actively harmful for an agent. A task's mat_ token is revoked the moment the task reaches a terminal state, so every call an orphaned agent makes after cancellation gets that message — and it reads as an instruction to go find a working credential. In GH #7522 one did exactly that: it read the daemon owner's profile PAT out of ~/.multica and kept working under the member's identity. A 401 on a task-scoped token now says the token was revoked because the task ended, and not to retry with another credential. The status code alone cannot distinguish the two cases, so HTTPError records which kind of credential the request carried; nothing else about the client is read, and exit classification is unchanged (still ExitAuth). This is defence in depth, not the fix for #7522 — cancellation not terminating the process tree is (PR #7531). It removes the motive. Co-authored-by: multica-agent <github@multica.ai> * fix(cli): base the task-token claim on the request, not the client Review found three problems with the first pass. TaskScoped read the client's own Token field, which answers "what does this client hold", not "what did the failing request send". DownloadFile deliberately sends no Authorization header for an absolute signed URL, so a 401 from object storage would have been reported to an agent as a finished task purely because the client happened to hold a mat_ token. It now reads resp.Request, which is the request that actually went out — also correct across a redirect that strips the header, which the caller's own request object would not be. newHTTPError goes back to a plain function. A test drives a real client through both paths instead of hand-building the error, and fails if the claim moves back to client state. The copy asserted a cause the CLI cannot verify: 401 also covers a malformed token, one sent to the wrong server, and one dropped by an unrelated cleanup. It now says the token was rejected and to stop, without naming a reason. "Stop, and do not fall back to a profile or member credential" is true in every one of those cases; "the task finished" is not. The kind is renamed to KindTaskTokenRejected to match. The Chinese copy used 任务 for an agent execution. Per the glossary, 任务 is the product entity a user files (an issue) and an agent execution stays lowercase `task`; the old wording said the user's issue was rejected. A test now fails if 任务 reappears in this message. Co-authored-by: multica-agent <github@multica.ai> * docs(cli): stop asserting the cause in the comments too The runtime copy no longer claims why a task token was rejected, but three comments still did: TaskTokenPrefix called a 401 "a normal end-of-task signal", TaskScoped described the token as "finished", and the wiring test quoted the old wording. A reader following the comments would have reintroduced exactly the claim this PR removed. They now say what the CLI can actually see — a task token was rejected, there is no sign-in that brings it back, and the reason is not visible from here. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: J <bohan@devv.ai> Co-authored-by: multica-agent <github@multica.ai>
B
Bohan Jiang committed
617d416029a039350de106781e87e931fa5c7a57
Parent: 46b5d9e
Committed by GitHub <noreply@github.com>
on 8/25/2026, 8:27:55 AM