Commit Graph
9059 Commits
Author SHA1 Message Date
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 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 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 ab0848293e feat(slack,telegram): stream files by handle
Both add-ons implement the streaming side of the connector route: they
read a file from the platform and put it to the host chunk by chunk, and
they pull the file to send from the host and stream it into the
platform upload. Neither holds more than one chunk in memory.

Slack uploads with the external upload flow and tells Slack the length
first. Telegram sends a multipart body of known length, and a picture
over 10 MB goes as a document, because sendPhoto refuses more. The
client of a streamed file has no total timeout; the context carries the
bound.

The manifests declare the limits that the platforms state, and
files.transfer. Telegram moves to manifest API 2 for it. The limits are:

  Slack: 1 GB per file, as in the help center article
    https://slack.com/help/articles/201330736-Add-files-to-Slack
  The upload API names no per-file cap
    https://docs.slack.dev/reference/methods/files.getUploadURLExternal
  Telegram, public Bot API: bots download up to 20 MB with getFile and
    send up to 50 MB (10 MB for photos)
    https://core.telegram.org/bots/faq
  A local Bot API server lifts both (no download limit, 2000 MB upload)
    https://core.telegram.org/bots/api#using-a-local-bot-api-server
    The add-on has no setting for another server, so it declares the
    limits of the public one.

Slack gets the host operation prefix connector. in the official host
policy, as Telegram has, so it can call the two file operations.

Assisted-by: Claude:claude-sonnet-5-5 go test
2026-11-03 15:54:45 +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 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
Ettore Di Giacinto 164d51f263 feat(connectors): read the builtin grants of the agent at the connector gate
The grants of an agent were stored and listed, but only the proposal kinds
read them. The connector gate now reads them too, so a tool exists when
the sender holds the capability on the binding, the agent holds the grant
and the chat did not switch the tool off.

It covers triggers.propose, triggers.run, talkback.channels,
talkback.new_channel and files.upload. Every question that goes through
the gate asks the grants: the capabilities of a turn, the check of a
feature that has a sender, and a run without a sender, so a trigger that
fires from a message pauses when the grant goes.

An agent with no grant list keeps the capabilities that are not
sensitive, as before. A sensitive one (talkback.new_channel) needs the
explicit grant. Once any grant is on the list, the list is the whole
answer. The act.* grants are not enforced: the action policy of unattended
runs that uses them does not exist yet.

Assisted-by: Claude:claude-sonnet-5-5 go test
2026-11-03 15:41: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 0921098539 test(addons): guard core against naming a shipped add-on
One manifest-driven guard replaces the Go-only provider guard. It reads
addons/*/manifest.json and fails when the Go strings of core and pkg,
the React UI source, the locale files or the core docs hold the id of an
add-on with a role (as a whole string or a tool-name prefix) or one of
its declared titles as a whole word.

Third-party mentions (OAuth login, release sources, token formats) and
ordinary words sit in a commented allow-list. The special cases that
remain (propose_change memory kind, the Account memory tab, the Telegram
conversion, the Tables listing shim) sit in knownDebt, each with the item
that removes it, and a ceiling that only goes down.

Reword the sandbox sentences in the container docs and use neutral
sample names in the UI catalog page.

Assisted-by: Claude:claude-sonnet-5-5 go test
2026-11-03 15:38:07 +00:00
Ettore Di Giacinto 15e2321d50 feat(connectors): edit the permissions of a binding on the shared connector page
Bindings saved before permissions were enforced have no capabilities, so
they deny files from senders, triggers, memory writes and talk-back, and
the owner could only fix that through the REST API.

The shared connector page now has a section for it, so every connector
add-on gets it: a switch and a description for each capability with the
sensitive ones marked, the role matrix (never a sensitive capability, never
above the ceiling), pairing with a code shown once, the approved channels
for talk-back, and a read-only view of the access check with the latest
decisions. A binding with no capability shows a notice that says why the
features are off, with an "Allow the usual" button that turns on the
non-sensitive everyday set.

The kinds endpoint returns the closed list of capabilities with a title,
a description and the sensitive and usual flags. The field is additive.

Assisted-by: Claude:claude-sonnet-5-5 go node playwright swag
2026-11-03 15:36:21 +00:00
Ettore Di Giacinto fd0f676722 fix(studio): stop the web tools group title from matching an add-on name
The provider guard flags any core string that contains the name of a
shipped add-on that provides a generic role. The web add-on has the id
and title "Web", so the built-in tools group titled "Web" in the tools
picker (and the schema example for it) failed the guard on master.

Rename the group title to "Look things up online", which matches its
"cannot look anything up online" off-state text. Update the schema
example and the Playwright fixtures and specs that read the title.

Assisted-by: Claude:claude-sonnet-5-5 go test eslint
2026-11-03 15:02:03 +00:00
Ettore Di Giacinto 6c8e479670 docs(api): regenerate the swagger files for the approval setting
Assisted-by: Claude:claude-sonnet-5-5 swag
2026-11-03 14:59:30 +00:00
Ettore Di Giacinto 69b89ac049 feat(studio): add one-off timers and plain-word answers to chat triggers
"Do it in 10 minutes" and "at 18:00" now work from a connector chat. The
agent proposes a timer with when {"in": ...} or {"at": ...}; it runs
the model once, reports to the chat it was set in, and is removed, also
when the run failed. A timer is at least one minute and at most a year
away. Recurring triggers keep their one-hour floor. Every proposal and
confirmation text states the cost: one model run.

A proposal is answered with a plain word in the same thread: yes, ok,
no, cancel. The code stays as a fallback when several proposals wait.
Only the whole message of an allowed sender counts, and only when the
capability gate allows it, so nothing the agent read can approve.

The approval setting of a binding now takes effect. In auto mode the
trigger is made at once and the chat is told how to cancel it; in
auto_readonly mode that holds only for a proposal that says its runs
only read. The owner's limits bound it, and a proposal over a limit is
asked instead, with the reason. Recurring triggers made this way end
after the lifetime.

Assisted-by: Claude:claude-sonnet-5-5 go
2026-11-03 14:59:30 +00:00
Ettore Di Giacinto eaea737b53 feat(connectors): let the owner choose how chat triggers are approved
A connector binding gets an approval setting: ask, auto or auto_readonly,
with four limits for the two automatic modes (live triggers, minutes
between runs of a recurring trigger, runs a day, lifetime in days). The
setting is generic, so every connector add-on can show it, and the
shared connector page of the add-ons now draws it. A binding that sets
nothing asks, as before.

This change stores, validates and exposes the setting. The next change
acts on it.

Assisted-by: Claude:claude-sonnet-5-5 go swag node
2026-11-03 14:59:30 +00:00
Ettore Di Giacinto 24c837e453 feat(studio): grant built-in capabilities to a saved agent
A saved agent now has a Builtins list: the capabilities its owner gave
it (memory.write, profile.write, skills.write, pages.write,
dashboard.write, triggers.propose, triggers.run, talkback.channels,
talkback.new_channel, files.upload, act.files, act.addons, act.send,
act.spend and the opt-in group saved_agents). The list is empty by
default, and nothing turns a grant on for a template, an import or a
write that omits the field.

A chat of the agent holds the grants the user may use at the turn
(agent scope). A tool exists when the grant is held, the chat did not
switch its group off and the run can hold rights: a sub-agent or a run
that started by itself never does. propose_change and revert_change
now exist only for a chat that holds the grant of at least one kind,
and the schema lists only those kinds.

propose_change gains the kinds skill, page and dashboard. A skill is a
SKILL.md for the personal or the agent layer, and a skill with
locked: true is refused, also as a shadow of a locked personal skill.
A page is a set of files checked against the Pages rules and written
as one change set. The dashboard is a home.json checked against its
schema. All three are re-checked when the owner confirms or edits.

GET /api/chats/agents/builtin-grants lists each grant with its title,
description and sensitive flag. The agent record, the write body and
the export file carry builtins; an old file imports as before and an
unknown name is dropped in an import and refused in a write.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt swag
2026-11-03 14:56:42 +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 b06faab397 test(studio): list send_message in the built-in registry spec
The registry spec knew every built-in tool by name. send_message is
governed by the capability of the binding and the operator switch, so it
has no group and no setting.

Assisted-by: Claude:claude-sonnet-5-5 go
2026-11-03 14:49:34 +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 11f7e2800a refactor(realtime): drop an external project reference from a comment
Assisted-by: Claude:claude-sonnet-5-5 git go
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
localai-org-maint-bot ef8f783f3c docs(api): explain Responses reasoning controls
Document reasoning.effort and request metadata forwarding for the Responses API.

Assisted-by: Codex:gpt-6
(cherry picked from commit e86876c6f3)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
Aniruddh Krovvidi f06ddbbb34 fix(responses): apply reasoning effort and request metadata
Signed-off-by: Aniruddh Krovvidi <akrovvidi05@gmail.com>
(cherry picked from commit 6ec73be618)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
nexxtmobile.de 8451212bd7 docs(realtime): explain transcript language tags
Assisted-by: Codex:gpt-6
Signed-off-by: nexxtmobile.de <git@nexxtmobile.de>
(cherry picked from commit 005a2d4ce8)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
nexxtmobile.de 60d2294338 fix(realtime): strip EOU language tags from the LLM context
ASR backends like Nemotron-3.5 append end-of-utterance language tags to
transcripts ("<de-DE>", "<en-US>", ...). The committed transcript feeds
the LLM conversation (the user item Transcript field), but the per-utterance
tag is unreliable for short/ambiguous utterances (a German "Hallo." tagged
<en-US>) and the LLM treats it as a language signal, switching the reply
language against the session instructions.

commitUtteranceWithTranscript now strips the EOU tags from the transcript
that goes to appendUserItem/generateResponse (the LLM context). The emitted
input_audio_transcription event keeps the raw transcript (debugging /
client display).

Regression tests: stripEOULanguageTags unit specs + a client-commit spec
asserting the emitted event keeps the raw tag while the LLM user message is
tag-free.

Signed-off-by: nexxtmobile.de <git@nexxtmobile.de>
(cherry picked from commit dff488044c)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
jaeminbird d9b2f9c900 fix(anthropic): honor object tool_choice on /v1/messages
Anthropic sends tool_choice as an object: {"type": "auto" | "any" |
"none"} or {"type": "tool", "name": ...}. The endpoint only read the
"tool" object, so {"type": "none"} still offered the tools and
{"type": "any"} did not require a call. The object was also forwarded
to the backend unchanged, and the llama.cpp backend turns any object
tool_choice into "required", so even {"type": "auto"} forced a tool
call there. Read the object form, and forward the OpenAI equivalent.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: jaeminbird <jaeminbird@gmail.com>
(cherry picked from commit 5bd07865a3)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
jaeminbird fb07f676a9 fix(anthropic): report max_tokens when the token budget is exhausted
/v1/messages always answered stop_reason "end_turn" for text replies,
even when generation stopped at max_tokens. Clients that continue a
truncated turn (the Anthropic SDKs, Claude Code) treated the cut-off
reply as complete. Use the same check the OpenAI endpoint uses for
finish_reason "length", in both the streaming and non-streaming paths.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: jaeminbird <jaeminbird@gmail.com>
(cherry picked from commit d905ab5fdc)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
Stefan Walcz c41469e7fb fix(backends): build whisper and parakeet-cpp for ROCm GPUs
whisper passed -DGGML_HIPBLAS=ON, which ggml no longer knows, so the
hipblas image was a CPU-only build (same issue as #10666 in
qwen3-tts-cpp). Use GGML_HIP with the ROCm clang and forward the gfx
targets. parakeet-cpp enabled PARAKEET_GGML_HIP but did not forward
the gfx targets; forward them the same way.

Docs-Not-Needed: build flags only, no user-facing behaviour or option changes.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de>
(cherry picked from commit 0cb6c908da)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
Stefan Walcz 9480996021 fix(gallery): keep the pinned revision's sha256 in the checksum bot
The checksum bot dropped the revision from URIs like
.../resolve/<revision>/<file> and asked Hugging Face for the file on
main. When the repo changed a file after the pin, the bot wrote main's
sha256 next to a URI that still downloads the pinned file, so the
install fails the checksum.

Pass the revision to get_paths_info and hf_hub_download.

Docs-Not-Needed: CI helper only, no user-facing behaviour changes
Assisted-by: Claude Code:claude-opus-5-5
(cherry picked from commit fe16ba6f1c, mudler/LocalAI#12995; line endings of the file kept as on master)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
localai-org-maint-bot c75c608f90 fix(gallery): pin Stable Audio SFX weights
The mutable upstream download no longer matches the gallery checksum.
Pin the file to an immutable revision with the published expected hash.
Regenerate the compatibility gallery and document installation.

Fixes #12924

Assisted-by: Codex:gpt-6
(cherry picked from commit 800855297b)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
mudler 5823baf4c3 ⬆️ Update ikawrakow/ik_llama.cpp
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit f86ab8c01e)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
mudler b20166fd5e ⬆️ Update antirez/ds4
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit d94d354b17)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
mudler ecb1d95daf ⬆️ Update 0xShug0/audio.cpp
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit 6d2f383f23)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 14:40:50 +00:00
Ettore Di Giacinto 474e3eefbe refactor(addons): pick engine job backends by gallery tag
The finetune.start and quantize.start operations defaulted to the
backend names "trl" and "llama-cpp-quantization", and the backend
lister had one method per engine family. Both were special cases that
a new engine could not use.

The lister now answers for a gallery tag. A start call that names no
backend gets the first installed backend of the tag in gallery order,
and a named backend must carry the tag. With no engine installed the
call fails with a 400 that says so. The shipped gallery order keeps
trl and llama-cpp-quantization as the defaults, and a test pins it.

The change is additive for add-ons: no manifest field or operation
changes.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt
2026-11-03 14:39:13 +00:00
Ettore Di Giacinto 6eb187dab0 test(ui): allow the GitHub service names in the provider strings test
The GitHub add-on made "GitHub" a provider name, so the test flagged three
strings that name the service itself: the OAuth sign-in button, the footer
link to the project repository and the "GitHub releases" package source in
the egress registries hint. None of them names the add-on, so add narrow,
reasoned allow-list entries for exactly those keys.

Assisted-by: Claude:claude-sonnet-5-5 node npm
2026-11-03 14:33:25 +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 b129694ffe feat(http): add an opt-in client address to the access log
Operators could not tell which client sends requests that are refused
with 401. Add --log-client (LOCALAI_LOG_CLIENT, off by default). When
set, the HTTP request log line also carries duration, remote, user_agent
and, when present, request_id.

The address is the TCP peer. It is never read from X-Forwarded-For,
X-Real-Ip or Forwarded, because LocalAI has no trusted-proxy setting and
any client can forge those headers. Behind a reverse proxy the value is
the proxy address. Authorization headers, cookies, query strings and
bodies are never logged. User agent and request id are cut to a fixed
length and have control characters replaced.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt
2026-11-03 14:32:55 +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 0772b68bd9 feat(ui): give the agent form the same Tools and Skills lists as the chat
The agent form offered a different set of choices than the chat: grants
as checkboxes for add-ons, connectors and skills, a free-text list of MCP
servers, and nothing for the built-in tool groups, so a person could not
tell which tools an agent can use.

The form now draws the lists of the Tools menu with the same components
(SwitchPanel, SkillsPanel) and the same rows (useToolGroups), fed by
useAgentTools: the draft lists of a new chat on the agent's model, with
the agent's grants and switches applied. A switch changes the form, and
the agent is saved with it:

  - built-in groups: builtins_off and builtins_on (an opt-in group such
    as saved agents starts on only when listed);
  - add-ons, connectors, skills: the existing grant lists, so what is
    switched on is what is granted. A granted entry this account cannot
    see stays in the list, marked, so it can be removed.

The network choice and the folder and mode choice stay where they were.
The MCP servers of the model stay a free-text list in their own fold,
since a model that is not chosen yet has none to list.

Assisted-by: Claude:claude-sonnet-5-5 eslint playwright
2026-11-03 14:30:07 +00:00
Ettore Di Giacinto f34e27e913 feat(ui): describe every switch of the chat Tools menu
The Tools menu built its rows inside the composer, drew only some of
them with a description, and showed a group it had no words for ("changes",
the memory and profile edits) by its raw ID.

The rows now come from useToolGroups, and every row has the same three
parts: a title, a description of what the model can do with the switch on
(and, once it is off, what it loses), and a state line (who serves the
tools, whether commands are isolated, why a switch cannot be used). The
words come from the server, which declares a title and a description for
each group and each add-on; a translation by group ID wins when the
locale has one, so a group added later needs no change here. An add-on
row shows the title its manifest declares instead of its ID.

SwitchMenu is split into the chip and SwitchPanel, and SkillsMenu into
the chip and SkillsPanel, so another page can draw the same lists inline.
New strings are in every locale.

Assisted-by: Claude:claude-sonnet-5-5 eslint playwright
2026-11-03 14:30:07 +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 0a731996ba fix(addons): copy credentials into declared capabilities
The host reads the declared capabilities of the installed generation to
decide whether a tool call needs a credential. Copy the new field, and
teach two guard tests that GitHub the service is not the add-on.

Assisted-by: Claude:claude-sonnet-5-5 [go]
2026-11-03 14:20:28 +00:00
Ettore Di Giacinto 3bbb40b6ff feat(addons): GitHub settings page, registration and docs
Add the GitHub connection page (fine-grained token, repository list,
read-only note, Test that checks each listed repository), let the shared
connector page hide messaging settings for a kind that carries no
messages, register the add-on in the package matrix and the catalog, and
document the credential slot and the GitHub add-on.

Assisted-by: Claude:claude-sonnet-5-5 [go node]
2026-11-03 14:20:22 +00:00
Ettore Di Giacinto 3d687a209c feat(addons): GitHub add-on skeleton and read tools
Add the github add-on: manifest with a credential slot, a read-only
tool set (code search, files, trees, issues, pull requests and diffs)
with size caps and untrusted-content framing, rate-limit mapping, a
connector route that checks the token, and a fake GitHub API for tests.

Assisted-by: Claude:claude-sonnet-5-5 [go]
2026-11-03 14:20:12 +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 b261f90c0b docs(api): regenerate the swagger files for agent folders
Assisted-by: Claude:claude-sonnet-5-5 swag
2026-11-03 14:11:47 +00:00
Ettore Di Giacinto 878c9ee9d7 docs(agents): describe several folders for a saved agent
Cover the folders field, the nested-folder mode rule, the new mount
names, the shared-folder budget that no longer refuses a chat, and agent
file version 3.

Assisted-by: Claude:claude-sonnet-5-5
2026-11-03 14:11:37 +00:00
Ettore Di Giacinto 81106dd0d2 feat(ui): edit several folders on a saved agent
The Files section of the agent editor is now a list of rows. Each row
has a folder picker, a read or write switch and a remove button. An
Add folder button stops at eight. A folder inside another one shows the
mode of the outer folder, because that is the mode it works at. The
agent chips, the Tools menu line and the Files page marks read the whole
list. The strings are in all nine locales.

Assisted-by: Claude:claude-sonnet-5-5 eslint playwright
2026-11-03 14:10:52 +00:00
Ettore Di Giacinto db596e1696 feat(studio): accept a list of agent folders in the API and the agent file
POST and PUT on /api/chats/agents take folders, a list of path and mode
rows, up to eight. The old folder field still works and is read as a
list with one entry. Responses carry folders, the effective mode of each
row and a notice with the count of shared folders a chat does not mount.

A folder inside another folder of the same agent takes the mode of the
outer one, because the file store gives a node the highest mode among
the roots above it and cannot hold a read only folder inside a write
folder. The editor data shows that mode.

An agent with one folder still exports as version 2. An agent with
several exports as version 3 with folders. A server that reads version 2
refuses version 3. Import keeps a folder that is not found as a row
that is not chosen.

Assisted-by: Claude:claude-sonnet-5-5 go gofmt
2026-11-03 14:02:02 +00:00