Commit Graph
8889 Commits
Author SHA1 Message Date
Ettore Di Giacinto 4fbfc4586b fix(addons): keep the cause of a connector failure
localConnectorCall and the kinds lookup turned every error from the
connector source into a bare ErrUnavailable. VerifyTokenWithMe treats
that as "could not tell" and saves the binding, so a definite refusal by
the add-on was indistinguishable from an outage, and the cause was lost.

An add-on refusal (invalid request, or denied for this person) now keeps
its type through Refused. Other errors still read as ErrUnavailable but
wrap the cause, so errors.Is and logs see it. The forwarding carrier maps
the owner's 400 back to ErrInvalidRequest instead of ErrUnavailable.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:45:52 +00:00
Ettore Di Giacinto 5295c932c0 fix(addons): check the private egress phrase on the owner
With add-ons enabled /api/settings rejects chats_egress_allow_private, so
the typed phrase check there never ran on the only write path, the
host-settings-update action. The page asked for the phrase but dropped it
from the patch, and a direct admin call could turn the switch on without
it.

The patch now carries chats_egress_private_confirm and hostsettings.apply
rejects turning the switch on without it. The phrase is hashed into the
operation receipt like the rest of the patch and is never stored. The e2e
fixture enforces the same rule and the egress spec asserts the phrase in
the owner patch again.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:45:52 +00:00
Ettore Di Giacinto 022e33f5d6 docs(addons): document add-on roles, distributed owner, delivery and Sandbox isolation
Add a table of what runs in the combined, owner and frontend roles, and
explain that an owner must join distributed mode with the frontends.
Describe pull and push connector delivery, the one-connection-per-Slack-app
rule and the reinstall needed for new Slack scopes. State what the Sandbox
needs to report an isolated backend, and that gVisor needs no /dev/kvm.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:41:10 +00:00
Ettore Di Giacinto 7e14a26a15 feat(addons): expose the shared model picker to add-ons
sdk.ui gains ModelPicker (multiple, allowCustom, clearable, field
variant, alias tags). HomeModelPicker and ModelSelector stay as names for
the same picker so existing add-ons keep working. The add-ons guide
lists the new component and its props.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:40:07 +00:00
Ettore Di Giacinto 6ffc9e82c1 feat(ui): use the shared model picker everywhere
Settings, config fields, the failover and router editors, the agent task
form, the scheduling rule sheet and the agent job form now use
ModelPicker. SearchableModelSelect, ModelMultiSelect and the scheduling
ModelPicker are removed; ModelSelector stays as the name add-ons use and
draws the shared picker. Aliases come from the capabilities listing, so
the scheduling sheet no longer builds its own hints.

A broken alias is listed, marked and not selectable. The picker's style
sheet now loads with the picker, so the e2e specs that read a settings
group right after opening it wait for its first field.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:40:07 +00:00
Ettore Di Giacinto dbb8e8a022 feat(ui): make the home model picker the shared model picker
Move HomeModelPicker to components/ModelPicker and give it what the
other pickers had: multiple selection with chips, free text, a clear
button, a search box on demand, a form-field look, and loading and
empty states. Every entry shows 'alias of X', disabled, pinned or
broken-alias tags from the capabilities listing, and entries that
cannot be used are not selectable. Auto-select skips them.

Add-ons keep HomeModelPicker as a name for the same component.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:40:07 +00:00
Ettore Di Giacinto 688b12a038 feat(ui): resolve alias models fully in the capabilities listing
An alias entry now takes its capabilities, backend, MCP, voice cloning,
upscale scale, 3D operations and loaded state from the model it points
to, and keeps its own name, disabled and pinned state. An alias that
cannot be resolved (unknown target or a chain) is logged at warn and
listed with alias_of and alias_error instead of vanishing.

The chat model checker refuses a broken alias when it is saved.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:40:07 +00:00
Ettore Di Giacinto fc087c13e8 fix(ui): style the host controls of an add-on like the Settings page
The controls sit in a fieldset, which drew the browser border and
padding around the rows. The rows also showed raw setting keys such as
chats_egress_ceiling next to their labels.

Reset the fieldset, hide the raw keys in the add-on controls, and make
the row label fall back to the key in words, so a snake_case key is never
a label. Add e2e and unit tests that fail when a raw key is drawn.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:40:00 +00:00
Ettore Di Giacinto 4eab1148be fix(ui): keep the chat following the bottom after a send
A smooth scroll to the bottom from far up reports a distance above the
pin threshold on its way down. The scroll handler read that as the reader
scrolling up and unpinned the thread, so the new message and the answer
that streamed in after it stayed out of view.

Only a move up unpins the thread now. A reader who scrolls up while an
answer streams is still left where they are.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:40:00 +00:00
Ettore Di Giacinto 9fafea8fdf fix(chats): show the network switcher and say why execution status is unknown
The Network row of the Tools menu could never show the Off, Registries and
All switcher: the host stopped setting `applies` because isolation alone does
not prove network-policy enforcement, and nothing else set it.

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

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

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

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

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:39:30 +00:00
Ettore Di Giacinto 024b7196de fix(mcpconns): guard the dial when private addresses are allowed
With mcp_connectors_allow_private on, a connector used a plain transport
with the environment proxy and no address check, so its URL could reach
cloud metadata addresses. Build the private path on the safehttp guard
with private addresses allowed: it resolves once, checks every answer,
and keeps the metadata addresses blocked. An optional DenyAddrs hook
refuses further addresses, for example the server's own.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:39:12 +00:00
Ettore Di Giacinto 59e3f01739 fix(safehttp): block more cloud metadata addresses and NAT64
Add the Azure wire server (168.63.129.16), Oracle (192.0.0.192) and
Alibaba (100.100.100.200) addresses to the metadata list. The Azure one
is public, so web_fetch reached it. Check the IPv4 address inside a
64:ff9b::/96 address too, so private addresses and metadata addresses
cannot be reached through NAT64 when private addresses are allowed.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:39:12 +00:00
Ettore Di Giacinto 1b90305125 fix(sandbox): cap the default bash timeout at the maximum
A call with no timeoutSeconds ran for the 120 second default even when
an administrator set a lower maximum. Use the smaller of the two.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:39:12 +00:00
Ettore Di Giacinto e06a6244c8 fix(sandbox): clean up and back off after a failed idle suspend
The sweep only logged a failed suspend and tried again every minute,
holding the entry for up to a minute each time. When the guest was gone
the entry kept pointing at the dead sandbox, so the chat's bash failed
until the sandbox reached its maximum age.

Drop the sandbox on ErrGone so the next call makes a fresh one. For any
other error, wait longer after each failure (two minutes, doubling to
thirty) and reset the wait on the next call. The log now names the user
and conversation.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:39:12 +00:00
Ettore Di Giacinto d1aa963ba1 fix(studio): refuse a host command too long for its approval card
The approval card cut the command to 2000 bytes while the full text ran,
so a prompt-injected model could hide a tail behind harmless text. Refuse
the call with a message instead, so what the person allows is what runs.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:39:12 +00:00
Ettore Di Giacinto 64e0ecd3f8 fix(studio): wake the waiter of a sub-agent whose turn was reaped
A parent waits on a channel that only the end of the child turn on the
same process fills. When the reaper ended the child turn as dead, nothing
filled it, so a background report blocked until the server stopped.
ReapStale now reports an interrupted status to the local waiters.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:53 +00:00
Ettore Di Giacinto 2f03946238 fix(studio): bound the model call of a compaction
A compaction ran on a context with no deadline, so a model that never
answered held the chat busy for good. Give it a 10 minute limit.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:53 +00:00
Ettore Di Giacinto d1954be534 fix(studio): read chats without taking a write lock
GetChat of the SQL store opened a write transaction with a no-op update
on every read, and the JSON store saved the record on every read, only
to rewrite the legacy builtin:docx style markers to addon:docx.

Rewrite the markers once when the store opens, in an idempotent step that
changes a row only if it still holds the value it read. A read now
normalizes the markers in memory and never writes. A failed rewrite is
logged and does not stop the store.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:53 +00:00
Ettore Di Giacinto 8079414946 fix(studio): cut sub-agent titles on a rune boundary
The title of a sub-agent chat was cut at a byte count and could end in
the middle of a UTF-8 character. Use the rune-safe CapText. Also use the
slices package for the tool lookup and the waiter removal.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:53 +00:00
Ettore Di Giacinto dafaa33590 fix(studio): keep queued messages when a turn is stopped
Stop dropped the messages that waited for the next turn, although they
were in the transcript already, so the person saw them but the model
never did. They now stay queued and go to the model, ahead of the next
message.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:53 +00:00
Ettore Di Giacinto 55ad3d0b28 fix(studio): cap the messages that wait for the next turn
The queue of messages that wait while a turn ends or the harness swaps
had no limit. Cap it at 32 messages and 256 KiB, and refuse a message
over the cap with ErrTurnRunning before it reaches the transcript.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:53 +00:00
Ettore Di Giacinto 8ff3fb8780 fix(studio): do not refuse a message that is already in the transcript
When a turn was stopped between Send logging a message and queuing it,
Send returned an error although the message was in the transcript, so a
client retry logged it twice. The message now waits for the stopped turn
to end and starts the next one.

The fake harness gets an Inject hook and a step that ignores a stop, to
reach that window in a test.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:53 +00:00
Ettore Di Giacinto cc0ab51438 fix(studio): time turn heartbeats with the database clock
A turn beat was written with the clock of the frontend that runs the
turn and compared with the clock of the frontend that reaps. A skew of
about the turn timeout made the reaper end live turns.

ReapTurns now takes an idle duration, and the GORM store reads the time
from the database for the claim, the beat and the cutoff. The JSON store
is single-process and keeps the local clock.

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

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

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

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

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

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

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

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

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:35 +00:00
Ettore Di Giacinto 81dede330f refactor(studio): cheaper blob Exists and accurate filesystem docs
Exists checked a blob by opening and closing it. After the symlink
check, one Lstat is enough.

NewFS said it needs exclusive creation, but publication renames over an
identical blob, which is harmless because the name is the hash. LinkTo
on the filesystem store copies and never links; say so.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:35 +00:00
Ettore Di Giacinto 50177f8e2b fix(studio): stage workspace uploads outside the revision lock
With PostgreSQL and a filesystem blob store, Put and CommitUpper took
the global revision row lock before reading the upload. A slow or large
upload then blocked every other write and purge and kept a pool
connection for its whole duration.

Split the filesystem blob Put into Stage (read, hash, fsync a temp
file) and Publish (rename). The workspace stages first, takes the lock,
then publishes, so a purge still cannot remove a blob between
publication and the row that references it. Publish syncs only the
folder that gains the name; newly created folders are already synced by
mkdir.

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

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

The combined and frontend roles are unchanged.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:38:11 +00:00
Ettore Di Giacinto 51e120c00d test(studio): give the fence probe turns a bound that fits a slow runner
The agent folder probes run one turn that calls about a hundred real file
tools on the stub tool harness. Under -race that turn takes 7 to 25 s, and
on a loaded CI runner it passed the 20 s bound of until, so the spec
"finds the token through the same probes when the agent has no folder"
failed with "timed out waiting for the turn to end". The turn always
ends; the time goes to race-instrumented SQLite statement parsing and a
one-time PDF engine compile, not to a wait or a lock.

Add untilWithin and use a five minute bound for the end of a probe turn
and for settling. The turn is still checked for its end, so a turn that
never ends still fails the spec.

Assisted-by: Claude:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:30:08 +00:00
mudler-agentandEttore Di Giacinto 579e20ab11 fix(ci): repair the failing master checks (#13051)
* test(ui): fix stale and timing-bound Home and NotFound specs

The Home slash menu spec dropped /skill from its expected list, but the
test server always keeps built-in skills, so /skill is offered. Restore
the expected list.

The NotFound spec loaded fourteen removed addresses in one test. A CI
trace showed about four seconds per address, so the test passed the 30
second limit before the loop ended. Run one test per address.

The Home skeleton spec delayed the answer by a fixed 800 ms, which a
slow runner can spend before it paints. Hold the answer until the
skeleton was seen.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): register new Ginkgo cases in the suite inventory

The harness compares the cases a suite reports with the cases listed in
scripts/addons/suites.json. The Sandbox document skills change added
five packaging cases that no entry listed, and the apps entry listed 265
cases while its four reports list 274. Both suites failed with an
inventory mismatch although every spec passed.

List the five packaging cases in the contracts entry and its packaging
report, and the nine missing apps cases in the apps entry.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(ui): let the route change finish before counting add-on reads

The apps polling spec leaves the add-ons page for a lazily loaded route
while the page clock is paused. The transition does not finish until the
clock moves, so the first step of the 180 second run still saw the
add-ons page mounted and fired one more read. The spec failed in four of
six local runs. Its sibling spec already advances the clock and settles
before it counts.

Do the same here, and wait for the NotFound page so the check is made on
a page that no longer holds the add-ons list.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(studio): stop the scoped-chat spec timing out on the nib harness

The spec "opens a scoped chat with load_skill without reentering session
access resolution" opened its chat on the real nib harness. Starting a
nib session takes about a second under -race and more on a loaded CI
runner, so Open sometimes passed the two second budget and the spec
reported a re-entrant resolution that did not exist. The distributed
test job failed on it while the plain tests job passed.

Run the spec on the stub harness, so Open only builds the chat tools,
which is what the spec is about. Widen the hang guard to 30 seconds: a
real re-entry blocks on the open lock for good, so any finite bound
tells it from a slow run.

Assisted-by: Claude Code:claude-sonnet-5-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-03 08:34:58 +01:00
mudler-agentandEttore Di Giacinto 7904b3d0ab feat(addons): declare typed host capabilities (#13034)
* feat(addons): model execution status observations

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

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

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

* feat(addons): visualize execution status traces

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

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

* fix(addons): honor the viewer artifact root

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

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

* feat(addons): add typed extension contracts

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

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

* fix(addons): reject noncanonical extension keys

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

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

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

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

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

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

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

* feat(addons): project typed home commands

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

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

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

* fix(addons): preserve typed lifecycle declarations

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

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

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

* fix(addons): stabilize fixture archive digests

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

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

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

* feat(addons): resolve explicit host policy grants

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

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

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

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

* feat(addons): select explicit deployment grants

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

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

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

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

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

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

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

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

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

* test(addons): guard host operation registry parity

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

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

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

* feat(addons): fence status observation effects

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

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

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

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

* fix(addons): drain callbacks before retirement

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

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

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

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

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

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

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

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

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

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

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

* test(addons): compose owner status with runtime

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

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

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

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

* feat(studio): scope execution status observations

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

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

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

* fix(studio): isolate consumer cancellation

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

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

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

* feat(addons): track selection mutation epochs

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

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

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

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

* feat(addons): expose owner binding transport

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

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

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

* feat(studio): fence selection snapshots with epochs

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

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

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

* fix(studio): enforce persistent JSON ownership

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

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

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

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

* test(application): isolate warning log capture

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

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

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

* feat(studio): resolve local owner execution bindings

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

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

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

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

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

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

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

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

* feat(studio): consume typed execution providers

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

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

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

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

* test(application): isolate auth startup lifetime

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

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

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

* feat(ui): resolve installed Home app commands

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

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

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

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

* fix(ui): accept resolved Home command metadata

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

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

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

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

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

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

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

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

* fix(ui): isolate command status across sessions

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

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

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

* feat(addons): declare audited host requests

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

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

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

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

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

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

Assisted-by: Codex:gpt-5

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

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

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

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

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

* fix(studio): resolve current execution selection

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

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

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

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

* refactor(addons): drop selection revision tracking

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

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

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

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

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

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

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

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

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

* fix(ui): guard composer and session publication

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

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

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

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

* fix(ui): expire cancelled upgrade reconciliation

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

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

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

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

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

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

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

* refactor(addons): remove implicit sidebar pins

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

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

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

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

* feat(addons): declare typed model destinations

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

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

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

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

* refactor(ui): resolve declared model launch actions

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

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

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

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

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

* fix(addons): validate Ginkgo hooks before focus

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

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

* fix(addons): restore Go CI contract checks

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* refactor(addons): share qualified tool identities

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

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

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

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

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

* refactor(addons): share execution declarations

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

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

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

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

* test(addons): retain document skill cases

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

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

Assisted-by: OpenAI:gpt-6.1-sol

* test(addons): follow owner-managed upgrades

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

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

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

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 23:50:40 +00:00
mudler-agentandEttore Di Giacinto 10722d8e3b fix(addons): run accepted upgrades on the owner (#13035)
* fix(addons): run accepted upgrades on the owner

HTTP cancellation during a verified backup can prevent package commit.
Accept browser upgrades durably before replying and execute them under the
owner lifecycle, with bounded admission and same-operation polling.

Keep synchronous lifecycle callers unchanged. Reuse the publication and
data recovery journals rather than replay interrupted migration. Expose
real stages and safe terminal causes, and recover browser observers after
reload without resubmitting work.

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

* fix(addons): isolate upgrade observers by session

Reset action identities when the principal changes and restore pending
identities before polling. A different account must never submit an ID
owned by the previous account.

Clear stored upgrade observers directly in logout before navigation.
Cleanup also handles a fresh store with no bound principal.

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

* refactor(addons): use lifecycle phase constants

Share named lifecycle phases between the journal and upgrade reducer.
Use typed events in transitions and trace tests instead of repeating wire
literals. Keep operation DTOs, serialized values and rule labels unchanged.

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

* fix(addons): verify accepted upgrades to completion

Preserve typed pin refusals before accepting an upgrade and report the
same transition conflict while execution holds the manager lock.

Poll accepted operations in HTTP and real-process acceptance tests before
asserting publication or failure. Exercise reload and truthful stages in
the rendered Activity test, retaining version details without cancellation
controls or estimated percentages. Register the new verification cases.

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

* test(addons): await the Tables upgrade receipt

An accepted upgrade can still serve its previous generation. Poll the
same operation through another frontend until it reaches a terminal state
before checking the new version, retained data, grants and backup.
Verify that a repeated submission returns the same completed receipt.

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

* test(addons): poll the winning upgrade operation

Concurrent submissions return acceptance before publication. Observe the
winning browser session and operation until completion before asserting
the installed generation. Keep the losing transition conflict check and
require the winning ID to replay the same terminal receipt.

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

* test(ui): model authoritative owner settings

Host controls now read and patch revisioned owner policy through dispatch.
Update rendered fixtures to serve that strict envelope rather than local
runtime settings. Keep private confirmation browser-only, send per-key
provider changes, and keep node-local audit retention outside owner forms.

Preserve the skill default in the tools fixture so an unpinned choice
matches the provider the owner reports.

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

* test(ui): assert the owner policy write envelope

Verify that the private-network switch sends no owner write before its
confirmation phrase. After confirmation, assert the actual dispatch action,
operation identity, expected revision and policy-only patch.

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

* test(addons): cover shutdown and lifecycle exclusion

Exercise owner shutdown during the real backup path and verify Close
joins cleanup before releasing storage. Cover synchronous disable while
an upgrade is accepted, including the journal guard before reconciliation.

Document that accepted upgrades have no UI cancellation control. Register
the new lifecycle cases in the canonical suite inventory.

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

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 22:47:17 +01:00
mudler-agentandEttore Di Giacinto 8f9a0b281d test: update stale tests after the add-on owner and Sandbox skill changes (#13045)
* test(e2e): use a core built-in skill in the agent journeys

#13038 moved the docx, pdf, pptx and xlsx skills into the Sandbox
add-on, so core no longer embeds them. Three journeys still named docx
and xlsx as built-in skills. The backend e2e suite then failed: reading
/builtin/docx answered 404, and the system prompt had no docx entry.

The journeys now use localai-apps and localai-workspace, which core
still embeds. They keep their intent: an agent lists only the skills it
names, a plain chat lists all of them, and built-in skills are readable
but cannot be deleted.

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

* test(ui): serve owner host settings in the add-on e2e fixtures

#13033 moved the network policy and the skill provider pins to the
add-on owner. The add-on Settings tab and the skills switch now read
and write them with the host-settings-read and host-settings-update
dispatch actions. The specs still mocked only /api/settings, so the
owner read failed and the network block never rendered. Six specs
failed on master.

mockAddons now answers both actions from a revisioned policy, checks
the expected revision, applies patches like hostsettings.Patch and logs
each patch. The specs assert the owner patch instead of the
/api/settings POST. They also follow two other changes of #13033: the
audit retention stays on the Settings page, and *.example.com is a
valid host pattern.

The document skill wording follows #13038: an add-on provides docx and
xlsx, it does not replace a built-in.

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

* test(routes): port the UI capability tests to Ginkgo

The capability tests from #13024 used t.Fatal and t.Errorf, which the
forbidigo lint rule rejects. The routes package already runs a Ginkgo
suite, so the two tests are now specs in it, with the same models and
the same assertions.

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

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 22:09:45 +01:00
mudler-agentandEttore Di Giacinto 8b4f3c35e8 fix(ci): use native workflow path filters (#13037)
* ci: select affected checks and share add-on builds

Keep validation check identities present while omitting expensive steps
when explicit dependency rules prove they are unaffected. Unknown inputs
and diff failures retain broad validation.

Select add-on package legs on PRs and master without weakening catalog
checks or changing release publishing. Manual package runs validate only,
including their reusable Sandbox guest image dependency.

Share bounded, same-run production fixtures across the five add-on test
groups. Verify checkout identity, tools, paths, modes and checksums before
using them. Keep instrumented coverage builds separate.

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

* fix(ci): validate documentation test contracts

Documentation is executable input to Tables, recovery, diagnostics,
gallery and other add-on tests. Select all domains for docs changes
instead of treating the whole directory as inert. Cover known readers,
unfamiliar paths and deleted guides in classifier regression fixtures.

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

* fix(ci): gate affected jobs before allocation

Classify each workflow once and gate whole expensive jobs. Limit add-on
matrices to affected packages and host groups instead of allocating all
runners before deciding whether their steps should run.

Select documentation readers explicitly and derive host consumers from
local Go imports. Keep unknown source inputs conservative. Preserve
coverage, production fixtures, publish locks and manual full validation.
Remove the unrequested weekly schedules.

Reject classifier failures and unexpected dependency skips in stable
aggregate checks. Document required-check migration without claiming
access to protected-branch settings. Test actual YAML job and matrix
eligibility against representative paths and the original triggers.

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

* fix(ci): validate executable and fixture seams

Include executed fixture binaries and their production imports in host
selection. Lexical Go imports alone miss the mock backend started by
the UI test server and add-on suites.

Rebase restored artifact timestamps only after identity and checksum
validation. Preserve the existing stale-source checks across a later
consumer checkout and reject subsequent source edits.

Validate package plan outputs before allowing dependency skips. Missing
or malformed plans must not turn failed publication preparation green.
Cover all three review findings with before-fix reproductions and
actual workflow eligibility regressions.

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

* fix(ci): use native workflow path filters

Replace calculated dependencies with visible PR and master path lists.
Run complete area suites without classifier runners or result plumbing.
Restore original packaging validation and publication dependencies.

Keep one production fixture build for the five original add-on groups.
Verify artifact identity and freshness without changing suite inventories.
Document allowlist maintenance and required-check migration limits.

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

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 21:16:37 +01:00
mudler-agentandEttore Di Giacinto 74e293ef38 feat(sandbox): own document authoring skills (#13038)
* feat(skills): reserve document provider slots

Separate document provider slots from protected core skill names.
Keep existing same-name shadows declarations compatible.

Normalize legacy document OFF markers in JSON and database stores.
Persist normalization under the existing lock or transaction so an
explicit re-enable cannot be overwritten by a stale read.

Cover persistence, rollback, ownership and concurrent replacement.
Refresh the contracts inventory from the passing Ginkgo report.

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

* feat(skills): gate Sandbox document procedures

Resolve document providers against the selected isolated command backend.
Keep Office guidance available without command execution and preserve
explicit provider pins when their backend is unavailable.

Reuse authorized chat access during tool construction to avoid reentering
the session lock. Recheck access and capabilities when loading guidance.

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

* fix(skills): resolve saved-agent child command grants

Describe saved-agent child commands from their current agent scope rather than the inherited tool map reserved for ad-hoc children. Cover index, picker and loader behavior for permitted, absent and revoked Sandbox grants without changing saved skill selections.

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

* feat(sandbox): install and exercise guest-owned PDF helpers

Preserve PDF helper sources and MIT attribution in the guest image. Exercise valid, empty-text and defective PDFs during the image build and through the packaged isolated command route. Register the packaging regression in the exact suite inventory.

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

* feat(sandbox): own document skills and consolidated references

Move the four document authoring trees out of core and package their text-only procedures with Sandbox. Preserve source coverage with offline fingerprints, guest PDF helper paths, and package-local license notices.

Exercise actual skill receipt materialization across install, upgrade and uninstall; keep stale built-in cleanup and report-derived suite inventories current.

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

* feat(ui): explain document skill providers and unavailable selections

Show provider-owned document procedures without removed built-in fallbacks, retain remaining built-in replacement notices, and translate install guidance in every locale. Cover picker labels, saved names and real API OFF normalization, and document grants, isolated execution and unchanged extraction.

Acknowledge the intentionally injected GORM callback error in preference regression tests for lint.

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

* docs(agents): fix document guidance formatting

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

* fix(ui): correct rebased skill label import

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

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 21:03:50 +01:00
localai-org-maint-botandmudler 5b2414f6bc chore(model-gallery): ⬆️ update checksum (#13021)
⬆️ Checksum updates in the model library

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: mudler <2420543+mudler@users.noreply.github.com>
2026-11-02 18:59:58 +01:00
localai-org-maint-botandmudler 547027221f chore: ⬆️ Update mudler/vllm.cpp to a087e3bf7fd61e838e848ff44dec44f8267751ca (#13020)
⬆️ Update mudler/vllm.cpp

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: mudler <2420543+mudler@users.noreply.github.com>
2026-11-02 18:59:43 +01:00
localai-org-maint-botandmudler d977ccccb6 chore: ⬆️ Update ggml-org/llama.cpp to 785322d17f2d51e5185cf996a1f4e46d87640841 (#13019)
⬆️ Update ggml-org/llama.cpp

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: mudler <2420543+mudler@users.noreply.github.com>
2026-11-02 18:59:23 +01:00
localai-org-maint-botandmudler f6c5d223c5 chore: ⬆️ Update leejet/stable-diffusion.cpp to 290148d2e31ca6ef9757d567905776b5f3646db5 (#13018)
⬆️ Update leejet/stable-diffusion.cpp

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: mudler <2420543+mudler@users.noreply.github.com>
2026-11-02 18:59:10 +01:00
mudler-agentandEttore Di Giacinto a9e19746be fix(addons): make owner settings authoritative (#13033)
* feat(addons): add a durable host policy store

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

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

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

* fix(addons): reject malformed host policy JSON

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

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

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

* fix(addons): decouple host policy validation

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

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

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

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

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

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

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

* fix(addons): keep policy provenance private

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

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

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

* feat(addons): initialize owner host settings

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix(addons): guard legacy settings writes

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

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

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

* test(addons): use the existing Ginkgo suites

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

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

* feat(egress): bound wildcard host policy grants

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

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

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

* fix(egress): authorize each new connection

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

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

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

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

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

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

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

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

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

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

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

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

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

* fix(addons): finish owner settings controls

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

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

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

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

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

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

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

* fix(addons): document guarded settings IO boundaries

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

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

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

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

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

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

Verified gateway/application authority race tests and scoped lint.

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

* test(addons): inventory owner policy gateway cases

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

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

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

* test(ui): scope library settings apply action

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

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

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

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

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

Verified focused application and route race tests and scoped lint.

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

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

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

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

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

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 18:48:39 +01:00
mudler-agentandEttore Di Giacinto 16acc8926b feat(addons): simplify Office and discover Tables (#13029)
* feat(tables): list accessible databases

Let agents discover existing databases through the host gateway.
Keep listing actor-scoped and separate from executable tools.

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

* test(tables): verify database listing authority

Exercise model discovery through the real owner and carrier.
Check shared access, revocation, denial and resource limits.

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

* feat(tables): bundle database search guidance

Teach agents to discover existing databases before querying records.
Package and verify the skill with the Tables add-on.

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

* test(tables): cover packaged chat skill gates

Load the real receipt-verified Tables instructions through the temporary
Studio workspace. Check the index and load refusal after runtime, chat
switch and saved-agent grant changes.

This fixture starts at the receipt and broker boundary; packaging tests
cover artifact construction and integrity separately.

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

* feat(office): parse Markdown and CSV inputs

Convert bounded text inputs into document and workbook structures.
Render supported Markdown formatting without fetching external content.
Preserve spreadsheet fields as strings rather than inferring formulas.

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

* fix(office): preserve list markers, breaks and PDF page margins

Cover actual DOCX breaks and tabs, explicit zero list starts, empty list rejection, and PDF margin/pagination regressions.

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

* fix(office): guard Markdown parsing work

Check cancellation and nesting during parser callbacks.
Validate table dimensions before padded table allocation.
Keep parser state isolated to each conversion.

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

* fix(office): recognize tables before applying limits

Leave mismatched table-like paragraphs as ordinary Markdown.
Keep oversized real tables bounded before allocation.

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

* feat(office): simplify document creation inputs

Accept Markdown for DOCX and PDF, and CSV for single-sheet XLSX files.
Replace nested creation schemas to reduce model tool grammar complexity.
Keep existing read, edit and PowerPoint behavior and internal office IDs.

Present the add-on as Simple Office and document the compatibility change.
Keep same-style Markdown fragments editable in generated DOCX files.

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

* fix(ci): align add-on fixtures and test inventories

Tables fixtures must carry the skill their manifest declares. Preserve
its bytes in package tests and mark only binaries executable in UI
fixtures. Assert that installation keeps the shipped skill intact.

Register the new Tables tests and the connector case from master in
exact suite inventories. Include the new database read in tool counts.

Run Office regressions through Ginkgo and check reader close errors.
Keep all document assertions and the existing CI gates intact.

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

* fix(ci): restore Office and packaging checks

Keep the generated Office label consistent with Simple Office. Install
Poppler for the PDF layout regressions on Linux and macOS.

Separate successful runtime setup from the 300 ms deadline under test.
Keep the timeout, cancellation, stop and admission assertions intact.

Refresh the packaging inventory from all 209 executed cases, retaining
repeated platform checks and the Office and Tables skill checks.

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

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 12:48:08 +01:00
localai-org-maint-botandmudler 11fad911c4 chore(website): refresh the counters (#13028)
Co-authored-by: mudler <2420543+mudler@users.noreply.github.com>
2026-11-02 08:36:59 +01:00
mudler-agentandEttore Di Giacinto a6589e4e60 fix(connectors): preserve disabled add-on discovery (#13030)
fix(connectors): preserve discovery when add-ons are disabled

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

Assisted-by: nib:gpt-6.1-sol

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 08:36:45 +01:00
mudler-agentandEttore Di Giacinto 9206959564 fix(connectors): route calls through the owner (#13026)
* fix(connectors): route calls through the owner

Route connector discovery and calls through the private carrier so
runtime-free frontends use the owner runtime. The owner validates each
call scope and loads stored credentials rather than trusting caller tokens.

Limit prospective token checks to identity requests. Cover pinned peer
admission, scope refusals, stored credentials, and bounded decoding through
the actual carrier.

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

* fix(connectors): preserve host authority denials

Reject connector creation and token replacement when the owner denies
access. Keep transport and platform outages permissive during token
verification. Cover both cases through the real carrier.

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

* fix(connectors): distinguish authority outages

Preserve ErrUnavailable when the connector authority cannot serve calls.
Only explicit authority denials should reject otherwise complete saves.

Cover create and token replacement during an authority outage, including
persisted bindings and subsequent calls through the owner.

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

* test(connectors): cover owner routing contracts

Exercise owner routing through the authenticated carrier, including
polling, cancellation, payload limits, stored tokens, and transport errors.
Synchronize the test fixtures for concurrent calls and race checks.

Document multi-frontend readiness and token verification behavior so
operators can distinguish empty discovery from owner outages.

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

* test(connectors): cover discovery failure fences

Assert failed discovery leaves persisted bindings unchanged and skips
runtime calls during connector creation.

Distinguish healthy empty discovery from lost ownership and runtime
fencing after a real persistence failure. Verify discovery recovers
when a new owner restores the runtime.

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

* fix(connectors): explicitly ignore response close errors

Use the existing deferred close pattern for carrier responses and the owner-routing regression test to satisfy errcheck.

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

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 01:16:55 +00:00
mudler-agentandEttore Di Giacinto 5b13a606f9 fix(agents): allow aliases in the model picker (#13024)
Reuse the chat picker for agent creation and editing so agents can
select chat-capable aliases and retain their alias names.

Resolve capability flags through the existing one-hop alias resolver.
Keep alias metadata and ID-based permissions unchanged, and give broken
or chained aliases no usable capabilities.

Cover alias persistence, default selection, filtering and permissions.
Document that agents save the alias name rather than its target name.

Assisted-by: nib:unknown

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 00:52:44 +01:00
mudler-agentandEttore Di Giacinto e03f427ed5 feat(workspace): use filesystem storage without S3 (#13012)
* fix(workspace): coordinate shared filesystem blobs

Sync filesystem publication and isolate materialized tool files from
immutable blob inodes. Reject symlink paths within the blob tree.

Opt shared PostgreSQL participants into revision-lock coordination for
ingest, reference publication, and purge checks and deletion. Preserve
legacy ingest order when the explicit option is disabled.

Verify process publication and interruption, injected failures, and
shared-schema PostgreSQL races across independent connection pools.

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

* fix(workspace): bound shared filesystem blob cleanup

Honor immediate caller cancellation and give delayed PostgreSQL cleanup an independent 30-second budget for revision-lock acquisition and reference checks. Preserve blobs when cleanup cannot acquire the lock.

Cover cancellation, delayed cleanup, and pool release. Convert publication tests to Ginkgo/Gomega while retaining independent-process child bootstrapping.

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

* feat(workspace): opt into shared filesystem blobs

Allow Files participants to share immutable blobs without S3. Require
PostgreSQL metadata and reject invalid explicit roots at startup.
Keep default storage and model staging unchanged. Document the volume,
permissions and coordinated backend-switching contract.

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

* fix(workspace): clean up failed shared startup

Cancel the owned startup context and close the auth pool when explicit
shared Files startup fails. Preserve the caller context and legacy
startup behavior.

Cover early validation and late Files failure with PostgreSQL-backed
cleanup assertions.

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

* fix(workspace): select blob storage automatically

Use configured S3 for every workspace participant, including owners
without distributed services. Otherwise retain DataPath/chats/blobs
through the direct filesystem adapter, without another path setting.

Derive PostgreSQL publication and purge coordination from the blob
backend so non-distributed owners also protect shared filesystem bytes.
Keep shared mount and metadata identity an explicit operator contract.
Remove the opt-in probe and document storage selection without migration.

Verify automatic selection with red/green regressions, S3 constructor
spies, focused startup tests and blob race tests. Exercise shared files
and PostgreSQL coordination against an owned PostgreSQL 16 fixture.

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

* fix(workspace): clean up configured S3 startup failures

Own the startup child context for configured S3 as well as PostgreSQL metadata. Cancel background work, shut down opened resources and close the auth pool on fatal initialization errors without canceling the caller context or changing legacy JSON/SQLite without S3.

Regress missing-bucket failures using temporary SQLite and no-auth owners through the production cleanup helper. Verify pool closure, owned-context cancellation and resource shutdown.

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

* fix(workspace): resolve filesystem CI diagnostics

Mark best-effort cleanup errors explicitly and inspect the config type
without copying its atomic fields. Document why the validated blob
paths do not permit file inclusion.

Keep publication, synchronization and PostgreSQL coordination unchanged.

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

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-01 18:41:33 +01:00
mudler-agentandEttore Di Giacinto b39d37f559 feat(agents): limit saved agents to one folder (#13009)
* Store the folder and its mode on a saved agent

A saved agent gets FolderID, the workspace node of its folder, and
FolderMode, read or write. The folder is kept by node so that a rename
or a move is followed. A mode without a node is an agent whose folder
is not chosen yet; Validate accepts it, and refuses a node without a
mode.

The GORM store adds two columns with defaults, so AutoMigrate fills
the rows of existing agents as agents without a folder. The JSON store
reads a file without the keys the same way. Both stores pass the new
contract case.

Assisted-by: Claude:claude-opus-5-5

* Keep on a chat whether its saved agent had a folder

A chat whose agent is deleted runs today without the agent's grants.
When that agent had a folder, nothing is left to say which folder the
chat may read, so the chat must be refused instead of getting the whole
home. ChatRecord.ScopedAgent records that the agent had a folder.

CreateChat writes the flag and SetScopedAgent changes it. UpdateChat
leaves it alone, like the tool switches, so the loop's stale copy of
the record cannot clear it. Both chat stores read chats stored before
the flag as not scoped; the GORM column has a default, so AutoMigrate
is the migration.

Assisted-by: Claude:claude-opus-5-5

* Add the design and plan for agent folder scope

A saved agent works on the whole home folder of its user. The spec
lets the user give an agent one folder and a mode, read only or read
and write, and confines every chat that runs it to that folder.

The workspace store enforces the folder through a fence on the actor,
so a caller that forgets it gets less access, not more. The spec maps
every caller that builds a user actor today, including the host bash,
the sandbox runner and the add-on workspace operations, and how each
is moved to the chat's access.

The plan splits the work into ten lanes; the store fence and the agent
record come first.

Assisted-by: Claude:claude-opus-5-5

* Resolve the folder of a saved agent by node

A person chooses an agent's folder by its path in their home; the
agent keeps the node, so a rename or a move is followed. The new
FolderResolver turns the path into the node and back, always as the
user. A path is refused when it is not a live folder, when it is
LocalAI/ or Chats/ or inside them, or when it names a parent. The
home itself ("/") is allowed.

The workspace stores cannot find a node by its ID yet. PathOf reads
the newest change sets of the home first, checks the path it finds
with Stat, and else lists the folders the user can read. A store that
implements NodeByID is used instead. Nothing is cached, so every
frontend answers the same.

SaveAgent keeps the stored folder when it replaces an agent, so a
caller that knows nothing of folders cannot widen one.
SaveAgentWithFolder sets or clears it. ImportAgent looks the folder of
a file up in the importer's home, and saves a folder that is not there
as unset: the mode without a node, which does not start.

Assisted-by: Claude:claude-opus-5-5

* Add a fence to the workspace actor

A chat whose saved agent has a folder must reach only that folder and
the chat's own folders. The store needs to know which folders those
are on every call, so the actor carries them.

Actor.Fenced returns a copy that reaches only the given folders, by
node ID, each at most at its mode. The fence is an unexported field:
a copy of the actor keeps it, and only Fenced can set it. Fencing a
fenced actor adds a layer, and a node is reached only when every
layer allows it, so a fence never grows.

The helpers in export.go let both store implementations apply the
same rules: the mode a fence allows on a node, its roots, and the
limit of MaxFenceRoots roots per call.

Assisted-by: Claude:claude-opus-5-5

* Show and choose the folder of an agent in the API

The agent routes carry folder. A write takes {path, mode}: the server
turns the path into the folder's node, and refuses with 400 a path
that is not a folder of the user's home, or is LocalAI or Chats. A
write without folder keeps the agent's folder, so a client that does
not know the field cannot widen the agent; {path: ""} clears it. A
read answers the folder's path now, its name, the mode and a state:
ok, missing or unset. The node never leaves the server.

An agent with a folder exports as version 2, with the folder's path
and mode. A server that reads only version 1 refuses that file, so a
scoped agent is never imported elsewhere as one with all the files.
An agent without a folder still exports as version 1. The import
reads both versions and looks the folder up in the importer's home;
a folder that is not there saves the agent as unset, with a warning.

The Swagger spec, the API instructions and the agents docs page show
the field and the new file version.

Assisted-by: Claude:claude-opus-5-5

* Enforce the actor fence in both workspace stores

The store is the one place every file call of a chat goes through, so
it applies the fence: a caller that forgets the agent's folder gets
"not found", never the whole workspace.

- The mode of a fenced actor on a node is the lower of its grants and
  the fence. A root in the Trash counts for nothing, and is back after
  Undo, because the fence holds the node ID.
- A fenced actor never takes the administrator shortcut, so it cannot
  grant, revoke or list grants, and ReadablePrefixes gives only fence
  roots or folders inside them.
- A root cannot be deleted, moved or renamed by the fenced actor, by
  any path: Move, Delete, Undo, an upper layer, or a delete of a folder
  above a nested root.
- A fence with too many roots or a bad root is ErrInvalid.

The shared suite in wstest runs the new specs against the JSON store
and the GORM store (SQLite and PostgreSQL).

Assisted-by: Claude:claude-opus-5-5

* Map the model's paths through the chat's place

A chat's file tools spelled every path as a path in the user's home.
A chat whose agent has a folder sees that folder at /workspace/home and
its own chat folder at /workspace/chat, so the mapping must come from
the mounts of the chat's view, not from the principal.

session.Place maps a path the model writes to a workspace path: "/x",
"x", "home/x" and "/workspace/home/x" are x in the primary mount,
"/workspace/<name>/x" is x in the mount of that exact name, and the
skill layers keep their names. It refuses "..", NUL, control characters
and invalid UTF-8 wherever they point, and a /workspace path that is no
mount. Encoded and look-alike separators stay part of a name. Place is
only a mapping: the store's fence enforces the access.

session.Access holds a turn's actor, view and place. It has no exported
fields and one constructor, so a tool cannot be built from a principal
string. Owner gives the user's own actor without the fence.

Assisted-by: Claude:claude-opus-5-5

* Build a chat's view from its folders and describe it

BuildView took the home of the principal as the folder at
/workspace/home. A chat whose agent has a folder needs that folder
there, at the agent's mode, and its own chat folder beside it.

BuildView now takes the primary folder and the chat folder. The chat
folder is mounted at /workspace/chat, read and write; the name chat is
reserved like the skill layer names. The store caps each mount at the
actor's mode, and a fenced actor gets the shared folders read only.
Every caller passes the home and no chat folder, so an unscoped chat
keeps today's view.

LayoutPrompt takes a ScopedPrompt. A scoped chat hears only of its
folders: the agent's folder by its name and mode, with a line that it
cannot see the user's other files, the chat folder and where its
working files go, and the shared folders. It gets no hint to build a
page, because pages live outside its folder. An unscoped chat keeps
today's text.

Assisted-by: Claude:claude-opus-5-5

* Find a workspace node by its ID

A caller that keeps a node ID, such as the folder of a saved agent,
had no way to ask the store where that node is now. It had to search
the journal and then list folders, which costs one query per folder
in a large home.

Store.NodeByID returns a live node at its current path, so a rename
or a move of the node or of a folder above it is followed. It answers
ErrNotFound when the node or a folder above it is deleted, and when
the actor cannot read it; a fenced actor finds only nodes inside its
fence. Both stores implement it and the shared suite checks it. The
remote store of the Sandbox add-on refuses it, like its other calls.

Assisted-by: Claude:claude-opus-5-5

* Name the work folder of a scoped chat at /workspace/chat

A chat whose agent has a folder keeps its chat folder in the home, but
sees it as a mount of its own at /workspace/chat. Its work folder is
then /workspace/chat/work, not a path under /workspace/home.

workfiles reads /workspace/chat as the chat folder: a helper file under
its work folder is working, a script beside the attachments is a stray
helper, and a folder named chat in the agent's folder is an ordinary
folder. ScopedPrompt steers the model to that work folder and sends the
deliverables to the agent's folder, or to the chat folder when the
agent may only read. It says nothing of pages or of the settings
folder, which are outside the agent's folder. The chat picks the text
by its view.

Assisted-by: Claude:claude-opus-5-5

* Look up the folder of an agent by node

The folder resolver searched the journal of the home and then listed
folders to find where an agent's folder is now. The stores can now
find a node by its ID, so the resolver asks them directly and the
search is gone.

Assisted-by: Claude:claude-opus-5-5

* Resolve the file and media tools through the chat's access

The file tools mapped paths to the user's home and acted as the chat's
actor, and the media tools did the same through files.HomePath. A chat
whose agent has a folder must reach only its folders, and search must
start in the agent's folder, so the count of documents still being
indexed cannot count files elsewhere in the home.

files.Service.Tools and media.Service.Tools take a session.Access. Every
path goes through its place and every call acts as its actor. A write
to a read-only folder is refused before the store sees it. A search
with no path starts at the primary folder, and in a scoped chat also
looks in the chat folder; the pending count is taken below that root.
files.HomePath is removed.

UnscopedAccess builds today's access of a chat (the home and the
shared folders), and the studio service takes the turn's access from
one function, turnAccess, which gives that access until the chat
access resolver replaces it.

The new specs run the paths that try to leave the folder (parents,
absolute and doubled paths, encoded and look-alike separators, NUL,
other cases) through every file tool and the media tools, and check
that nothing outside the folder is read, named or changed, and that
search counts no pending file outside it.

Assisted-by: Claude:claude-opus-5-5

* Resolve the access of a chat at every turn

A chat whose saved agent has a folder must reach only that folder. Up
to now every chat got the user's actor, which reaches the whole home.

The chat's access is now resolved from the stores at every turn
start. With no agent folder it is the actor and the view of today.
With a folder the actor is fenced to the agent's folder at its mode,
the chat's own folder read and write, and the shared folders and the
skill layers read only; the chat's folder is made before the fence,
because a fenced actor cannot make it. A sub-agent takes the folder
of its parent's agent, and its own agent's folder inside it.

A folder that is deleted, unreadable or not chosen refuses the chat
with ErrAgentFolderMissing; it never falls back to the whole home.
A chat records whether its agent had a folder, so a chat whose agent
was deleted is refused too. CreateAs refuses such an agent at once.

session.Access carries the actor and the view, has no exported fields
and one constructor. Open takes it, the turn tools are built from it,
and an open chat compares the fence as well, so an edit of the agent
restarts the harness at the next turn on any frontend. The person's
uploads and renames use the owner's actor, without the fence. The
files a command changed are read as the chat can read them.

Nothing is kept between turns, so frontends cannot disagree.

Assisted-by: Claude:claude-opus-5-5

* List a scoped chat's files by its place in the prompt

The turn prompt lists the chat's files with the paths the model uses.
For a chat whose agent has a folder those paths come from its mounts,
and a file the chat touched outside its folders, for example before
the agent got a folder, must not be named.

Manager.FilesIn is Files with a place. For a scoped place, each file
is named by its mount (/workspace/home for the agent's folder,
/workspace/chat for the chat folder) and a file outside every mount is
left out. Other places give what Files gives. The prompt's file list
uses the turn's place; the chats API keeps the user-wide list.

Assisted-by: Claude:claude-opus-5-5

* Answer 409 when an agent's folder is missing

A chat with a saved agent whose folder is missing or not chosen does
not start. The chat endpoints now say so with 409 and the type
agent_folder_missing, both when the chat is created and when a
message is sent to an open chat, instead of a server error.

The agent docs say what a chat with a folder reaches, when a change
of the folder applies, and that nothing is kept between turns.

Assisted-by: Claude:claude-opus-5-5

* Point a scoped chat at rules and work folders in its own folders

The rules nudge walked from the chat folder up to the home root and
named AGENTS.md files there, and the stray-helper note named the work
folder under /workspace/home. A chat whose agent has a folder must hear
only of its own folders.

The nudge takes the turn's access: it reads paths through the place,
starts at the primary folder (the agent's folder) and stops there, so
it never points above the folder or into the chat folder mount. The
helper note names /workspace/chat/work/ in a scoped chat, and a
sub-agent of a scoped parent gets the scoped work folder text.

Assisted-by: Claude:claude-opus-5-5

* Find workspace actors built outside the chat access

An actor built from a principal has no fence and reaches the whole
home. A spec now parses the studio service, the command runners, the
add-on host operations and the application wiring, and fails on a
workspace.Actor literal, a files.UserActor call or an Actor(principal)
call in a function that is not on the allowlist. Each allowed entry
says why: a host read of the user's settings, a user surface, or a
caller that a later change moves to the chat's access. An entry that
builds no actor any more fails the spec too, so the list stays exact.

Assisted-by: Claude:claude-opus-5-5

* Build the expected view of a chat with the view's folders

The spec of a chat without an agent folder compared its view with
BuildView called with the old arguments, so the test package did not
compile after the merge of the view builder. It now passes the home
as the primary folder and no chat folder, which is the view of an
unscoped chat.

Assisted-by: Claude:claude-opus-5-5

* Give every file tool of a turn the chat's resolved access

The resolver of a chat's access built the view of a scoped chat
itself, with no shared folders, while every other chat got its view
from the session manager. And the file tools, the media tools, the
chat's file list in the prompt, the rules nudge and the work folder
lines each asked for an access of their own, the view of the whole
home. A chat whose agent has a folder therefore had a fenced actor
but home-relative paths: it reached its folder only through paths
such as /Projects/Acme/a.txt, and its prompt named the home.

The session manager now builds every chat's view: UserAccess mounts
the home, and the new ScopedAccess mounts the agent's folder at home,
capped at the fence's mode, the chat folder at chat and the shared
folders read only. It refuses an actor without a fence, and a folder
outside the fence is an error, never a view of the home. The
resolver calls it with the innermost folder, so a sub-agent of a
saved agent still works in its own folder.

The access that the resolver builds once at the start of the turn
now goes down to every consumer. A sub-agent of a saved agent acts
for its parent chat with the same access (Access.ForChat), and a
generic sub-agent gets its tools from the parent's access. The seam
that gave the unscoped access, and the files service's own view
builder, are removed, so a new consumer cannot build a view by
mistake. A turn whose access has no folder gets no tools rather than
the home.

A scoped chat now sees its folder at /workspace/home and its chat
folder at /workspace/chat in its tools, its search, its prompt and
its work folder lines. A chat whose agent has no folder is unchanged.

Assisted-by: Claude:claude-opus-5-5

* Find chat views built outside the chat access

The turn's access is resolved once and handed down, but nothing
stopped new code from building a view of its own, which shows a
scoped chat the whole home. A second scan now parses the same
sources as the actor scan and fails on a call of BuildView,
SkillLayerMounts, NewAccess, UserAccess or ScopedAccess, or on a
store's View, in a function that is not on its allowlist. The
resolver is the one allowed caller; the host bash, the sandbox
runner and the add-on host operations are listed until they take
the chat's access. A stale entry fails the scan too.

Assisted-by: Claude:claude-opus-5-5

* Keep a sub-agent inside its parent's folder

A saved agent started from a scoped chat could name a folder
outside the chat's own, and the child was made before its first
turn failed with a message about a missing folder. spawn_agent now
checks the agent against the chat's agent from the stores before it
makes the child: no folder is fine, the same folder or one inside
it is fine, anything else fails with "this agent works in another
folder than this chat". The child never has more reach than its
parent, in folder or in mode.

The ScopedAgent flag of a sub-agent was reset to what its own
agent had at each turn. A child of a scoped chat with an agent
without a folder lost the mark, so deleting the parent's agent
would have given that child the whole home. The flag now follows
both folders.

New specs cover a generic child, a saved-agent child outside,
inside and at the parent's folder with the lower mode, a child
without a folder, and the work folder wording of a child of a
read-only or read-write parent.

Assisted-by: Claude:claude-sonnet-5-5

* Show agent folders in list_agents and limit them by scope

list_agents gave a scoped chat every saved agent, including ones
that spawn_agent would refuse, and told the model nothing about
where an agent works. Each entry now carries the name of the
agent's folder and its mode, or folder_missing when the folder is
deleted or not chosen. The path is left out, since it would show
the folders above the chat's own. A chat whose agent has a folder
lists only the agents that fit under that folder, by the same check
spawn_agent makes.

The docs for saved agents as sub-agents and for the agent folder
describe both.

Assisted-by: Claude:claude-sonnet-5-5

* Keep the triggers of a scoped agent to its folder

A chat of an agent with a folder reaches only that folder, but its
triggers still worked on the whole home. A file trigger could watch
any folder, the rules of a run were read from the home root down
with the user's actor, the first message named home paths, and a run
whose agent's folder was gone fell through to a failed chat start
after the host had already read the home's rules. The trigger tools
of a scoped chat could also list, change or propose triggers that
reach the whole home.

The agent's folder now bounds every step:

- Saving a file trigger (create, update, edit, confirm) refuses a
  folder outside the agent's folder, and a trigger of an agent whose
  folder is missing or not chosen.
- Each run checks the agent's folder before it reads anything. A
  missing or unset folder skips the run with "the agent's folder is
  missing" and switches the trigger off; a watched folder that is not
  inside the agent's folder does the same with its own reason. No
  chat is made and the owner is told.
- The folder check and the rules are read with an actor fenced to
  the agent's folder, read only, so no AGENTS.md above it is read.
  The rules of a schedule run start at the agent's folder. The first
  message names folders and the file as the chat sees them, under
  /workspace/home, and the rules nudge of the run's chat counts those
  rules as given.
- In a scoped chat the trigger tools read folders as paths of the
  chat's place, refuse one outside the agent's folder, and see only
  the triggers of that agent.

The trigger watcher takes the service's administrator actor, made
once next to the change feed's, and the trigger functions leave the
actor scan's allowlist.

Assisted-by: Claude:claude-opus-5-5

* Send connector replies through the chat's access

The files a connector reply sends were listed from every change made
in the chat's name and read with the user's actor, so a reply of an
agent limited to one folder could send a file from anywhere in the
home that some host-side write had attributed to the chat. A message
to an agent whose folder was missing made or reopened a chat, failed
to start it, and answered with a generic error.

A reply now resolves the chat's access once, lists the turn's files
through its place (a file outside the chat's folders is left out)
and reads each one with its fenced actor. A file the access cannot
reach is skipped without being named. Before it makes a chat, a
message checks the bound agent's folder; when it is missing or not
chosen, the sender gets one reply, "This agent cannot work right
now: its folder is missing.", the refusal is logged and the binding
stays on. The same reply covers a folder that goes away between the
check and the turn. readChatFile leaves the actor scan's allowlist.

Assisted-by: Claude:claude-opus-5-5

* Name a read-only refusal of a commit by its guest path

The store names an upper entry that a read-only mount refused by
its path in the view (/home/x), not by its store path. The note
that a command's output ends with took every path but an
outside-view one for a store path, so it told the model that
"/home/x was not saved", a path the guest does not have. A path
that no mount holds but that starts with a mount name is now taken
as a path of the view and gets the /workspace prefix.

Assisted-by: Claude:claude-opus-5-5

* Take the actor and view of command runners from the chat's access

The host bash and the sandbox runner made their own actor and view:
the host bash a user actor from the principal and the view of the
whole home, the sandbox runner a plain actor by default and the view
from its own mount requests. Neither knew the chat's agent folder,
so a command of a scoped chat could reach the whole home.

The runner now has no default actor or view. Options.Actor takes a
context and returns an error, and Options.View returns the view
itself; both are asked at every call. A view of another actor than
the chat's is refused, and a sandbox whose actor or mounts changed
is made again, so an agent's new folder or mode applies from the
next command. A failing resolver fails the call, also with a sandbox
running.

The server wires both hooks to the chat's resolved access: the new
Service.CommandActor and Service.CommandView, the latter refusing an
actor that is no longer the chat's. Access.SandboxView adds the skill
layers to the turn's view, at the names the file tools use. The host
bash refuses a fenced actor, at the tool and again at each call, as a
command on this machine reads what the server can read. The Sandbox
add-on passes a label actor and its home view: the host binds each
workspace operation to the call's chat.

The lane 5 entries leave the actor and view scan allowlists.

Assisted-by: Claude:claude-opus-5-5

* Offer no host bash to a chat whose agent works in one folder

A command of the built-in bash runs on this machine and reads every
file LocalAI can read. No workspace rule confines it, so a chat whose
agent works in one folder cannot have it, whatever chats_host_exec
says. The tool's build now refuses a fenced actor, so the chat gets
no bash, its prompt says nothing runs commands, and the shell group
of the Tools menu gives the reason: the agent works in one folder
and runs commands only in the Sandbox add-on. Sub-agents of such a
chat have the fenced actor too.

The docs of saved agents and of the commands on this machine state
the rule and the Sandbox layout of a scoped chat.

Assisted-by: Claude:claude-opus-5-5

* Tell a scoped chat where its Sandbox commands start and what they keep

The commands block of the prompt told every chat with the Sandbox
that a command starts in /workspace/home, the user's home, and that
what it saves there or in a writable shared folder is kept. In a
chat whose agent works in one folder, /workspace/home is that folder,
the chat folder is /workspace/chat and the shared folders are read
only. The block now says so, and with a read-only folder it names
only /workspace/chat as kept.

Assisted-by: Claude:claude-opus-5-5

* Name a scoped chat's attachments where its commands see them

The model learns of an attached file by its guest path, which was
always /workspace/home/Chats/<chat>/<file>. In a chat whose agent
works in one folder, /workspace/home is that folder: the file and
media tools still found the file through the place's chat alias, but
a command sees the chat folder only at /workspace/chat, so the path
named nothing for it.

The text the model gets now names a file of the chat folder at
/workspace/chat/<file> in a scoped chat, through the chat's place:
when a message is sent, queued or injected, in the summary of the
chat, and in the model state rebuilt after an edit, a regenerate, a
fork or an import. What is stored keeps the home spelling, which
SplitMessage reads back, and an unscoped chat is unchanged.

Assisted-by: Claude:claude-opus-5-5

* Remove the duplicated allowlist entries of the actor scan

Assisted-by: Claude:claude-opus-5-5

* Read and write the folder of an agent in the agent helpers

The agent API now answers an agent's folder with its path, mode and
state, and a write keeps the stored folder unless it names one. The
page helpers learn that shape: the folder field of a save is sent only
when the person changed it, an empty path clears it, a missing or
unset folder that the person did not replace is kept, and the mode is
read unless write was chosen. Small helpers say whether a listing
entry can be chosen (not a folder LocalAI manages, not a shared
folder), which folders agents use for the Files page marker, and
whether a chat error is the missing-folder conflict.

Assisted-by: Claude:claude-opus-5-5

* Move the folder walk of the Move dialog into a folder picker

The agent editor needs the same walk through the person's folders as
the Move dialog, so the breadcrumb and the folder list move into
FolderPicker, with their styles, and the Move dialog draws it. The
picker can show a folder as one that cannot be chosen, with the
reason next to it, and a folder that cannot be read now offers to try
again; a folder still loading says so instead of an empty list.

Assisted-by: Claude:claude-opus-5-5

* Choose the folder of an agent in the agent form

The agent form gets a Files section after the instructions: All my
files, or One folder chosen in the folder picker, and whether the
agent's chats can read the folder or read and change it. The picker
shows the folders LocalAI manages as ones that cannot be chosen, and
Home itself can be chosen. A folder that was deleted, or an imported
agent whose folder was not found, is said in place with an offer to
choose another; the rest of the form still saves and keeps it.

A save sends the folder only when the person changed it, so editing
another field never touches it. Picking One folder and choosing
nothing stops the save, as it would otherwise save an agent on all
the files. The Export section now downloads the file the server
writes, which names the folder, and says so.

Assisted-by: Claude:claude-opus-5-5

* Show the folder of an agent on the agents list and its page

An agent with a folder shows it in the list's line and on its page,
with its access in words and a lock when its chats can only read it.
A folder that is gone, or an agent with no folder chosen yet, shows
a warning that opens the form; the agent's page also says so above
its facts and offers to choose another folder.

Assisted-by: Claude:claude-opus-5-5

* Mark the folders that agents work in on the Files page

A folder that a saved agent works in gets a small marker with the
agent's name, or the number of agents, and a tooltip that names each
one with its access. Inside such a folder, a line over the list says
the same. The page reads the agent list it can already fetch, so the
files API is unchanged, and the page still shows all the files.

Assisted-by: Claude:claude-opus-5-5

* Show a chat's folder and say when its agent's folder is missing

A chat with an agent that has a folder shows the folder next to the
agent in the message box, with a lock for read only, and the Tools
menu says which files the chat's tools reach. When the folder is
gone, the chip says so and opens the agent's form.

A chat whose agent's folder is missing does not start, and the server
answers with a conflict. The message box now says that in plain words,
keeps the message, and offers Edit agent, instead of the server's
text.

Assisted-by: Claude:claude-opus-5-5

* Test the folder of an agent across the web interface

The agent stubs now list folders by path, read a written folder back
as the server does, export a version 2 file for an agent with a
folder, import one whose folder is not found as unset, and refuse to
start a chat whose agent's folder is missing.

The specs choose a folder and see it on the list, switch the access,
leave an unchanged folder out of the save, clear it, keep and replace
a missing folder, keep Chats and LocalAI out of the picker, retry a
folder that cannot be read, see the Files marker, the chat's chip and
Tools line, the missing-folder chat error with Edit agent, and the
import warning. A render smoke opens the picker on the real server,
and one spec checks that the copy names no internal path.

Assisted-by: Claude:claude-opus-5-5

* Describe the agent's folder in the web interface

The agent docs said what the folder field does in the API. They now
also say where the web interface sets it, how the agents pages, the
Files page and a chat show it, and what a chat whose folder is
missing offers.

Assisted-by: Claude:claude-opus-5-5

* Record the chat an add-on call was made for

An add-on's workspace operations run as the user of the live request,
with no idea which chat made it. Only an action carries a chat, and
only as a key in its resource ID; a read tool such as the Office
add-on's read-docx carries none. So a chat that should reach only one
folder could reach the whole home through an add-on.

The in-flight record of a request now keeps, next to the envelope, a
binding to the chat the request was made for: the user's principal
and the chat ID. It is host-side only and never a field of the
envelope, which installed add-ons decode with unknown fields refused.
A context carries the binding from the chat turn to the place that
records the request. The broker gives each host operation the chat
and the scope of its live request. An operation ID that requests of
two chats share is not live, so neither chat is trusted.

Assisted-by: Claude:claude-opus-5-5

* Keep the chat binding of a runtime call on the host

The runtime client records each call's envelope for the host operation
channel. It now records the chat binding too when the call's context
carries one, so the add-on's host operations know which chat made the
request. The envelope sent to the add-on is the same bytes with or
without a binding: a spec checks that the body of a bound call equals
the body of an unbound one and still decodes with unknown fields
refused.

Assisted-by: Claude:claude-opus-5-5

* Carry the chat binding in a forwarded add-on request

A frontend that is not the add-on owner forwards an add-on call in a
signed request. The chat the call was made for now rides in that
request as a signed field, chat, and the owner puts it back on the
context of the call it serves, where the runtime client records it.
A changed, added or removed binding fails the signature check, and a
signed but incomplete one is refused. A request that no chat made has
no chat field and signs as before.

An owner that does not know the field refuses a forwarded call that a
chat made, so the owner must be upgraded before the frontends.

Assisted-by: Claude:claude-opus-5-5

* Bind every add-on tool call of a chat to the chat

The chats consumer ran an add-on tool with the conversation ID only,
which the owner used to resolve a selection and, for an action, as the
key of the add-on's state. Nothing told the host which chat a read
tool or a change came from.

The tool adapter now has ExecuteChat, which takes a ChatCall (the
chat's user and ID, from the chat turn's own record) and binds the
call to the chat on the host side for every tool kind: a read with no
resource, an action, and a change made at once. The chats adapter
refuses a call that names a conversation without that binding, so a
chat's call cannot be made unbound by mistake. Other consumers, such
as the OpenAI-compatible route, keep calling without a binding.

Assisted-by: Claude:claude-opus-5-5

* Run add-on workspace operations with the chat's access

The workspace operations of add-ons (materialize, commit, changes,
export and import) worked on the whole home of the request's user. A
chat whose agent has a folder could therefore read and write any file
through the Office or Sandbox add-on.

They now resolve their access from the chat binding of the live
request, through a new Access hook that the chats service answers
(Service.AddonAccess). A chat's call gets the chat's access, as its
file tools do: in a chat whose agent has a folder, the add-on's "/" is
that folder, /workspace/chat names the chat folder, the agent's mode
applies, and paths resolve through the chat's place, so a parent
reference is refused and an encoded separator is part of a name. The
mounts go through Access.SubView, which builds a view only inside the
access, for its fenced actor. When an add-on mounts its "/" at home,
the host adds the chat folder at chat, so the Sandbox sees the chat's
layout without a change. Changes list only what the access may read,
and a commit entry outside the view is a conflict.

A call whose chat cannot be resolved is refused and never runs
user-wide. An action request without a binding is resolved by its
chat key, as before, and refused when it names no chat. A call that
no chat made (an add-on page, the OpenAI-compatible route) still works
on the whole home, through Manager.PersonAccess.

The four lane 6 entries leave the actor scan and the view scan.
PersonAccess joins the view scan's matcher, with AddonAccess as its
one allowed caller. An end-to-end spec runs the real Office and
Sandbox add-ons in a scoped chat.

Assisted-by: Claude:claude-opus-5-5

* Build the chat access of the file index route test

files.Service.Tools takes a session.Access since the chat access
change, so the route test of the text index passed a bare actor and
its package did not compile under the auth tag.

The test now builds the access as the chats service does for a chat
whose agent has no folder: the user's home as the primary folder of
the view, then session.NewAccess. The assertions are unchanged.

Assisted-by: Claude:claude-opus-5-5

* Test the folder of an agent through every surface of a chat

The specs of the folder feature each test one surface. This suite
runs one chat of an agent limited to a folder through all of them at
once, on an indexed SQL workspace: the file and media tools, the search
and its pending count, a command of the Sandbox in a fake sandbox, a
read call and an action call of an add-on through the real workspace
operations bound to the chat, a generic and a saved sub-agent, the
trigger tools, a file trigger run, a connector reply, a renamed folder,
a mode change, an edit that narrows the folder (also seen by a second
frontend) and a deleted folder.

The files outside the folder hold a token that the scripted model never
writes. After each spec the suite collects every text a person or a
model can see: tool results and errors, prompts, messages, transcripts
with their cards, live events, chat file lists, tool panels,
notifications, connector replies and files, the add-on's data directory
and the connector log. The token must be in none of them, and the files
outside must be unchanged.

A control spec runs the same probes from an agent without a folder and
checks that they do find the token, so the probes and the collection
cannot pass by seeing nothing.

Assisted-by: Claude:claude-opus-5-5

* Complete the docs of the folder of an agent

Each change of the folder feature documented its own part. This
makes the pages one account of it:

- Saved agents: the missing and unset states in one bullet, what the
  folder does not limit (MCP servers, web tools, collections, pages),
  that the Files page stays user-wide, when an edit applies, the
  downgrade caveat, and the folder as a stateless row of the cluster
  table. The Sandbox of a scoped chat mounts the agent's folder and the
  chat folder only, as the add-on asks for them; the page said it also
  had the shared folders and skills.
- Chats: the chat folder at /workspace/chat for the model, sub-agents
  of a scoped chat, and commands only in the Sandbox.
- Add-ons: the Sandbox and Office sections say what / is in a chat whose
  agent has a folder.
- First agent: the optional folder step and the "Folder missing" case.

Assisted-by: Claude:claude-opus-5-5

* Draft the PR description and release note of agent folders

No release notes file exists for the next release yet, so the draft
text lives next to the spec and the plan. It carries the release note
paragraph with the downgrade caveat and the upgrade order of a cluster.

Assisted-by: Claude:claude-opus-5-5

* List the chat binding specs in the per-report add-on inventories

The chat binding change added five specs to the gateway and runtime
packages and named them in the case lists of the gateway, artifacts and
apps suites. These three suites also keep one case list per Ginkgo
report, and the verifier compares each report with its own list. The
new names were missing there, so the three suites failed with an
inventory mismatch.

A dry run of every Ginkgo suite's commands now lists the same It names
as each report's inventory, in both directions.

Assisted-by: Claude:claude-opus-5-5

* Hide stale paths from a fenced actor's history

A folder moved into a fence makes its parent identities readable.
Their historical paths can still name locations outside the fence.

Hide each historical side when its readable parent's current path
differs. Reuse the generic title when filtering hides a side. Keep
the journal and unfenced history unchanged so Undo uses identities.

Assisted-by: nib:gpt-6.1-sol

* List the chat binding cases in Tables tools

The Tables tools runner includes every adapter spec in its output.
List the four chat binding cases so the exact inventory accepts them.

Assisted-by: nib:gpt-6.1-sol

* Keep shared agent folders unset in exports

A followed folder outside the home cannot round-trip through a home path.
Export its mode with an empty path so import warns and stays fail-closed.
Responses keep public shared paths without exposing other internal paths.

Distinguish deleted scoped agents from unscoped agents in the guidance.

Assisted-by: nib:gpt-6.1-sol

* Refuse scoped chats over the folder limit

Keep every required shared root in each intersecting fence. Refuse
creation and turn access when the complete set exceeds 32 roots.
Reserve a chat root before its folder exists and fence listings empty
on refusal. Propagate shared enumeration failures instead of omitting
roots.

Map capacity refusals to a fixed 409 response and give connectors a
safe capacity message. Check boundaries, retries, background access,
and HTTP handlers with focused regressions.

Assisted-by: nib:gpt-6.1-sol

* Verify unset imports refuse chat startup

Assert the folder-specific refusal without a harness start or saved chat.
Clarify public response paths and unset exports in the API metadata.

Assisted-by: nib:gpt-6.1-sol

* Keep the folder retry test under user control

A background refresh consumes the one-shot successful response before
this test observes its load error. Isolate the mocked Files page from
the live workspace stream and agents list. Keep reads failing until
the error assertions pass, then allow the actual Retry click to recover.

Check that Retry reads again and removes the error, without changing
the existing assertions or their timeouts.

Assisted-by: nib:gpt-6.1-sol

* Test and document folder capacity refusals

Use the existing Ginkgo suites for capacity regressions. Exercise a
successful turn, refusal after shares change, and a retry through a
second service on the same stores. Check that refusal leaves no claim
or pending message.

Verify that connectors send one fixed refusal and that triggers stay
enabled after capacity failures. Document the 32-root budget and the
all-required-roots-or-refuse contract.

Assisted-by: nib:gpt-6.1-sol

* Control crashes in the restart budget test

The crash fixture exits after 100 ms, before startup necessarily finishes.
A busy runner can fail the readiness handshake instead of testing restarts.

Start a normal fixture and kill each published incarnation explicitly.
Keep the exact restart count and persistent failed-state assertions.

Assisted-by: nib:gpt-6.1-sol

* Check capacity test cleanup errors

Assert service and workspace cleanup through Ginkgo. Keep service cleanup before workspace cleanup and report close failures instead of discarding them.

Assisted-by: nib:gpt-6.1-sol

---------

Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-01 18:30:08 +01:00
mudler-agentandEttore Di Giacinto 9bd5d0f915 fix(ci): publish images after large mixed diffs (#13011)
Quiet grep exits on the first image-visible path. Under pipefail,
echo can then fail with SIGPIPE and the gate incorrectly skips images.

Consume the full path list without changing the exclusion rules.
Test the actual workflow gate with disposable Git history, including
large mixed diffs, and run the regression in the lightweight lint job.

Assisted-by: nib:gpt-6.1-sol

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-01 15:43:32 +01:00