Repository navigation
Commit 3592f85
authored
Land the UX work: chart sources, search, calm computer start, waiting rows, app cards, owner apps, per-Bot files (#847)
* Make every chart say where its figures came from
A chart now carries a basis (read, given, own knowledge), the sources it was
read from and an as-of date, and draws them under the plot. Figures the Bot
recalled without looking up are marked as unchecked. The model has to give a
basis; older conversations without one still render, with no footer.
* Tell Bots to look figures up before charting them
* Let an admin turn on and give an app to a Bot in one press
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.
* Say on the app card's marker whether the app was blocked when the Bot 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.
* Give an admin the fix itself on an app card, not a request to themselves
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.
* Carry the task on by itself once a Bot's app card says the app is ready
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.
* Record who may give an app to a Bot, and whether its owner or an admin gave it
* Tell a Bot its computer is still starting, rather than failing, when a cold start outlasts the wait
* Answer a screenshot of a computer that is not ready with its state, without waking it
* Tell whether a Bot holds a search tool, from its refs or its tool names
holdsWebSearch reads the grant refs a built-in Bot is built with, and
researchRoleOfToolName reads the mcp__server__tool names a Bot running its
own loop is offered. Either counts a search tool from any research
provider, without needing its reader alongside.
RESEARCH_APP_NAMES lists the catalogue's search apps by title, the name
show_app_access resolves. There is no app called "search", so a Bot with
none has to name one, and listing every provider keeps the choice with
the administrator. A test holds the list to the catalogue's titles.
* Send a search to the search tool, and never to the browser
Asked to "search for" something in a demo, a Bot drove its browser to a
search engine. The computer guidance told it to navigate whenever asked
to look at, check or read anything, and never to claim it cannot browse;
the one line saying a page is not a search read as a hedge beside them.
computerGuidance now takes what the run holds and says one thing:
- With a search tool, finding information goes to it, and the computer
is for signing in, clicking through, filling in forms, or opening a
page the person asked to see.
- Without one, the browser never goes to Google, Bing or DuckDuckGo to
look something up. The Bot answers from what it knows, says it has not
checked it, and calls show_app_access once with one of Parallel
Search, Exa Search or Firecrawl so the person can ask for it.
"Never claim you cannot browse" is gone; opening a page the person names
is still something the Bot does.
Every path gets the right wording. Built-in Bots are worded in
builtInAgentConfiguration from the tools they are built with, so a
narrowed run follows what it was offered. agent-bot and agent-langgraph
work it out from the tool names each run offers them, which are the
only view they have of their grants.
* Say in computer_navigate's description that it is not a web search
The tool description repeated the old prompt's "look at, visit, open or
check a website", which reads "check the latest on X" as a page to open.
It now says to open a particular site and never a search engine, so the
tool list agrees with the guidance beside it.
* Format the not-ready screenshot route tests
* Type the still-starting headless test's fetch double
* Report a live screen being retried as reconnecting, not as a problem
* Show a computer starting calmly, and a problem only when there is one
* Say what a conversation is waiting on, on reads and on every busy event
The busy signal said only that something was running, on the socket and
nowhere else. Read what a conversation waits on from the rows that
already say so: an unfinished hop it asked, folded with its relay, and
an open routine run. The channel read and the roster carry the list, and
every busy event carries the whole list at the moment it is sent.
* Tell the asking conversation when a hop is queued and when the queue moves
The delivery's busy signal fires inside the run, while the row still
says the hop is in flight, so it cannot be what says the hop is over.
The runner now settles after finish or release, with the relay already
queued, and the desk announces a hop the moment it is offered.
* Show a conversation as working while a routine fires into it
Signalled around the firing and cleared after the run row has its
outcome, since the open run row is what the conversation reads.
* Show a computer call turned away by a cold start as not done yet, and tell the Bot to try again
* Have a Bot say what it is about to do before its first computer action
* Tell a Bot's owner when an app is theirs to give it, and let an admin decide which apps are
* Let a Bot's owner give it their own apps, and take back only what they gave
* Keep a Team Bot on each teammate's own account for apps its owner gave it
* Restore the canvas and image prototypes the calm-start tests stub
* Keep what a conversation waits on in the roster and in the open channel's read
Busy events now carry the whole work list. It replaces the row's list in
the roster and the channel read the conversation screen uses, and both
are refetched with the roster after a reconnect or a resync. The sidebar
stays lit while a hop or a routine is pending between turns.
* Show who a conversation is waiting on, with a running clock, where the answer will land
"Asking Ana… 0:12" for a Bot its Bot asked, "Ana is running a routine…"
for a routine, at the end of the transcript, from the server's list.
Hidden while this tab's own turn runs. ProgressRow is the reusable line
for later waits.
* Let an admin choose on an app's page whether Bot owners can give it
* Ask a Bot's owner on the app card whether to let it use their app, in one press
* Give a new Bot your connected apps, and switch them on its page
* Keep a chart's sources line readable when the model's arguments are odd
Separate the unchecked note from the sources list, name a repeated source once,
and skip sources or dates that are not the shape the schema asks for instead of
breaking the card.
* Test the sources line with an unchecked note and malformed arguments
* Count a routine firing as the conversation moving on past an app card
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.
* Write the withdrawal into the trail when an admin's one press is undone
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.
* Word the no-search guidance as a condition, not a claim that the Bot holds no search tool
Search tools the catalogue roles do not recognise (a Composio toolkit, a server added by URL, Exa's advanced search granted alone) would otherwise be contradicted by the prompt.
* Name computerGuidance, not the removed COMPUTER_GUIDANCE, in a copilot test comment
* Tell an admin's one press to try again, not to approve a request
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.
* Refuse an owner a grant on an action their app does not advertise
* Record nothing when an owner's grant lands on one that already stands
* Say where an owner gives a write action, in the give route's note
* Format the owner grant refusal
* Tell a Bot its computer is still starting when the supervisor's own health wait runs out
The supervisor answers a container that is up but not yet healthy with a 503 after 60s, which is
well inside the server's 120s ensure timeout, so the docker and desktop paths never reached the
gentle still-starting answer. The supervisor now marks that 503 as starting and the provider maps
it to ComputerStillStartingError. Also awaits a rejects assertion that was never checked.
* Report a docker computer whose browser is still booting as starting, not ready
Docker says running as soon as the process exists, so status called a booting container ready, the
screenshot route went on to /ensure, and the poll was held for up to a minute on the health wait.
The supervisor now passes the healthcheck verdict through its list, and running with a starting
healthcheck reads as starting. An older supervisor sends no verdict and reads as before.
* Answer a cold sandbox start within 45s, under a load balancer's idle timeout
locate waited up to 120s inside the person's request. Behind an ALB's default 60s idle timeout
the browser got a bare gateway error, and the action could still land once the pod came up after
the model had been told it failed. locate now gives up at 45s with the still-starting answer while
the pod keeps coming up; update's background restart keeps the full resume budget.
* Keep the computer panel honest when a not-ready answer goes stale
A screenshot failure after an 'absent' answer kept the old state, so the side panel went on
calling a failing computer off, and an off computer is never timed out, so the failure was never
shown. A failure now clears the polled state and the status read resumes. A not-ready answer also
drops the last frame, which showed a sleeping computer as live and kept its stream reconnecting.
* Format the stale-state computer panel test
* Read a conversation's open hops through an index instead of scanning every hop
The work list is read on every roster page and every busy event. It filtered
work_items on payload->>'threadId' with no index, and joined each relay to its
hop by building 'relay:' || key for every row, so both sides scanned a day of
finished hops. A partial index on unfinished rows by thread, and a join that
cuts the hop key out of the relay key, turn it into two index lookups.
* Keep what a conversation waits on to the person whose thread it is
Every member of a shared channel has a thread of their own in it. The work list
was broadcast to all members on every busy event, read from whichever thread the
signal came from, so another member learned which Bot somebody asked and lost
their own list to it; the roster also listed every member's routine runs.
Hops are now read from the reader's thread and routines from the reader's own;
the list goes only to the thread's owner, and the other members hear the busy
flag alone, as before.
Each list is stamped with the database clock just before it is read, so a
browser can tell an older list that arrived late from a newer one.
* Ignore a work list read before the one a conversation already holds
Busy events are read on whichever replica sent them and can arrive out of
order. The roster and the open conversation now keep the list with the latest
stamp, so a late event no longer puts back a hop that has already settled.
* Keep the assertion-shape guard green across the work-list tests
The branch moved four allow-listed offences down their files, and its own tests
added four new ones: an absent busy flag and work list asserted through a ?.
chain, and an expected undefined key in a toEqual. The moved entries are
re-keyed; the new ones assert the missing key directly instead.
* Check the first-action line in both computer guidance variants
* Follow the moved headless-components lines in the assertion-shape allow-list
* Follow the moved computer-supervisor and computer-sandbox lines in the assertion-shape allow-list
* Hold the admin's approve call to an absent body, so the assertion can fail
* Hold the owner's give call to an absent body, and follow the moved siblingReads line in the allow-list
* Add a changelog line for chart sources
* Add a changelog line for search routing
* Add a changelog line for the app card carrying on
* Add a changelog line for the handoff progress row
* Add a changelog line for the calm computer start
* Add a changelog line for owner app grants
* Mark a Shared-account audience an owner's grant set, so it can be withdrawn with it
* Let an owner's grant set a Shared app's audience, and read it only while owners decide
* Set the audience when an owner gives a Shared app, and withdraw it on take-back or a rule change
* Test an owner's Shared-app grant: usable at once, scoped to the Bot, withdrawn with the grant or the rule
* Say on a Shared app's page that owners who give it let their Bots use the team account
* Document owners giving Shared apps, and add the changelog line
* Say where a chart's figures came from in Slack and Teams too
* Give each Bot its own files on a computer many Bots share
* End a hop at once when the other Bot's address is one this deployment will not dial
* Recognise a refused address by its message, since the AG-UI client keeps only that
* Show the asked Bot's avatar on the waiting row, and drop the clock
* Read container health in its own file, so its test does not need dockerode1 parent 2b65797 commit 3592f85
141 files changed
Lines changed: 39124 additions & 572 deletions
File tree
- agent-bot
- src
- tests
- agent-computer
- src
- tests
- agent-langgraph
- src
- tests
- app
- src
- components
- agents
- app-sidebar
- bot-profile
- channels
- computer
- gallery
- plugins
- lib
- app-access
- channels
- computers
- copilot
- enterprise
- plugins
- routes/_authed/admin/plugins
- tests
- charts/openbot
- templates/computer
- docker/s6/s6-rc.d/computer
- docs
- plugins
- server
- drizzle
- meta
- src
- admin
- agents
- channels
- components
- computer
- db/schema
- delivery
- plugins
- plugin-store
- route-groups
- routines
- team-bots
- tests
- supervisor
- src
- tests
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
42 | 119 | | |
43 | 120 | | |
44 | 121 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
20 | | - | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
21 | 34 | | |
22 | 35 | | |
23 | 36 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
5 | 42 | | |
6 | 43 | | |
7 | 44 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
31 | 30 | | |
| 31 | + | |
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
54 | | - | |
55 | 54 | | |
56 | 55 | | |
57 | 56 | | |
| |||
61 | 60 | | |
62 | 61 | | |
63 | 62 | | |
64 | | - | |
65 | 63 | | |
66 | 64 | | |
67 | 65 | | |
68 | 66 | | |
69 | 67 | | |
| 68 | + | |
70 | 69 | | |
71 | 70 | | |
72 | 71 | | |
| |||
197 | 196 | | |
198 | 197 | | |
199 | 198 | | |
200 | | - | |
201 | | - | |
202 | | - | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
203 | 202 | | |
204 | 203 | | |
205 | 204 | | |
| |||
245 | 244 | | |
246 | 245 | | |
247 | 246 | | |
248 | | - | |
| 247 | + | |
| 248 | + | |
249 | 249 | | |
| 250 | + | |
250 | 251 | | |
251 | 252 | | |
252 | 253 | | |
| |||
286 | 287 | | |
287 | 288 | | |
288 | 289 | | |
289 | | - | |
290 | | - | |
291 | | - | |
292 | 290 | | |
293 | 291 | | |
294 | 292 | | |
| |||
1209 | 1207 | | |
1210 | 1208 | | |
1211 | 1209 | | |
1212 | | - | |
1213 | | - | |
1214 | | - | |
| 1210 | + | |
| 1211 | + | |
1215 | 1212 | | |
1216 | 1213 | | |
1217 | 1214 | | |
| |||
1228 | 1225 | | |
1229 | 1226 | | |
1230 | 1227 | | |
1231 | | - | |
| 1228 | + | |
| 1229 | + | |
1232 | 1230 | | |
1233 | 1231 | | |
1234 | 1232 | | |
| |||
1242 | 1240 | | |
1243 | 1241 | | |
1244 | 1242 | | |
| 1243 | + | |
1245 | 1244 | | |
1246 | | - | |
| 1245 | + | |
1247 | 1246 | | |
1248 | 1247 | | |
1249 | 1248 | | |
| |||
1283 | 1282 | | |
1284 | 1283 | | |
1285 | 1284 | | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
1286 | 1288 | | |
1287 | 1289 | | |
1288 | 1290 | | |
| |||
1310 | 1312 | | |
1311 | 1313 | | |
1312 | 1314 | | |
| 1315 | + | |
1313 | 1316 | | |
1314 | | - | |
| 1317 | + | |
1315 | 1318 | | |
1316 | 1319 | | |
1317 | 1320 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
0 commit comments