In the current development builds, this add-on is in the development channel of the add-on library, so it can change.
At a glance Topic Detail What it adds One calendar for each user, inside LocalAI. A chat or a saved agent reads and changes events. There is no account to connect. Who installs it An administrator, from Add-ons, Find add-ons, with Install and turn on. With authentication off, you are the administrator. Who turns it on A person needs the addons feature. It is off by default for new users on a server with accounts. The person switches the add-on on for a chat in the Tools menu. A saved agent uses it only when you name it in the agent. The Calendar add-on gives you a calendar inside LocalAI. Ask a chat or a saved agent “what is on Thursday?” or “add dinner with Sam at 8” and it reads or changes the calendar. There is no account to connect, no sync and no setup: install the add-on and use it.
In the current development builds, this add-on is in the development channel of the add-on library, so it can change.
At a glance Topic Detail What it adds One address book for each user, inside LocalAI. A chat or a saved agent looks up and keeps contacts. There is no account to connect. Who installs it An administrator, from Add-ons, Find add-ons, with Install and turn on. With authentication off, you are the administrator. Who turns it on A person needs the addons feature. It is off by default for new users on a server with accounts. The person switches the add-on on for a chat in the Tools menu. A saved agent uses it only when you name it in the agent. The Contacts add-on gives you an address book inside LocalAI. Ask a chat or a saved agent “what is Marco’s number?” or “add Anna, anna@example.org” and it reads or changes the book. There is no account to connect, no sync and no setup: install the add-on and use it.
Add search providers with a key and PDF reading to the web tools of a chat.
Subsections of Work tools
Simple Office
Note
Add-ons need a development build or a release newer than 4.12.1.
The Simple Office add-on (ID office) lets a chat make, read and edit Word, Excel, PowerPoint and PDF files. It does not need a Sandbox. In the current development builds, it is in the development channel of the add-on library, so it can change.
At a glance
Topic
Detail
What it adds
11 tools: read, edit and create for Word, Excel and PowerPoint, and read and create for PDF. Files are saved in your files. It runs on Linux and macOS.
Who installs it
An administrator, from Add-ons, Find add-ons, with Install and turn on. With authentication off, you are the administrator.
Who turns it on
A person needs the addons feature. It is off by default for new users on a server with accounts. The person switches the add-on on for a chat in the Tools menu. A saved agent uses it only when you name it in the agent.
The Sandbox add-on can make the same files with Python and gives full control of the layout. The two add-ons work on their own. See Sandbox: run Python on your files.
Sandbox: run Python on your files
Note
Add-ons need a development build or a release newer than 4.12.1.
In the current development builds, this add-on is in the development channel of the add-on library, so it can change.
At a glance
Topic
Detail
What it adds
A bash tool that runs commands and code in an isolated sandbox with your files mounted. It makes charts, converts files and runs scripts.
Who installs it
An administrator, from Add-ons, Find add-ons, with Install and turn on. With authentication off, you are the administrator.
Who turns it on
A person needs the addons feature. It is off by default for new users on a server with accounts. The person switches the add-on on for a chat in the Tools menu. A saved agent uses it only when you name it in the agent. You switch Sandbox on in the Tools menu of a chat. You need chats and their files on.
Sandbox is an add-on. It lets a chat run commands and code, for example to make a chart, convert a file or run a script. The commands run in an isolated sandbox with your files mounted. The Sandbox add-on is for Linux. In the current development builds, it is in the add-on library in the development channel, so it can change.
What you need
An administrator who installs the add-on. See the add-on user guide.
A host that can run an isolated backend. The add-on tries KVM (Cloud Hypervisor) first, then gVisor. KVM needs /dev/kvm. gVisor needs runsc and permission to create a user namespace. If neither works, no command runs, and the chat says so.
The chats and their files must be on, because the sandbox works on your files.
A user who has the addons feature, on a server with accounts.
Turn it on for a chat
Open the Tools menu of the chat.
Switch Sandbox on.
When it is on, the bash tool of the chat runs in the sandbox. Without it, a chat can run commands on the server only if an administrator allows it, and then there is no isolation. See Running commands.
How it works
Each user and chat has its own sandbox. The model cannot name another user or chat.
Your files are mounted under /workspace/home. Each command starts there.
After each command, what changed is saved to your files as one change. You can undo it in the history of Files. If someone else changed the same file, their version stays and the sandbox version is saved as a copy.
A sandbox is suspended after 10 minutes without use and removed after 24 hours.
An answer is cut at 64 KiB. A command stops after 120 seconds by default, and after 28 minutes at most.
Network
A sandbox has no network device. HTTP and HTTPS go through a proxy that asks LocalAI for each connection. The Network row in the Tools menu has three choices: Off, Package registries and All public sites. The limit that an administrator sets is a ceiling. The default limit is Off, so pip install does not work until an administrator allows it.
When a command asks for a host that the chat does not allow but the limit allows, the chat shows a card. Choose Allow once, Allow for this chat or Deny. See Network of the Sandbox commands.
What is installed
The base profile has Python with pandas, matplotlib, openpyxl, python-docx, python-pptx, pypdf and Pillow, and tools such as jq, ripgrep, sqlite3, zip, git and curl. The media profile adds ffmpeg, sox and ImageMagick. The video profile adds a Rust toolchain and a video renderer. You pick the profile in the Tools menu, unless an administrator limits the choice. Programs cannot be installed with apt. See Sandbox profiles.
Example: ask “Make a bar chart of the numbers in sales.csv and save it as a PNG”. The model reads the file, runs a Python script, and saves the image in your files.
Troubleshooting
You see
Do this
The chat says it cannot run commands
Open the Sandbox page. It names the backend that runs, or why none works. An administrator sees the reason for each backend.
gVisor fails with cannot read client sync file
The host needs an AppArmor profile that allows user namespaces, and a container that may create them.
pip install fails
The network is Off. Ask an administrator to raise the limit.
Add-ons need a development build or a release newer than 4.12.1.
In the current development builds, this add-on is in the development channel of the add-on library, so it can change.
At a glance
Topic
Detail
What it adds
Databases of tables that you own or share: typed rows, filters, a schema and sharing. A chat reads and changes them with tools.
Who installs it
An administrator, from Add-ons, Find add-ons, with Install and turn on. With authentication off, you are the administrator.
Who turns it on
A person needs the addons feature. It is off by default for new users on a server with accounts. The person switches the add-on on for a chat in the Tools menu. A saved agent uses it only when you name it in the agent.
Status
Tables is a first-party addon for LocalAI. This page describes the database
lifecycle, the typed schema operations, and structured row queries and
confirmed row changes that exist today, how a chat model uses them as tools,
and the host side of managing databases from the UI: creating, renaming,
sharing and deleting a database, and choosing the database of a conversation.
A table, with the filter and sort of a view, exports as a private CSV file
(see CSV export). Import and public file links are not
available. The Tables pages (the native UI) are described in
The Tables pages.
Tables is a separate executable under addons/tables. It is not linked into
the LocalAI server, and nothing of it runs unless an administrator installs
and enables the addon. It reuses the root Go module and needs CGO for SQLite.
go build ./addons/tables/...
Deployment, trust and limits
One owner. A database is a SQLite file on the disk of the one LocalAI instance
that owns the add-on. Other frontends forward to it; while the owner is down every
Tables call fails with 503 on every frontend, and nothing is applied later. See
Singleton owner and its limits.
Trust. The Tables executable is trusted code that runs as the owner’s operating
system user. Tables itself never learns who a caller is: the host gives it the
database ID and the action, and the host decides the role and the approval.
Storage and backup. The data is under the data directory of the add-on, and
backups are made by the administrator through the recovery routes (see
Backup, upgrade and restore). A copy of the data
directory made while the add-on runs is not a consistent backup.
Ownership mode. Named-user mode keeps databases private to their owners and
shareable. Local-owner mode has one owner and no sharing, so the database page shows no Share button. The mode is stored with the
data and is changed only while the host is stopped, with an explicit mapping (see
Moving between the modes).
CSV. An export is a private, temporary file with the formula policy described in
Spreadsheet formulas: it protects a spreadsheet, not a
program that trusts CSV.
Updates. The host never upgrades Tables by itself. An administrator checks for an
update and applies one named candidate (see
Update detection and upgrade); the data is
backed up first.
Optional contributions. The package declares its UI pages and its tools. A host
that has no UI still runs the tools, and a UI never gains a tool the manifest does not
declare. The chat actions it declares are placed and drawn by the host; the page cannot
add a button to the chat of its own.
What a database is
Each database is one SQLite file with its own metadata. The file is named by an
opaque resource ID that LocalAI assigns. Tables does not choose the ID, does not
store who owns the database and does not decide who may open it. LocalAI keeps
owners and grants and checks them before every call.
Property
Value
Location
<addon data directory>/tables/databases/<resource-id>.db
Modes
directory 0700, files 0600
Journal
WAL, foreign keys on
Name
1 to 80 characters, no control characters, no leading or trailing space
Size
at most 1 GiB per file, enforced on every connection; a write past the cap fails with a capacity error
Locks
busy timeout 3 s, 10 s limit for one operation
A resource ID must match ^[a-z][a-z0-9-]{0,63}$. Any other value is refused
before it reaches the file system. Symbolic links are never followed, in the
data directory or as a database file.
Databases are private by default. A new database belongs to the person who
created it and nobody else can list or open it until the owner shares it with
a grant. Administrators who install the addon get no access to databases.
Share with your team describes the role model
and the sharing API that other kinds of resources use with the same rules.
Tools
The manifest declares the executable tools below. The one host-owned exception
is addon__tables__databases-list, described in Database discovery.
That name is reserved: a Tables manifest cannot redefine it.
Tool
Kind
Effect
databases-create
change (schema)
Create the database for a reserved resource ID.
databases-rename
change (schema)
Change the stored name. Owner role only.
databases-inspect
read
Name, revision, table names and the tables with their columns.
Rename a column. Arguments table, column, newName.
columns-drop
change (schema)
Drop a column and its values. Arguments table, column.
rows-query
read
Select rows of a table with a filter, sort and page. See Row queries.
rows-aggregate
read
Count, sum, avg, min and max with an optional group-by column.
rows-export
read
Export the rows of a table, with an optional filter and sort, as a private CSV file. See CSV export.
rows-insert
change (write)
Insert up to 100 rows. Arguments table, rows.
rows-update
change (write)
Set columns on rows named by ids or a filter. Arguments table, set.
rows-delete
change (write)
Delete rows named by ids or a filter.
resource-exists
read
{"exists": bool}; used by LocalAI to settle interrupted creations.
Tool names use hyphens because LocalAI manifests accept only
[a-z][a-z0-9-]* identifiers.
Databases of a saved agent
Tables opts in to per-agent resources. A saved agent that names Tables lists the
databases it may use, each as Read or Read and change; with none listed it
uses none, and the list tool shows it only those databases. Creating a database is
not limited, but a new database is on no list until you add it to the agent. See
Resources of an agent. This needs
LocalAI with feature level 2 (see Feature level); an
older LocalAI refuses this version of Tables when it installs it.
Database discovery
addon__tables__databases-list takes exactly {} and reads the host’s resource
registry, not the Tables executable. It is available only through existing
authorized actor-bound consumers when Tables is visible, enabled and running,
and the backend supports host resource listing. The tool exists because the Tables manifest declares it under capabilities.resources.list (see Resource listing); a copy of Tables with an older manifest keeps it until the add-on updates. Existing chat opt-ins, hidden
add-ons and agent allowlists still apply. Shared MCP sessions and consumers
without a principal do not gain access.
The normal bounded data result contains only database IDs, optional display
names and the caller’s role:
Only active databases owned by or shared with the caller are listed; installing
Tables grants no data access. Credentials and grants are rechecked on each call.
Permission, registry and owner/transport failures remain errors, not empty lists.
A successful empty result has databases: [].
Results are ordered by resource ID and limited to 100 databases. There is no
pagination, cursor, search or total. truncated: true means the list is
incomplete; an omitted database is not evidence that it does not exist. Names
may be absent or duplicated: use opaque IDs, inspect the chosen database with
addon__tables__databases-inspect, then query its actual tables and columns.
Ask for clarification when the choice is ambiguous; never create a database
or change the chat’s selection as a discovery fallback. Inspect and query
independently reauthorize the ID.
This projected host read does not expose resource management, sharing,
selection changes or mutations as discovery tools.
In the chats (/api/chats), the agents and the triggers, a change made with
these tools is applied at once: turning Tables on for the chat or the agent is
the consent. LocalAI still previews the change, checks the person’s role and the
revision the model read, and records the receipt, so a stale change is refused
and a repeated operation applies once. Because nothing waits for a confirmation,
undo is the safety net: Tables keeps a change set for every row and schema change
a chat makes, and the chat shows an Undo button on it (see
Undo of a chat change). On every other surface (the OpenAI-compatible
API, MCP, an API key), changes normally return proposals for human confirmation.
Authorized server-side chats can also apply database creation and schema changes
with undo. Check the actual result kind: proposal is not completion; applied
confirms a change. The model cannot manufacture approval or identity arguments.
Sharing and whole-database deletion are not model tools.
Bundled discovery skill
Tables packages the tables-readonly skill for structured-data questions and
table searches. Load it to learn the workflow: list existing databases when
needed, disambiguate names, inspect tables and columns, then query relevant rows
or aggregate totals. Database listing is bounded to 100 and has no pagination;
row queries have their own cursor.
The skill needs no command runner. Existing installation, running-state, chat
switch and agent-grant gates control its availability. Its read-only guidance
does not revoke other tools granted to the chat. It never creates a database or
changes conversation selection as a discovery fallback.
Model tools and approval cards
A chat can offer the tools of Tables to its model. The person turns it on per
chat with the add-ons control in the chat header, which lists each addon that
offers tools with a switch and shows how many are on. The /addons command of
the composer turns all of them on or off, and it appears only when an enabled
addon offers tools. The chat then sends metadata.addon_tools with each
request, and the server decides what this person may call: the opt-in grants
nothing by itself.
POST /v1/chat/completions
{"model": "...", "messages": [...], "metadata": {"addon_tools": "true"}}
When only some addons are on, the request adds metadata.addon_tool_ids, a
comma-separated list of addon ids. The model then sees and can call only the
tools of those addons; a call to any other addon is refused as an unknown tool.
Without the list, every addon the person may use is offered.
A tool is named addon__<addonId>__<tool>, for example
addon__tables__rows-insert. The name is valid for OpenAI function names and
fits in 64 characters. The namespace is reserved: an MCP server of a model
cannot publish a tool under it, and an MCP session never runs one.
The schema of each tool is in capabilities.toolDefinitions of the verified
manifest (addons/tables/manifest.json). The host serves it from the
manifest, not from the addon process, and rejects a manifest whose definitions
declare a keyword outside the closed set, an identity or approval property
(actor, userId, role, approved, confirm, digest, proposalId,
operationId, tool and similar), more than 32 properties, or more than 8 KiB
for one schema. A declared tool without a definition is not offered to a
model: resource-exists is one.
Every tool that works on a database takes resourceId. The host takes it out
of the arguments and binds it to the request. A model cannot name a database
it has no grant for: the same denial answers every tool.
{"kind":"result","result":...}. A result over 128 KiB is refused, never cut. A file result is {"kind":"artifact"} with private artifact references and no bytes.
On the OpenAI-compatible API and every other consumer, a tool call never
applies a change. It stores a proposal that only the proposer’s own browser
session can confirm. The tool adapter has no confirm, cancel or resolve
operation, and no tool of any consumer maps to those routes. The model is told
that nothing changed and that a person must confirm.
The chats are the one exception. Their adapter stores the proposal and confirms
it in the same call, for the signed-in owner of the chat, with the credential of
a chat turn. No HTTP request can carry that credential, so a key, an MCP session
or a browser tab cannot use it. The result is
{"kind":"applied","applied":{id,addonId,resourceId,action,count,undoable,changeId,undo,reason},"note":...}.
undoable is true when Tables kept a change set: changeId names it and undo
says in one line what an undo does. Otherwise reason says why not, for example
that the data was too large to keep. The note tells the model to say plainly what
changed and whether the person can undo it, and that only the person can: the
model has no tool for it. A call the add-on refuses or finds stale leaves nothing waiting: the
proposal is withdrawn and the model gets the error. A call whose outcome is not
known is not repeated and not withdrawn: it is reported, and the operation ID
resolves it.
These names do not exist and are refused with a typed error: confirm,
approve, apply_proposal, yes, any sharing, grant, database deletion or
retained-data purge name, and any name that is not defined by the manifest of
an enabled addon. Arguments that carry identity, role, approval or operation
fields are refused before they reach the owner, so a model cannot forge the
actor, the role or an approval, and a repeated or oversized argument object is
refused as well.
The approval card
Where a tool answers with a proposal (not in the chats, which apply the change
at once), the host’s card is drawn inline.
The card loads the stored proposal from the host by its ID. It shows the
add-on, the database (the requested name for a creation), the operation, the
exact count of effects, a bounded sample, the expiry and the state. The words
of the model are never the preview. Confirm and Cancel are the host’s buttons:
Confirm sends the digest that was displayed with the X-LocalAI-Confirm
header from the person’s own session.
The card is disabled for an expired, blocked, applied, cancelled, rejected or
unknown-outcome proposal and says why. It forgets what it loaded when the
person logs out, another person signs in, the addons feature is withdrawn or
the host stops answering for the proposal. The stored conversation keeps the
proposal ID only: no rows, no digest, no sample.
GET /api/addons/proposals?state=pending lists the caller’s own pending
proposals, newest first, at most 32. It is where a change proposed by an agent
or a scheduled job waits: those callers can read and propose within their
permissions and can never confirm, because a key has no browser session.
Consumers
The consumer inventory is executable. The tests scan the sources for code
that discovers or executes tools and fail for a file that has no decision
here, so a new consumer cannot ship without one. A routed consumer takes an
immutable actor from the authenticated request and goes through the broker.
A rejecting consumer never serves add-on tools: the namespace is refused or
filtered there.
Consumer
Decision
What it is and why
chat-completions
route
POST /v1/chat/completions with metadata.addon_tools. The actor is the named user or key of the request.
direct-api
route
The /api/addons routes called by the UI, scripts and agents. The actor is the named user or key of the request, revalidated on every call.
addon-tool-executor
route
The executor that serves the namespace inside a chat request. It holds one actor value and has no setter.
localai-assistant
reject
The in-process admin MCP server. It has no per-call principal and its catalog holds no addon tool.
model-mcp-sessions
reject
MCP sessions built from model configuration and shared by every user of the model. The namespace is dropped from discovery and refused at execution.
anthropic-messages
reject
POST /v1/messages. A request with addon_tools gets addon_tools_unsupported (400).
open-responses
reject
POST /v1/responses. A request with addon_tools gets addon_tools_unsupported (400).
realtime
reject
The realtime WebSocket and WebRTC sessions. Long-lived sessions cannot be revalidated between utterances.
legacy-mcp-chat
reject
POST /v1/mcp/chat/completions passes no broker, so addon_tools gets the same typed answer.
mcp-discovery-endpoints
reject
Listing of servers, prompts and resources. They execute no tool.
studio-chats-mcp
reject
The MCP tools of a chat on the server-side chat loop, from the shared sessions of the chat’s model. The executor and the chat’s bridge both drop the namespace, and the MCP Apps proxy of a chat refuses it.
studio-chats-mcp-content
reject
The prompts and resources of the MCP servers of a chat’s model. They execute no tool and their sessions are shared by every user of the model, so they carry no actor. The user needs the MCP feature and the model must have MCP servers.
studio-chats-mcp-connectors
reject
The tools of the MCP servers a user added as connectors. They are not add-on tools: they run on the user’s own server with the connector’s credential. The chat drops a tool in the add-on namespace or with the name of a built-in; the MCP Apps proxy applies the same rule and checks the owner on every call.
studio-chats-connector-addons
reject
The messages of a connector add-on, such as Telegram: the add-on route /connector and the host operation connector.inbound. They carry messages, not tool calls. The operation is authorized by the add-on’s live incarnation credential and by the stored binding, which names the owner and the agent; an argument cannot choose the user. The turn gets only the tools of the binding’s agent, through studio-chats-addons.
studio-chats-assistant
reject
The LocalAI Assistant tools of an assistant chat, for an administrator. They are the instance management tools, not add-on tools.
studio-chats-client-tools
reject
The LocalAI client tools that the host runs for a chat’s user outside a model turn, such as the confirmation of a voice profile proposal. They are the inference and instance tools of the LocalAI client, not add-on tools.
studio-chats-addons
route
The add-on tools of a chat, action tools included. The chat’s user is the actor, with the credential of a chat turn; the account and the add-ons feature are checked again on every call.
distributed-agent-worker
reject
Workers that execute MCP tools for a frontend. The tool and discovery calls refuse the namespace, and the frontend refuses it before the request is sent.
A shared model MCP session is a rejection on purpose. One session answers
every user of a model and keeps no current-user state, so it could not carry
an actor. A deployment that needs add-on tools for an agent uses the
/api/addons routes with that agent’s own key.
Undo of a chat change
A change a chat, an agent or a trigger makes at once can be undone, within the
limits below. Tables records the inverse of the change inside the database file,
in the same transaction as the change, bound to the revision the change produced.
Change
What is kept
What an undo does
rows-insert
the row IDs
deletes exactly those rows
rows-update
the previous values of the columns the change set, for each row
puts the previous values back
rows-delete
every value of every deleted row, with its row ID
inserts the rows again under the same IDs
tables-create
the table
drops the table
tables-rename, columns-rename, databases-rename
the previous name
renames it back
columns-add
the column
removes the column
columns-drop
the column definition (type, required, unique, default) and the values of every row
adds the column back under its own identifier with its data and its rules
tables-drop
the table definition, every row and the row counter
recreates the table with its rows and row IDs
databases-create
nothing: the creation itself
deletes the database, only while nothing touched it since
The rules:
Stale. A change set is bound to the revision of the change. It can be undone
only while the database is still at that revision, so an undo never overwrites later
work. Any later change of any table makes it stale, and the answer says what changed
after it (for example, “inserted 1 row into People”). One refinement keeps this
useful: an undo is itself a revision with the state of the one before the change,
so a run of changes unwinds from the newest to the oldest. A rename of the
database does not move the revision, so its change set is stale only when the
database was renamed again.
Idempotent. An undo has an operation ID. The same ID with the same change is one
undo and returns the same receipt; another ID for a change that is undone already
is already_undone.
Bounds. The inverse of one change is at most 4 MiB, and a database keeps at most
64 MiB of inverses, dropping the oldest first. A change whose inverse would be larger,
such as dropping a large table, keeps none, and its result says so at apply time:
undoable is false with the reason “cannot be undone: the data it changed is too
large to keep”. Tables never claims an undo it cannot carry out.
Retention. A change set can be undone for 7 days. After that the answer is
expired and the stored data is dropped. This period is fixed in this version; no
setting changes it.
No redo. An undo is a recorded change of the database, but it has no change set of
its own, so it cannot be undone in turn. Make the change again instead.
Roles. The role of an undo is the role of the change: owners and editors undo a
data change, only the owner undoes a schema change or the creation of a database.
Not a tool. The undo is the person’s own action from a browser tab (see
Undoing a change a chat made). An API key, an
MCP session, an agent or a model cannot call it, and no tool name for it exists. This is
a decision: the model is told the change ID and that the person can undo it, so it
cannot silently undo its own work or anyone else’s.
Change sets live in the database file, in the table tables_changes. They are part of
every backup and restore of the add-on’s data, keep their retention across a restore
(the time is absolute), and are removed with the database when it is deleted or purged.
Nothing else holds them.
Several frontends
The tool adapter keeps no runtime state, so it is stateless in the sense of
the distributed-state rule. Each frontend builds the tool list from the apps
list its owner answers for the caller, and sends every call, proposal and
pending list through the same carrier as the other addon routes. A frontend
that is not the owner never holds a package, a proposal or a policy record.
The apps list carries the tool definitions of each enabled addon, at most 32
KiB per addon, so the model-facing schema is the one in the verified manifest
of the installed generation. If the owner cannot answer, discovery fails and
the chat request answers 503 rather than offering stale tools.
Admin lifecycle tools
The LocalAI Assistant catalog has no add-on lifecycle tool (install, enable,
disable, upgrade, uninstall, library changes). This is deliberate. The
Assistant is one process-wide server with no per-call principal, and its
mutating tools rely on the model asking the person for confirmation in prose.
Add-on lifecycle changes are administration with persistent effects on data
and processes, so they are not exposed that way. Listing and status are read
with GET /api/addons/apps and the addons-installed management read, which
need a named administrator. A conversational lifecycle tool needs a per-call
principal and a host approval card first; it is not part of this release.
REST-to-MCP parity for /api/addons is intentionally partial for that reason,
and TestToolHTTPRouteMappingComplete stays unchanged. The UI-only resource
routes (create, rename, share, delete, selection) are likewise not tools:
sharing and deletion are never tools, and the consumer matrix test scans the
catalog for any /api/addons route.
How creation survives a crash
Creation has three steps, in this order:
LocalAI records a pending resource for the creator. A pending resource is
invisible to everyone, its creator included.
The addon creates the database file. It builds the file under a temporary
name and links it into place only when it is complete.
LocalAI commits the resource, which makes it usable.
The addon operation ID is derived from the reserved resource ID, so repeating
a step is one operation. If LocalAI stops between steps, it settles every
pending resource at the next start by calling resource-exists:
No database: the pending record is dropped.
Database present: the resource is activated for the creator.
The addon is installed but cannot answer: LocalAI stops its startup with a
recovery-required error. It never guesses.
Deleting a database follows the same rule from the other side. LocalAI removes
the record of a deleting database without calling databases-delete only when
resource-exists answers that the database is gone. If the addon cannot
answer, the deletion stays pending and is retried.
A database file without a record cannot be reached by anyone. A creation whose
outcome is uncertain keeps its reservation, and that reservation counts against
the per-user limit of pending resources until its receipt is found, the person
who asked stops waiting, or the next start. Stopping to wait drops the
reservation only when resource-exists says there is no database. A pending
chat proposal keeps its reservation, so a restart does not remove a database
you can still confirm.
Managing databases from the UI
Creating, renaming, sharing and deleting a database are the person’s own
actions in the Tables pages, through the routes described in
UI-only resource management. They
are not tools: a model never sees them and sharing and deletion are never
proposals.
databases-create and databases-rename are the same tools that chat uses,
run by the person’s explicit request with no approval card.
databases-delete is an admin tool. The manifest lists it in
capabilities.adminTools and not in tools, so it has no model definition and
the declared-tool rule refuses it on every other path. The addon runs it only
under the host action delete and only for the revision the owner confirmed.
It removes the file and its journal files and records the operation in the
ledger; repeating the operation, also after a crash between the removal and the
receipt, gives the same receipt.
The manifest capabilities.resources maps create, rename, inspect and
delete to these tools, with the noun database.
A database is shared whole, as viewer or editor, with named users picked by
account. An owner and an editor see the same rows; only the owner changes the
schema, shares, renames and deletes. Deleting asks the owner to type the name,
removes the data and every grant, and never uninstalls the addon.
The database of a chat
The owner keeps which database each conversation works on, per person. A tool
call that names no resourceId uses it, so a person can say “add a row” after
choosing a database once. The model never sets it, and the owner authorizes it
for the person on every call: a database whose grant was withdrawn or that was
deleted is refused like an unknown one. With none selected the tool answers a
select_resource result and runs nothing.
The Tables pages
Tables ships its own UI as a standalone ES module in the package. It is loaded by
the host after the host was built, so installing or updating Tables changes the
pages without a rebuild of LocalAI. The My add-ons page shows a Tables card.
Where
What
Databases list
Your databases and the ones shared with you, each with your role, who shared it and, for the owner, how many people have access. Create, rename and delete (owner).
A database
The tables as tabs, the typed grid of the selected table, a structured filter, sort by column, cursor paging and the row actions of your role.
Schema (owner)
Create, rename and drop tables and columns. A drop says what is lost and asks for the name to be typed.
Share (owner)
The whole database is shared as viewer or editor with named accounts found by a picker. Local-owner mode says that sharing is not available and makes no sharing call.
Select database
A chat action in the composer menu: the databases you may use with your role, the one selected for this conversation, and a way to clear it.
What each role sees:
Role
Rows
Schema
Share, rename, delete
Owner
read, add, edit, delete
yes
yes
Editor
read, add, edit, delete
read only
no
Viewer
read
read only
no
Hiding a control is never the control. The server checks the role of every call
again, and a crafted write of a viewer, a schema change of an editor or a share
of anybody who is not the owner is refused with the answer of an unknown
database.
Editing rows
Typed editors. Text, whole number, number, yes or no, and date and time
(entered and shown in UTC). A value of the wrong type is refused in the form
and nothing is sent. Integers beyond 2^53 are kept exact. An optional column
has an Empty switch that sends null.
Filters. A list of conditions over the table’s own columns, joined by
all or any, with the operators the column type supports. They become the
filter of rows-query; there is no free text and no expression. The row
ID is filterable as _id.
Paging. The add-on’s cursor goes forward and the page keeps the cursors it
used to go back. A cursor that the add-on no longer accepts (the data
changed) starts again at the first page and says so.
Batches.Add rows takes up to 100 rows. A selection is changed or
deleted in steps of at most 1000 row IDs, each step saved on its own with its
own operation ID. If a later step fails, the page says how many rows were
changed before it.
Revisions. Every change sends the revision the page read. If another
person changed the database, the save is refused as stale, the form keeps what
you typed and offers Reload and apply again, which reads the revision and
sends the same change once more under a new operation. An unknown outcome
keeps the operation ID, so repeating is the same change and never applies
twice.
A row editor sends only the fields you changed. A datetime keeps its stored
fraction of a second (up to nine digits) when you edit another field, and two
spellings of one instant, such as another UTC offset, are no change.
Row and schema changes made in these pages are the person’s own action. They
need no confirmation card and are not proposals, and they still go through the
host’s POST /api/addons/resources/{resourceId}/apply: the role of the action,
the declared tool definition, the admission of the resource, the revision and
the receipt (see Changing data and schema from the UI).
The database of a chat
Choosing a database in the Select database action stores it for you, the
conversation and the add-on. A chat tool call that names no database uses it,
and the chat sends the conversation ID in the X-LocalAI-Conversation-Id header
only when the conversation has add-on tools enabled. Two people in the same
conversation ID, or one person in two conversations, never share a selection. A
database that is no longer shared with you is refused like an unknown one.
Package and build
The UI source is addons/tables/ui. node addons/tables/ui/build.mjs writes
dist/ui/tables.js, dist/ui/tables.css and dist/ui/icon.png, which the
manifest names under capabilities.ui (entry, css, icon, pages,
navigation, mainPage, chatActions). The package declares no
dependencies, so it has no lockfile: the build uses the bundler that the host UI
already has installed (core/http/react-ui/node_modules) and needs nothing to
download. node addons/tables/ui/test/run.mjs runs its unit tests. The module imports
nothing: React, the components, the typed calls and the navigation arrive through
the host SDK, and every class of the style sheet starts with tbl-. The UI is
translated to the languages of the host; a missing key reads the English text.
Schema operations
Only the owner of a database changes its schema. LocalAI refuses the schema
action for every other role before it calls the addon, and the addon refuses
a schema tool that does not arrive as a schema action with an actor and the
preview or apply scope, so a caller of the runtime transport cannot use a row
action to change a schema.
Tables and columns have a label and a stable internal identifier. The label is
what people see. It is 1 to 64 characters, with no control, format or
bidirectional characters, no leading or trailing space, and it is unique in its
scope ignoring case. The internal identifier (t_..., c_...) is random and
never changes. Labels are never part of an SQL statement, so a rename touches
only the catalog and no label can inject SQL.
Logical type
Stored as
Default value
text
TEXT
a string up to 1024 bytes, no control characters
integer
INTEGER
a 64-bit integer
real
REAL
a finite number that SQLite reads back exactly
boolean
INTEGER, 0 or 1
true or false
datetime
TEXT, UTC
an RFC 3339 time, stored in UTC
A default is validated for its type and written through a strict literal
encoder, never copied from the request. There is no type change, trigger,
function, expression, computed column, view or raw SQL.
A required column needs a default when the table has rows. On an empty table
it needs none.
A unique column is a unique index. A default that would repeat in more than
one row of a unique column is refused. Several NULL values are allowed.
Dropping a column rebuilds the table inside the same transaction: create the
new table, copy the rows, drop the old one. A failure leaves the original.
Limits: 50 tables per database, 64 columns per table.
Revisions, receipts and previews
Each database has a revision pair, schema and data. Every schema change
raises schema by one. The preview returns the revision it was made against,
and apply must send that revision back. If the database moved, apply is
rejected with {"error":"stale","revision":{...}} and nothing changes. Both
numbers must match, because the data a drop destroys depends on the rows.
The change, the new revision and the operation receipt commit in one SQLite
transaction. After a crash the receipt is either in the database or the change
did not happen. The same operation ID with the same request returns the same
receipt and never applies twice. The same ID with another request is a
conflict. An operation that was refused leaves no receipt, so its ID can be
used again. If a reply is lost, LocalAI asks for the receipt, which the addon
finds in the database even when its own ledger was not updated.
A preview runs the change in a transaction that it rolls back, so it accepts
only what apply would accept. Its summary has this shape:
rowsLost of a column counts the rows that hold a value in it. For a table it
counts all rows and columnsLost its columns. LocalAI shows count and
sample in the confirmation card.
Row queries
rows-query and rows-aggregate run under the read action, so viewers can use
them. The addon builds each statement from internal identifiers and binds every
value as a parameter. A column or table label is only looked up in the catalog;
it never becomes SQL text. There are no joins, raw SQL, expressions, functions
or subqueries.
Rows come back as {"id": n, "values": [...]} in the order of the
columns of the reply. id is the stable row ID of the table. It never
changes and is never reused after a delete. Filters and sorts may name it with
the label _id; no column may use that label. Omit columns to select all.
Filter is a tree of and, or and conditions: at most 8 levels and 64
nodes. Operators are eq, ne, lt, lte, gt, gte, in (1 to 100
values), is-null, is-not-null, and for text columns contains,
starts-with and ends-with. A boolean column supports eq, ne and the
null tests only. A node is exactly one of and, or or a condition; unknown
fields in the arguments are refused.
Text matching.eq, ne and the order operators on text are
case-sensitive and compare bytes. contains, starts-with and ends-with
are ASCII case-insensitive and treat %, _ and \ in the value as plain
characters. The value is at most 1024 bytes.
NULL. A comparison (eq, ne, lt, …, in) never matches a NULL
value, so ne does not return rows where the column is empty. Use
is-null for those, for example or of ne and is-null. A comparison
with a null value is refused. In a sort, NULL comes first ascending and last
descending. Aggregates ignore NULL values; count without a column counts
rows.
Sort takes at most 4 columns. Rows with equal keys are ordered by row ID,
so a page boundary is stable.
Pages.limit is 1 to 500 and defaults to 50. When more rows match,
hasMore is true and cursor is set; send it back with the same query to get
the next page. A cursor is valid only for the same query and the same database
revision; after any change the addon answers that the cursor is no longer
valid and the caller starts again. There is no total count. Offsets beyond
100000 rows are refused, so add a filter. A page is also cut at 512 KiB of
JSON, and hasMore is set.
Values in JSON. text is a string, integer a JSON integer, real a finite
number, boolean true or false, datetime an RFC 3339 string. Datetimes are
stored in UTC with nine fractional digits and returned in UTC. Integers can be
sent as a number or a decimal string; integers beyond +/- 2^53 are returned
as strings so no precision is lost.
Aggregates.aggregates holds 1 to 8 of {"fn": "count" | "sum" | "avg" | "min" | "max", "column": "..."}. sum and avg need an integer or real
column; min and max work on integer, real, text and datetime. groupBy
names one column; groups are ordered by the group value and limited to 500,
with truncated set when more exist. A sum that overflows 64 bits is an
error, not a wrapped number.
Cancellation. A query is bound to the request context and to a per
operation deadline; a cancelled or slow query is interrupted and returns an
error. Each read sees one snapshot of one revision.
Row changes
rows-insert, rows-update and rows-delete are changes under the host
write action: Editors and Owners may propose them and the proposer confirms
them in the host card. The addon refuses a row tool that arrives under another
action and a schema tool that arrives under the write action.
Insert takes 1 to 100 rows; each row maps column labels to values. A
column that is left out gets its default, or NULL. A required column without a
default must be given. An explicit null for a required column is refused
even when it has a default. The whole batch commits or none of it.
Update and delete name their rows by exactly one of ids (distinct
positive row IDs, all of which must exist) or filter. There is no way to
touch every row without a filter that selects them, and no truncate. One
operation changes at most 1000 rows; a broader filter is refused with a
request to narrow it. An operation that matches no row is refused.
Constraints. Type, required and unique rules are enforced by the
database. A violation rejects the whole operation, names the column by its
label and leaves the data unchanged.
Preview. The preview runs the same code in a transaction that it rolls
back. It reports the exact count, a sample of the first 10 rows (for update,
the values before and after of the changed columns; for delete, the full
rows), an affectedHash of the exact row ID set, and dataLoss.rows for a
delete. A change that would break a unique rule fails at preview, so the card
never offers it.
Receipt. The payload holds count, the affected ids and affectedHash.
The receipt commits in the same transaction as the rows and the data revision
step. LocalAI reports success only from the receipt.
Revisions and staleness
The revision is conservative and per database. Every committed row change
raises data by one in the same transaction; a schema change raises schema.
Apply compares both numbers with the revision of the preview inside the
transaction. If either moved, the change is rejected with
{"error":"stale", ...}, and a new proposal and a new preview are required.
Because the revision is unchanged, the set of rows a filter selects is the set
that was previewed: a filter is never evaluated against rows that arrived after
the preview. A write to any table of the database makes a pending row proposal
stale, even when it touched unrelated rows. Two proposals made on the same
revision cannot both apply: exactly one wins and the other is stale.
Row changes use the receipt rules above: the same operation ID with the same
request replays one receipt, another request under the same ID is a conflict,
and a rejected operation does not use up its ID.
CSV export
rows-export writes the rows a table, a filter and a sort select to a CSV
file. It is a read: it runs under the host read action, so an Owner, an
Editor and a Viewer may export what they can read, and it needs no approval
card. A chat tool call and the Export CSV button of a table page run the
same tool.
columns, filter and sort follow the rules of Row queries.
columns may name _id to write the row ID as a column; empty exports every
column in schema order. maxRows is optional. There is no limit and no
cursor: an export is the whole result, not a page.
What the file holds
One snapshot. The export reads the database in one read transaction, so every
row comes from the same revision. A change that commits while the export
runs is not in the file, and the export never blocks it. The file is written
row by row through a small buffer; its memory use does not grow with the
number of rows.
RFC 4180 records with a header row of the column labels and CRLF line ends.
A field is quoted when it holds a comma, a quote, a CR or LF, or starts or
ends with a space or tab. A quote inside a field is doubled.
UTF-8, with no byte order mark. Text is written as it is stored. A
spreadsheet that guesses the encoding from the file may need to be told to
read it as UTF-8.
NULL is an empty field with no quotes. An empty text value is "". A reader
that does not tell the two apart reads both as an empty value.
Booleans are true or false. Integers are decimal and exact to 64 bits.
Reals are the shortest decimal that reads back as the same number; very large
or very small values use an exponent. Date-times are RFC 3339 in UTC.
Spreadsheet formulas
A spreadsheet runs a cell that starts with =, +, -, @, a tab or a
carriage return as a formula. The export therefore writes a single quote
before every text cell, and every column label, that starts with one of
those characters, as the OWASP guidance recommends: =1+1 is written as
'=1+1. A spreadsheet shows the quote as the text marker, not as part of the
value.
Limits of this policy:
Cells of integer, real, boolean and date-time columns are never prefixed. A
negative number stays a number (-4).
The file now holds '=1+1, not the stored =1+1. A program that reads the
CSV back must remove that quote itself; the export does not mark which cells
it changed. A text value that really starts with ' and a formula are
therefore not told apart after the round trip.
A text value such as -5 apples also gets the prefix.
The policy protects the file against being run as a formula in a spreadsheet.
It is not a defence for a program that treats CSV as trusted input.
Backup, upgrade and restore
The databases are SQLite files under the data directory of the add-on, with one
ledger. The host backs them up with VACUUM INTO while the add-on is stopped, so the
copy is consistent and holds no write-ahead log (see “Data recovery” in the add-ons
reference). Backup, restore and purge are not tools and not part of the manifest:
the administrator’s page uses the recovery routes, and no model can reach them.
Tables records the format of its data. store.FormatVersion is the format this
build writes; it is kept in the format key of each database and, for the whole store,
in the ledger. At start the executable calls Upgrade, which compares them, runs
Migrate(ctx, fromVersion, toVersion) when the data is older and writes the progress
to migration.json. Each database migrates in one transaction, so a step that fails
leaves that database exactly as it was, and the store version is recorded last, so a
run that stopped resumes. Data in a newer format than the executable reads stops the
start with exit status 9 and is left as it is: the host then keeps the add-on
unavailable instead of running it on data it cannot read. No step exists yet because
the first format is the only one. The table of change sets (tables_changes) is
created when a database first needs it and is ignored by a build that does not know
it, so it needs no migration.
Limits
Limit
Value
Rows in one export
1,000,000
Size of one file
64 MiB, and never more than the host’s file size bound
Time for one export, snapshot to last byte
25 seconds
Exports staged at once in the addon
16, at most 4 for one person
A result past a bound makes the tool answer {"exported": false, "error": "too-large"} and creates no file; add a filter to export less. A maxRows
above the row bound is refused the same way, not clipped. A staged export that
the owner did not fetch within ten minutes expires; the addon removes expired
files when it handles the next export or fetch, and removes every staged file
when it starts.
How the file reaches the person
There is no separate asynchronous export job in the addon. The CSV is written
inside the read call, within the 25 second bound, because the owner needs the
exact size before it moves a byte. A result that does not fit those bounds
fails with too-large instead of running longer. The transfer from the addon
to the owner’s private store is the background job: the read returns at once
with the artifact in state transferring, and the page and the chat ask the
host for its state until it is final. Cancelling the transfer, a revoked share
and a stopped addon end it and remove the partial bytes.
The addon writes the CSV into its private directory and answers with one
output description: a name (<table>.csv), the media type text/csv, the exact
size and an opaque handle bound to the operation, the person and the database.
LocalAI streams the bytes from the addon, checks the size and replaces the
handle by its own artifact identifier. The page, the chat and the model get
that identifier, the name, the size and the expiry. They never get the addon’s
handle, a path or a URL. The transfer runs in the background; the artifact
shows transferring, then complete, failed or cancelled, and an export
that did not complete never becomes downloadable.
GET /api/addons/artifacts/{id} is the transfer state, name, size and
expiry. GET /api/addons/artifacts/{id}/download is the file, as an
attachment with X-Content-Type-Options: nosniff and
Cache-Control: private, no-store. Both need the person’s own session or
API key.
The file is private to the person who exported it. Sharing the database does
not share a file. A person who is not the principal, an unknown identifier
and an expired file all get the same 403.
Access to the database is checked again when the transfer completes and at
every download, and a revocation stops a download in progress. After a
viewer’s share is withdrawn the viewer’s earlier files stop downloading.
A file already downloaded cannot be recalled.
A file expires one hour after it was made, and the person’s quota is
100 files. Expiry removes the file only. The database and any backup are
not touched.
A disabled addon does not serve downloads; enabling it again serves the
files that have not expired.
In single-owner mode there is one local owner and no separation between
people, as for every other operation.
In the pages and in chat
A table page has an Export CSV button for every role. It exports the table
with the filter and sort of the view, optionally with the row ID, and then
shows the host’s file card: name, size, expiry and the download button. In
chat, an export tool call is answered with the same card. The chat stores only
the artifact identifier; after expiry the card says the file expired.
Limits
One name per database, no uniqueness rule across databases.
Rename needs the schema revision from the preview. A changed schema makes
the rename rejected, and a new preview is required.
The data directory holds the databases and a small operation ledger. Both
are kept when the addon is disabled or uninstalled.
How to try it
The addon is not published in a catalog yet. Acceptance builds the package locally and
installs it from a catalog on loopback; it never pushes, tags or signs. Build the executable, put it in a
package with manifest.json from addons/tables, and install the package from
a local source as described in Addon contracts. The suite
sh scripts/addons/verify.sh tables-databases runs the store and the real
executable under the real supervisor. The suite
sh scripts/addons/verify.sh tables-schema covers the schema operations,
revisions and receipts.
The suite sh scripts/addons/verify.sh tables-rows covers queries, filters,
aggregates and confirmed row changes with the real executable.
The suite sh scripts/addons/verify.sh tables-tools covers the model tools,
the consumer inventory, the pending proposal list and the approval card in a
real browser against the real owner.
The suite sh scripts/addons/verify.sh tables-csv covers the CSV export: the
snapshot and encoding rules, the output handles, the private artifacts of the
real owner and the download from the owner and two frontends.
The suite sh scripts/addons/verify.sh tables-ui covers the pages: the node
tests of the field codecs, the filter builder, the batch bounds and the build,
the UI-intent change route against the real owner, and a browser spec with
real sessions for the roles, private defaults, sharing and a concurrent revoke,
destructive deletion, a stale tab after an upgrade or a disable, the database of
a chat for two conversations and two people, keyboard, 400 pixels, both themes,
clearing on sign-out and local-owner mode.
The suite sh scripts/addons/verify.sh tables-acceptance is the end-to-end
acceptance (see Acceptance).
The suite sh scripts/addons/verify.sh tables-admin covers the UI-only
management of databases with the real owner, real accounts and the real
executable: private defaults, the owner, editor and viewer matrix, sharing and
the user picker, deletion and its crash reconciliation, the database of a
conversation, and the answers of a forwarding frontend.
Calendar
Note
Add-ons need a development build or a release newer than 4.12.1.
In the current development builds, this add-on is in the development channel of the add-on library, so it can change.
At a glance
Topic
Detail
What it adds
One calendar for each user, inside LocalAI. A chat or a saved agent reads and changes events. There is no account to connect.
Who installs it
An administrator, from Add-ons, Find add-ons, with Install and turn on. With authentication off, you are the administrator.
Who turns it on
A person needs the addons feature. It is off by default for new users on a server with accounts. The person switches the add-on on for a chat in the Tools menu. A saved agent uses it only when you name it in the agent.
The Calendar add-on gives you a calendar inside LocalAI. Ask a chat or a saved agent “what is on Thursday?” or “add dinner with Sam at 8” and it reads or changes the calendar. There is no account to connect, no sync and no setup: install the add-on and use it.
What it keeps
There is one calendar for each user, shared by all of that user’s chats and agents. It is one SQLite file in the data directory of the add-on. An event has:
a title, a start and an end, or a whole day (an all-day event);
notes, a place and the attendees, each as free text;
a time zone, as an IANA name such as Europe/Rome, and optionally a repeat.
A time is a local date and time with a zone name, the way a person says it. Set the default time zone once, on the Calendar page (it starts as the zone of the server). A new event uses it unless you or the agent name another one. An event keeps the zone it was written in when you change the default.
A repeat is daily, weekly, monthly or yearly, with an interval (“every 2 weeks”) and an optional last day. The local time stays the same across a change of daylight saving time. A monthly repeat on the 31st skips months that have no 31st. You can change or delete the whole series, or skip one occurrence.
There is no sync with another calendar, no invitation, no second calendar and no reminder that pushes a message. You can export the whole calendar as one ICS file from the page and import it elsewhere.
The page
Open Calendar in the add-ons. It has a month view, an agenda list of the next 60 days, a form to create or change an event, the default zone and the export button. Click a day to add an event, or an event to change, skip or delete it. The page speaks the language of the web UI.
What the agents can do
Tool
Does
events-list
Lists a range of days with repeats expanded; with no arguments, today and six more days.
events-search
Finds events by words in the title, notes, place or attendees.
event-get
Reads one event in full.
event-create, event-update, event-delete
Add, change and delete an event. With occurrence (a date), event-delete skips only that day of a repeating event.
The reads run at once. The writes are action tools: they run at once, as the user, with no confirmation card, and an unattended run (a trigger or a job) needs the act.addons grant for them. The add-on ships a skill that tells the model when to use them.
In a messaging chat, the owner and the senders paired with the bot can read and change the calendar. Every other sender the bot lets in can read it but not change it (the tools.write rule, see Add-ons that keep the owner’s own data).
Limits
An event title has at most 200 characters, notes 4000, a place 200 and attendees 500. One user keeps at most 5000 events. A listing covers at most 400 days and returns at most 500 occurrences; the page says when a list was cut.
Contacts
Note
Add-ons need a development build or a release newer than 4.12.1.
In the current development builds, this add-on is in the development channel of the add-on library, so it can change.
At a glance
Topic
Detail
What it adds
One address book for each user, inside LocalAI. A chat or a saved agent looks up and keeps contacts. There is no account to connect.
Who installs it
An administrator, from Add-ons, Find add-ons, with Install and turn on. With authentication off, you are the administrator.
Who turns it on
A person needs the addons feature. It is off by default for new users on a server with accounts. The person switches the add-on on for a chat in the Tools menu. A saved agent uses it only when you name it in the agent.
The Contacts add-on gives you an address book inside LocalAI. Ask a chat or a saved agent “what is Marco’s number?” or “add Anna, anna@example.org” and it reads or changes the book. There is no account to connect, no sync and no setup: install the add-on and use it.
What it keeps
There is one address book for each user, shared by all of that user’s chats and agents. It is one SQLite file in the data directory of the add-on. A contact has:
a name, and an organisation;
phone numbers and email addresses, each with a free label such as mobile, home or work (up to 10 of each);
a birthday, as YYYY-MM-DD, or --MM-DD when the year is not known;
an address and notes, each as free text.
There is no sync with another service, no CardDAV, no groups, no photos and no merging of duplicates. Import of vCard files is not part of this version. You can export the whole book as one vCard file from the page.
The page
Open Contacts in the add-ons. It has a search box, the list of contacts, the detail of the one you pick, a form to add or change a contact, and the export button. The search matches every word you type against the name, organisation, phone numbers, email addresses, address and notes. It ignores case and accents, and a phone number matches however it is spaced. The page speaks the language of the web UI.
What the agents can do
Tool
Does
contacts-search
Finds contacts by words, and gives a short card for each match with the phones and emails.
contact-get
Reads one contact in full.
contact-create, contact-update, contact-delete
Add, change and delete a contact. A phones or emails list in an update replaces the whole list.
The reads run at once. The writes are action tools: they run at once, as the user, with no confirmation card, and an unattended run (a trigger or a job) needs the act.addons grant for them. The add-on ships a skill that tells the model when to use them.
In a messaging chat, the owner and the senders paired with the bot can read and change the book. Every other sender the bot lets in can read it but not change it (the tools.write rule, see Add-ons that keep the owner’s own data). The book is private to its user: a tool call can never name another user’s book.
Limits
A name has at most 200 characters, an organisation 200, a label 40, a phone number 40, an email address 254, an address 500 and notes 4000. One user keeps at most 2000 contacts.
A vCard export is a vCard 3.0 file. A birthday without a year is written as --MM-DD, which some programs do not read.
Web search
Note
Add-ons need a development build or a release newer than 4.12.1.
The Web add-on (ID web) serves the chat tools web_search and web_fetch in place of the built-in ones. The built-in tools work with no add-on and no key. The add-on adds more. In the current development builds, it is in the development channel of the add-on library, so it can change.
At a glance
Topic
Detail
What it adds
Search providers that need a key (Brave Search, Tavily and Google Programmable Search), tried in an order an administrator chooses, and the text of PDF files in web_fetch.
Who installs it
An administrator, from Add-ons, Find add-ons, with Install and turn on. With authentication off, you are the administrator. An administrator sets the providers on the Settings tab of the add-on.
Who turns it on
A person needs the addons feature. It is off by default for new users on a server with accounts. The person switches the add-on on for a chat in the Tools menu. A saved agent uses it only when you name it in the agent. The add-on has its own switch. The Tools menu switch of the Web row changes only the built-in tools.