A frontend kept the last apps list per person and answered 200 with a
stale mark when the owner timed out. Two frontends then disagreed: one
with a cached list answered 200, the other a typed 503. The acceptance
contract is that every frontend gives the same typed answer when the
owner cannot answer, so the server cache is gone.
The route now answers owner_busy (timeout) or owner_unavailable with a
503 from every frontend. An owner that is up and busy still answers
from its snapshot, with the operation of each add-on in the list. The
Add-ons page keeps the last list in the browser and shows it with the
busy notice, so it never goes empty. The specs, the docs and the
swagger spec follow the new contract.
Assisted-by: Claude:claude-sonnet-5-5
A person can now choose four save levels on the Memory tab: Save it and
tell me, Ask me first (the default), Always confirm and Never. Always
confirm is like ask, and nothing can loosen it: no agent or messaging
setting overrides it, and the model cannot undo or forget a note by
itself (revert_change is refused, and the person uses the Undo).
One function, effectiveSaveLevel, combines the rules. The strictest wins,
in the order auto, ask, always, never. The inputs are the level of the
person, the rule of the saved agent, the messaging row and the chat.
A table test covers every combination.
A saved agent has an optional memory_save field: always or never. It can
only be stricter than the person, so auto is not offered and the API
refuses it. A write without the field keeps the stored rule, and use_mine
clears it. It is stored by both stores and is part of an agent export and
import. The agent form shows three cards under When it wants to save a
note.
The new row In messaging apps (messaging_save on GET and PUT
/api/chats/memory) takes ask (the default), paired_private or never. With
paired_private a note is saved at once only when the sender is paired and
the chat is one-to-one, and it goes to the notes of that conversation,
with an Undo. The limits stay: five notes a reply, the size limit, and a
note that looks like a secret still waits. The row cannot loosen the level
of the person when that is always or never. A setting that cannot be read
reads as ask.
The connector turn now carries whether the chat is one-to-one and whether
the sender is paired, in the turn grant and in the worker JSON. Both read
as false when unknown. The Telegram, Slack and Discord transports already
set Direct; new specs pin it for private, group and supergroup chats,
direct messages, channels, group conversations and threads.
A note from a messaging chat goes to the conversation first. It reaches the
notes of the agent or of the person only from a paired sender, only when
the memory choice of the agent allows that scope, and only after that
sender confirms it. Under Always confirm only the paired sender who asked
can confirm. An agent whose memory is None still saves nothing.
The script of the confirm-in-chat specs now proposes a note for the
conversation, because a sender who is only listed cannot write to the
notes of the person.
Assisted-by: Claude:claude-sonnet-5-5
The list stays on screen with a banner when a refresh fails. The banner
text changed from "may be out of date" to "Showing the last known
state.", so the polling spec waited for text that is no longer shown.
Assert the banner by its test id, as the busy-owner spec does.
Assisted-by: Claude:claude-sonnet-5-5
A saved agent now has a memory choice: Mine and its own, Its own only,
Mine, or None. The host enforces it. The notes in the prompt and the
scopes of context_read follow the choice, and a memory note may go only to
the scope the choice allows: the agent's own notes, or the person's. The
propose_change tool offers the model only those scopes, a note needs no
scope, and any other scope is refused. None offers no memory kind.
An agent saved before the field has no choice and behaves as before: it
reads notes when its add-ons name Memory and the model picks the scope.
A write without the field keeps the stored choice. The person's Off, a
private chat and the agent's grant still decide first. Sub-agents get no
notes and cannot save; triggers read per the choice and cannot save.
Messaging chats read as before.
The agent form has a Memory section with the four choices, an "As before"
note for older agents, a switch for the permission to save, and the line
that memory is off in the settings. New agents start with Mine and its own.
Deleting an agent that has notes asks to keep, export or delete them
(DELETE /api/chats/agents/{id}?memory=keep|export|delete). Kept notes show
on the Memory page under Deleted agents, and the notes list has a tab for
each agent. Copying an agent never copies its notes.
Assisted-by: Claude:claude-sonnet-5-5
A new opt-in tool group, Past chats (history), gives a chat two read-only
tools. search_chats finds messages in the earlier chats of the person
(query, scope this_chat, this_agent or all, limit, since) and read_chat
reads one chat from a message number. A call returns at most 10 hits with
300 character snippets, or 20 messages and about 6 KiB. Every result is
wrapped as untrusted text, a closing line in an old message cannot end
the wrapper, and the tool says plainly when nothing is found.
What the tools never reach: another user's chats, private chats (a chat
with the notes of memory switched off), sub-agent runs, and every chat
that no person started, which leaves out the messaging chats that hold
the words of other senders. A saved agent needs the new grant
history.read, which is off by default and turns the group on when it is
saved. An agent that works in a folder may search only its own chats. A
run that starts by itself, a messaging chat and a sub-agent do not get
the tools. The grant, the switch and the agent are checked again at each
call. Each call writes one log line with the tool, user, chat, scope, a
hash of the query and the chats returned, and never a query or a message.
The Tools menu and the agent form show the group and the grant through
the existing lists. The docs and the API notes describe both.
Assisted-by: Claude:claude-sonnet-5-5
The filter row of the chat search showed from the first typed character,
when a search has not started yet, so a change of a filter did nothing.
It now shows from two characters, or while a filter is on.
Assisted-by: Claude:claude-sonnet-5-5
The confirmations, refusals and notes that the host writes in a chat
without the model were fixed English: "Started a new chat", the question
about a proposal, the file notes, the pairing replies. They now come from
a catalog (core/services/studio/hostmsg) with a template for English,
Italian, German, Spanish, French and Portuguese (Brazil), and the same
{placeholders} in each. Text for the model (tool descriptions, the held
back note, the thread block) stays English.
A connection has a reply_language setting: auto (default), en, it, de,
es, fr or pt-BR. With auto the host scores the sender's message against
the common words of the six languages, with no model and no dependency.
A message that does not show a language ("ok", "yes", "thanks") changes
nothing, and a short message cannot move a conversation to another
language. The last clear language of a sender and of a chat is kept for
24 hours; a trigger that writes on its own uses the chat's. The setting
is stored in the JSON and the gorm store, is kept by an update that
leaves it out, and is on the connection page under "How it answers".
Every sentence goes through msg or a hostmsg.Voice. A guard test fails
when a fixed text reaches say, Post or Outgoing, when a key is not in the
catalog, or when a key is never used. The reply parser now reads the
yes, no and undo words of the six languages, which the new texts ask for.
Assisted-by: Claude:claude-sonnet-5-5
Memory now has one switch, On or Off, in the Memory tab of the account
settings. Off means nothing from the notes is put in the prompt, the
model has no memory skill, tool or note kind, nothing is proposed or
saved (also by triggers and other runs that start by themselves), and
the model is told in one line that memory is off. The notes stay and can
still be read, exported and deleted.
A chat can be private: the Memory row of its Tools menu is the memory
switch group, stored with the chat. The narrowest level wins: chat,
agent, person, server. A saved agent cannot widen the choice of the
person. The tools list says which level is off (memory.code, blocked_by,
fix); the chat shows a chip and one line, and the agent form shows the
person's switch as a read-only line.
New API: GET and PUT /api/chats/memory for the switch and the save level,
and GET /api/chats/memory/export for a zip of the notes. The Memory tab
gains the switch, the three save levels, Export and Forget everything.
Assisted-by: Claude:claude-sonnet-5-5
The chat search of the Chats page and of the Ctrl or Cmd+K dialog kept
the list of the newest chats and only filtered it, so a chat that was not
in the list could never be found. From two characters the list now shows
the answer of GET /api/chats/search: the hits best first, each with the
chat, who wrote the line, its date and a snippet. The matched words are
marked from the offsets the server sends and drawn as text nodes, so
message text is never read as HTML. Filters for agent, source and date
narrow the search. Arrow keys move through the hits, Enter opens one, and
Escape clears the search before it closes the dialog. The list shows a
searching, empty, failed (with Try again), still-indexing and partial
state.
A hit opens its chat at the message: /app/chats/<id>?seq=<n> scrolls to
the line when the transcript has it and marks it for a moment, again on a
second click. The new strings are in every locale.
Assisted-by: Claude:claude-sonnet-5-5
The library cards, the first-run list, the install dialog and the
upgrade dialogs now name an add-on by its translated title and
description, taken from the library entry or the installed record.
A language without text, and English, keep the English text, field by
field. The installed list and the library page are in the alphabetical
order of the language of the user, and a search finds an add-on by its
translated name and by its English name.
The Home commands test and the provider strings test read the English
title now that the title of an add-on is a language map.
Assisted-by: Claude:claude-sonnet-5-5 javascript
A page loaded while the owner was busy had no list to draw, and a failed
read could look like an empty install. The apps list read now carries
the stale mark of the server. The page keeps the last list (from the
server, or from the copy this tab stored), shows a notice that names the
add-ons being upgraded, offers a retry, and asks again with the existing
backoff. Each add-on that is changing shows "Upgrading..." with its
phase. An error, or a stale copy of an empty list, is a load error, and
the empty state appears only when the owner answered with an empty list.
The new strings are in all eight locales.
Assisted-by: Claude:claude-sonnet-5-5
A name with no spaces (an agent name, a path, a URL, an id) pushed the
document wider than a phone and made the page scroll sideways.
- page header: on a phone the header is a wrapping column, which sizes
its one line to the widest item. Make it a non-wrapping column and let
the title and supporting line break anywhere.
- agent form: the back button, the collection and name chips, the agent
page facts and the connection group heading now wrap long text.
- add e2e/no-horizontal-overflow.spec.js: it draws the main pages with
unbroken long content at 390 and 768 px, light and dark, and asserts
that the document is not wider than the window.
Assisted-by: Claude:claude-sonnet-5-5
The shared connector page kept its words in English text maps inside the
components, and only Studio shipped translations. Move the maps to JSON
bundles (addons/internal/connector-ui/src/i18n) and add bundles for the
Telegram, Slack, Discord, GitHub, Jellyfin, Home Assistant and Talk pages
in German, Spanish, French, Indonesian, Italian, Korean, Brazilian
Portuguese and Simplified Chinese. English stays the default, and a key
that a bundle lacks reads in English.
Plural sentences now go through the translator, so each language picks
its own plural form. The "More" and "optional" labels, the sender line and
the dates follow the language of the page instead of the browser.
A new gate, addons/internal/i18ncheck, fails when a language lacks a
bundle, a bundle lacks a key or has an extra one, a placeholder differs
from English or a value is empty. It runs in make test-i18n and so in the
UI unit tests of CI, with no baseline. Playwright specs render the pages
in Italian, check that no language runs text out of its box on a phone,
and save screenshots when UI_SHOTS_DIR is set.
Assisted-by: Claude:claude-sonnet-5-5 node
The texts for a blocked capability put a noun phrase into slots that
need a verb, so some read badly ("was not given writing to other
channels", "before reminders and scheduled tasks"). Each capability now
has an action phrase in the infinitive and a noun phrase. Every code is
a complete sentence built from the action phrase, for the owner and for
the sender and the model, and most say what to change.
The note for the model now sets whole sentences apart with a space and
asks the model to relay the reason plainly. A table-driven test renders
every code for every capability and checks grammar, length and that no
internal name leaks. Codes, JSON shapes and behaviour do not change.
Assisted-by: Claude:claude-sonnet-5-5
The word "Memory" named collections of documents, notes about the
person, and RAM in the same navigation. Collections are now Knowledge:
the sidebar item, the page title and text, the Home action, the agent
form section and the chat tool group. The tool group "Memory and
profile changes" is now "Suggested changes", since it is the generic
ask-first mechanism for notes, rules, skills, pages and the dashboard.
The Settings section "Memory and models" is now "Models in memory".
Only display strings change. Ids, routes, locale keys, group ids,
grants and stored data stay as they are. All nine locales are updated;
the docs name the Knowledge page and say which search the default
engine does.
Assisted-by: Claude:claude-sonnet-5-5
The panel "What this bot can do" showed "Not checked yet: who is
sending" although the sender is the one picked in the selector. It now
lists only the links that were really not checked, and hides the line
when none remain. Capability titles are shown as sentences with the
product name spelled LocalAI. The test host messages now use the words
of the real host, so the specs and the screenshots read as in use.
Assisted-by: Claude:claude-sonnet-5-5
The connection page of a messaging add-on listed what a sender could do
for the saved settings only, low in the form. Two blocks now sit on the
page and read the form as it is.
Readiness, at the top of the form, has one row for the token, agent, who
may use the bot, permissions, triggers, files and notifications. Each row
has a state, one sentence and at most one fix. Only rows that need
attention show at first. A ticked permission that nobody can reach, such
as a list of channels with no person, is named.
"What this bot can do" replaces the old list. For a chosen kind of sender
it shows each permission as allowed or blocked, the sentence of the host
for the first blocker, and one button taken from the kind of fix: jump to
the setting, open the agent in a new tab, open Settings (an administrator
only), pair a sender or add a person. It asks the access preview 400 ms
after the last change, drops stale answers, works for a connection that
is not saved yet, and says when the form has unsaved changes. The latest
decisions stay below it.
Assisted-by: Claude:claude-sonnet-5-5
Chats and agents can read a Home Assistant instance and, if the person
allows it, switch things in it.
Read tools: server summary, areas, entity search, state, history,
logbook and the catalogue of callable services. Results are bounded,
marked as untrusted data, and stripped of pictures, access tokens,
secrets and locations.
Write tools: call-service for quiet services (lights, switches, input
booleans, media transport) follows the write mode of the connection;
call-physical-service always needs a confirmation. A deny-by-default
allowlist decides which services a model may call. A hard exclusion
list that no setting can lift covers locks, alarms, sirens, valves,
cameras, garage-type covers, restarts, reloads and shell, system and
add-on management. Calls name entities one by one, up to a cap.
Entities can be excluded by domain, area, pattern or label.
One connection is one instance: the token is the credential and the
address is its one allowed resource, so the host refuses any other
address before the add-on runs. The add-on speaks only to that address,
refuses redirects, verifies TLS, and accepts a self-signed certificate
only through a pinned SHA-256 fingerprint.
The policy lives in the add-on data folder and is edited on the add-on
page by an administrator. For that, the host no longer asks a
credential for a uiOnly tool of a credentialed add-on, since the page
has no chat to name a connection.
Assisted-by: Claude:claude-sonnet-5-5 go node
People talk to a saved agent from Discord, in direct messages and where the
bot is mentioned in a channel or thread. The add-on keeps one Gateway link
per bot token (discordgo runs hello, identify, heartbeats and resume), queues
the messages that address the bot, and hands them to the host's poll. It
sends, edits, splits at 2000 characters, moves files by handle (10 MB), and
reads thread history.
Safe defaults: guild channels answer only on a mention or reply, or in a
thread the bot follows; bots, webhooks and system messages are never read;
every message carries allowed_mentions with an empty parse list and mentions
in model text are broken up; a thread's chat is its parent channel so an
allowlist of channels covers it. No typing indicator, no reactions, no
embeds, no slash commands.
Tests use a fake Gateway (hello, identify, ready, resume, heartbeat ack,
message and thread events, reconnect, close codes) and a fake REST server
(429 with Retry-After, create, edit, upload, history, CDN). The page has its
own node tests and a browser spec.
Assisted-by: Claude:claude-sonnet-5-5 go
A note, a profile, a skill, a page or a dashboard change that the agent
proposed in a messaging chat could not be confirmed there: a plain "yes"
went to the model, and the tool result sent the person to Studio, which
the connector prompt says they do not read.
The host now writes the question in the chat, with the kind, the scope
and the lines that will be saved, and answers the reply before any model
turn. yes/ok/confirm saves, no/cancel drops, a four-character code picks
one when several wait, and "undo" takes back a save for 30 minutes. Only
a sender with proposals.confirm may answer; the kinds that change what
the agent may do need a paired sender, and in a shared chat only the
sender who asked. The tool result tells the model what the host did, and
the Studio wording stays for Studio chats.
The permission text of "Confirm changes from the chat" now says what it
covers. The proposal source keeps the asking sender.
Assisted-by: Claude:claude-sonnet-5-5
A connection kept one chat per person for good, with no way to ask for
a new one. A person can now send /new (or !new, for Slack, which keeps
messages that begin with a slash for itself) as the whole message.
The host reads the command after the pairing check and before any
model turn, so the agent never sees it and the logs record only the
ids. It drops the link from the person, or from the shared thread or
group, to its chat. The old chat stays in Studio. The link is in the
shared store, so it works with several frontends and for every
connector add-on.
In the chat mode and in a thread of a group, everyone shares the chat,
so only a paired sender may reset it. The thread mode answers that
every message already starts a new chat. A chat with a running turn is
left alone and the sender is asked to repeat the command later.
Telegram did not treat /new@BotName as addressed to the bot in a
group; it does now, and keeps the text. The connection page gets one
help line under Conversations.
Assisted-by: Claude:claude-sonnet-5-5
A trigger proposed from a chat can carry act.* permissions only when
the connection lists them in approval.acts. The connector settings
page had no control for it, so runs always read only.
Add a switch "Runs may act" under the approval mode of Propose
triggers. When on, four sensitive checkboxes (files, add-ons, send,
spend) say what a run may do. A box is disabled with a reason when the
agent lacks the matching grant, but a ticked one stays ticked and can
be unticked. A line says how the approval modes treat acts, and the
row of Propose triggers lists the acts that are set.
The page sends acts together with the mode and limits, because the
server replaces the whole approval object. An empty list means runs
only read. Names outside the four are rejected before saving.
Assisted-by: Claude:claude-sonnet-5-5
The add-on jobs stop in prepare.sh when less than 20 GiB is free, and
the hosted runner image no longer leaves that much. Add a small script
that removes the unused preinstalled toolchains in parallel and prints
df before and after, and run it first in both jobs.
The Memory delete spec expected one status element. The page live
region and the toast container both carry role=status, so two match.
Check the page live region on its own and the toast as the second.
Assisted-by: Claude:claude-sonnet-5-5
Notice draws what is off, why, who can fix it and one fix button, in
that order, with a live role set by severity. FixNotice draws the same
markup for add-on UIs, styled by the host's ui.css.
The chat notice for commands now says that the server's built-in bash
is off. An administrator gets the Settings row and a link to the
Add-ons page; anyone else is told to ask an administrator. Hide keeps
working. The Add-ons library card, the update notice and a turn on
refused as installed_incompatible use the notice, with an Upgrade fix.
Assisted-by: Claude:claude-sonnet-5-5
The approval mode of proposed triggers sat at the bottom of the
connection page, after roles, channels and pairing, and was easy to
miss. Show it as an option of the permission Propose triggers: nested
and indented, greyed with a reason while the permission is off, with
its limits in a closed Limits list. The row says the current choice
("Proposals: asks first") without opening anything. The bottom section
is gone; an older host with no capability list still shows it as one
block. The saved approval object does not change.
Assisted-by: Claude:claude-sonnet-5-5
An allowlisted person can use the ticked permissions and only the
sensitive ones also need a pairing. Say that rule under the title of the
permissions form, and show the pairing notice only when a sensitive
permission is ticked while nobody holds a live pairing. Add a notice for
an allowlist that holds only channels, whose members are not trusted one
by one.
Assisted-by: Claude:claude-sonnet-5-5
The agent form switched the same capability in two places: a Tools
switch and a permission at the bottom that changed what the switch
meant. Show one switch per capability and put its permissions under it
as options, greyed with a reason while the switch is off. The stored
data and the API do not change.
Triggers, memory and profile changes, files and saved agents each hold
their options. Saved agents is one switch: either stored value turns it
on, off clears both, and on adds only the group so it never turns off
the defaults of the messaging connections. Reply, start new channels
and the permissions that apply when nobody is watching have a section
each. Every switch and option has one line that says what it lets the
model do, and the three trigger switches have names that cannot be
mixed up. The Permissions fold is gone: its list is now in Tools.
The form names a messaging connection of the agent that does not allow
an option the agent holds, with a link to that connection. The chat
Tools menu draws the same rows and shows the held options read only.
Assisted-by: Claude:claude-sonnet-5-5
The permissions form of a connection gets two buttons that fill it from
the agent of the binding. "Allow the usual set" ticks the usual
capabilities the agent holds and says which ones it left out because
the agent does not hold them. "Same as the agent" copies, once, every
capability the agent holds that has a binding capability, after a
confirmation that lists the sensitive ones. Both only fill the form, and
are off with a reason when the binding has no agent or its permissions
cannot be read. A capability the agent does not hold cannot be switched
on.
The form also says when ticked capabilities cannot be used yet: the host
lets a sender use one only with a pairing or a role that holds it, so
with nobody paired and no role grant the ticked ones do nothing. The
notice links to pairing. A link with ?binding=<id> opens that binding at
its permissions.
Assisted-by: Claude:claude-sonnet-5-5
The library card of an installed development build showed only the
installed version when the check had no answer, so a missing or failed
check looked like nothing to update. The card now says when the status
is unknown, when the check failed and when the add-on is up to date.
Add checker coverage for an install that recorded the channel name and
for a registry lookup that fails: neither reads as up to date.
Assisted-by: Claude:claude-sonnet-5-5
Chromium reported 175.99998 px before focus and 176 px after, so the
exact toBe check failed in about half of the runs. Allow half a pixel.
Assisted-by: Claude:claude-sonnet-5-5 playwright
The host access step added a fourth tab after Sources, so End now
selects Host access. The spec still expected Sources and failed on
every run.
Assisted-by: Claude:claude-sonnet-5-5 playwright
Sign-in off, or a policy file that is the only source, locks the UI host
grants. An unsigned file must not be staged or installed then: the owner
refuses file-begin with locked_by_deployment and the install dialog shows
the lock and disables the install.
Assisted-by: Claude:claude-sonnet-5-5 go eslint
Add an Install from file button to the Add-ons page. The file goes to the
owner, then the install dialog opens with the host access step, the file
sha256 and the notice that nothing signs a local file. An upgrade does
not turn the add-on on, an older version needs a tick, and the same
version is refused. The link that local-ai addons install prints opens
the dialog for the staged file. The list, the details and the host access
page show that the add-on came from an unsigned local file.
Assisted-by: Claude:claude-sonnet-5-5 eslint playwright
When the owner keeps UI grants locked because sign-in is off, the
overview and the preview say so. The Host access page and the install and
upgrade dialogs show the operations read-only with one sentence that
says why, and the dialogs approve no operation. The sentence is added to
every locale.
Assisted-by: Claude:claude-sonnet-5-5 node
A memory note is still a proposal that the person confirms by default.
An add-on can declare the user setting with the role memory.save-mode,
and the person then picks: ask first, save it and tell me, or never.
With auto, a note from the person's own Studio chat is applied at once
through the whole proposal path (grant, gate, checks, rate limit, audit)
and confirmed as the person, so it is one change set that revert_change
and the Undo on the chat restore. The chat shows "Saved to memory" with
Undo and no confirmation card. The record carries auto: true and the
audit says the write was automatic.
Safety rules live in the host. A connector chat always asks, a trigger or
job has no tool, a saved agent cannot widen the choice, and a failure to
read the setting asks. At most 5 notes are saved unasked in a turn; a note
over 4 KiB or one that looks like it holds a secret becomes a normal
proposal. With never the memory kind is hidden from propose_change and a
stray call is refused. Only memory reads the setting.
Assisted-by: Claude:claude-sonnet-5-5 go node playwright
An add-on can declare userSettings in its manifest: a key, the allowed
values and a default. The host keeps each person's answer in the account
database every frontend shares (one file when accounts are off), checks
every write against the declaration, and never asks the add-on.
GET and PUT /api/addons/{id}/user-settings read and write them. A write
needs the person's own browser session, so no API key, model or tool can
change one. A setting may carry a role that the host reads for its own
rules; the host fixes the values and the default of a role so a manifest
cannot loosen it. Pages reach their settings through sdk.userSettings,
which is bound to the add-on that calls it.
Assisted-by: Claude:claude-sonnet-5-5 go node
The message box kept the global 2px focus-visible outline: that rule
ties with .home-textarea on specificity and loads later, so the box
showed its own ring inside the card ring, a double border.
The message box now draws no outline or shadow on focus. The card keeps
the one indicator, a 1px inset accent edge plus a faint 2px ring (was
3px), with no transition under prefers-reduced-motion. The chips and
buttons keep their own keyboard rings. Home and chat share these rules.
Assisted-by: Claude:claude-sonnet-5-5 playwright eslint
A chat now enables a set of connections for an add-on whose tools run with
a credential, and the model names one of them in each call. This applies to
every add-on that declares a credential in its manifest.
- The Tools menu row of the add-on lists the connections with a checkbox
each. The saved agent names a set too (an empty set means each chat
chooses). A chat that a connector binding started keeps that binding
alone. With one connection, it is the set. With none enabled, the tools
are not offered.
- With one connection enabled the tools are unchanged. With two or more,
the host adds a required `connection` argument to every tool of the
add-on in the definitions it gives the model: an enum of the connection
names (the display name folded to lower case, digits and hyphens; the
description keeps the display name). The add-on never sees the argument.
- The name is checked against the set the chat has now, not against all the
connections of the person. A missing or unknown name returns a sentence
that lists the allowed names, and nothing is sent. A write tool never runs
without the name. Text in a tool result cannot widen the set, and the
owner checks the chosen binding against the set again.
- The resource list of the named connection is the only one that applies.
Results, the approval card and the text for a pending change name the
connection; the audit line has the binding ID.
- A connection name must be unique per person and add-on among the
credential-only connections, compared by name fold. Older duplicates get
a numeric suffix in the model's list.
- The stored chat, draft and agent values move from one binding ID per
add-on to a list of IDs. A single string is read as a set of one in the
stores and in every request, so older data and clients keep working.
PUT /api/chats/{id}/connections takes `connections`; the older
`connection` field still works.
Assisted-by: Claude:claude-sonnet-5-5 go gofmt ginkgo node eslint playwright swag
A turn can hold several model messages and tool runs. The cards of its
proposals (memory and profile changes, triggers) were drawn under the
message that followed the tool call, so a turn like "propose, say a
first line, read a file, explain" showed the card above the explanation.
While a turn streamed, a card read mid-turn sat above the streaming
text as well.
placeProposals now moves the cards of a turn to the end of it: onto the
last model message, or, while the turn streams or has no message yet,
into a row that the page draws after the streaming text. The tool lines
and results keep their place in the fold. Cards that were answered
already draw as compact lines, so they need no other rule.
Assisted-by: Claude:claude-sonnet-5-5 playwright eslint
A chat never uses a connection that the host picked for it. For an add-on
whose tools run with the token of a connection (such as GitHub):
- With one connection, it is selected. With several, the Tools menu row
of the add-on has a Connection selector, and until the person picks one
the tools are not offered and the row says so. With none, the row says
so and links to the Connectors page. The choice is stored with the chat
and its draft, like the other switches, and follows a fork, a
sub-agent and the runs of a trigger the chat proposed.
- A saved agent can name its connection per add-on (agent form field, API
field connections). Its chats use it, show it read only and cannot
change it. An agent with no choice follows the same rule as a chat. A
chat that a connector binding started keeps using that binding.
- CredentialFor takes the selected binding and uses only it, so the
resource list of that binding is the only one that applies. A call with
no selected binding is refused with the code select_connection and a
note the model can relay. The first-match code (connectors.Pick) is
removed.
- Tool results and proposal cards say which connection acted, for example
"as Work, repository acme/widgets". A proposal is applied with the
connection it was previewed with. The credential use is logged with the
binding ID.
Contract: the credential slot takes an optional resourceLabel (the word
for the resource in that line). The agent export file does not carry the
connections, since a binding ID belongs to one server.
Assisted-by: Claude:claude-sonnet-5-5 go gofmt ginkgo node playwright protoc swag
Show the host operations an add-on asks for in the install dialog, hold
an upgrade that asks for more until an administrator approves it, and add
a Host access tab with the grants of each add-on, the deny rules and the
blanket switch. All strings are in every locale.
Assisted-by: Claude:claude-sonnet-5-5 playwright eslint
Shape the answers of the host-grants routes for display, build the
approval that an administrator sends with an install or an upgrade, and
let the install flow carry it. A typed refusal keeps its type and its
details so the pages can say what happened.
Assisted-by: Claude:claude-sonnet-5-5 playwright eslint
Token help now works: a button opens GitHub's fine-grained token page,
the steps are open while there is no connection, and a table lists the
permissions for read only and for write modes. The page says it does not
answer mentions yet.
Errors sit under their own field. The repository list splits on lines and
commas only, so "not a repo" is one entry and is named in the message.
The connection name defaults to the first repository.
The write mode cards each carry one consequence line, and "Allowed" is
now "Acts without asking". The Test result is drawn per repository in the
row, with a tone, a busy line, and a plain text when GitHub refuses the
token. The unit tests and a browser spec cover the page.
Assisted-by: Claude:claude-sonnet-5-5 node playwright eslint
The Tools list of the agent form gained six grant rows, so the count is
14 and the Triggers switch needs an exact name. Two folder specs also
went to the edit page, which has no facts panel, instead of the agent
page.
Assisted-by: Claude:claude-sonnet-5-5 playwright
A binding needs a running add-on that serves its kind, so the real
server of the UI tests, which runs none, answers 400 instead of
storing an inactive binding. The spec now checks that answer and that
nothing leaks. Switching and listing stay covered by the add-on specs.
Assisted-by: Claude:claude-sonnet-5-5 playwright
The server renamed the group title, but the English locale kept "Web"
and the Tools menu reads the locale first. The menu, the home composer
and the agent form showed a name that the specs and the server did not.
Assisted-by: Claude:claude-sonnet-5-5 playwright
The Indonesian chat namespace was completed, so the ratchet of the
completeness gate failed the UI unit tests until the baseline went down.
Assisted-by: Claude:claude-sonnet-5-5 node
The trigger form has a Permissions section that lists the act.*
grants like the grants of an agent: a plain-words line on each,
sensitive ones marked, and a grant the chosen agent does not hold
cannot be ticked. It says in a callout when a run only reads and why:
no acts, the agent holds none of them, the proposal said read only,
or a read only connection made it. The details page shows the same,
the trigger list marks a read only trigger, and the proposal card has
a Permissions row, so the person sees the acts before pressing Create.
The editor always sends the acts it loaded, so saving a form never
drops them silently. New strings are in every locale.
Assisted-by: Claude:claude-sonnet-5-5 node playwright