Data holds many records per agent; for one record per end user, write to the end user’s record instead (Identify users).
Verified against lua-cli 3.33.0.
Before you begin
- A skill on the agent to hold the tools (Add a tool to a skill).
- A test collection name. Collections are shared by the sandbox and production (About environments), so a local run writes the same data the live agent reads.
1
Store an entry with search text
Data.create takes the collection, the object to store, and an optional string that is embedded for semantic search. Put every term a question might use into that string: it is stored beside data as the entry’s searchText and is the only text search() matches against.src/skills/tools/AddKnownIssueTool.ts
entry.product) and through entry.data. Store searchText as a plain string in deployed code; the options object is covered under Options you may need.2
Find entries by meaning
Data.search embeds the query and returns the closest entries as a flat array, best match first, each with a score from 0 to 1. limit is at most 20 and scoreThreshold defaults to 0.6.src/skills/tools/FindKnownIssueTool.ts
3
Filter entries exactly
Data.get takes a Lua Query filter over the stored fields and returns one page as { data, pagination }. Entries here are plain objects, so read fields through entry.data. Pages hold at most 100 entries.src/skills/tools/ListOpenIssuesTool.ts
$eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, and $exists are the field operators, and $and and $or combine clauses at the root.4
Update and delete
Data.update merges the fields you pass into the entry and keeps the rest; pass a replacement string as the fourth argument to re-index it. Data.delete removes one entry by id.src/skills/tools/ResolveKnownIssueTool.ts
5
Register the tools in a skill
The skill’s The agent file below is the quickstart’s; if yours differs, add only the highlighted lines to your own
context says who may add and resolve issues; only tools in a skill on LuaAgent.skills are compiled.src/skills/known-issues.skill.ts
LuaAgent.src/index.ts
6
Verify
Store an entry, then search for it with different words. Both runs hit the real collection.The query shares no word with the stored title, and the match still scores above 0.7. Clean up with
Output
resolve_known_issue and "remove": true.7
Release
lua push uploads a version and changes nothing for end users; lua version create snapshots the agent; lua version promote <n> makes that snapshot live and is also the rollback path (Release an agent to production).lua version create prints ✓ Created v<n> (staged). Run `lua version promote v<n>` to deploy.; <n> comes from that line, promote accepts <n> or v<n> and asks no confirmation, and in a script n=$(lua version list --limit 1 --json --ci | jq -r '.[0].version') reads it.Options you may need
Declare an index for filtered fields
When a collection grows large, a filter on a field without an index slows down and eventually fails with an error naming the field. The third argument ofcreate() and fourth of update() also accept { searchText, index: ['product'] }; a nested pair such as [['product', 'status']] declares a compound index. At most 2 fields per index, 3 declarations per call, and 5 indexes per agent; an index is dropped 14 days after its last declaration or filtered read.
Local runs only. The deployed runtime doesn’t accept the options object yet, fails with
searchText must be a string, and never forwards index. In deployed code, pass searchText as a plain string and declare indexes from lua test.Read one entry by id
Data.getEntry('known-issues', id) returns the entry with direct field access and throws when no entry has that id; entry.save() writes back fields you assigned on it.
Share a collection between agents
Data belongs to one agent: no other agent can read it. When several agents of one organization need the same reference data, such as a product catalog one agent keeps up to date and every support agent reads, an org admin creates a shared collection and names which agents may read it and which may write it. Agents then use it with Data.shared(name). Available in lua-cli 3.50.0 and later.
A shared collection belongs to the organization, not to any agent. Agents never create one or change who may use it: only an org admin can, through the admin API. Do not store end customers’ personal data in a shared collection; it is organization reference data, and erasing an end user’s data from one agent does not reach it.
1
Create the shared collection (org admin)
An org
admin or owner posts the collection’s name and its two lists of agent ids. Find the organization id as orgId, and the agent ids, in lua agents --json --ci.Every id must be a live agent of the same organization; an empty list is allowed. It answers
201:Output
2
Change, list or delete it (org admin)
PUT replaces both lists, so send the full lists every time. An omitted installableBy keeps the current one, and [] clears it. GET lists the organization’s shared collections with their lists, sorted by name. DELETE ends access at once, then removes every entry of the collection; it cannot be undone, and it answers { "deleted": true, "purgedEntries": <n> }. Creating the same name again starts an empty collection.members:manage permission on the organization; creating, changing and deleting need members:assign-role, which admin and owner hold. A change applies to the next Data.shared call. PUT and DELETE on a name the organization does not have answer 404. Every create, change and delete is recorded in the organization’s audit log.3
Write from the writer agent
Data.shared(name) returns the same methods as Data (collections, create, get, getEntry, update, patch, increment, search and delete), with the same arguments, filters, indexes and semantic search. A shared collection is a namespace: it can hold several collections, here 'products'.src/jobs/SyncCatalogJob.ts
Data.shared(...) returns writes back to the shared collection: entry.update(), entry.save() and entry.delete() never touch the agent’s own Data.4
Read from the reader agents
A reader may call
collections, get, getEntry and search; a writer may call those too.src/skills/tools/FindProductTool.ts
Let a template’s installs join
A template whose agents need a shared collection declares it in itssharedData section (Lifecycle hooks). The template never creates the collection and never adds itself: an org admin lists it once in the collection’s installableBy, and from then on every install of that template in the organization adds its agent to the collection, and every uninstall takes it off again.
- A template not on the list never joins, whatever it declares.
maxAccess: 'read'lets installs joinreadersonly. Withwrite, an install that asks forwritejoinswriters, but only when the person installing holdsmembers:assign-role; anyone else’s install waits for an admin.- Every join and leave is recorded in the organization’s audit log, with the template and the person who installed it.
code === 'shared_data_forbidden' (status 403), so an agent can never learn about, or reach, another organization’s shared collections.
Audit. Every write records the agent, the collection and the entry id in the organization’s audit log, never the stored value.
Billing. Shared collections follow the usual Data rules, and the embedding work for searchText and search() is billed to the agent that made the call, not to the organization’s other agents.
Limits. The same limits as Data apply per call (Data limits); the index limits count per shared collection. A shared collection name is 1 to 128 of A-Z a-z 0-9 _ . -, and each list holds up to 200 agent ids.
Test sessions and lua test. A test session of a deployed agent may read a shared collection but not write to it: a write rejects with shared_data_test_session_write_refused (409), so a test never changes the data every other agent reads. Under lua test and other local runs there is no organization list to check, so every call rejects with shared_data_unavailable_offline. Test the reading and writing code against a deployed agent, and keep the logic that shapes the data in plain functions you can test locally.
If it isn’t working
search() returns nothing for text that is clearly in the entry
search() returns nothing for text that is clearly in the entry
Only
searchText is embedded, not data. Include the fields you expect people to search by, and re-index existing entries with Data.update(collection, id, {}, newSearchText).entry.title is undefined on get() results
entry.title is undefined on get() results
get() returns plain entries: read entry.data.title. search(), create(), and getEntry() return instances with direct access.A filter is rejected with a FILTER_* code
A filter is rejected with a FILTER_* code
The filter used an operator or a depth outside the grammar. The codes and limits are on the Lua Query page.
Next steps
Data reference
Every method, return shape, limit, and error.
Lua Query
The full filter grammar and its limits.
Identify users
One record per end user, and keeping accounts apart.
Add knowledge
For documents the agent should answer from, use knowledge instead.

