Commit Graph
904 Commits
Author SHA1 Message Date
Ettore Di Giacinto bb3933e9d5 feat(connectors): put the trigger approval under Propose triggers
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
2026-11-04 10:17:25 +00:00
Ettore Di Giacinto b349946b49 feat(connectors): state the permission rule and narrow the pairing notice
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
2026-11-04 10:17:25 +00:00
Ettore Di Giacinto 22ca7403f1 feat(ui): one switch per capability in the agent form
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
2026-11-04 10:17:25 +00:00
Ettore Di Giacinto 9a98a39748 feat(connectors): copy or fill permissions from the agent
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
2026-11-04 10:17:25 +00:00
Ettore Di Giacinto 3edcec1813 feat(studio): let the owner allow acts for triggers proposed from a chat
A trigger proposed from Slack or Telegram was always read only, and
the person had no way to let its runs act except to edit the trigger
on the Triggers page afterwards.

The approval of a binding now has an acts list, the act.* permissions
that a proposal from its chats may carry. It is empty by default, so
nothing changes until the owner sets it. It is stored in the same
approval JSON (no migration), checked against the closed list in the
API, and always limited to what the agent holds.

The acts of a proposal are what the model asks for, within the list of
the binding and the grants of the agent. The rest is dropped and
named in the proposal text, and the model is told which acts it may
ask for and to ask the person first when the job changes things. A
read only proposal now says what the person can do next.

For a proposal with acts, auto_readonly still asks. auto starts it at
once only when every act is allowed for the binding, nothing was
dropped and every sender of the turn is paired; otherwise it asks. A
trigger made without asking also keeps to the acts of its binding at
run time.

Assisted-by: Claude:claude-sonnet-5-5
2026-11-04 10:17:23 +00:00
Ettore Di Giacinto 5df9c43578 fix(studio): trust a sender who is on the allowlist for capabilities that are not sensitive
A binding with its capabilities ticked still gave nothing to the
owner's own allowlisted account: Authorize wanted a pairing or a role
even for capabilities that are not sensitive, so the agent had no
reminder, memory or talk-back tool.

A sender whose ID or @name is an entry of the allowlist now holds every
capability that is not sensitive, within the ceiling of the binding and
while no switch is off. The decision says Via "allowlist". A sensitive
capability still needs a pairing, and a role never grants it.

The entry of a channel or group lets its members talk to the bot but
does not trust them: the trust is matched against the sender only, and
a binding that accepts anyone lists nobody. Such a sender still gets
capabilities from a pairing or a role. The capability gate takes the
handle of the sender for the same match, and a sensitive request
ignores the allowlist as it ignores roles.

When a tool is held back for a missing right, the turn now carries a
short note with the reason in plain words and tells the model to relay
it, instead of saying that it has no such tool. The reasons are kept
in the grant of the turn, so a worker reports the same words. The
access view lists a "listed" sender apart from one let in through a
channel.

Assisted-by: Claude:claude-sonnet-5-5
2026-11-04 10:11:25 +00:00
Ettore Di Giacinto a1157fb811 Tell the sender when an answer shows a file that was not attached
A model could write a paperclip line with a made-up file name and
never call attach_file. Nothing was sent and the sender got no note.

When the answer of a connector turn has a paperclip followed by the
name of a sendable file, and no file went and no other note was
sent, the host now adds one plain note. The answer is not changed.
Prose that names a file and a paperclip alone do not match.

The attach_file description and the connector guidance now say to
use the exact path the tools showed, and never to write a file name
or a paperclip line by hand. A spec covers a folder name with a
space.

Assisted-by: Claude:claude-sonnet-5-5
2026-11-04 09:14:18 +00:00
Ettore Di GiacintoandClaude Sonnet 5.5 c3566febd3 Report every add-on update in one check and say why an old package will not start
Checking several add-ons of one library in a row reached the registry
only for the first, because the library interval also throttled the
per-add-on tag resolution. The others answered unknown at once, so a
check-all reported one update and the next pass the next one.

The library interval now spares only the library fetch; each add-on is
throttled by its own interval.

Turning on an installed package whose manifest this host refuses now
fails with the typed installed_incompatible cause and a message that
says to upgrade, instead of the generic owner-unavailable 503. The
upgrade of such a package stays allowed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 07:26:08 +00:00
Ettore Di Giacinto 653ba1afb8 fix(addons): stage no file while the UI host grants are locked
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
2026-11-03 22:53:34 +00:00
Ettore Di Giacinto 0e76e7ea75 docs(addons): document install from a file and make addons-zip
Describe the zip, the trust model of a local file, the install, upgrade
and downgrade rules, the routes and their error types, the split
deployment upload and the make target. Regenerate the Swagger spec.

Assisted-by: Claude:claude-sonnet-5-5 swag
2026-11-03 22:52:50 +00:00
Ettore Di Giacinto 5d194ceab5 docs(addons): say that UI grants apply live, and document the lock without sign-in
The section on host policy still said that policy is restart-only. UI
grants and denies apply to the next call and re-sync the add-ons; only
the policy file and flags are read at startup.

Document the lock of UI grants while sign-in is off and the flag that
lifts it, the hold on a package from another library or signer, the
failed uninstall, the audit-before-write rule and that a blanket mode
fixed by a flag is not stored.

Assisted-by: Claude:claude-sonnet-5-5
2026-11-03 22:52:09 +00:00
Ettore Di Giacinto 7079666185 feat(memory): choose how a note is saved on the add-on page
Declare the save-mode user setting (ask, auto, never; default ask) and
edit it on the Short memory page with the settings kit, a line for each
option, and the rule that messaging apps and background runs always ask.
The page reads and writes through sdk.userSettings and keeps the earlier
choice on screen when a save fails. The memory skill now says what to
tell the user for each answer of propose_change.

Assisted-by: Claude:claude-sonnet-5-5 node
2026-11-03 22:46:22 +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 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 cccf95e662 docs(addons): explain how the host grants pieces fit
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 0cb1490a12 fix(http): believe X-Forwarded-Prefix only from a trusted proxy
StripPathPrefix and the redirect from / and /browse still read
X-Forwarded-Prefix from any peer, after BaseURL and BasePathPrefix
were gated on --trusted-proxies. A client could make the server strip
a path prefix or add one to a redirect.

Gate both on the same trusted-proxy resolver. The resolver is now
stamped into the request before StripPathPrefix runs, and the check is
exported as ForwardedHeadersTrusted. The explorer has no
--trusted-proxies option and keeps its old behavior by trusting every
peer for this one header. The docs say that the prefix needs a trusted
proxy.

Assisted-by: Claude:claude-sonnet-5-5 go-test
2026-11-03 21:50:57 +00:00
Ettore Di Giacinto 3553e40940 docs(addons): document host access in the UI
Describe the install step, held upgrades, the Host access page, deny
rules and the blanket switch.

Assisted-by: Claude:claude-sonnet-5-5 playwright eslint
2026-11-03 21:45:05 +00:00
Ettore Di Giacinto 1a64a33f3d feat(github): show a real approval card for GitHub write proposals
A GitHub write that waits for the person used to show the raw preview
JSON in the host's approval frame. The add-on now declares an
approval-card component for each of its six write tools. It shows what
will be done, where (with a link to the issue or pull request), as which
connection, and the exact stored text that will be posted, plus a note
that the text is public.

The proposal does not name the connection, so the card reads the saved
connections and names the one that lists the repository.

Assisted-by: Claude:claude-sonnet-5-5 node playwright eslint
2026-11-03 21:43:57 +00:00
Ettore Di Giacinto 6abe5b1882 feat(github): rework the connection page of the GitHub add-on
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
2026-11-03 21:43:57 +00:00
Ettore Di Giacinto 8cf6153ea6 feat(connectors): let a credentials-only kind have bindings without an agent
A connector kind can declare agentOptional. A binding of such a kind may
have no agent: it holds the token, the resource list and the write mode
for the tools of a chat, and never receives or answers a message. The
GitHub kind declares it, so a person no longer makes a throwaway agent
only to store a token.

Without an agent the host refuses every setting that needs one:
listening, accepting senders, an allowlist, a notification chat,
talk-back channels, permissions, pairing and trigger approval. The
places that read the agent of a binding handle the empty value:
inbound routing, the polling loop, notifications, pairing, the capability
check (deny by default) and the grant switch. A kind that does not
declare the key still needs an agent on every binding.

The binding page makes the agent field optional for such a kind with
help text, and the list shows "Credentials only".

Assisted-by: Claude:claude-sonnet-5-5 go gofmt ginkgo node protoc swag
2026-11-03 21:29:29 +00:00
Ettore Di Giacinto 27f1c632f0 docs(addons): document policy file v2, deny rules and the file-always-opened rule
Describe schemaVersion 2 and its deny object, the deny entry grammar, the
--addon-host-deny flag, the blanket and UI grant settings, and the rule
that a selected policy file is always opened for its deny, even when a
flat --addon-host-ops list replaces its grants.

Assisted-by: Claude:claude-sonnet-5-5
2026-11-03 21:24:45 +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 d8bd3879f2 feat(studio): let a trigger proposal carry its acts, shown before the yes
A trigger run with nobody present only reads unless its trigger holds
act.* grants, and until now only the REST API could set them. An agent
that proposes a trigger can now ask for acts too:

- propose_trigger takes an optional acts list. The names must be
  act.* grants the agent of the chat holds; anything else is refused
  and nothing is stored. A read_only proposal cannot carry acts.
- The proposal stores the list. Confirming it, in Studio, by a plain
  yes or by confirm CODE, grants exactly that list. No tool changes
  the acts of a trigger afterwards. Two proposals that differ only in
  their acts are kept apart.
- The text of a proposal in a messaging app now has a Permissions
  line, in plain words with sensitive classes marked: read only, or
  what a run may do. The confirmation repeats it. A proposal that asks
  for acts is never set up without a yes, whatever the approval mode.
- The trigger REST object gains acts_held, read_only and
  read_only_why, computed with the same rules as the run, so a client
  can show why a run only reads.

Assisted-by: Claude:claude-sonnet-5-5 go ginkgo
2026-11-03 20:53:02 +00:00
Ettore Di Giacinto 3bd93e18e7 fix(chats): let the draft add-ons list answer for the draft's switches
A new chat with the sandbox switched off in the composer showed no
"switched off" notice. GET /api/chats/draft/addons took only the model
and the agent, so it always reported the default commands state.

Accept the draft's switches as comma-separated query parameters
(addons_off, builtins_off, builtins_on), check them as the create request
does, and apply them to the draft record before the commands state is
worked out. The composer sends its choices each time an add-on or a
built-in group is switched, so the notice follows the switch.

Assisted-by: Claude:claude-sonnet-5-5 go-test playwright
2026-11-03 19:38:55 +00:00
Ettore Di Giacinto 9b40c24a30 feat(agents): warn in the editor when a first grant turns off connector features
An agent with no grant list keeps its non-sensitive connector features
(trigger tools, talk-back, files from senders). Granting any one built-in
makes the list the whole answer, so the others stop working. The editor
had no grants list at all, so nobody could see this.

Add a Permissions section to the agent editor with the declared grants.
When the first grant is ticked on a saved agent that had none, a notice
names the connector grants that would turn off and a button adds them.

Also fix the files.upload text. The gate enforces it on files a sender
attaches to a message, not on the agent sending files out, so the grant
is now "Receive files".

Assisted-by: Claude:claude-sonnet-5-5 go-test playwright
2026-11-03 19:38:55 +00:00
Ettore Di Giacinto 6cbee5181c fix(http): believe forwarded headers only from a trusted proxy
BaseURL and BasePathPrefix read X-Forwarded-Proto, X-Forwarded-Host,
X-Forwarded-Prefix and Forwarded from any caller when LOCALAI_BASE_URL
is not set. A client could pick the scheme and host that end up in the
SPA <base href>, generated links and redirects.

Gate them on the trusted-proxy resolver (--trusted-proxies): the headers
count only when the TCP peer is trusted. An empty list trusts nobody, so
the request's own scheme and host are used. LOCALAI_BASE_URL still wins
over everything for the origin.

This changes behavior for setups behind a proxy that rely on these
headers without setting LOCALAI_BASE_URL: they must now list the proxy
in --trusted-proxies. The docs say so.

Assisted-by: Claude:claude-sonnet-5-5 go-test
2026-11-03 19:38:36 +00:00
Ettore Di Giacinto 4b47d8c3d6 docs(web): document the Keenable search provider
Assisted-by: Claude:claude-sonnet-5-5 go-test
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
(cherry picked from commit d0250b4d611105b4dc29d56ac7f69d004ad07451)
2026-11-03 19:29:02 +00:00
localai-org-maint-bot 8a62b90d02 docs(mcp): describe remote model and system tools
Format the HTTP route constants with gofmt.

Assisted-by: Codex:gpt-6
(cherry picked from commit 9d4b99da60)
(cherry picked from commit 367a9ff9432c84f700f3f83206fab2d7b4ed85ac)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 19:24:13 +00:00
Ettore Di Giacinto 9e6a7a077c fix(realtime): leave speech-to-speech client commits at the session rate
The speech-to-speech path labels its transcription WAV with the session
rate for client commits, so resampling first handed it 16 kHz audio under
a 24 kHz header. Resample only in the chained pipeline, and say so in the
docs.

Assisted-by: Claude:claude-sonnet-5-5 go-test
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
(cherry picked from commit 82cc411af54cf884e4a44a3e96292a323ba5611c)
2026-11-03 19:23:38 +00:00
nexxtmobile.de fbfb2ccadf fix(realtime): resample client commits to 16 kHz before transcription
The committed-utterance WAV is written with a fixed 16 kHz header. The VAD
commit path stores audio that is already resampled to 16 kHz (vadCommit),
but the client-commit path (input_audio_buffer.commit) passed the raw
buffer at the session input rate. For a 24 kHz OpenAI Realtime session the
backend decoded 24 kHz audio as 16 kHz (1.5x speed/pitch): garbage or empty
transcripts, then input_not_committed on the follow-up response.

commitUtterance now resamples from session.InputSampleRate to
localSampleRate, so both paths hand 16 kHz audio to the 16 kHz header.
This replaces the earlier approach of this PR (header at the session rate),
which mislabelled the already-resampled VAD audio (review feedback).

Tests go through the real paths with a 24 kHz session: commitUtterance and
a server_vad commit via vadTick; both must produce a 16 kHz WAV of the
resampled length. The client-commit spec fails without the fix.

Fixes #12747

Signed-off-by: nexxtmobile.de <git@nexxtmobile.de>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 05ba891da6)
(cherry picked from commit 56e2b6d0cc5cbf1944ef50735f7b499d998eb25c)
2026-11-03 19:23:38 +00:00
Cristi An 23839b74a2 feat(api): accept request-level chat_template_kwargs
Clients written for llama-server, vLLM or SGLang switch template variables
per request with a top-level chat_template_kwargs object, most often
{"enable_thinking": false}. OpenAIRequest had no such field, so it was
dropped: with a Qwen3-style template thinking stayed on, the reply landed
in message.reasoning and content came back empty.

Fold the field's scalar values into the request metadata, so it takes the
path metadata already takes (gRPCPredictOpts, ResolveChatTemplateKwargs).
Booleans become "true"/"false", strings pass through, numbers become their
decimal string; nested values are skipped. An explicit metadata key wins.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Cristi An <c4xp@msn.com>
(cherry picked from commit 2cd7425e87)
(cherry picked from commit 5e86ad6bd5286cc8db398d278adaa32e562223ca)
2026-11-03 19:22:19 +00:00
Ettore Di Giacinto 4129eca571 docs(cloud-proxy): keep the OrcaRouter example neutral
Drop the vendor marketing claims and the specific upstream model ids,
which go stale. Keep the working passthrough configuration.

Assisted-by: Claude:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
(cherry picked from commit eec17103b4514f1cb0f6193c3683f34f71a3d1b9)
2026-11-03 19:20:44 +00:00
XiaoHuo888-hue 48549f5701 docs(cloud-proxy): add OrcaRouter passthrough example
Document OrcaRouter as a first-class cloud-proxy upstream. It is an
OpenAI-compatible gateway, so it plugs into the existing passthrough
mode with provider: openai, mirroring the OpenAI/Anthropic examples.

Assisted-by: Claude:deepseek-v4-flash-0731
Signed-off-by: XiaoHuo888-hue <xiaohuo888hue@users.noreply.github.com>
(cherry picked from commit 329ee0ee89)
(cherry picked from commit 6b2d84294d0198bdaa2f27288993eb7cfad0b0b1)
2026-11-03 19:20:44 +00:00
Thibaut MÉLEN 9cc7b63d89 docs: add Nika to Agentic Libraries & Assistants integrations
Signed-off-by: Thibaut MÉLEN <20891897+ThibautMelen@users.noreply.github.com>
(cherry picked from commit aaf03eb042)
(cherry picked from commit 01c2a4e5477bcc6fcb92ab15c4d898f62d29ed9e)
2026-11-03 19:20:44 +00:00
localai-org-maint-bot 786fd13087 fix(qwen3-tts-cpp): report incomplete batch warmup
Report failed warmup requests without failing model load. Document the
warmup limits and use the reported LocalAI benchmark measurements.

Assisted-by: Codex:gpt-6
(cherry picked from commit ec50871dd6)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 19:19:59 +00:00
nexxtmobile.de 8c44b3de33 docs(text-to-audio): document qwen3-tts-cpp max_batch
Signed-off-by: nexxtmobile.de <git@nexxtmobile.de>
Assisted-by: Claude:claude-opus-5-5
(cherry picked from commit 074bc84f0c)
2026-11-03 19:19:53 +00:00
Stefan Walcz 2247835401 feat(config): mark links a profile answer names without retrieving them
A model can cite a plausible deep link it never opened, written from
memory. The client cannot tell it from a link a search tool returned,
because server-side MCP tool results never reach it.

evidence_check.urls compares every http(s) link of the final answer,
bare or inside a Markdown link, with the URLs of the request (system
prompt, system, user and tool messages) and of the request's successful
MCP tool results. Links are compared without scheme, fragment, "www."
and trailing slash; a link counts as retrieved when it equals an evidence
URL or is a shorter form of one at a path or query boundary. Unbacked
links (at most five) are listed in an appended note; urls_note replaces
the default note, with {urls} as the placeholder for the list.

Assistant messages are not evidence, so an invented link is marked again
when the client sends it back on the next turn.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de>
(cherry picked from commit 523e8cd3c4)
2026-11-03 19:18:43 +00:00
localai-org-maint-bot 041f2337df fix(chat): preserve complete tool source URLs
Keep balanced parentheses in source URLs and decode MCP text arrays before
extracting links. This preserves Wikipedia paths and escaped query
separators instead of appending truncated citations.

Assisted-by: Codex:gpt-6
Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de>
(cherry picked from commit 9b81cb20ab)
2026-11-03 19:18:35 +00:00
Stefan Walcz 23012a7c4a feat(config): let a profile alias require a tool call on the first turn
A system prompt that tells a model to look things up first does not stop
it from answering from its own knowledge, for example when the question
comes with a picture.

profile.require_tool makes the first model call of a chat request that
offers the profile's server-side MCP tools use tool_choice "required".
Every follow-up call of the server-side tool loop is made with the
client's tool_choice again: a "required" that stayed in place would make
the model call a tool after every tool result and never answer until the
iteration limit.

With a tokenizer template the backend enforces "required" with a grammar
built from the chat template's tool-call format. Where LocalAI builds the
tool-call grammar itself, the first call's grammar leaves out the
no-action function instead.

The first call is not forced when no server-side tool was offered, when
the client set tool_choice or function_call itself, or when the request
does not end with a user message, so a request returning a client tool's
result cannot bounce tool calls between client and server.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de>
(cherry picked from commit 2801938aac)
2026-11-03 19:18:35 +00:00
Stefan Walcz 864c176cd2 feat(config): append tool result URLs to uncited profile answers
A research profile asks the model in its system prompt to cite its
sources, but a model can answer from a search result without naming a
single link, and the client never sees the server-side tool results that
held them.

evidence_check.sources appends one line with the distinct http(s) URLs
returned by the request's successful MCP tool calls (at most five, in
order of appearance) when the final answer names no URL itself. An answer
that already links something is left unchanged, and nothing is added when
no tool result holds a URL, so a link is never made up. The label is
configurable through evidence_check.sources_label (default "Sources") for
profiles that answer in another language.

It runs in the existing evidence gate, on the same streaming and
non-streaming paths as the number note.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de>
(cherry picked from commit 4ee6ff1395)
2026-11-03 19:18:35 +00:00
localai-org-maint-bot 4fdfba79b3 fix(p2p): balance active federated connections
Reserve capacity when selecting a worker and release it when forwarding
ends. Completed connections no longer bias routing toward new workers.
Keep active counts through offline periods and choose randomly on ties.

Add regression tests and document per-instance TCP connection counts.

Fixes #12992

Assisted-by: Codex:gpt-6
(cherry picked from commit 66cf106490)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 19:18:06 +00:00
Ettore Di Giacinto 6b181cd20b feat(studio): enforce the action policy of unattended runs
A trigger run or an agent job has nobody to approve a step, and so far
the approval mode auto_readonly and the act.* grants were only recorded.
The prompt was the only thing that kept such a run from acting.

Every tool call of an unattended run now goes through one check at the
call. A tool that only reads runs. Any other tool needs the grant of its
class, and the check fails closed:

- act.files for write_file, edit_file, move_file, delete_file and the
  built-in bash;
- act.addons, or act.addons:<id>, for add-on tools whose declared kind is
  not read, plus act.spend when the add-on gates the tool with a
  permission;
- act.spend for MCP tools.

A trigger holds its new acts list within the grants of its agent. A
trigger that declared read only runs, or that a binding in auto_readonly
mode made without asking, is refused every non-read tool whatever it
holds. A binding that cannot be read counts as auto_readonly. A job
holds the act grants of its agent. Sub-agents follow their parent, and a
saved agent cannot be started from an unattended run.

A refused call answers with a plain sentence the model can pass on, not
an error, and writes one audit log line. Triggers set acts through the
REST API; a model never can. A trigger whose plan names a file it writes
keeps act.files.

Assisted-by: Claude:claude-sonnet-5-5 go ginkgo swag
2026-11-03 19:12:29 +00:00
Ettore Di Giacinto 3cef8d5a91 feat(ui): show the builtin grants in the shared Tools list
The Tools list gains a group, "What the agent may change", with one
row per builtin grant: its title and a plain-words description, and a
mark on the sensitive ones. The agent form switches them through
useAgentTools; a chat of a saved agent lists the grants the agent
holds, read only. Both draw the group with the same useToolGroups and
SwitchPanel code.

The group has no group setter, so Select all and Disable all, and the
"N of M on" count, leave it alone: a bulk click must not grant a
right.

Assisted-by: Claude:claude-sonnet-5-5 node
2026-11-03 19:10:39 +00:00
Ettore Di Giacinto cf1041ca96 feat(ui): list agent skills on the Skills page
The Skills page asks for the skills of the saved agents with
agents=true and shows them with the agent's name. A skill of the
agent layer says who proposed it, can be removed (to the Trash) and
is not offered to other agents. The list of a plain chat or of the
agent form never carries them.

Assisted-by: Claude:claude-sonnet-5-5 go node
2026-11-03 19:10:39 +00:00
Ettore Di Giacinto dcd180ced8 feat(studio): add the agent skills layer
A saved agent keeps its own skills in LocalAI/agents/<agent>/skills.
Only a chat that runs that agent sees them. They sit between the folder
and personal layers, so an agent's version of a skill wins for that
agent, and they count as granted without being listed in the agent's
skills.

An agent writes one by a skill change proposal, as before; the new
layer only reads them. A proposal for an agent skill cannot take the
name of a core skill or of a document slot (read from slots.json).
When writing-skills loads in a chat of a saved agent it starts with
"Do not write files. Call propose_change with kind skill.", and the
skills.write grant makes it visible to the agent.

The Skills list gains the agent layer, with the agent's ID and name,
and a skill can be read or removed (to the Trash) with the agent
query parameter.

Assisted-by: Claude:claude-sonnet-5-5 go swag
2026-11-03 19:10:39 +00:00
Ettore Di Giacinto afb06fd42f docs(addons): describe manifest api 2 only and update the test inventory
The add-on guide names one manifest api, one way to move a file and no
migration path, and drops the section on arguments of older tool
versions. The Swagger files are generated again, which also brings in
fields that the last generation missed, and the test of the apps view
now emits every field it documents. The inventory of the add-on suites
lists the specs by their new names.

Assisted-by: Claude:claude-sonnet-5-5 go swag
2026-11-03 16:57:13 +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 7efb20bd1f feat(studio): read and send large connector files by handle
For a kind that declares files.transfer, the connector manager gives the
add-on a handle and not the bytes, in both directions.

Inbound, the add-on streams the file to the host. The chat upload then
reads it from the blob store, so a 6 MB PDF reaches the chat. Outbound,
the manager checks the access of the chat to the file, gives the
process that runs the add-on only the hash and the size of the content,
and the add-on pulls the chunks. The file limit of such a kind has no
inline cap: it is the smallest of the host ceiling, the ability the kind
declares and the limit the owner set for the bot, and the sentence names
the number and the layer. A kind without transfer keeps the 1 MiB inline
cap, and its sentence says so.

Checks: the files of one message may add up to three times the limit;
the media type that the add-on states must be among the types the kind
declares; an outbound file must have a type that the kind declares and
must not start with an HTML page. Every transfer writes one audit line
with direction, binding, size, result, layer and time, and no name or
content. The binding page no longer says "larger files need streaming"
for a kind that streams.

The specs run the real Telegram and Slack add-ons, the real gateway and
the real file operations over scripted platform APIs, with a 6 MB file in
each direction.

Not in this change: a daily byte counter per binding, and a virus scan
(the table has a hook that accepts everything).

Assisted-by: Claude:claude-sonnet-5-5 go test
2026-11-03 15:54:45 +00:00
Ettore Di Giacinto f5f4b7ea58 feat(http): add --trusted-proxies to resolve the real client address
Echo's RealIP trusted X-Forwarded-For and X-Real-Ip from any peer
when no IPExtractor is set. That fed the login and registration rate
limits and the API trace client address, so a client could dodge a
limit by rotating a forged header.

Add pkg/clientip, one resolver for the whole server. With
--trusted-proxies / LOCALAI_TRUSTED_PROXIES (IPs and CIDRs, empty by
default), the forwarded chain is read only when the TCP peer is
listed. The client is the right-most X-Forwarded-For entry that is
not a trusted proxy; a malformed entry stops the walk. With the flag
unset every request resolves to its TCP peer.

The resolver is set as Echo's IPExtractor and used by the access log
(--log-client), so all consumers agree.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt
2026-11-03 15:48:38 +00:00