The handle leaves the table before the audit hook runs, so a loaded
machine could read the list too early. Wait for the outcome.
Assisted-by: Claude:claude-sonnet-5-5 go
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
The two connectors move files through connector.file.get and
connector.file.put but their manifests asked for nothing, so the install
dialog and blanket approval could not cover them. Declare both as
optional: a connector works without files. Delivery is pull, so neither
calls connector.inbound and it is not declared.
The audit of official packages now reads the operation constants of the
shared connector code. A deployment whose policy file already grants the
connector prefix sees no change.
Assisted-by: Claude:claude-sonnet-5-5 go
The install dialog and the host access page need a title, one sentence
and a risk word for each operation an add-on may ask for. Add
DescribeOperation next to the supported list, and a test that fails when
an operation has no description.
Assisted-by: Claude:claude-sonnet-5-5 go
Show the host operations an add-on asks for in the install dialog, hold
an upgrade that asks for more until an administrator approves it, and add
a Host access tab with the grants of each add-on, the deny rules and the
blanket switch. All strings are in every locale.
Assisted-by: Claude:claude-sonnet-5-5 playwright eslint
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
Shape the answers of the host-grants routes for display, build the
approval that an administrator sends with an install or an upgrade, and
let the install flow carry it. A typed refusal keeps its type and its
details so the pages can say what happened.
Assisted-by: Claude:claude-sonnet-5-5 playwright eslint
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
The three write mode cards now sit on one row when the form is wide
enough and stack on a phone. The repository hint is shorter so the two
line clamp no longer hides its end behind a More link at 390 px.
Assisted-by: Claude:claude-sonnet-5-5 playwright
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
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
An expiry ended a file handle while a put created the pipe and its
cancel function, which the race detector reports. The expiry must not
wait for a put that holds the handle lock, so guard the two fields with
a small lock of their own.
Assisted-by: Claude:claude-sonnet-5-5 go
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
The audit hook runs on the goroutine that closes a transfer while the
spec reads the list, which the race detector reports. Read the outcomes
through a locked copy.
Assisted-by: Claude:claude-sonnet-5-5 go
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
A poll that began before the add-on stopped could still be counted, so
the spec for a binding without its add-on failed at times. Take the
count after a short quiet and require that it does not grow.
Assisted-by: Claude:claude-sonnet-5-5 go ginkgo
The Tools list of the agent form gained six grant rows, so the count is
14 and the Triggers switch needs an exact name. Two folder specs also
went to the edit page, which has no facts panel, instead of the agent
page.
Assisted-by: Claude:claude-sonnet-5-5 playwright
A binding needs a running add-on that serves its kind, so the real
server of the UI tests, which runs none, answers 400 instead of
storing an inactive binding. The spec now checks that answer and that
nothing leaks. Switching and listing stay covered by the add-on specs.
Assisted-by: Claude:claude-sonnet-5-5 playwright
The server renamed the group title, but the English locale kept "Web"
and the Tools menu reads the locale first. The menu, the home composer
and the agent form showed a name that the specs and the server did not.
Assisted-by: Claude:claude-sonnet-5-5 playwright
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
NewAuthority reads the store on every call through an immutable view, so
a UI grant, revoke or deny applies to the next host call without a
restart. The file and flag deny rules do not depend on the store.
Assisted-by: Claude:claude-sonnet-5-5 go
The hostgrants package holds the owner document of host operation grants
made in the UI, the UI deny rules, the blanket switch and the hints used
to pre-tick a reinstall. Writes use compare-and-set on a revision and
operation receipts, with atomic persist like the host settings store.
The package also holds the pure policy logic: deny (file, flag, UI) over
file prefix grants over exact UI grants, with identity and pending-entry
checks, the grant plan (approved, declared, registered, not denied),
blanket evaluation, startup reconciliation and an fsynced audit file.
Assisted-by: Claude:claude-sonnet-5-5 go
A policy file may now carry a deny object. Deny rules name an add-on ID
or "*" and list prefixes, exact operation names or the single token "*".
Schema 1 files parse as before; a deny key in a schema 1 file, or a
version above 2, is an error that names the problem.
The selected file is now always opened, even when a flat
--addon-host-ops list replaces its grants, because a deny must not
vanish when someone adds a flag. An invalid selected file still stops
startup.
Add --addon-host-deny (LOCALAI_ADDON_HOST_DENY), unioned with the file,
and the --addon-host-blanket and --addon-host-ui-grants flags with their
config options.
Assisted-by: Claude:claude-sonnet-5-5 go
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
The broker, the supervisor and the manager each copy the grant map at
start. An Authority answers what an add-on may call right now, so a
later change can apply to the next call without a restart.
Add the interface, the Reason type and EffectiveSet, with a static
implementation over today's prefix map. Nothing uses it yet and
behavior does not change.
Assisted-by: Claude:claude-sonnet-5-5 go
The Indonesian chat namespace was completed, so the ratchet of the
completeness gate failed the UI unit tests until the baseline went down.
Assisted-by: Claude:claude-sonnet-5-5 node
The trigger form has a Permissions section that lists the act.*
grants like the grants of an agent: a plain-words line on each,
sensitive ones marked, and a grant the chosen agent does not hold
cannot be ticked. It says in a callout when a run only reads and why:
no acts, the agent holds none of them, the proposal said read only,
or a read only connection made it. The details page shows the same,
the trigger list marks a read only trigger, and the proposal card has
a Permissions row, so the person sees the acts before pressing Create.
The editor always sends the acts it loaded, so saving a form never
drops them silently. New strings are in every locale.
Assisted-by: Claude:claude-sonnet-5-5 node playwright
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
Many pushes to master left over a hundred runs queued, because every
push used its own concurrency group. Group push runs by ref and cancel
the in-flight run on master for the test, lint and E2E workflows. PR,
tag, schedule and dispatch runs keep groups that a master push cannot
cancel.
The intel cache job pushes one fixed tag, so it now keeps at most one
pending run per event and never cancels a running build.
image.yml and backend.yml are unchanged: they build from the diff of a
single push, so replacing a queued run could skip a code commit. Document
the policy in .agents/ci-caching.md.
Assisted-by: Claude:claude-sonnet-5-5 python3
The job downloads one artifact by name into a path, so the v5 single-ID path change does not apply. Every other workflow already uses v8. Applies mudler/LocalAI#12719.
Assisted-by: Claude:claude-sonnet-5-5 gh git
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Patch bump of the pinned protobuf in all transformers requirements files. Applies mudler/LocalAI#12408.
Assisted-by: Claude:claude-sonnet-5-5 gh git
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
The suite runs 16 specs that the inventory did not list and no longer
runs 2 that it did, after the legacy argument shims were removed. The
required-case comparison failed in the Add-on Tests job. List the specs
that run and drop the two that are gone.
Assisted-by: Claude:claude-sonnet-5-5 python
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
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
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
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>
The standalone 'local-ai mcp-server' HTTP client (httpapi.Client) called
GET / for both list_installed_models and system_info, expecting the legacy
welcome JSON (ModelsConfig / Version / InstalledBackends). That endpoint was
unregistered when the React UI moved to /app, so GET / now 301-redirects to
the SPA. The client uses LocalAI's hardened httpclient, which refuses all
redirects by default, so both tools failed with:
Get "/app": httpclient: redirect blocked: refusing to follow redirect ...
These two tools have never returned data over HTTP (the client was added
after / became a redirect). Repoint them at live, non-redirecting routes:
- list_installed_models -> GET /api/models/capabilities
(returns id/backend/capabilities/disabled/pinned; also makes the
capability filter, previously a no-op over HTTP, actually work)
- system_info -> GET /version + GET /backends + GET /system
The dead routeWelcome constant is removed and the three new routes are
added as route* constants per the package convention. Test fakes and the
Tool<->REST drift map in coverage_test.go are updated to the new routes,
and new specs cover list_installed_models (all / filtered / empty) and
system_info (version + backends + loaded models).
Assisted-by: LocalAI-Agent:qwen3.8-27b-heretic-abliterated-uncensored
Signed-off-by: L0ria <loria@hno3.eu>
(cherry picked from commit d123083bc0)
(cherry picked from commit f352b4278f764ad25902882d26afb761fa1b953d)
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)
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)
Connecting a connector must reach both MCP transports. Adds specs that resolve a
Streamable HTTP server promptly and a legacy SSE server, through the same
mcptransport.Connect path that agent runs use.
Signed-off-by: rossg <ross@golder.org>
(cherry picked from commit 4feaf9db39)
(cherry picked from commit fab3cfa21c13747e03115d7cc39f2466962f16e8)
Keep all three Swagger representations aligned with the request schema.
Assisted-by: Codex:gpt-6
Signed-off-by: Cristi An <c4xp@msn.com>
(cherry picked from commit 0e4c6ef69f)
(cherry picked from commit d0266e6dcc49eed99d77aa40fb723ab4cfa6480a)
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)
qwen3's chat template wraps <think> in {% if enable_thinking %}, but the
prefill detectors scan the raw template without evaluating the condition.
With streaming and no tools, the synthetic prefill made the Go-side extractor
classify the whole answer as reasoning whenever the client sent
metadata.enable_thinking=false.
Skip prefill detection when the request disables thinking. Real
<think>...</think> blocks that a model emits anyway are still extracted from
the content itself.
Fixes#12939
Assisted-by: ZCode:deepseek-flash
Signed-off-by: xianshujun <xianshupang@gmail.com>
(cherry picked from commit cee7ed4b32)
(cherry picked from commit 29618866d07a05624934bf9f595c95a68c938461)
Signed-off-by: Dedy F. Setyawan <dedyfajars@gmail.com>
(cherry picked from commit 2413f19b7d)
Conflict resolved against current master: keep the strings master already
has for chat.json and add the translations this change supplies for keys
master lacks. Keys added to master after the PR was opened are still
untranslated and fall back to English.
Assisted-by: Claude:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
(cherry picked from commit 8d4799d91b33b2af204e7cc5386d8b984d09f04b)
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)