Commit Graph
198 Commits
Author SHA1 Message Date
Ettore Di Giacinto 3ca176d15d feat(addons): lock UI host grants while sign-in is off
With authentication off every caller that reaches the port is the same
local owner, and the checks that tell a person from a script are
headers the caller chooses. Any process or agent that could reach the
port could grant host operations, set deny rules and turn the blanket
approval on.

UI host grants are now locked by default in that mode. The policy file
and flags still grant, and every write route answers 403
locked_by_deployment with a sentence that says why. The overview and the
preview carry the lock and its reason for the page. The new flag
--addon-host-ui-grants-without-auth turns the UI grants on knowingly and
logs a startup warning. With sign-in on nothing changes.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt
2026-11-03 22:52:09 +00:00
Ettore Di Giacinto fd634c5f5c fix(addons): fail closed on audit errors at install, audit refused callers and restarts
Staging the grants of an install now goes through Store.UpdateAudited, so
an install fails and the document stays as it was when the audit line
cannot be written.

A write from an API key, an MCP session or a tool call was refused in the
HTTP layer before it reached the owner, so the refusal left no audit line.
The route now hands the refused write to the owner without the human
mark; the owner refuses it and records the credential kind, never the
credential.

Manager.HostGrantsChangedReport names the add-ons the re-sync started or
stopped, and the owner writes a restart-for-grant line for each. The
event constant was never used before.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt
2026-11-03 22:52:09 +00:00
Ettore Di Giacinto 65f8116b1f fix(addons): keep a deployment blanket mode out of the grants document
Stage copied the blanket mode that a flag fixes into host-grants.json
at the first install. After the flag was removed the document still
said "any", so the switch stayed on without anyone typing the
confirmation text.

The deployment mode is now applied at read time only. The document keeps
what a person set, and a grant made under the flag records revision 0,
which the validator accepts for a blanket grant of the deployment.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt
2026-11-03 22:52:09 +00:00
Ettore Di Giacinto 23beb6dc80 feat(chats): let a person choose how memory notes are saved
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
2026-11-03 22:46:22 +00:00
Ettore Di Giacinto 7ce4507da1 feat(addons): keep per-person settings that an add-on declares
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
2026-11-03 22:46:22 +00:00
Ettore Di Giacinto ad39954af1 feat(addons): let a chat enable several connections of a credentialed add-on
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
2026-11-03 22:33:28 +00:00
Ettore Di Giacinto e28dd8a2a0 feat(addons): connect the host grants store, the Authority and the routes
Open the grants document and its audit file under the owner state
directory, and make the Authority read them on every call, with the
deployment floor, the deny rules and the blanket switch from the flags.
An unreadable document refuses every operation. Adapt the store to the
manager's GrantStore and the manager to the host access service of the
routes, and wire that service on the owner only. A frontend keeps the
floor and stores nothing.

Add an end-to-end test of install with approval, revoke, deny, a held
upgrade, restart and the split deployment.

Assisted-by: Claude:claude-sonnet-5-5 go
2026-11-03 22:06:43 +00:00
Ettore Di Giacinto c56c1ac343 feat(addons): choose the connection of a credentialed add-on explicitly
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
2026-11-03 22:02:30 +00:00
Ettore Di Giacinto 055c456d9e fix(studio): use the backend's default window when context_size is unset
A chat on a model whose config has no context_size got a window of zero,
so the harness fell back to its own default for compaction and for the
context meter, while the backend actually runs with its default window.
The meter and the compaction trigger then disagreed with the model.

Report the window that a request can use, the same value that
/v1/models/capabilities gives (backend.EffectiveRequestContextSize). A
model with no local config, such as a remote one, still gives zero.

Assisted-by: Claude:claude-sonnet-5-5 go-test
2026-11-03 21:50:57 +00:00
Ettore Di Giacinto 8aa81f1401 feat(addons): approve host operations at install and upgrade
An administrator can now grant host operations when an add-on is
installed or upgraded. The manager checks the approval against the
package it actually stages: the digest must match, and only operations
the verified manifest declares and the host registers are granted, never
a denied one. A required operation that is neither held nor approved
refuses the install with approval_required, or blocked_by_policy when a
deny rule forbids it. An upgrade that needs more required operations is
held and the old version keeps running. Optional operations are never
granted without approval, and a manifest that asks for fewer operations
shrinks nothing.

Grants are written to a GrantStore before the journal commits the
operation and removed if it fails, and they count only once the
operation completed. Uninstall deletes them after the journal. A start
reconciles the store with the journal. The automatic upgrade never
approves; only the blanket hook of the store can. HostGrantsChanged
stops an add-on that lost a required operation and retries one that was
refused.

Assisted-by: Claude:claude-sonnet-5-5 go
2026-11-03 21:50:00 +00:00
Ettore Di Giacinto bb23939477 test(e2e): give the test connector transport to the first application only
An application a spec starts on its own, such as the isolated ones of
the router specs, took the shared test transport over and pointed its
file transfers at a table with no blob store. Which specs ran before
decided if the file specs of Telegram and Slack passed, and they failed
in CI with "addon owner unavailable". The transport now keeps the table
of the first application.

Assisted-by: Claude:claude-sonnet-5-5 go ginkgo
2026-11-03 21:42:50 +00:00
Ettore Di Giacinto c605b26fc7 feat(addons): ask the Authority on every host operation call
The broker copied the allowed prefixes when an add-on started, so a
change of grants needed a restart of the add-on. Take an Authority
instead and ask it on each call: a revoke or a deny applies to the next
call, and a call that already runs finishes. A refused call answers the
same text whatever the reason; the reason goes to the log.

The supervisor opens the host endpoint when the add-on holds an
operation or its manifest declares one, so an add-on with an optional
operation and no grant starts degraded and a later grant works at once.
The broker now exists whenever the add-on manager does. The manager,
the apps list and the tool filters read the same Authority, which is the
startup policy until another one is set. Admission also tells a denied
operation from a missing grant.

Assisted-by: Claude:claude-sonnet-5-5 go
2026-11-03 21:33:36 +00:00
Ettore Di Giacinto 3607c08c17 feat(connectors): grant the usual permissions once to old bindings
Bindings saved before permission enforcement have an empty capability
list, so file upload, triggers, memory and talk-back stay denied until
the owner sets them. Give those bindings the set that "Allow the usual"
grants, one time.

An empty list is stored the same whether the owner never chose or
cleared every switch, so the store keeps a migration record instead of
a flag on each binding. The record is claimed atomically with a lease,
so several frontends starting together run it once, and a finished
record is never claimed again: later empty lists are the owner's choice.
Each binding is written only while it still has no capabilities, so a
save made at the same moment is not overwritten.

Only bindings whose kind is running and lists permissions are changed;
sensitive capabilities are never granted. One log line per binding and
one summary. LOCALAI_SKIP_BINDING_CAPS_MIGRATION skips it.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt protoc
2026-11-03 21:17:39 +00:00
Ettore Di Giacinto 23b2451983 test(e2e): bind connectors with a kind, caps and file transfers
The connector specs broke when bindings began to need a kind, to hold
no capability until the owner enables it, and to move file bytes by
handle only. The Telegram specs now name the kind. The Slack file spec
enables files.upload and pairs the sender, since a sender without a
role needs a pairing to send files. A test transport that can move
files gets the transfer table of the application, so its add-ons
declare the files ability as installed ones do.

Assisted-by: Claude:claude-sonnet-5-5 go ginkgo
2026-11-03 19:16:22 +00:00
Ettore Di Giacinto f60e464fb3 test(studio): create connector bindings with a kind
A binding needs a kind now, so the route specs name the platform, and
the spec for an offline binding checks that nothing is kept while no
add-on serves the kind.

Assisted-by: Claude:claude-sonnet-5-5 go test
2026-11-03 17:05:24 +00:00
Ettore Di Giacinto c5f636d1b9 refactor(addons): move connector file bytes by handle only
A connector add-on no longer carries file bytes in the connector
envelope. The ops media and send_file need a transfer handle, and the
gateway refuses a call to them without one, or for a kind that declares
no files.

- The manifest field files.transfer, the inline file of a request and
  of an answer (ConnectorFile) and the 1 MiB cap
  (MaxConnectorFileBytes) are removed. The 2 MiB limit of a connector
  call between hosts drops to 1 MiB, because it holds text and no file.
- A kind that declares no files moves no file: the host answers a sender
  who attaches one with a plain sentence and does not offer attach_file.
  A direction left out of a declared files block still gets the host
  ceiling.
- The inline layer is gone from the file limit, from the API
  (inline_bytes) and from the Files section of the connector page.
- The Telegram and Slack add-ons keep only the streaming paths. The
  tests moved to the handle path, and the studio test rig gives every
  connector add-on a table of handles.

Assisted-by: Claude:claude-sonnet-5-5 go test node
2026-11-03 16:57:04 +00:00
Ettore Di Giacinto 2a491cb2cf refactor(addons): remove the shims for older add-on data
No release carried add-ons, so nothing reads older add-on data or
manifests. Delete the code that did.

- Connectors: the kind and the add-on of the Telegram connectors that
  core once carried are gone (LegacyAddon, KindTelegram, the stored
  binding normalization, the platform titles). A binding needs a kind,
  and its add-on is the one that serves the kind. The conversion of the
  old agent pool lists every connector as dropped instead of making a
  Telegram binding, and its notes say so.
- The library index reads only the id field of an entry, not addonId.
- A host control that has a capability block (the network policy) is
  attached by that block alone, not by a tool that replaces bash.
- The Office create tools no longer answer for the argument names of an
  earlier schema, and the identity file note about old addresses is
  gone.
- The known debt of the provider-name guard drops the entries of the
  conversion and of the resource listing shim, and its ceiling goes
  from 13 to 5.

Assisted-by: Claude:claude-sonnet-5-5 go test
2026-11-03 16:56:49 +00:00
Ettore Di Giacinto 1834d4fd72 refactor(addons): make manifest api 2 the only version
No release ever shipped add-ons, so there is no installed base on api 1.
The manifest must now declare api {"min": 2, "max": 2}; a manifest with
api 1, no api or a wider range is refused with a message that names the
range.

Every field that was gated on api 2 is now part of the contract, so the
Min*API constants and their gates are gone, and so are HostAPIMin and
the apiMin host field. Three older-manifest shims go with them: the
resource listing the host implied for one add-on, the legacyArgs hint
for arguments an older version of a tool took, and the tests that
checked the gates. All shipped manifests and the test fixtures declare
api 2.

Assisted-by: Claude:claude-sonnet-5-5 go test
2026-11-03 16:15:07 +00:00
Ettore Di Giacinto 4ba1b7b2bd feat(addons): move connector file bytes by handle
A connector add-on that declares files.transfer no longer carries file
bytes in the connector envelope. The host puts a random handle in the
request of the op media (write) or send_file (read), and the add-on
streams the file with two new host operations, connector.file.put and
connector.file.get, in chunks of at most 256 KiB.

The process that runs the add-on registers the handle when the call
arrives and ends it when the call returns. A handle is bound to the
add-on, so another add-on gets the same answer as for an unknown
handle. An unfinished inbound file is dropped with its partial bytes,
and a handle with no call for two minutes expires. Inbound bytes go
to the blob store of the workspace, outbound bytes come from it, so a
cluster needs the shared object store and no shared disk. The file is
never held in memory. The host overwrites the size and hash of an
inbound file with its own record.

The change is additive: the field files.transfer needs manifest API 2,
a kind without it keeps the inline path and its 1 MiB cap, and the host
never sends it a handle. A call that carries a handle may run up to 15
minutes. The ceiling of a file also stays under the largest file of a
workspace.

Assisted-by: Claude:claude-sonnet-5-5 go test
2026-11-03 15:54:45 +00:00
Ettore Di Giacinto 2b588a7350 fix(connectors): hold add-on write tools to tools.write in connector chats
A write tool of an add-on that holds a credential was protected only by
the write mode of the connection and the confirmation of the proposal.
In a connector chat it now also needs the capability tools.write for the
sender of the turn, through the same Authorize path as the other
capabilities: the tool is not offered without it, a call is checked again
and every decision is audited. The default is no.

The rule is by shape, not by name. The manifest says an add-on holds a
credential, and contract.ToolPermission maps its non-read tools to
tools.write. The chat reads the permission from the tool definition, so
the core names no add-on.

Also close a hole in local-owner mode, where a chat turn and the browser
share one credential: a proposal that waits for a person could be
confirmed from a chat turn. The call of a chat tool now carries a mark,
and the owner refuses to confirm such a proposal under it. A remote owner
does not see the mark; local-owner mode has no remote owner.

Assisted-by: Claude:claude-sonnet-5-5 go test
2026-11-03 15:41:45 +00:00
Ettore Di Giacinto e790271c63 feat(addons): let the GitHub add-on write, under a per-connection write mode
A connection now has a write mode, chosen on the add-on page: off (the
default), propose (each change waits for the person to confirm) or auto
(a chat of the person makes it at once). The host enforces it for every
write tool of an add-on that holds a credential, before the add-on is
called, and holds the call to the repository list of the connection.

Host:
- Binding.WriteMode, stored in both stores and in the connector API
  (write_mode). Absent means off, so existing bindings stay read only.
- A credentialed add-on may declare write tools. They name the resource
  with the credential slot argument, never resourceId. A tool can set
  approval "always", a ceiling that keeps a proposal even in auto.
- The proposal broker fetches the credential for the preview and again
  for the apply, so it is never stored. A mode lowered between the
  proposal and the confirmation refuses the apply. A proposal that
  awaits a person cannot be confirmed by a chat turn.
- New capability name tools.write for the roles a connector kind
  declares.

Add-on, tools (kind write): comment-issue, comment-pr, create-issue,
edit-issue (title, close or reopen), add-labels, review-comment-pr.
They preview plain facts, refuse a stale target, cap and screen the
text for secrets, and keep a receipt journal plus a hidden marker in the
text, so a change whose answer was lost is not sent twice.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt ginkgo node
2026-11-03 14:53:20 +00:00
Ettore Di Giacinto 337a034601 feat(addons): declare document slots and legacy arguments in the manifest
The four document skill names were code in the contract, the hint for
binary files kept its own list of formats and libraries, and the chat
boundary rewrote the old arguments of one add-on by its ID.

A skill that shadows a name can now declare the slot it serves: kind,
file extensions, name words and a library hint. A tool can declare
legacyArgs, the arguments it refuses now, each with a hint. Both need
manifest api 2. The four default slots move to a data file that the
contract reads, so a manifest that declares nothing keeps working.

The host takes the replaceable names, the files a skill covers and the
hint for a script from the skills the add-ons list. It compares no
format or add-on ID. A guard spec fails when it does again. The Office
manifest declares its slots and legacy arguments and accepts api 2.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt
2026-11-03 14:45:43 +00:00
Ettore Di Giacinto b8626132ae feat(connectors): let the agent write to listed channels with send_message
A connector chat whose sender holds talkback.channels gets the tool
send_message. It writes in the conversation, or as a new top-level
message in a channel that the owner listed on the binding (id, label,
approved; at most 10). The tool names no platform.

An unlisted channel is refused and the error lists the ones that are.
A listed channel that the owner did not approve needs the sensitive
talkback.new_channel for each message, so only a paired sender can use
it. The agent cannot add or approve a channel, so text it read in a page
or a file cannot widen where it writes.

Messages go through the outbox (rate limit and queue), with the size cap
and the mention breaking. Each attempt adds an audit row with channel,
kind, capability, length and a hash, never the text.

A trigger can report to an approved channel with delivery target
channel. The binding, the capability and the listing are checked again
when the run ends; any failure sends the notice to the Inbox.
LOCALAI_DISABLE_CONNECTOR_TALKBACK turns both off.

Assisted-by: Claude:claude-sonnet-5-5 go swag
2026-11-03 14:42:21 +00:00
Ettore Di Giacinto b15905e90f fix: restore the files that the previous commit removed by mistake
The previous commit was made on a stale base and so reverted work that
landed on master before it. This commit puts every file back to the
state of its parent's parent. The permission enforcement of the
previous commit stays.

Assisted-by: Claude:claude-sonnet-5-5 go
2026-11-03 14:33:05 +00:00
Ettore Di Giacinto 9e6d6520bd feat(connectors): enforce add-on permissions on each connector turn
The capabilities, role grants and pairing that a connector binding
stores now decide what a sender can make the agent do.

Authorize is one pure function: the capability must be in the owner's
ceiling for the binding and the sender must be on the allowlist. A
sensitive capability then needs a paired sender; roles never count for
it. Any other capability needs a pairing or a role that the owner
granted and that the kind lists. The default is no.

Each message gets a grant for its sender. A message that joins a
running turn narrows it to what both senders may do. The tool builder
offers a gated tool only for a held capability, and the tool checks
again when the model calls it, so a call that gets through is refused
with the reason. Today this covers propose_change (memory and profile
kinds), revert_change, the trigger tools, and files sent to the bot. A
chat worker gets the grant with the start request.

The capability gate that the trigger features ask is now answered by
Authorize, and the application sets it. Other features can add a
global switch with AddCapabilitySwitch.

Every use records an allow or deny row (time, binding, sender IDs,
roles, capability, result; no text or codes). GET
/api/chats/connectors/{id}/access returns the effective permissions
per kind of sender and the latest rows, for an add-on settings page.

A binding saved before this change has no capabilities, so its senders
lose these features until the owner enables them.

Assisted-by: Claude:claude-sonnet-5-5 go swag
2026-11-03 14:32:01 +00:00
Ettore Di Giacinto 15740acbb9 feat(studio): describe every built-in tool switch and let a saved agent set them
The Tools menu of a chat listed a ninth built-in group, "changes", by its
raw ID with no text, and only some rows said what a switch did. The
registry now carries a title, a description of what the model can do when
the group is on, and what happens when it is off, for every group. Groups
that nobody described fall back to the label and description of their
first tool, so a new tool group needs no change in the interface.

An add-on row can show the title and description its manifest declares:
the broker may implement the optional AddonLister, and the lists carry
both fields next to the ID.

A saved agent gets builtins_off and builtins_on. builtins_off is a
ceiling: a chat of the agent shows the group off with a reason and cannot
switch it on, and a sub-agent follows it. builtins_on lists the opt-in
groups the agent's chats start with. Both are additive columns, a write
that does not name them keeps the stored ones, and groups the server does
not offer are dropped so an imported file still loads. An agent with
switches exports as version 3, so an older server refuses the file
instead of giving the agent every tool; a single-folder agent without
switches still exports as version 2.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt swag
2026-11-03 14:30:07 +00:00
Ettore Di Giacinto e0da582988 feat(addons): credential slot, resource allowlist and rate-limit error
Add a manifest credentials declaration, host-enforced per-binding
resource lists, Request.Credential and ResourceKey set by the host, and
a typed ErrRateLimited that reaches the model as rate_limited.

Assisted-by: Claude:claude-sonnet-5-5 [go]
2026-11-03 14:20:12 +00:00
Ettore Di Giacinto cfcef8346c feat(studio): load the context files that add-ons declare
A turn now asks the add-on broker which running add-ons declare context
files, and loads their index files with the loader that already reads
registered sources. The same rules as for add-on skills apply: a stopped
add-on, one the chat switched off, one the chat's agent does not grant,
and one that cannot work for the user bring nothing. memory_read serves
the topics of those sources. The Memory page lists the files of any
installed add-on, running or not, because the files belong to the person.

The channel notes folder of a chat is now the one propose_change writes
for the same conversation: the binding and a hash of the chat. A note the
person confirmed is read back by the conversation that proposed it. Before,
the loader built the folder from the binding alone, so the two never met.

The prompt names the save tool only in a chat that has it, so a chat that
switched propose_change off is not told to call it.

Assisted-by: Claude:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 13:57:28 +00:00
Ettore Di Giacinto 13e96219ba fix(studio): read an alias's model settings from its target
The chat harness took the context window, agent options and input
modalities from the config of the name a chat asked for. An alias has
no model of its own and the loader gives it the generic default
window, so a chat on an alias budgeted against 4096 tokens and failed
on tool schemas even when the target ran with a far larger window.
Resolve the alias to its target first, as the capabilities endpoint
already does.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 12:07:30 +00:00
Ettore Di Giacinto 3f5b23e3e7 feat(addons): add a host ceiling for connector files to the owner policy
The key connector_files_ceiling_bytes sets the largest file a connector
moves. Zero means the default of 100 MB. The key is always written, and
a policy document stored before it reads as the default. The connector
manager reads the ceiling from the owner policy at each resolution.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 11:47:50 +00:00
Ettore Di Giacinto fc6abfca8c refactor(studio): name the network from the add-on's declaration
The chat's network view, the administrator view and the line the model
reads about the network of its commands now take their name from the
capabilities.network an add-on declares, or from the title of its
commands tool when it declares none. The sentences no longer say
"Sandbox" or "the sandbox". The API gains a network object with the
title and the preset titles next to the existing addon field.

The text of the built-in bash and of the host-exec setting names an
add-on that provides bash instead of one add-on.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 10:58:46 +00:00
Ettore Di Giacinto 61c54dc833 refactor(studio): word the command provider text without naming an add-on
The prompt, the Tools menu reasons, the network error texts, the CLI help,
the API instructions and the assistant tool descriptions said "Sandbox" for
the add-on that runs commands. Core must not know that name.

The reason a chat has no commands now uses the title the provider declares
("the Zeta add-on is off for this chat") or, when none is installed, says
"the add-on that runs commands". Text that has no provider in hand speaks of
the network of commands or of an add-on that isolates them.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 10:43:37 +00:00
Ettore Di Giacinto 0cd265e86e merge: bring master into the proposal UI branch
Union of both case lists in the add-on suite inventory; the swagger
files are regenerated from the merged code.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 10:21:41 +00:00
Ettore Di Giacinto 0c72b2c96f refactor(studio): decide command eligibility from the execution status
Document skills, the Commands block of the prompt and load_skill no
longer compare an add-on ID or provider with "sandbox". The host asks
whether the add-on that ships a skill serves the chat's commands and
whether the typed execution status reports them isolated. Skills that
need that are listed by the broker from the add-on's manifest, and the
text names the provider by the title its manifest declares.

An add-on whose commands tool declares no descriptor behaves as a
provider of commands under its own title and keeps the isolation check
for its document skills.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 10:07:46 +00:00
Ettore Di Giacinto 0c08e17a30 feat(addons): add an execution descriptor to the tool contract
A tool that shadows bash can now declare an execution descriptor: its
class, a title and a note for the model, and the skill kinds whose
runtime it supplies. The host reads it to build its text and to decide
which skills need isolated execution, instead of knowing add-on IDs.

The field is optional and needs manifest API 2. The host now speaks
API 2 and still accepts add-ons that declare only API 1, so manifests
that declare nothing new keep working unchanged.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 10:07:46 +00:00
Ettore Di Giacinto b4734eef68 merge: bring master into the proposal UI branch
The Memory page reads the context-file sources that master added.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 09:42:29 +00:00
Ettore Di Giacinto 14d7fc631c feat(studio): wire the proposal store and document change proposals
Open the proposal store beside the chats (auth database, file or memory)
and document the mechanism for add-on authors: the wire record, the
limits, the gate, the audit, the REST routes and how a cluster shares
it. Update the registry and actor-scan specs for the new tools.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 09:37:07 +00:00
Ettore Di Giacinto 52c59004ac test(chats): clarify configured timeout success
Describe the 1.2-second case as success within its configured timeout,
not as exceeding the actual 60-second connector default.
Remove trailing whitespace from the P2P command example.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 09:21:06 +00:00
Ettore Di Giacinto e49f456ea3 fix(p2p): bind resource limits at CLI boundaries
Pass the resource-limit choice through application and node configuration.
Keep only the exact string true enabled, including worker and explorer
entry points. Leave other legacy environment reads unchanged.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

Assisted-by: Claude Code:claude-sonnet-5-5
2026-11-03 09:21:06 +00:00
Ettore Di Giacinto d01e3cc63b test(chats): avoid a misleading tool variable
Name the MCP tool explicitly so the Ginkgo-only lint rule does not
mistake its Run method for testing.T.Run.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 09:21:06 +00:00
Stefan Walcz 650b2c7acd fix(chats): give MCP connector tool calls the MCP tool timeout
A tool call through an MCP connector was cut after the connector
package default of 60 seconds, while the model's own MCP servers allow
360 seconds. Research and document tools behind a connector take
minutes, so a saved agent lost them mid-turn with "connection closed:
calling tools/call: client is closing" and the model fell back to a
weaker tool.

The connector service now gets the MCP tool timeout the rest of
LocalAI uses (DistributedConfig.MCPToolTimeoutOrDefault, 360 s by
default). The package default and the per-call output cap are
unchanged.

Tests in core/application start the connector service on JSON stores
against a slow MCP server: the timeout defaults to the MCP tool
timeout, a tool slower than the old cap but within the timeout
returns, and a tool past the timeout is still stopped.

Docs: chats-connectors.md, "How chats use them".

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de>
(cherry picked from commit 99f663a777)
2026-11-03 09:17:42 +00:00
Ettore Di Giacinto 9fafea8fdf fix(chats): show the network switcher and say why execution status is unknown
The Network row of the Tools menu could never show the Off, Registries and
All switcher: the host stopped setting `applies` because isolation alone does
not prove network-policy enforcement, and nothing else set it.

Let the provider report enforcement in its typed execution status
(`networkPolicyEnforced`). The host sets `applies` only when that report is
true and the status is available and isolated. The Sandbox reports it for the
gVisor and KVM backends, whose guests reach the network only through the proxy
that asks the host. Providers that do not report it keep the "not enforced"
text.

When the host cannot read the status, give the unknown state a reason
(`unknown_reason`) in the API and the Tools menu: owner unreachable, not
reported, stale, invalid, not selected, no answer or reported unknown. Log the
class of each failure, rate limited. After a failure that a restart of the
add-on owner can cause, let a long turn bind and read again, at most once every
five seconds, instead of keeping the first failure for the whole turn.

The Sandbox now reports "unavailable" with a reason when it started without
settings, and explains a backend it does not know.

When the Sandbox document skills are hidden because the Sandbox is not known to
be isolated, say so in the skill list, in the turn note and in the
`load_skill` error, instead of "This chat has no skills now".

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:39:30 +00:00
Ettore Di Giacinto fd808e4be0 fix(studio): reap only stale turns when the database is shared
Without a distributed setup, the chat reaper ended every claimed turn in
the database at startup. When a second process shares that database,
this stopped the live turns of the other process: the last outcome
became interrupted, queued messages were re-queued and goals paused.

Reap only the turns that stopped beating for the turn timeout, at
startup and on a timer. A process that died leaves a claim that goes
stale, and the next tick frees it.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:53 +00:00
Ettore Di Giacinto c9643cc429 refactor(application): tidy workspace file setup
initStudioFiles had a named result it never used, and the setup
repeated the same strict-or-disable branch five times. Fold them into
one local helper.

workspaceStartupCleanup changed options.Context as a side effect and
the cancel func of its derived context was only reachable through the
cleanup. Return the context and the cleanup, and let the caller assign
the context.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:35 +00:00
Ettore Di Giacinto 8d386cfe0e feat(studio): check the shared blob volume at startup
Without S3, nodes that use PostgreSQL metadata and the filesystem blob
store must mount the same blob folder. Nothing checked this, so a
volume that was not shared made files written on one frontend look
missing on another while the index said they existed.

The first node to start writes a random id in the blob root and records
it in the database. Every node compares the two at startup and fails
with a clear error on a mismatch or a missing id file.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:35 +00:00
Ettore Di Giacinto 62eefedf32 refactor(studio): one blob sweep for every workspace store
The PostgreSQL store carried its own copy of the delayed blob removal.
Its timer was never stopped, it ran under context.Background(), and the
30 second bound was a bare number.

Replace blob.RemoveLater with blob.Sweep. The reference check may now
return a release func, so the PostgreSQL check can keep the revision
lock until the last removal. A Life context ends pending removals, and
SweepTimeout names the bound. Both stores cancel Life on Close, and the
application now closes the PostgreSQL store with the JSON one.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:35 +00:00
Ettore Di Giacinto 12a7b9ca19 fix(addons): stop the add-on owner from running chat background work
A process started with --addons-role=owner polled the connector bindings,
ran triggers, loops and agent jobs. It sets no distributed settings, so it
had no leader lock and did the same work as the leading frontend. Every
inbound chat message ran twice, and the run on the owner failed because
the owner has no models.

The owner now builds the connector manager but does not start its polling
loop, and it starts no trigger, loop or agent job host. The manager stays
because frontends get the stored bot credentials from it. A pushed
connector.inbound message would start a chat turn on the owner, and
nothing forwards it to a frontend, so the owner refuses it. Push connector
add-ons need a combined or distributed process.

The combined and frontend roles are unchanged.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:11 +00:00
mudler-agentandEttore Di Giacinto 7904b3d0ab feat(addons): declare typed host capabilities (#13034)
* feat(addons): model execution status observations

Keep observation lifetimes explicit before runtime integration. The pure
reducer rejects retries and stale replies using exact binding identity.

Cover all eleven trace families, the complete transition matrix and
bounded histories with safety and liveness checks.

Assisted-by: nib:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): visualize execution status traces

Generate a self-contained viewer from the real reducer and shared traces.
Check independent expectations, coverage and bounded invariants before
exporting data. Detect stale artifacts and nondeterministic generation
without maintaining a second reducer in JavaScript.

Assisted-by: nib:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): honor the viewer artifact root

Keep default viewer output under SDD_LOCAL_ROOT when configured.\nLet go generate use the same default without forcing a source artifact.\nRetain the portable fallback and explicit output and check semantics.

Assisted-by: nib:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): add typed extension contracts

Validate optional home commands, execution bindings and requested host
operations without changing manifest versions or granting authority.
Decode bounded execution status payloads conservatively and document
strict staging compatibility for older hosts.

Assisted-by: nib:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): reject noncanonical extension keys

Reject case aliases before typed decoding can override canonical fields
or bypass presence checks. Keep locale maps outside exact-key checks.

Add regression coverage for status, declaration objects, and nested
entries. All status errors return unknown readiness and isolation.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): reject root enums for status tools

Status tools must accept the empty object. The supported schema enums
contain only strings, so a root enum excludes every object.

Reject root enums only for execution status bindings. Cover incompatible
root constraints and optional properties without changing shared validation.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): project typed home commands

Resolve declared home destinations and display metadata in the installed
Apps view. Pin packaged command icons to immutable asset URLs.

Cover strict declaration transport, immutable receipts, exact health echo,
and process restart. Keep legacy packages and disabled installed intent.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): preserve typed lifecycle declarations

Copy execution and host-operation declarations from installed receipts.
The existing capability completeness test exposes the missing fields.

Exercise real staged Manager upgrades, activation rollback and recovery.
Check installed Apps metadata through pinned mTLS frontends for each actor.
Verify mutation isolation and name receipt replay coverage accurately.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): stabilize fixture archive digests

Map iteration changes tar entry order between identical publishes.
Sort fixture filenames so unchanged packages retain their digest.

Check repeated publish bytes, digests and lexical archive entry order
with and without a manifest transform. Register the regression case.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): resolve explicit host policy grants

Load strict administrator policy files once before application startup.
Honor explicit empty overrides without reading ignored policy files.
Copy programmatic and manager grants to isolate startup authority.

Keep the legacy CLI fallback until the official deployment policy ships.
The generic resolver already defaults to empty grants.

Verify config, CLI, hostops and execution-status checks with GOMAXPROCS=2,
-p=2 and -ldflags="-s -w" to fit the available build disk.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): select explicit deployment grants

Move official host grants out of the generic executable into reviewed
host deployment policy. Stock Docker explicitly selects the shipped file;
unconfigured binaries and nil or empty programmatic grants grant nothing.

Document the standalone upgrade break and deliberate operator selection.
Do not infer consent from installed addons or merge explicit overrides.
Guard policy packaging and generic configuration boundaries with tests.

Verify executionstatus, viewer -check, config, CLI, packaging and root
architecture tests with auth, GOMAXPROCS=2 and bounded build parallelism.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): gate activation on required host ops

Refuse unmet required requests before package publication or runtime
withdrawal. Keep installed intent unavailable when startup grants change.
Optional requests permit degraded operation and never create authority.

Carry bounded admin diagnostics through Apps and the owner carrier.
Keep startup policy immutable and broker authorization authoritative.

Verify executionstatus, viewer -check, lifecycle, packages, updates,
hostops, Apps and gateway offline with auth and bounded parallelism.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): guard host operation registry parity

Compare startup admission names against real builtin registrations so
registry drift cannot silently change request satisfaction.

Cover dynamic progress and stop operations and connector inbound.
Reject missing, extra, duplicate and case-changed names with negative
controls. Record both regression cases in the suite inventory.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): fence status observation effects

Keep observation ownership in admitted runtime operations. Deny all
correlated callbacks before dispatch, including inbound-class callbacks.
Reject status outputs before callers can open or adopt artifact handles.

Retain bounded denial correlations until the runtime endpoint closes so
late callbacks cannot recover ordinary authority. Preserve independent
ordinary operations and document the dedicated read API for T9.

Verify the status model, viewer, and race-enabled gateway, hostops and
runtime suites with the pinned offline environment.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): drain callbacks before retirement

Server.Close does not wait for active callback requests. Clearing retired
observation correlations first can restore inbound authority.

Stop endpoint admission before closing connections. Drain admitted requests
before releasing correlations, and refuse dispatch after Stop. Keep locks
out of handlers and cleanup waits so cancellation cannot deadlock teardown.

Cover pending inbound callbacks, decoded authorization paused across Stop,
and handlers that ignore cancellation. Preserve ordinary authorized work
on other endpoints. Document delayed reclamation under the existing bound.

Verify the status model and viewer, then race-test gateway, hostops,
runtime, the model and root architecture checks with auth enabled.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): bind owner status to exact clients

Add authenticated, versioned status reads over the owner carrier.
Revalidate actor permissions and provider selection before dispatch and
before returning an observation. Keep status separate from authority.

Admit the captured client against its immutable identity under the
withdrawal lock. Never dispatch an old binding through a replacement.
Generate runtime operation IDs on the owner and retain the T8 effect fence.

Cover forged identities, strict DTOs, stale replies, restart ordering,
wire discriminators, and unchanged model exclusion of uiOnly tools.
Document the T10 callback and request-shell integration requirements.

Verify the model and viewer, then race-test gateway, runtime, tools,
and the model with auth enabled.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): compose owner status with runtime

Exercise installed addon processes through the authenticated mTLS carrier.
Keep runtime clients real while channel barriers control lookup ordering.

Reject captured clients after same-generation replacement before dispatch.
Reject completed status reads when replacement precedes owner consumption.
Prove fresh bindings succeed and record named tools and host operation IDs.

Verify the model and viewer, then race-test gateway, runtime, tools,
the model, and the protocol fixture package with auth enabled.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(studio): scope execution status observations

Run the execution status reducer within one explicit request lifetime.
Coalesce consumers without retries and withdraw stale or canceled reads.

Keep owner admission and production lifecycle integration separate for
review before wiring the shell into requests and turns.

Assisted-by: OpenAI:ChatGPT
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(studio): isolate consumer cancellation

Consumer cancellation during validation must not withdraw a valid
observation from other consumers. Return unknown locally before shared
invalidation, while preserving parent closure and freshness failures.

Cover cancellation and deterministic deadline expiry with one-read
reducer traces and live-consumer checks.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): track selection mutation epochs

Detect selection ABA at config and owner mutation boundaries. Clone
provider pins for Studio readers and share synchronized selection state
across config value copies.

Track lifecycle publication, candidate rollback and recovery transitions.
Keep reads and unchanged publications stable. These lifetime epochs do
not replace current-incarnation checks or grant execution authority.

Verify race tests for config and the add-on Manager, plus application
compilation with auth tags. Request binding remains separate work.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): expose owner binding transport

Add a host-only binding API with an optional owner resolver. Forward
conversation claims over authenticated mTLS with strict versioned DTOs.
Compare full bindings during validation and deny missing resolvers.

Keep ordinary tool execution unchanged through an optional backend.
Test trusted resolver fixtures, stale bindings, denial and compatibility.
Application authority and store revisions remain a separate milestone.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(studio): fence selection snapshots with epochs

Read actor-scoped records and opaque mutation tokens under store locks.
Invalidate tokens after successful eligibility changes, including ABA.
Keep reads, rejected writes and turn history from invalidating tokens.
Clone mutable records so callers cannot bypass mutation tracking.

Support authoritative revisions only for memory-only JSON stores.
File stores lack exclusive ownership; GORM lacks persisted revisions.
Fail closed for both rather than trust frontend-local state.
Ordinary chats remain unchanged; application integration stays separate.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(studio): enforce persistent JSON ownership

Acquire lifetime sidecar locks before loading or repairing JSON data.
Enable selection revisions only under cooperating exclusive ownership.

Close drains operations before releasing ownership and rejects later use.
Clean up unpublished stores when application initialization fails.
Retain published locks through Shutdown because HTTP users may remain live.

Document coordinated upgrades and the advisory local-filesystem boundary.
Keep GORM revisions unsupported pending a separate approved design.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Codex:gpt-5

* test(application): isolate warning log capture

Run warning assertions in isolated test subprocesses. The suite leaves
background log producers running, which race with global logger changes.

Keep the parent logger unchanged and check child exits and completion
markers. Preserve pin persistence and ownership warning assertions.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(studio): resolve local owner execution bindings

Resolve execution providers from owner-held chat and agent snapshots.
Fence bindings with selection epochs, mutation tokens and live runtime
identity. Recompute every component during status authorization.

Keep named-user and SQL observations unavailable until authoritative
access and shared selection revisions can detect ABA changes.

Drain restart test producers before releasing JSON store ownership.
Register six product resolver cases in the gateway suite inventory.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(studio): bound product scopes to chat turns

Keep request-local product state alive through tool calls, checkpoints
and persistence. Give chat priming its own scope instead of seeding the
next turn with a closed observation.

Exercise one reducer read per scope across prompt and tool consumers.
Check closure of pending reads on normal completion and interruption.
Provider wiring and safety projection remain separate work.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(studio): consume typed execution providers

Replace the Sandbox-specific status heuristic with owner-bound typed reads.
Share one reducer observation across turn consumers and close it at the
outer scope boundary. Keep priming and standalone requests independent.

Sandbox exposes a separate callback-free typed read while preserving its
private Settings payload. Unknown backends never imply isolation.
Provider isolation does not prove host network-policy enforcement.

Local-owner JSON bindings are supported. Named-user and SQL bindings
remain explicitly unavailable and fail closed.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(application): isolate auth startup lifetime

Shutdown does not join every background producer. Auth startup tests
can therefore delete their temporary directory while a writer remains.

Run each case in a bounded child process. The parent waits for child
exit before deleting the fixture, preserving every startup assertion.
Production shutdown and JSON ownership retention remain unchanged.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(ui): resolve installed Home app commands

Project Home navigation from installed addon declarations, not product IDs.
Reserve native and skill names and suppress every colliding addon alias.

Re-read the authenticated registry before navigation and reject stale
actors, generations, immutable entries and destinations. Keep mounted
consumers polling through disabled and failed states. Abort pending reads
on actor changes and ignore replies from obsolete request epochs.

Studio declares its own alias. Preserve legacy redirects and pin migration.
Add unit coverage, localized Home copy and command lifecycle documentation.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): accept resolved Home command metadata

Apps resolves inherited titles beyond the explicit manifest label cap.
Bound resolved labels to 147 Unicode characters, including secondary pages.
Keep the backend's 60-character limit for explicit manifest labels.

Match packaged icon paths to the contract's ASCII alphabet and image types.
Accept dotted names while rejecting traversal, encodings and foreign assets.

Verify 68 focused cases, 657 total cases, lint, i18n checks and the UI build.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): bind safety notices to actual providers

Warn for addon host execution and unknown isolation without changing tool
permissions or built-in host consent. Reset dismissal when the admitted
binding, observation, actor or chat context changes.

Expose presentation-only status identity from the admitted request.
Keep configured network policy visible when enforcement is unverified.
Render bounded provider explanations as escaped React text.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): isolate command status across sessions

Invalidate command observations when actors or authentication sessions
change, before effects can expose a previous actor’s reply.

Keep actual host execution warnings generic instead of recommending
Sandbox installation or repair. Cover both regressions with source-hook
lifecycle tests and provider notice assertions.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): declare audited host requests

Declare actual official hostclient operations without inferring requests
from deployment grants. Keep optional settings and egress checks separate
from required workspace and job operations.

Verify all twelve manifests through strict package fixtures and assert
that requests never self-grant. Guard generic provider and Home dispatch
without banning historical routes, native Studio, or deployment policy.

Document the audited operations and unresolved named-user and SQL gaps.
Refresh Sandbox catalog metadata for its existing typed status tool.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): wire exact extension inventories and real provider selection

Run contract, official packaging and root architecture gates through actual report inventories. Exercise two installed execution providers through the real owner resolver and distinguish callback pass-through coverage. Share strict report validation with focused-scope handling and missing/skipped artifact self-tests; document direct evidence and remaining harness limits.

Assisted-by: Codex:gpt-5

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): expire registry reads on auth refresh

Bind registry authority to the actual authentication session epoch.
Same-user refresh now rejects old polls and pending Home navigation
before effect cleanup, even when permissions and metadata stay unchanged.

Exercise both races through the production registry provider harness.
Verify 59 focused tests, 669 total tests, lint, i18n, and the UI build.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(studio): resolve current execution selection

Use existing principal-scoped chat and agent reads for advisory status.
Revalidate current named credentials instead of requiring historical SQL
revisions. Local-owner mode also works with the existing GORM store.

Remove resolver nonces and historical revision prerequisites. Keep exact
runtime binding and existing status admission hooks. Document current-read
consistency without promising revoke-and-restore detection.

Verify the SQLite regression fails before the fix. Two separate full race
runs pass: application 262 specs (3 skips), auth 257, gateway 115, tools 91.
Cover named chat, API key and session credentials and current-state denial.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* refactor(addons): drop selection revision tracking

Use current selection comparisons for advisory status instead of mutation
history. Keep actor, credential and conversation identity explicit in the
model. Hash current inputs at the gateway without exposing raw store data.

Remove unused epochs, revision interfaces and JSON ownership lifetimes.
Preserve current authorization, runtime identity, mutexes and atomic writes.
Document current-read semantics without historical ABA guarantees.

Verify all affected packages with the race detector and auth tag. Check
the trace viewer, prerequisite self-tests and 18 chat-command unit tests.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): scope Apps reads and provider status

Bind Apps replies and registry ingestion to their originating auth session.
Reject late replies before effect cleanup on refresh or account switch.

Use selected-provider command DTOs in the composer instead of private
Sandbox discovery. Keep unknown isolation unknown and remove specific
add-on installation recommendations from the affected native surfaces.

Test production Apps, registry, switches and composer paths with controlled
cleanup and two arbitrary providers. Update the matching feature docs.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): guard composer and session publication

Remove the deleted Sandbox variable from the full composer render.
Report unknown command isolation explicitly in the about card.

Bind Apps records and admin action results to their originating actor
and actual authentication epoch before cleanup can run. Reject stale
request admission and verification without changing server authorization.

Add full-component render and pending session-transition regressions.
Enable no-undef for the production composer in normal UI lint.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): expire cancelled upgrade reconciliation

Bind cancellation reconciliation to the initiating session authority.
Discard status replies after expiry without publishing old outcomes.
Match the exact operation identity before clearing a held action.

Cover pending cancellation reads, expiry before reads, and preservation
of a newer attempt with the same action key.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): bind background upgrades to auth lifetime

Keep background upgrade authority in AuthProvider, independent of views.
Expire pending observations before auth consumers render or logout waits.
Preserve same-session navigation and exact-operation cleanup.

Cover cancellation, completion, and verification after the origin unmounts
with actual provider transitions and the real upgrade store.
Thread captured authority through Activity retries and document expiry.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* refactor(addons): remove implicit sidebar pins

Start every account without implicit Studio or Talk pins. Preserve saved
user selections and durable explicit pins across reopen and frontends.

Remove startup seeding, seed metadata APIs, and first-install exceptions.
Apply equal retention to all absent IDs without dropping database tables.
Keep store initialization, cached errors, and the leader sweep unchanged.

Verify genuine RED regressions before removal. Race checks pass for all
42 pin specs, 10 focused application specs, and 10 pin endpoint specs.
Update pin documentation; renamed specs have no exact inventory entries.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): declare typed model destinations

Let installed addons own model launch destinations without teaching core
about addon identities or private routes. Keep native chat and completion
outside the declaration vocabulary.

Validate strict keys, unique capabilities, declared pages and SDK route
bounds. Carry cloned typed metadata through Apps using the shared
contract type, and add owning Studio, Talk, Face and Voice declarations.

Verify strict contract RED before implementation, then 1276 passing
specs across contract, Apps, packages, runtime, protocol, config and manager.
Verify 178 packaging and architecture specs and exact carrier inventories.
Frontend dispatch and route cleanup remain a separate milestone.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* refactor(ui): resolve declared model launch actions

Resolve installed model actions through the shared addon registry. Keep
native chat and completion launches, and never promote absent providers.

Refresh actor-filtered Apps before navigation and reject stale authority,
provider metadata, generation, immutable entry and destination claims.
Use named menu choices for collisions without loading addon modules.

Remove unreleased addon-specific redirects and migrate browser fixtures
to canonical declared pages. Preserve native chat handoff and addon-local
navigation adapters. Update user documentation and historical plans.

Verify three production UI consumers fail against their previous code,
then pass all 13 mocked-HTTP browser regressions. Verify 696 host unit
cases, full quiet lint, targeted no-undef, locales, inline styles and build.
Full backend/browser integration remains outside these mocked-HTTP checks.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): validate Ginkgo hooks before focus

Ginkgo emits null container hierarchies for suite hooks. Require hooks\nto pass before applying focus only to It cases. Normalize null\nhierarchies without weakening exact required-case comparisons.\n\nRun reporter regressions in prerequisites. Register the declared uiOnly\ntool case and remove an update fixture case excluded by the existing\nmanager focus. Both inventories match the complete CI case output.\n\nVerify 12 reporter regressions, 39 prerequisite cases, three existing\nreports, and fresh runtime, tools and focused update manager suites.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): restore Go CI contract checks

Align route fixtures with explicit pins and declared network controls.
Keep network enforcement unknown instead of inferring it from isolation.

Propagate viewer output errors and select output paths with CLI arguments.
Check test writes and use Ginkgo assertions without copying atomic state.
Register the three migrated binding cases in the exact suite inventory.

Document two narrow security false positives for administrator file access
and symbolic credentials in reducer traces. Focused race tests pass.
The available golangci-lint reports zero issues in affected packages.

Assisted-by: OpenAI ChatGPT [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): align browser coverage with addon contracts

The generic provider refactor removes implicit install advice and private
Sandbox discovery. Update browser fixtures to use the installed command
metadata and typed execution observations without restoring those paths.
Keep command warnings, consent, disabled switches and privacy assertions.

Home now holds a live command consumer. Exercise polling shutdown on a
route without that consumer, after its lazy transition finishes. Preserve
the bounded cadence, error backoff and disable-stop assertions.

Restore the remembered-voices link through the addon's compatibility
router. Cover native migrations and NotFound for removed generator aliases.

Validation: 293 affected browser cases and three native service cases pass.
The gallery command passes all 74 inventoried cases. Full UI units, quiet
lint, locale parity, production build and diff checks pass.

Assisted-by: OpenAI ChatGPT [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): bind status to owner provider policy

Resolve typed status with the same current owner pins as execution.
Fail closed on owner policy outages and fingerprint authoritative pins.

Exercise CAS updates, conflicts, stale bindings, and policy outages.
Retain host settings when the named-account fixture replaces its gateway.
Refresh exact gateway and Apps inventories from passing reports.

Validation: the new regression fails before the fix. All 16 focused
binding and policy cases pass. Gateway passes 361 exact cases and Apps
passes 265 exact cases across their configured package reports.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* refactor(addons): share qualified tool identities

Use one formatter and contract-component parser for add-on identities.
Keep callable names bounded to 64 characters while recognizing long
unavailable stand-ins without granting dispatch authority.

Validate selected execution names and their provider ID at the owner
binding boundary. The real adapter already emits canonical identities.
Update exact importer and case inventories from passing reports.

Migrate the binding backend test to Ginkgo and check shutdown errors
reported by lint in the affected packages.

Validation: behavioral RED proves malformed stand-in acceptance and
plain selected-name acceptance. Affected full race suites pass: tools
94 and Studio 638, with zero skips. Application passes 275 of 278;
three existing PostgreSQL cases skip without their configured URL. All 12 owner-binding cases
pass and match the exact inventory. Scoped golangci-lint reports no issues.

Assisted-by: Codex:gpt-5 golangci-lint
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* refactor(addons): share execution declarations

Share pure declaration predicates across manifest validation, gateway
observations, and owner bindings. Keep authorization and exact-runtime
admission checks local. Reuse the strict manifest schema validator so
raw status definitions fail closed without changing manifest acceptance.

Share the workspace-operation prefix scan without changing grant sources.
Add declaration parity and workspace-prefix tables and update exact
inventories from passing reports.

Validation: two new gateway cases fail before the fix; all 14 pass after.
Race tests pass: contract 245, gateway 136, tools 94, and focused
application binding/workspace cases 22. Scoped golangci-lint: 0 issues.

Assisted-by: Codex:gpt-5 golangci-lint
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): retain document skill cases

Match the contract inventory to the passing report after rebasing.\nKeep the new document-provider case and updated reserved-name cases.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

Assisted-by: OpenAI:gpt-6.1-sol

* test(addons): follow owner-managed upgrades

Accepted upgrades belong to the owner and cannot be cancelled by UI.
Exercise observer expiry during reads, completion and verification.
Supply the real upgrade store to source-executed auth harnesses so
logout reaches its synchronous authority invalidation.

Match whole-package inventories to fresh Manager and gateway reports.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 23:50:40 +00:00
mudler-agentandEttore Di Giacinto a9e19746be fix(addons): make owner settings authoritative (#13033)
* feat(addons): add a durable host policy store

Keep bounded host policy in one owner-managed durable store. Use revision
checks and retained receipts to reconcile updates without stale writes.

Require a sole migration source and explicit owner selection for clusters.
Restrict provider pins to tools and skills that existing consumers replace.
Document the internal scope; UI and carrier integration remain separate.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): reject malformed host policy JSON

Reject duplicate keys, noncanonical fields, and incomplete durable
structures before JSON decoding can discard evidence of corruption.
Validate provider keys before deletion so invalid removals cannot commit.

Keep empty provider sets canonical when writing migrated policies.
Regressions cover malformed documents, unchanged storage on rejection,
and replay with case-sensitive operation IDs.

Assisted-by: OpenAI:ChatGPT
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): decouple host policy validation

Move shared egress parsers into a dependency-light package so the gateway
can consume host settings without importing itself through core/config.
Keep config wrappers and a host type alias to preserve existing callers.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): carry host policy actions to owner

Keep host policy reads and updates on the authenticated owner. The owner
checks current administrator authority before updates and preserves CAS
conflicts and durable operation receipts across forwarding.

Provide read-only snapshots for trusted hosts over pinned mTLS. Missing
owner services never fall back to frontend policy. Application startup,
public settings routes and runtime consumers remain separate work.

Verify typed actions with local and real mTLS forwarding tests, including
revoked actors, strict payloads, receipt replay and unavailable stores.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): keep policy provenance private

Return a bounded policy projection from gateway reads, updates and replay.
Trusted host consumers use the same projection without migration metadata.
The private owner store retains provenance for migration records.

Cover local and pinned-carrier output, durable provenance and typed errors.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): initialize owner host settings

Open durable host settings only after the owner acquires its storage guard.
Keep frontends forwarding without importing their local configuration.

Preserve automatic migration for the sole combined host configuration.
Require explicit owner-local selection for non-default dedicated policy.
Close the store once on cancellation, shutdown or startup failure.

Cover migration, restrictive defaults, restart revisions, corruption,
duplicate owners and frontend isolation in the native bootstrap test.
Document the owner-only migration option and storage constraints.

Assisted-by: OpenAI:ChatGPT
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): close settings before the owner guard

Startup failures released the owner guard before closing host settings.
Use one deferred cleanup to preserve the close order on every failure.

Test real runtime and gateway failures, shutdown, and cancellation.
Check once-only closure and reacquire both locks after cleanup.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): enforce owner egress policy in Studio

Read owner policy for Studio defaults, views, cards and enforcement.
Frontend configuration must not override policy or mask an owner outage.
Propagate context and policy errors instead of reusing a cached allow.
Preserve local settings behavior when add-ons are disabled.

Cover existing consumers through independent pinned mTLS carriers.
Verify live tightening, pending-card outages and restart persistence.
Document the read semantics and the proxy verdict-cache boundary.

Assisted-by: OpenAI:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): read provider pins from owner policy

Studio reads current owner pins for tools and skills instead of frontend
configuration when add-ons are enabled. Policy failures stop selection
rather than silently choosing built-ins. Disabled add-ons retain local
configuration behavior.

Cover owner updates and outages, and document the read contract.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(studio): abort turns on mandatory policy errors

Sessions must not treat owner policy failures as optional tool outages.
Stop priming before harness startup and abort turns before model sends.
Preserve wrapped policy causes and existing optional connector behavior.

Cover sent turns, priming failures, and optional connector failures.
Document the failure contract without changing fallback or cache rules.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): use owner policy for auto-upgrades

Read fresh owner policy before each automatic upgrade pass. Skip the
pass when that policy is unavailable instead of trusting local settings.

Wire a real owner policy store into the transportless manager fixture.
Keep assertions for stale frontend settings and owner policy changes.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): guard legacy settings writes

Reject migrated owner policy fields before applying any local settings.
Omit those fields from legacy reads while add-ons are enabled, and keep
unrelated local settings available without an owner request.

Cover presence, case folding, duplicate keys and atomic rejection.
Document the legacy contract and unchanged disabled behavior.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): use the existing Ginkgo suites

Register settings authority regressions with the existing suites to
comply with the Ginkgo/Gomega test policy. Preserve the conflict,
compatibility, owner outage, and upgrade assertions without adding
another RunSpecs bootstrap.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(egress): bound wildcard host policy grants

Support descendant and public-any patterns through the shared parser.
Keep wildcard grants inside the administrator ceiling and public guard.
Preserve the legacy exact-host exception under the registries ceiling.

Keep policy strings lossless for existing receipts and stored exact hosts.
Test canonical matching, private exclusions, ports, and policy replay.
Proxy connection authorization and cache changes remain separate.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(egress): authorize each new connection

Remove cached allows so policy tightening and owner outages block new
connections immediately. Keep existing DNS, private-address and SNI guards.
Socket tests cover HTTP keep-alive, CONNECT and consumed grants.
Established streams may finish under their original authorization.

Expect the canonical runsc path in the gVisor test, matching production.
This preserves the path contract when the test temp directory is a symlink.
A short alias avoids Unix socket path limits in deep worktrees.

Verify with go test -race -count=1 -timeout=180s ./addons/sandbox/...
using an offline environment and a short alias to the worktree temp folder.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): save network policy through the owner

Use owner revisions for host controls instead of frontend runtime settings.
Keep drafts across conflicts and outages, and replay uncertain writes with
the same operation identity. Read authoritative policy after each save.

Accept bounded host wildcards while keeping extra registries exact.
Keep typed private-access confirmation out of owner policy patches.
Add rendered form and client tests, and document reconciliation behavior.

Assisted-by: OpenAI: GPT-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): fail closed on owner policy errors

Reject partial, mistyped, unknown and oversized owner snapshots before
publishing them to the form. Match the complete Go policy JSON shape.

Keep owner availability errors and uncertain operation identities when
Discard resets the local draft. Only reconciliation can reopen saving.

Tests reproduce both review failures and cover immutable retries,
malformed reloads and the rendered discard-then-edit regression.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): finish owner settings controls

Route skill switches, provider choices and upgrade controls through owner
policy. Preserve per-key changes when another frontend updates the policy.
Keep local tool execution controls on their existing settings path.

Extend the existing state checklist and add-on acceptance guidance so new
controls declare their authority and test both deployment topologies.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(studio): check policy errors in Tables skill tests

Adapt the Tables test added on master to the error-aware skillTurn API.
Assert that normal skill selection succeeds instead of discarding errors.

Verified with make test-distributed-fakebus and scoped golangci-lint.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): document guarded settings IO boundaries

Explain the narrow gosec exceptions for the authenticated administrator
settings response and fixed owner-root files. Keep symlink, mode and type
checks intact; no request-supplied paths reach this store.

Explicitly ignore best-effort temporary-file and directory cleanup errors;
file and directory sync failures still determine durability.

Verified owner-store and handler race tests and scoped golangci-lint.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): run carrier policy cases in Ginkgo

Register projection, forwarding, validation and CAS cases with the existing
gateway suite. Check cleanup errors in test fixtures and explicitly ignore
response-body cleanup after decoding in the internal read client.

Verified gateway/application authority race tests and scoped lint.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): inventory owner policy gateway cases

Require all four owner-policy cases in both the per-report and aggregate
gateway and artifact acceptance inventories. Keep exact matching intact.

Verified real test reports with the existing strict Ginkgo report validator:
215 gateway cases and 252 artifact cases passed, without skipped cases.
The full browser readiness probe remains a separate CI gate.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(ui): scope library settings apply action

Select the exact Apply button in the runtime settings pending bar. The
owner upgrade control has a separate Apply upgrade setting button, so an
unscoped substring locator is ambiguous.

Verified the failing Playwright case against the real UI test server and
Google Chrome, with isolated server and browser temporary paths.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(egress): update route and invalid-host fixtures

Use the context-aware egress callback in route tests. Keep the last-valid
list regression meaningful by using a partial-label wildcard, since a
whole-label descendant pattern is now valid input.

Verified focused application and route race tests and scoped lint.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(settings): reject partial-label wildcard fixture

Keep the invalid-input regression while accepting the newly supported
whole-label descendant wildcard. Use api.*.example.com as invalid input.

Reproduced the full endpoint suite failure, then verified all 616 endpoint
specs pass and scoped lint is clean.

Assisted-by: nib:gpt-6.1-sol
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 18:48:39 +01:00
mudler-agentandEttore Di Giacinto a6589e4e60 fix(connectors): preserve disabled add-on discovery (#13030)
fix(connectors): preserve discovery when add-ons are disabled

Return healthy empty connector discovery for explicitly disabled add-ons while keeping missing configured gateways and unreachable nonowners unavailable. Cover offline Telegram persistence through the real connector transport and synchronize the exact lifecycle/apps test inventories.

Assisted-by: nib:gpt-6.1-sol

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 08:36:45 +01:00