Repository navigation
Let an admin fix an app card in one press, and carry the task on once the app is ready - #844
Closed
guidovizoso wants to merge 9 commits into
Closed
guidovizoso wants to merge 9 commits into
guidovizoso wants to merge 9 commits into
Conversation
Adds POST /api/plugins/for/:agentId/apps/:app/approve, admin only. It files the request the app's state needs, as the admin, and approves it straight away through the same code the inbox's Approve runs: the approve body moved into a shared carryOut, and the filing checks into a shared requestable, so the one press can't grant more or check less than the two presses would. The trail reads as the two presses: app_access.requested, then app_access.approved marked direct. No refs, so only read-only actions are granted, as the inbox does. A press that can't be carried out withdraws the request it filed; one of the admin's own that was already waiting is put back as it was.
… asked A refusal is blocked by definition, but show_app_access also answers for an app that is ready, and its card can't tell later whether the app became ready since or was ready all along. The tool now writes blocked: true or false from the state it found, so a card drawn already ready can offer to carry on only where something actually changed. Older answers without the field offer nothing.
An admin looking at an app that is off, or that a Bot wasn't given, now gets one primary button: "Turn on and give it to <Bot>" or "Give it to <Bot>". It calls the new one-press approve route, so the app is turned on and its read-only actions granted with the same checks and trail as the inbox. Send and write actions are still chosen on the Bot's page. Where one press can't do it (no broker key, a set-up left unfinished, a team account nobody connected) the admin gets a link to the app's admin page in a new tab instead. The card reads the app again when its tab has the focus back, so it follows whatever was done over there.
When the person finishes connecting in the other tab, or an admin turns the app on and gives it to the Bot, the card used to say "Ask again and the Bot can use it". Now the card that sees its app go from blocked to ready sends one message on the person's behalf, "Gmail is ready now. Please carry on.", through the same ask any transcript card uses. Why a message and not handoff-resume: that resumes a browser tool call that is still open. An app card is drawn from a call that already answered, and its turn has ended, so there is nothing open to answer. The next turn is what carries the task on. A visible message says why the Bot started again, and the Bot gets the whole conversation with it. Guards: - Only while the card is the latest thing: the person has said nothing since it, has nothing queued, and the Bot isn't working. The transcript passes this down with the tool call id. - Only once per card, across re-renders, remounts and tabs, by a mark in localStorage keyed by the tool call id, set before sending. - Only automatically where the card saw the change. A card drawn already ready (an admin approved while the conversation was closed) offers a Continue button. For show_app_access it does so only if the marker says the app was blocked when the Bot asked.
The card that carries a task on once its app is ready only did so while it was the latest thing, but a routine firing after it did not count, so a later connect sent "please carry on" onto the routine's work. Any user turn now counts. The rule is pulled out as latestFrom and tested.
A one press that could not be carried out withdrew the request it had just filed without a trail row, leaving an app_access.requested nothing answered. It now writes app_access.cancelled, marked direct, as a withdrawal does.
The one press withdraws the request it filed when it cannot be carried out, yet its refusals ended "then approve this request again". They now end "then try again" there; the inbox's wording is unchanged. The browser's fallback sentence follows the copy rules too.
Collaborator
Author
|
Landed in #847. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
When a Bot needs an app it can't use, it stops and draws an app card. Two things made that a dead end.
An admin was asked to request access from themselves. An admin looking at a card for an app that is off, or that the Bot wasn't given, got the same "Ask an admin" a member gets. Now they get the fix itself, one primary button: "Turn on and give it to Sales" or "Give it to Sales". It calls a new admin-only route,
POST /api/plugins/for/:agentId/apps/:app/approve, which files the request the app's state needs, as the admin, and approves it straight away through the same code the inbox's Approve runs. The approve body moved into a sharedcarryOut, and the filing checks into a sharedrequestable, so one press can't grant more or check less than the two presses. No refs are sent, so only the app's read-only actions are granted, as the inbox does; send and write actions are still chosen on the Bot's page. Where one press can't do it (no broker key, a set-up left unfinished, a team account nobody connected) the admin gets a link to the app's admin page in a new tab, with a sentence that says what is missing and ends "then try again". The card reads the app again when its tab has focus back.Once the app was ready, the person had to ask again. The card said "Ask again and the Bot can use it". Now a card that sees its app go from blocked to ready, because the person finished connecting in the other tab or an admin turned it on, sends one message on the person's behalf, "Gmail is ready now. Please carry on.", and the Bot picks the task back up with the whole conversation behind it.
latestFrom).localStoragekeyed by the tool call id and set before sending.show_app_accessit offers one only if the card's marker says the app was blocked when the Bot asked: the tool now writesblocked: true | falsefrom the state it found. Older answers without the field offer nothing.Where it runs
localStorage(with an in-memory copy for a browser that refuses storage). That mark is per browser, on purpose: it only stops a second send from the same person's other tabs.pending, so two admins pressing at once cannot both carry it out: the second is answered 409 "That request has already been answered." Putting a request back is a conditional update on the claim the same admin made.Boundary and audit
app_access.requested, thenapp_access.approvedmarkeddirect, as the two presses would. Its refusals writeapp_access.refused. A press that can't be carried out withdraws the request it filed and writesapp_access.cancelledmarkeddirect, so noapp_access.requestedis left unanswered; an admin's own request that was already waiting is put back as it was.blockedflag is written by the server into the tool's answer, and only decides whether the browser offers a Continue button.Changelog
Unreleased.How it was checked
server/tests/app-access-routes.integration.test.ts: one press turns an app on and gives it read-only, with the trail two presses leave; gives an app that is already on and grants no send; only an admin may press it, and nothing is filed for anybody else; a state with nothing for an admin to do is refused like a request; a press that can't be carried out leaves no request waiting; one of the admin's own already waiting is put back.app/tests/app-access-card.test.tsxandconnect-app-card.test.tsx: a card that sees the app become ready carries on once; a card the conversation has moved on from only says it is ready; a card drawn already ready offers Continue, which carries on once; no conversation, no carrying on.app/tests/transcript-latest.test.ts: what counts as the latest thing, including a file with no caption and a routine firing.app/tests/app-access-action.test.tsx,app-access-mutations.test.ts,shared/connect-prompt.test.ts,server/tests/app-access-tool.test.ts: the admin's button and its refusal sentence, the route's path with no refs, and theblockedmarker.