Skip to main content

Build sessions: safer endings, pasted keys and pushes that say what they did

Released: October 30, 2026 Available in lua-cli 3.64.0 and later.

โœจ New Features

lua build-session end deals with an open release first. Waiting for your decision, it prints the card and asks: Keep working (the default), Deny it and end the session, or Approve it (it goes live, then the session ends). The menu starts on Keep working and does not wrap around. A release still being prepared can be cancelled or kept, and one already going live is waited for. See end.
The CLI says right under your message that the key was taken out before it was saved or shown to Claude:
When Claude asks for it, secret <id> --from-pasted (or /secret <id> in the chat) saves it with nothing to type. If it was not a key, not-a-key <bsp_id> (chat: /notakey) sends it to Claude as text. See A key pasted into a message.
A release card names the agent, what changes for your customers, what stays the same, what was checked and what approving or denying does. A live change Claude asks you to approve, such as putting an older version back live, says what it changes, who it reaches and how to undo it.
defineWebApp({ viewers: { write, chat, mode } }) says what the people who sign in by phone or email to use only your app may do. write lets them call the appโ€™s POST, PUT, PATCH and DELETE routes, chat lets them use the agent pane, and mode says whether an admin invites them ('people', the default) or the appโ€™s own code adds and removes them ('app'). lua compile checks it and lua push sends it with the version. Lua applies it when App Viewers use the app.auth.accessLevel can now be 'use'. Your write routes then run with empty roles and scopes, so check in each one that the row belongs to auth.userId, and answer 404 when it does not.
With viewers: { mode: 'app' }, your appโ€™s code manages who may use it:
Adding works in a POST, PUT, PATCH or DELETE route, as the signed-in person, who needs full access to the app. Removing also works in a job or webhook, and only removes people your app added. Each person comes back with an outcome (added, alreadyMember, invalid_number, excluded, cap_reached and so on); an AppViewersError rejects the whole call when App mode is off or the caller lacks full access. 1 to 20 people a call. In lua test and lua dev both throw app_viewers_unavailable.
Lua.request.appAccess is { appId, accessLevel } on a turn sent from a web appโ€™s agent pane: 'full' for someone who can read the agent, 'use' for an App Viewer. Tools and pre/postprocessors use it to tell a pane turn, and an App Viewerโ€™s, from a Lua Workspace or WhatsApp turn. It is absent on every other turn and in lua test.
Name a file in the pages project with defineWebApp({ branding: { logo } }) (./src/logo.svg, or /logo.png from Viteโ€™s public/) and lua push ships it, so the sign-in page shows it. PNG, JPEG, WebP or a simple SVG (shapes, gradients, masks; no scripts, styles, images or text), at most 256 KB. A logo that cannot be used is a warning, never an error: the sign-in page then shows the appโ€™s initial.

๐Ÿ”ง Improvements

Its card says approving updates the saved project code now and nothing goes live, so nothing is watched.
lua push and push backup now keep your lockfile (up to 8 MB), tests, type definitions and project config folders such as .github/ and .vscode/, and never .env or credential files. lua push lists every file it left out. Set LUA_SOURCE_CAPTURE_POLICY=legacy to keep the old rule. See Backups and restore.
  • Denying a release from the terminal works with an API key too.
  • end and kill without --session no longer act on another terminalโ€™s session: with two or more sessions open and no terminal, they stop and list them.
  • start makes one session, and says plainly when your organization is at its session limit.
  • A push inside a build session says the work is staged in the session and goes live when the session is released.
  • lua features and lua models set inside a build session are staged with it: the release card lists the change, and it goes live only when you approve. The card also names changed tools nobody tested.
  • lua deploy persona says the deploy is recorded as a new agent version on an agent under versioning.

๐Ÿ› Bug Fixes

  • send after a session slept or restarted no longer prints the previous turn again.
  • A push refused because a build session holds the agent stops once, says nothing was pushed, and names whose session it is.
  • With several organizations, the error lists each with its id, and --org works together with --agent.
  • push agent inside a build session no longer prints โ€œPersona version undefined createdโ€.
lua models set could write a double comma into your agent config when it had a comment in it, and the agent then failed to compile. Every command that edits src/index.ts (lua models, lua init, lua sync, lua persona, lua apps new and lua apps delete) now checks the edited file still parses. If it would not, the file is left unchanged and the command tells you what to add by hand.

Live build sessions from the terminal

Released: October 25, 2026 Available in lua-cli 3.59.0 and later.

โœจ New Features

A live build session has Claude work on your agent in the cloud while you chat with it. These are the same sessions the Lua desktop app opens, so you can start in one and carry on in the other.
Build sessions are on for every organization from 25 October 2026. An organization that turns off its buildSessions.enabled feature has opted out; there, the commands fail with build_sessions_disabled. lua --help lists build-session only when LUA_BUILD_SESSIONS_ENABLED=true is set in your environment, and the commands work either way. See lua build-session.
In a terminal, lua build-session, start, chat and resume open a chat once the session is ready. Replies render as Markdown. A message typed during a turn is queued. Ctrl-C stops a turn; Ctrl-C twice at an empty prompt, or Ctrl-D, leaves the chat and the session keeps running./help lists the slash commands: /file, /paste, /interrupt, /try, /release, /status, /diff, /end, /approvals, /approve, /deny, /secret, /files, /save, /sessions, /switch, /new and /detach. See Slash commands.
Without --agent, a terminal lists your organizations, then that organizationโ€™s agents, with the projectโ€™s agent first and any session you already have on each. The last entry starts a fleet session on the supervisor agent you pick. See Pick an organization and agent.
list shows your sessions across every organization and agent, and use, resume, status, attach, stop, end / remove and kill work on any of them. The CLI remembers the current session; LUA_BUILD_SESSION_ID overrides it.approvals lists what is waiting on you across your open sessions, and approve, deny and secret answer it. A secret is typed hidden, never passed as an argument. Approving a release from the CLI needs a person signed in with lua auth; an API key needs the organizationโ€™s buildSessions.cliReleaseApprove setting.Drop a file on the terminal, or paste its path, to send it with your message. files lists the sessionโ€™s files, download [id] or download --all saves them, and chat --auto-save <dir> saves each file Claude shares as it arrives.
send <text> sends one message and prints its turn, and --json prints one JSON object per line:
When the organization runs out of credits, the session stops with credits_exhausted and a link to top up, and the CLI exits 13, a new exit code. See Errors and exit codes.

Files and page scripts in the browser, and saved logins everywhere

Released: October 25, 2026 Available in lua-cli 3.58.0 and later.

โœจ New Features

Turn files on for a browser with maxDownloadMb, the largest file in megabytes. Your planโ€™s limit caps it.
Each file is private to the person and stays scanning until the malware scan marks it clean. allowedMimeTypes is checked against the fileโ€™s contents, not its name. An upload accepts only the personโ€™s own files in the same organization, once they are clean. With files on, the agent can also download, save PDFs and upload a file the person just sent in chat. For an older file, it asks the person to send it again. See Files.
allowScripts: true together with allowedDomains lets your code run a script in the page. Lua logs each run without its text, and neither the agent in chat nor Browser.execute can run scripts. See Page scripts.
Use the browser covers which method to use, keeping a browser to the right sites, approvals, logins, files, scripts and cost. LuaBrowser lists every setting a named browser takes.

๐Ÿ”ง Improvements

Browser.login, job grants and lua browser run --login are available on every workspace. After a signed-in run, the next run picks up the same sign-in without signing in again. See Saved logins.

Signed-in browser runs from the terminal, and template taglines

Released: October 23, 2026 Available in lua-cli 3.57.0 and later.

โœจ New Features

Signs the browser in with one of your own saved logins for the agent before the task starts. Repeat it for up to 5 logins. The run never uses anyone elseโ€™s logins.
When the site asks for a code or an approval, Lua emails you a private link to finish it in the same browser, and the run waits for you. Saved logins are still rolling out: where they are not turned on yet, the run says so and stops. See Signed-in runs.
Pass the same state object as with any other decision model, plus at most one png or jpeg image. A second image, or another format, fails with decision_rejected before the call. See Other decision models.
The line under your templateโ€™s name on the Hire card, shown ahead of the description. Up to 90 characters.
lua.skill.yaml
lua marketplace template create --tagline sets it too. template draft brings back a tagline Lua staff edited on the listing, and template publish warns before replacing a different one. See tagline.

A per-turn note from a preprocessor, and a context reset

Released: October 19, 2026 Available in lua-cli 3.56.0 and later.

โœจ New Features

Give the model a short note for this turn only, such as state you read from your own data:
It is labelled as context the user did not write, never stored or shown to the user, and capped at 2,000 characters across preprocessors. The model treats it as data, not instructions. See Turn context.
A tool can start the agentโ€™s context afresh from the current turn, for example once a checklist section is recorded. From the next turn the agent reads this turn and what follows, not earlier messages or the observation log built from them. The person still sees the whole conversation, and nothing is deleted.
It never throws. It answers skipped when the agentโ€™s contextReset feature is off, outside a 1:1 chat turn, with observational memory off, when nothing older is left, or in a local lua test run. Turning on contextReset also resets the context once a day, on the first turn after 04:00 in the personโ€™s timezone, so the agent no longer sees earlier days. Give the agent what it still needs after a reset from your own data, for example with turnContext.

๐Ÿ”ง Improvements

lua push webapp and lua apps dev warn when an appโ€™s pages use @lua-ai-global/ui older than 0.3 and point to lua apps upgrade <name>. The push is not blocked. See lua apps upgrade.

Chat in the appโ€™s top bar, and web apps on the new Lua design

Released: October 16, 2026 Available in lua-cli 3.55.0 and later.

โœจ New Features

Lua adds the agent pane to every live web app, and the person opens it with Chat, the rightmost item in the appโ€™s top bar in Lua Workspace (in the browser at workspace.heylua.ai, and the desktop app 0.75.0 or later), after Pin. It always starts closed, every time the app opens and on every page change inside the app.Apps that bundle <AgentPaneLayout> get it too, with their own pane hidden, so no app has to be rebuilt or pushed again. The admin console shows no pane for apps for now. See Talk to the agent from your app.
@lua-ai-global/app-client 0.0.5 and later can open, close and ask the pane, and set what the agent gets with every message sent from it (at most 4 KB):
See Open or ask the pane from the page.
The pages drop @lua-ai-global/app-chat, the <AgentPaneLayout> wrapper in web/src/main.tsx and the paneโ€™s styles in web/src/index.css. <AgentPaneLayout>, <AgentPane> and useAgentPane are deprecated.
<AgentPaneLayout> gives way to what it wraps (a context prop becomes lua.pane.setContext), useAgentPane() becomes lua.pane, and @lua-ai-global/app-chat and its styles go once nothing imports them. A file it cannot rewrite is left as it is, with the steps printed.
See Move an older app off the bundled pane.
lua apps new builds the pages on @lua-ai-global/ui 0.3, the design Lua Workspace uses: flat surfaces, a 6px radius, denser controls, and Inter. lua apps upgrade <name> raises an existing app to it. 0.3 keeps every component and variant name of 0.2, so check the pages visually with lua apps dev before you push. See Look and theme.

๐Ÿ”ง Improvements

It serves no pane for pages that bundle none, because Lua adds it once the app is deployed; lua.chat still works locally. For pages that still bundle <AgentPaneLayout>, it points to lua apps upgrade <name>.
Apps built on @lua-ai-global/ui 0.2 get the new designโ€™s colours and shapes where Lua can apply them, until they are upgraded and pushed.

Tool labels in the chat, and lua apps upgrade

Released: October 15, 2026 Available in lua-cli 3.54.0 and later.

โœจ New Features

Tools can set a label โ€” one text, or { running, done } with {argName} placeholders โ€” that the chat shows while the tool runs and when itโ€™s done, instead of the toolโ€™s name.
Tools without a label work as before. See LuaTool.
Brings a web app made with an older lua apps new up to the current template, so it gets the agent pane without editing by hand. It shows the changes and asks first, then runs npm install in web/. A file it does not recognise, or one you changed, is left alone, and the command says what to do instead.
--force applies without asking and --skip-install skips the install.
Sets the variable without the value ever appearing on the command line or in your shell history. An empty value or a value typed at the terminal is refused.
auth.accessLevel is 'full' or 'view_only'. Someone a view was shared with view-only reaches only your GET routes, so GET routes must only read; other methods answer 403 VIEW_ONLY before your handler runs.
A template published by Lua can include views of its agent. Hiring it adds them to your agent once, as normal views you can rename or delete, and template updates leave them alone.

๐Ÿ”ง Improvements

For example when the agent already has 30 web apps or the name standard is used, instead of a generic error.
Pages that already include the agent pane donโ€™t get a second one when they are served.

๐Ÿ› Bug Fixes

Event deliveries carry no query or headers, so only bodySchema is checked for them. Recompile and push the webhook to pick this up.
A logged Error used to print {}, and an object that refers to itself broke the log line; they now print the errorโ€™s message and stack, and [Circular].

Chat with your agent inside your app

Released: October 13, 2026 Available in lua-cli 3.53.0 and later.

โœจ New Features

A new web appโ€™s pages have the agent pane: a chat with the appโ€™s own agent, docked beside the page. Each person has one ongoing conversation of their own per app, which also appears among their chats in Lua Workspace. lua apps new scaffolds it; an older app installs the package and wraps itself in the layout:
The page can tell the agent what is on screen (at most 4 KB, which the agent treats as unverified), start a message with useAgentPane().ask, refetch after every reply, or build its own chat with lua.chat. defineWebApp({ agentPane: false }) turns the pane off. See Talk to the agent from your app.
The local pane talks to your agent with the projectโ€™s local skills in the sandbox by default, or to the live agent with --chat-env production, on a conversation kept apart from your real one. Restart lua apps dev after changing a skill.
See lua apps dev.

Templates join shared Data collections, and the web app agent pane

Released: October 12, 2026 Available in lua-cli 3.52.0 and later.

โœจ New Features

A template lists the organizationโ€™s shared collections its installed agents need, and an org admin allows the template once in the collectionโ€™s installableBy. Every install then adds its agent to the collection, with no API key anywhere, and uninstall takes it off again.
lua.skill.yaml
Anything the organization has not allowed shows as needs-approval in the templateโ€™s setup status, and setup retry joins it once fixed. A change to sharedData asks installers to consent again. See Shared data and Let a templateโ€™s installs join.
Every web app has an agent pane, a chat with its agent inside its pages, unless defineWebApp({ agentPane: false }) turns it off. The setting travels with each pushed version, so the change takes effect once that version is deployed.

๐Ÿ› Bug Fixes

A PUT on a shared collection used to answer 400 whenever an agent already on its lists had been archived, deleted or moved to another organization. Agents already on a list no longer block the update; a deleted agentโ€™s id is dropped. Two updates racing on the same collection now answer 409 shared_data_collection_changed instead of one overwriting the other.

Preview a web app before it is live, and web app titles

Released: October 12, 2026 Available in lua-cli 3.51.0 and later.

โœจ New Features

lua apps preview <name> opens a version you pushed in Lua Workspace, on its own address, under a Preview banner, so you can click through it before anyone else sees it.
Its routes read the agentโ€™s Data, but their writes go to a copy that is deleted when the preview ends. Emails and messages are recorded instead of sent, and fetch calls fail with TEST_EGRESS_BLOCKED. Anyone who can edit the agent can open the link, for 24 hours by default. See lua apps preview.
defineWebApp({ title: 'Engineering Fleet' }) sets the name people see in Lua Workspaceโ€™s app header and app lists, up to 80 characters. name stays the appโ€™s identifier and URL. See Define the app.

๐Ÿ› Bug Fixes

A credential that may not change the agent exits 10, a missing app or preview 3, a refused credential 9 and an unavailable platform 11, like every other command; they used to exit 1. lua apps delete still exits 1 for a wrong, expired or used code, an account with no verified email, or a code email that could not be sent.

Shared Data collections, web-app approvals and supervision secrets

Released: October 11, 2026 Available in lua-cli 3.50.0 and later.

โœจ New Features

An org admin creates a shared collection and names which agents of the organization may read it and which may write it. Agents use it with Data.shared(name), which has the same methods as Data.
A call outside the lists, or for a name that does not exist or belongs to another organization, fails with shared_data_forbidden. Every write is recorded in the organizationโ€™s audit log. A test session may read but not write, and lua test refuses every call with shared_data_unavailable_offline. Setup and rules are in Share a collection between agents.
A web-app route can approve or deny a workflow approval, or answer a waiting step, as the signed-in person using the app, under the same approver rules as the inbox.
See Decide approvals from a route.
Agents.secrets(id).put(collection, key, value) writes one row into the targetโ€™s own Data from the supervisorโ€™s code, so a credential never passes through a model. It needs act and the collection in the grantโ€™s new secretWriteCollections, and answers with a fingerprint, never the value. See Agents.
Agents.versions(id).list({ status, offset }) filters by active, staged, superseded or deleted and pages past the newest 50.

๐Ÿ”ง Improvements

Where release is switched on, Agents.versions(id).delete(version) also removes a staged version that was never active, is over 24 hours old and is not the newest staged version.
A request from an appโ€™s page to the workflow decision routes is refused with 403 DECISION_ORIGIN_DENIED. Decide through a route with Workflows.viewer.

๐Ÿ› Bug Fixes

Browser.find actions are click, fill, hover, check and text; Browser.wait takes state: 'visible' only; Browser.keyboard actions are type and inserttext. The removed values never worked. Use Browser.press for single keys and key chords.

Decisions: any decision model by id

Released: October 11, 2026 Available in lua-cli 3.49.2 and later.

โœจ New Features

Set model to a vendor/model id and the decision runs on that model, with the same request and answer shape as Jev and OpenAI. The result reports provider: 'openrouter' and the exact model version that answered.
The list of models, with input types, context sizes and typical latency, is in Decisions. auto never picks one of these models, and fallback names Jev or OpenAI only.
Overrides the versionโ€™s model for one run. --provider accepts openrouter.

๐Ÿ”ง Improvements

provider: 'openrouter' without a model, or a Jev or OpenAI id on it, fails with decision_invalid and a message that names the provider to use. For a LuaDecision, both are compile errors, so lua compile and lua push stop before anything is written. An unknown id, or a model Lua does not serve because its vendor keeps request data, fails with decision_rejected and the vendorโ€™s message.
A URL without http:// or https://, --max-steps over 60, --max-cost outside 0.01 to 10 USD, and -e staging fail with the allowed range before any request is sent.
lua browser progress lines are written only when the terminal is interactive, and lua browser read prints a Matches: line with the number of selector matches. lua logs --type browser labels the caller โ€œCalled byโ€.

๐Ÿ› Bug Fixes

It is no longer reported as a rejected key.
The compile error names the setting and the range it accepts.

Decisions: one answer across the platform and safer versions

Released: October 10, 2026 Available in lua-cli 3.49.1 and later.

๐Ÿ”ง Improvements

Identical cacheable calls made at the same time now share one provider call across the whole platform. The calls that joined it come back with cached: true. A call that waits longer than its own timeoutMs fails with decision_timeout.
When provider is not set, a model of one provider picks that provider. A model of no provider fails with decision_invalid.
The result of Decisions.run carries decisionName and decisionVersion.
The questions of a decision are limited to 100,000 characters as JSON. A larger call fails with decision_invalid, and writing a larger definition answers 400.
Each decision shows the version live on the server next to the one the active agent version pins. When they differ, or the decision was deleted, the row names it, for example live 1.5.0, agent version pins 2.0.7.
lua decisions versions <name> lists the versions of a deleted decision. lua decisions publish <name> <version> turns it back on.
A decision in decisions: [...] must be written as new LuaDecision({...}) or defineDecision({...}) with literal questions. Namespace imports work, as in decisions: [shared.triage]. A decision built by a helper function fails the compile with a message that names the export. Two decisions with the same name fail it too, and the message names both files.
Promoting the agent version that is already active changes nothing. To undo lua decisions publish, publish another version or promote a different agent version.
A failed callโ€™s log row records the model and the questions that were requested. A row leaves out runId when the call has none.
A decisionโ€™s log row records the channel of the turn, the execution id, the primitive and, for a tool, the tool name. A workflow code stepโ€™s row names the workflow and the step. Filter by execution or by run to find the decisions it made.
DecisionError.message starts with the code, as in decision_timeout: openai did not answer in time. The text a model or a log shows carries the code.
A workflow can declare a LuaDecision in the same module. Decisions.odds, Decisions.choice and Decisions.score work at module top level. Decisions.ask and Decisions.run do not, and calling them there fails the compile.
lua test --json reports the decision code and decisionId, with the same exit codes as lua decisions run.
Decision calls made by lua test are logged with channel lua-test and the primitive under test, so lua logs tells them apart from real traffic. They are still billed.

๐Ÿ› Bug Fixes

lua version create pins, for each decision, the newest version pushed from code or the current version, whichever is newer. A draft saved in the dashboard without publishing is never pinned.
After lua decisions publish <name> <older version>, the next lua version create pins that version, unless you pushed a newer version from code after the publish. Then the newer push is pinned.
Promoting an agent version takes offline every decision that version does not pin. Decisions.run throws decision_not_found for it, and it shows no current version until you promote an agent version that pins it or publish one of its versions again. A decision that was pushed but never made current is not affected.
Writing a version to a deleted decision answers 409 with code decision_inactive. Publishing one of its versions turns it back on.
When the decision in lua.skill.yaml was deleted or renamed on the server, lua push says so in one line. It then pushes to the active decision with the name in your code, creates one when there is none, and updates lua.skill.yaml.
This is the same as every other versioned primitive.
When Decisions.run or Decisions.ask fails in a webhook, the webhook answers with the decisionโ€™s status and code instead of a 500: 400 decision_invalid, 404 decision_not_found, 422 decision_rejected, or 503 for a timeout or outage. The body is { code, message, decisionId }.
When a workflow step is killed or a connection closes, the provider call is cancelled. The log row says decision_cancelled, and nothing is charged.
A pre-processor that throws reports proceed with a warning, the same as live. Live, a pre-processor that throws lets the message through. To block on a failure, wrap Decisions.ask in try/catch and return block yourself.
A charged call is never missing from the usage report.
A published version answers the very next call, wherever that call lands.
When a workflow was declared in the same file as tools, webhooks, jobs or pre-processors, those primitives failed to load on the server. They now load. Workflows in their own file were never affected.

Browser: read pages and run tasks from any agent code

Released: October 9, 2026 Available in lua-cli 3.49.0 and later.

โœจ New Features

Open a page, wait for it, and get its content back, from any agent code.
A sign-in wall comes back as blocked: true with guidance, and failures throw a BrowserError with a typed code. See Browser.
The result also carries the steps taken, the cost and the final URL.
Browser.* is no longer limited to tools. Server-triggered code runs on the Lua cloud browser, so a job can check a JavaScript page every hour.
Declare a browser with its own allowed domains, limits and settings, attach it with browsers: [...], and pick it with { browser: 'name' } on any Browser call. Push it with lua push browser; list and delete it with lua browsers.
Try the agentโ€™s browser from the terminal under its pushed settings.
run prints each step as it happens, and Ctrl-C cancels it on the server. See lua browser.
Every browser event in one place: sessions, commands, page loads, blocked domains, walls, fallbacks, confirmations and task steps.
Make one web app version live on its own, without taking anything else you staged live. Pick the version with --set-version.

๐Ÿ”ง Improvements

maxSessionMinutes (default 10, at most 60) and the new idleTimeoutSeconds (default 120, at most 900) apply to Lua cloud browser sessions. A LuaBrowser value over a limit fails lua compile.
With the default fallback: 'browser-use', a page the Lua cloud browser canโ€™t get past (captcha, bot block, empty page) is retried once on a Browser-Use browser. fallback: 'none' keeps every page on Lua infrastructure.
The Browser global is fully typed, including Browser.execute, and under lua test every Browser method fails with a clear BROWSER_UNAVAILABLE error. Workflows keep concurrencyKey through lua workflows export and lua push. DecisionError.decisionId is on the public type.

Decisions: typed answers with probabilities

Released: October 7, 2026 Available in lua-cli 3.48.0 and later.

โœจ New Features

Send some state and typed questions, and get back typed answers with probabilities. Decisions.odds returns the probability that a statement holds, Decisions.choice returns a pick typed to your options, and Decisions.score returns a level along your scale, each with its confidence and the full probability map. No prompt to write and nothing to parse.
Jev answers by default. OpenAI Decisions answers when the call carries images, asks for EU processing or zero retention, or when Jev is down. Choice and score answers below minConfidence (default 0.7) carry uncertain: true. Works in tools, jobs, webhooks, triggers, processors, workflow code steps and lua test. See Decisions.
Declare the questions once, add the decision to the agent with decisions: [triage], and call Decisions.run('triage', state) anywhere in the agentโ€™s code. lua push stages a new version, and lua version promote makes it current. The dashboardโ€™s Decisions section edits and publishes versions without a deploy, and an earlier version can be made current again. See LuaDecision.
See the decisions on the agent, try one on a message, and make an earlier version current, all from the CLI. publish makes a version current at once, and delete turns a decision off and keeps its versions. --json gives machine-readable output.
A call costs 0.005 actions when Jev answers and 0.01 actions when OpenAI answers. Cached hits and failed calls cost nothing. See Decisions.
Every decision writes one log row with the provider that answered, the routing rule, every answer with its probabilities, the duration and the cost.

๐Ÿ”ง Improvements

When the first provider fails and a fallback runs, the fallback gets the time that is left. With under 100 ms left, there is no fallback. A short timeout on your call does not affect other calls.
A call takes up to 4 images, each at most 5 MB and at most 6 MB in total. Over the total, the call fails with decision_invalid and the message names the total. Question names and option labels are at most 64 characters, and any name works, including constructor or toString.
Jev takes about 32,000 tokens of input. On provider: 'auto', a larger state falls back to OpenAI and is charged at OpenAIโ€™s price. Pinned to Jev, it fails with decision_rejected and Jevโ€™s own message.
An empty state ("", a blank string, [] or {}) fails with decision_invalid, and the call is never sent or billed. Decisions.odds, Decisions.choice and Decisions.score throw decision_invalid for empty instructions, instructions over 4,000 characters, fewer than 2 or more than 64 options, fewer than 2 or more than 10 levels, or criteria that is not an object.
A cached hit reports usage.inputTokens: 0. Identical cacheable calls made at the same time share one provider call, and the calls that joined it come back with cached: true.
DecisionError.decisionId is the id of the callโ€™s log row, so you can find a failed call.
A model the provider does not know, such as an unknown OpenAI model, fails with decision_rejected (422), not as an outage. Under lua test and through the HTTP API, decision_timeout, decision_unavailable and decision_disabled (all 503) arrive with their own code, message and provider, the same as in deployed code.
Creating or renaming a decision to a name already in use answers 409, and so does writing a version number that already exists. Publishing a version of a deleted decision turns it back on.

My work lists, and a cap on message batching

Released: October 6, 2026 Available in lua-cli 3.47.0 and later.

โœจ New Features

Publish the open work the agent lists for a person, such as tickets they raised or hold and approvals waiting on them. A client that supports it shows the list beside the conversation. Each call replaces the whole list.
Up to 100 items, checked before anything is written. The list is kept apart from user.data. Keep action a request to show or open, never a decision. See setWorkItems().
A hard cap on how long a batch of messages is held, counted from the message that started it. The batch goes out after the quiet period or the cap, whichever comes first, so a person who keeps typing still gets a reply. See LuaAgent.

Custom MCP servers from the Tools page

Released: October 6, 2026 Adding and managing these servers needs no CLI update; lua mcp list and lua integrations show them from lua-cli 3.46.0.

โœจ New Features

The Tools page in the Lua app has a Custom MCP server card, and an agentโ€™s MCP Servers page in the admin dashboard has Add server. Enter the serverโ€™s https:// address and Lua detects how it signs in: no sign-in, headers such as an API key, or OAuth, with your own OAuth app when the provider needs one. Lua tests the connection and lists the serverโ€™s tools, so you can allow them all or choose them one by one. Nothing is saved until the test passes; a failed test shows the step it reached, a plain message, and a Ref to quote to support.On a private agent the server is yours, is added to all your private agents, and serves only you. On a shared agent it belongs to the agent, after you confirm, and serves everyone who uses it. The address and header values are encrypted and write-only, and private, internal, and cloud-metadata addresses are refused. See Add a custom MCP server.

lua mcp list shows who manages each server

Released: October 6, 2026 Available in lua-cli 3.46.0 and later.

โœจ New Features

lua mcp list groups the agentโ€™s servers by who manages them and labels each one: From your project (lua-cli), From integrations (Tools page), and Custom servers (Tools page). A custom server shows its host only, never its address, and server headers are listed by name only, never their values.
lua mcp activate, deactivate, and delete change only servers from your project; on any other server they stop before changing anything and say where to manage it. lua integrations list labels custom servers with their host, state, and how many of their tools are on, and lua integrations update wonโ€™t change them.
Agents.jobs(agentId).register({ versionId, jobId }) registers a job from a staged version on an agent you supervise, with the matching supervision grant. It is always refused under lua test and lua dev.

๐Ÿ”ง Improvements

An in-flight call to another agent is cancelled with the run.
Runs skipped because the organization is archived read SKIPPED (organization archived).

๐Ÿ› Bug Fixes

Renaming a skill no longer sends local tests to the sandbox entry of its old name.

Build web apps for your agent with Lua Apps

Released: October 5, 2026 Available in lua-cli 3.42.0 and later.

โœจ New Features

A web app gives the people who work with an agent a page of their own: a ticket board, a review queue, a dashboard. It has pages built with Vite, React, and @lua-ai-global/ui, and typed routes that run on Lua as the signed-in person, with Data, env(), and fetch.
List the app under webApps on your LuaAgent. The page calls the route with lua.api('POST', '/tickets', { title }) from @lua-ai-global/app-client. See About web apps and Write routes and pages.
lua apps new <name> scaffolds src/apps/<name>/ with app.ts and a web/ page project, lists the app on your agent, and keeps the page project out of the agentโ€™s TypeScript build. It never overwrites an existing app. lua apps dev <name> runs it locally: the page on the appโ€™s own Vite with hot reload, the routes in the local sandbox as you, against live Data and env().
See lua apps.
lua test webapp calls one route the way the platform would, with you as auth. lua push webapp builds the page with the appโ€™s Vite and stages a web-app version; it goes live with the agent version that pins it, and promoting an earlier agent version rolls it back. Bare lua push and lua push all include web apps.
See lua test and lua push.
A live app opens full page in Lua Workspace in the browser at https://workspace.heylua.ai/apps/<agentId>/<appName>, and the admin console lists an agentโ€™s apps at https://admin.heylua.ai/admin/agents/<agentId>/apps. There is no menu entry yet. Anyone who can read the agent can open its apps. Each app runs on its own origin, and the shell hands it a session: the page never sees a password or the shellโ€™s token. Admins can switch an app off and on from the admin console.See Build and open your first web app.
The page side of an app is now a package: lua.api<T>(method, path, body?), lua.onInit() for the shellโ€™s theme, lua.theme, lua.appName, and LuaApiError. Apps scaffolded with lua-cli 3.42 to 3.45 have a copy in web/src/lua/client.ts. To switch, run npm install @lua-ai-global/app-client in web/, import from the package, and delete the file. See Switch to the client package.

Merge two users who are the same person

Released: October 5, 2026 Platform change shipped on October 5, 2026. lua webhooks subscribe accepts user.merged and user.unmerged in lua-cli 3.46.0 and later; 3.45.0 and older refuse them.

โœจ New Features

Lua staff can merge two Lua users who are the same person into one. The kept user gets the absorbed userโ€™s login methods, channel identities, organization memberships and access (the higher role wins), API credentials and sessions, and conversation history with every agent. When both users talked to the same agent, the two conversations become one, in time order, and the agent remembers what either of them said. You canโ€™t start a merge from the SDK or the CLI.The absorbed userโ€™s id keeps working: User.get(absorbedUserId) returns the kept user, so ids in your records still resolve. User ids inside your own data, such as Data entries or your CRM, are not rewritten. Staff can undo a merge; undo moves back exactly what the merge moved, and anything written after the merge stays with the kept user.See When two users merge.
A webhook can subscribe to two new platform events:
Lua sends user.merged once to each agent the absorbed user had talked to, with mergeId, absorbedUserId, keptUserId, knewKept (true when this agent also knew the kept user), and at in event.body. user.unmerged carries the same fields when staff undo the merge. event.execution.eventId is the merge id, or <mergeId>-undo for user.unmerged, so key your handler on it.See Webhooks.

Device credentials from the CLI, and lua devices for every device

Released: October 5, 2026 Available in lua-cli 3.45.0 and later.

โœจ New Features

Devices refuse the scoped personal API keys the dashboard creates, so a board or script needs a device credential. Issue one from an email sign-in, bound to the projectโ€™s agent and one device name:
--operations defaults to commands,triggers; assets.upload allows CDN uploads. --out writes LUA_AGENT_ID, LUA_DEVICE_NAME, and LUA_DEVICE_CREDENTIAL to a mode-600 file and refuses to overwrite it without --force; without --out the secret is printed once. An API-key sign-in is refused: run lua auth configure and choose Email. Aliases: credentials, key.
lua devices test --command <name> sends one command without a prompt, so it runs under --ci, and prints the deviceโ€™s own error, for example returned an error: Unknown command: nope. lua devices test-trigger --trigger <name> now fires the trigger through the live pipeline, so the handler really runs; read it with lua logs --type device-trigger.

๐Ÿ› Bug Fixes

list showed only defineDevice declarations, so a self-describing device printed โ€œNo devices registeredโ€ while online. It now lists declared devices and devices that have connected, with online, offline, or disabled and a declared tag.
status says when a name has never connected to the agent, and how to re-enable a disabled device. enable notes that the device is offline until its client reconnects. remove refuses an unknown name (not_found, exit 3) instead of reporting it removed.

Generate images from code with AI.image

Released: October 4, 2026 Available in lua-cli 3.44.0 and later.

โœจ New Features

AI.image is the image sibling of AI.generate: describe an image and get back its hosted URL, ready to embed in a deck, a message, or anywhere a URL works. Like AI.generate, it runs outside the agentโ€™s chat pipeline and is available in tools, jobs, webhooks, triggers, processors, and workflow code steps. The image is produced server-side, stored on the CDN, and billed to the agentโ€™s organization (refused before any image is produced if the account is out of balance).
Options: model (a provider-prefixed image model like openai/gpt-image-2; defaults to the platform image model), size, quality (low | medium | high), and outputFormat (png | jpeg | webp). See AI reference.

Pin a persona version on an agent version

Released: October 2, 2026 Available in lua-cli 3.43.0 and later.

โœจ New Features

Pins a persona version, staged or published, on the new agent version instead of the newest published persona, so you can try a persona you just pushed before it goes live.
A draft, a missing version or another agentโ€™s version is refused, and the command fails rather than report success when the server did not pin it. See lua version.

๐Ÿ› Bug Fixes

They no longer stop on a prompt in a non-interactive run.
lua push all no longer prints a lua logs hint under a component that failed to push, because a failed push writes no logs.
A publish that moves a parent workflowโ€™s concurrency policy from forbid to allow warns with concurrency-policy-relaxed.

Slack: post from code, jobs, workflows and the model

Released: September 29, 2026 Platform changes shipped on September 29, 2026. The 'slack' channel type arrives in the next lua-cli release; lua-cli 3.41.0 and older donโ€™t include it in ChannelSendChannel.

โœจ New Features

Channels.send accepts channel: 'slack', its eighth channel. Every post goes through the agentโ€™s own connected Slack bot, never a Lua bot:
to.conversationId takes Lua.request.conversation.externalId from a Slack channel-thread or group-DM turn, a channel id (Cโ€ฆ, a new top-level message), Cโ€ฆ:threadTs (a reply in that thread), a member id (Uโ€ฆ, their DM with the bot) or a DM channel (Dโ€ฆ). to.userId finds the Lua user in the botโ€™s workspace by their email address. The bot must be in the channel (/invite @your-bot). An agent with more than one Slack connection passes options.channelIdentifier unless the conversation key names the bot.A post into a thread where the bot was already mentioned is recorded there, so the next mention knows what the agent posted, and a DM is recorded in the conversation of its own thread. Posts made by calling Slackโ€™s API directly are not recorded. A Slack rate limit answers 503 VENDOR_UNAVAILABLE with retryAfterSeconds instead of waiting. user.send() still never picks Slack.See Post from code.
A run started with replyTo: { channel: 'slack', threadId } now posts its result through the agentโ€™s own Slack bot; it used to be unusable. threadId is any Slack conversationId, usually Cโ€ฆ:threadTs. A redelivery never posts twice, and attachments go out as links.See Workflows REST.
With the outboundChannels feature on, an agent with a connected Slack bot gets sendSlackMessage. With recipientScope: 'current_user' it only DMs the person the agent is talking to; an agent with several Slack bots names which one sends. With anyone it posts to a channel, a thread or a person, and listSlackChannels lists the channels the bot is in. The normal reply already goes to the current Slack thread or DM, so the tool is for a different place or person.See Features.

Agent supervision, invokedBy, schedule health and Job-tier plugins

Released: September 28, 2026 Platform changes shipped on September 26 to 28, 2026. Declaring health and plugins in a lua-cli project needs the next CLI release; an older CLI drops both without an error.

โœจ New Features

An org admin grants one agent supervision over another agent of the same organization:
The supervisor then reads the target from code with Agents.list, Agents.logs, Agents.runs, Agents.data, Agents.versions, Agents.persona(id).versions, Agents.source and Agents.env(id).keys. Logs come without message bodies unless the grant holds observe-content, runs as metadata only, Data only from the collections the grant names, and environment variables as names only. Every call the grant allows or refuses is recorded in the targetโ€™s own logs (log source supervision). Supervised preview and release are not generally available.See Supervise another agent.
A turn created by Agents.invoke carries Lua.request.invokedBy = { agentId, supervisor, scopes, delegatedUserId? }, verified by the platform. supervisor is true only when an org admin granted the caller act supervision on this agent, so a tool can accept writes from a supervisor and refuse everyone else. A delegated user never grants owner rights.See Lua and Accept changes from a supervisor.
A scheduled workflow or LuaJob can declare how it should behave:
The owner gets one notice per incident when the schedule goes silent, keeps failing or is turned off by the platform, and the same notice closes when the schedule recovers or is paused. lua workflows schedules list has a Health column, lua jobs view a Health line, and every transition is a log line a drain can route.See Watch schedule health.
plugins: ['lua-agent-builder'] on a tier: 'job' agent step lets the coding turn restore its agentโ€™s source, compile, push staged versions, back up and create an agent version. It can never promote, deploy, activate, write webhooks or triggers, or read environment variables, and it works only in a run a person started. A person reviews and promotes the result.See Stage agent changes from a coding step.
POST /developer/agents/:agentId/versions/:version/promote accepts { "expectActive": <n | null> } and refuses a stale one with 409 PROMOTE_CONFLICT, writing nothing; POST /developer/agents/:agentId/persona/version/:version does the same with 409 PERSONA_ACTIVATE_CONFLICT. Each promoted version records promotedBy.See Promote safely.

๐Ÿ”ง Improvements

When one agent invokes a different agent, only the message, threadId, runtimeContext, webhookPayload and the reasoning effort reach the target; systemPrompt, model, clientContext and every other field are dropped, and the target runs with its own persona, model and tools. A self-invoke is unchanged.See What reaches another agent.
An agent in another organization is refused with cross_org_invoke_denied, on every path that can invoke an agent, and a userId who is not a member of the callerโ€™s organization is refused with delegated_user_not_in_org.

๐Ÿ› Bug Fixes

The deployed runtime rejected Data.create(collection, data, { searchText, index }) and the same object on Data.update with searchText must be a string, and had no Data.collections(). Both forms and collections() now work in deployed agents as they did in lua test.See Data.

CLI fixes

Released: September 28, 2026 Available in lua-cli 3.40.0 and later.

๐Ÿ› Bug Fixes

It no longer ignores the action and opens the interactive menu. lua persona sandbox versions lists persona versions without a prompt, and any other unsupported sandbox or production action exits 2 with a usage error instead of waiting on a prompt under --ci.
lua webhooks view, lua webhooks deploy and lua deploy webhook print https://webhook.heylua.ai/<agentId>/<name>.
A version in lua.skill.yaml that you raised by hand, or one staged by an earlier push and not yet deployed, is no longer reset to the active version.
It prints that the served version changed now and that the next lua version promote resets it, instead of claiming an agent version was created.
No second run is needed.
--account-label after OAuth is refused up front, and a missing --scope defaults to agent. lua integrations disconnect --scope user takes --forget, so it runs under --ci too.
lua skills versions --ci --json prints skill-version ids, lua marketplace skill publish --ci takes --version <semver>, lua preprocessors view --ci and lua postprocessors view --ci print the priority, and lua skills view, deploy and delete refuse --json instead of ignoring it.

v3.39.4

Released: September 25, 2026

๐Ÿ› Bug Fixes

lua push agent and lua push all no longer change the persona your agent serves. The pushed version is created as staged, the push prints Persona v<n> staged โ€” NOT activated, and the persona goes live only when you deploy it:
--auto-deploy on lua push agent runs that deploy for you; lua persona production versions marks the pushed version [STAGED] until then, and lua sync --check keeps reporting persona drift until the staged version is activated, because drift is measured against the served version. lua version promote still does not change the served persona. Together with 3.39.1 (workflows and agent versions) a plain lua push now changes nothing an agent serves apart from the model and model settings in the agent configuration. Older CLIs against the current platform stage as well; this release makes the CLI say so.

v3.39.3

Released: September 24, 2026

โœจ New Features

An export can now carry user_message and agent_response bodies. Like lua drains create --include-content, it asks you to acknowledge that end-user content will leave the platform; pass --yes to acknowledge non-interactively.
Without the flag an export never contains message bodies.

๐Ÿ› Bug Fixes

A 403 on --include-content was always shown as a missing logs:read scope. It now says that exporting content needs logs:manage.
The reserved clamp marker cannot be used as the base of a list-count field, matching what log already refused in your code.

v3.39.2

Released: September 24, 2026

๐Ÿ› Bug Fixes

lua drains create --sources user_message,โ€ฆ without --include-content created the drain with content included, so end-user message bodies were delivered although you had not asked for them, and lua drains update did the same when a content source was added. Content is now on only when you pass --include-content and acknowledge it; records from a selected content source without it arrive with lua.content.withheld: true.If you created a drain with those sources on 3.38.0โ€“3.39.1 without --include-content, run lua drains get <id> and check includeContent; turn it off with lua drains update <id> --no-include-content if you did not intend it.

v3.39.1

Released: September 24, 2026

๐Ÿ› Bug Fixes

A push published each workflow through a route that had become a scoped promote, so a plain lua push could leave your agent on a new active version without a promote. A push now stages only. Workflows that need activating are listed at the end of the push with the command that activates them, and nothing changes what your agent serves until you run it.
Starting a chat compiled the project with a server sync that re-pinned every primitive to the serverโ€™s active version, so a read-shaped command changed your manifest โ€” a webhook pinned at 1.0.1 came back pinned at whatever was deployed. Chat now links ids only and leaves the versions you wrote alone.

v3.39.0

Released: September 24, 2026

โœจ New Features

log.debug, log.info, log.warn and log.error write a log entry with fields beside the message rather than serialized into it.
The entry is an ordinary entry of the primitive that wrote it โ€” same source, same eventName, and the level sets the severity exactly as the matching console method does. lua logs prints the fields as a compact key=value line under the message, and lua logs --json carries them under metadata.fields.Up to 32 keys per call, counting the .count companion a string array adds; keys match ^[a-z][a-z0-9_]{0,63}$, and a key that looks like a credential, or the reserved lua_fields_clamped, is refused; values are a string, number, boolean or string array; 1 KiB per value and 8 KiB per call. A structural mistake โ€” a bad key, a nested object, an unsupported type โ€” throws LuaLogFieldsError at the call, so it fails the first time the line runs under lua test. An over-long value is clamped rather than breaking a working tool, and the call carries lua_fields_clamped: true to say so.Available in tools, conditions, skills, webhooks, triggers, jobs, processors, MCP servers and device handlers โ€” including a tool called as a step of a workflow, which runs the same bundle it runs standalone. Workflow script code keeps ctx.log, unchanged. console.log, console.info, console.warn and console.error are unchanged too โ€” an object passed to one is still serialized into the message text, and console.log is still a debug entry.See log.
A log drain carries each field as an app.<key> attribute on the record, beside the platformโ€™s own, with lua.log.structured: true to select the lines your code described rather than the ones it merely printed.
app. is yours โ€” the platform writes one reserved key of its own there, app.lua_fields_clamped, when a value was cut to fit โ€” so there is nothing to parse at the destination, and no new source or eventName to select: a drain already carrying skill receives them. A string array arrives joined with , plus a companion app.<key>.count, because a recordโ€™s attribute values are strings, numbers and booleans in the published logs/1.0 schema. A record too large for its destination sheds app.* values largest first and carries lua.app_fields.dropped with the number shed.Per destination: Better Stack lifts your fields to the top level alongside the resource ids, where it indexes them; New Relic keeps every platform attribute ahead of yours under its 255-attribute ceiling, so an overflow loses your fields rather than one you were already querying; Loki keeps them in the JSON line and never as a stream label.A drainโ€™s redact list drops them by their prefixed key ("app.query"), sharing the same 32-key budget, and the scrubber masks credential shapes in them as it does in any other string โ€” numbers and booleans are not scanned, so keep anything sensitive in a string. Fields are drain bytes and are metered as such; there is no new meter, metric or quota.Rolling out per deployment: until it is enabled, a log.* record is delivered exactly as the same line written with console.* would be. Ask [email protected] to have it enabled.See Emit your own fields.
Start an organization-wide export, wait for it, and download each gzip NDJSON part into a directory you name.
The window is at most 31 days and a job at most 10,000,000 records; narrow the window or the sources rather than raising either. One export runs per organization at a time, and a second invocation follows the running one rather than refusing. --no-wait starts it and prints the job id to poll later.Download links expire 24 hours after the export finishes and the files are removed after 7 days. Message bodies are included only for an export started by a key holding logs:manage. This replaces the 10,000-row CSV download, which is deprecated for one release.See lua logs export.
lua drains create --type s3 writes to an S3-compatible bucket on AWS, Cloudflare R2 or Google Cloud Storage, and splunk, loki, axiom, newrelic and sumologic join the presets.
A bucketโ€™s secret access key is never a flag, for the same reason a header value never is: name two environment variables with --credentials-from-env KEY_ID_VAR=SECRET_VAR, or assume a role with --role-arn and --external-id. A bucket drain proves you own the bucket by writing a _lua-verify-<token> object under its prefix โ€” lua drains verify finishes on its own when the role can read it back, and lua drains confirm settles the challenge when the grant is write-only, which is the grant we recommend.Each destination type is opened separately per deployment; one that is closed answers 422 DRAIN_TYPE_UNAVAILABLE with the live list. See lua drains.
A terminal refusal โ€” a 4xx that is not 408 or 429 โ€” drops a whole batch. replay puts its records back in the queue, for as long as they are inside the six-hour delivery horizon.
The read verbs โ€” lua drains status and lua drains deliveries โ€” now take a drainโ€™s exact name as well as its id, and print the mapping they resolved so the id is in front of you for the next command. The verbs that change something stay id-only. A name that matches nothing is refused locally, before anything is asked of the platform.See Replaying a lost batch and Addressing a drain.
delivery, trigger-execution, job-execution, workflow-run, voice-call and workflow-audit are selectable, bringing --sources to twenty-five. The first four are on for a new drain; an existing drainโ€™s source list is never re-derived, so nothing you have already configured changes.See Sources.
Narrow a query to one messaging channel, so one integrationโ€™s turns can be read on their own.
See lua logs.

๐Ÿ› Bug Fixes

class X extends LuaTrigger failed lua compile with Trigger `tool.name` must be a static string literal even when it declared no tool at all: the optional slots the base class declares were being read as the subclassโ€™s own. Every subclassed trigger was affected; defineTrigger({ โ€ฆ }) and new LuaTrigger({ โ€ฆ }) never were.
The verify, filter and transform of a class-based trigger are now read from the class and enforced on the platform like any other triggerโ€™s. See LuaTrigger.
A chat turn reaches the platform over a development channel, and a development channel is recorded against the sandbox environment โ€” so lua chat -e production, against the promoted agent, wrote log rows labelled sandbox. A log drain scoped to production, or a lua logs --environment production filter, saw nothing from that testing and looked broken.
A production session now declares the environment on the chat request, so the turnโ€™s rows are labelled production and a production-scoped drain receives them. It may not be enabled on your deployment yet; where it is not, -e production is recorded as sandbox, exactly as before.A -e sandbox session declares nothing, and neither does an --agent-version preview โ€” a snapshot that was never promoted is sandbox by every definition the platform uses. The declaration only ever raises a row, so nothing a caller sends can move a real production turn out of a drain that selects it. runtime rows are written from outside the turnโ€™s context and stay sandbox, so a drain selecting production alone sees a real part of the turn rather than all of it.See lua chat and Ship logs to your stack.
The same read counted the base classโ€™s own execute as a handler the subclass had declared, so a webhook or job with no handler passed lua compile and only failed when the platform loaded it. It now fails the build:
A handler inherited from a base class you wrote still counts as declared โ€” only what the class inherits from LuaWebhook or LuaJob itself does not. In the same correction, a subclassed webhook that sets no secret is unsigned rather than being refused for a secret that โ€œmust be a string literalโ€. See LuaWebhook and LuaJob.
lua test skill, lua test webhook, lua test job and the processor tests printed execution successful! and exited 0 when the primitive threw, so a CI step that ran one passed on a handler that was broken.
The throw now reaches the command: it prints the failure, exits non-zero, and under --json prints the { success: false, error: โ€ฆ } envelope on stdout instead of nothing. A primitive that deliberately returns an error value without throwing is unaffected โ€” it still reports success.
Both listed what had failed and still exited 0, so a scripted push or deploy passed on a partial result. Both now exit non-zero when any component fails or is skipped.lua deploy all also counts a component with no versions on the server as a failure โ€” naming the id, and saying to push it first or to remove a stale id from lua.skill.yaml โ€” instead of stepping over it silently and reporting a run that deployed nothing.
A workflow whose module ran a Math.ceil, Math.max or Math.floor while it was being loaded failed lua push:
Math is now complete in the compilerโ€™s sandbox. Math.random remains unavailable there, because a workflowโ€™s shape has to read the same every time it is compiled; Math inside a stepโ€™s own code was never affected.

v3.38.0

Released: September 22, 2026

โœจ New Features

Copy your agentsโ€™ execution logs to a destination you own, as they happen. A drain is configured on the organization and selects sources, severities, agents and environments; phase one ships four destinations: a generic HTTPS endpoint of your own, OpenTelemetry OTLP/HTTP, Datadog, and Better Stack.
Delivery is at-least-once with records[].id as the deduplication key, batched at 500 records or 1 MiB, retried with full jitter to a 60-second cap, dropped after a six-hour horizon, and auto-paused after 24 hours without a success. A healthy drain heartbeats every five minutes, so silence is never ambiguous, and it reports its own drops in band with a lua.drain.dropped record that bounds the gap.Deliveries to your own HTTPS endpoint are signed with HMAC-SHA256 over the uncompressed body, with a five-minute skew window and overlapping secrets during a rotation.See Ship logs to your stack, Delivery guarantees and Verify signatures.
Everything a drain sends is defined as JSON Schema (draft 2020-12) at https://docs.heylua.ai/schemas/logs/1.0, following the OpenTelemetry log data model and the GenAI semantic conventions โ€” so service.name, deployment.environment.name, gen_ai.tool.name and severityNumber mean what your existing tooling already expects.Changes within 1.0 are additive only: new attributes and new eventName values may appear at any time. A rename, a retype or a removal bumps the URL instead.See Event schema.
list, status, deliveries, create, update, delete, test, verify, pause, resume and rotate-secret, each with --json.
What a drain carries is narrowed by --sources, --min-severity, --agents, --environments, --format json|ndjson and --sampling debug=<n>.Header values are prompted, or read from the environment with --header-from-env; they are never passed as flags, where a shell history or a CI log would keep them. The HMAC signing secret is not an input at all โ€” the platform mints it and prints it once, at create and again at rotate-secret.test and verify wait for the delivery itself rather than reporting success off the acknowledgement: the platform does the sending, so each polls for the outcome and, when nothing lands in time, exits 1 naming the command that will show it once it does. lua drains status exits 2 when any drain is failing, after printing, so it works as a CI guard. --org <id> picks the organization on every verb; without it the projectโ€™s organization is used, or the credentialโ€™s when it reaches exactly one.See lua drains and Log drains REST.
The user_message and agent_response sources carry what end users said and what the agent replied, so a drain sends the bodies only when you ask for them.
--include-content prints an acknowledgement of what turning it on means โ€” the same words the dashboard shows โ€” and then requires a confirmation; --yes gives that confirmation non-interactively, and without either the command prints the acknowledgement and changes nothing.--no-include-content turns the bodies off again and leaves --sources exactly as they are, so the records keep arriving, marked as withheld, and you still see that a turn happened, on what channel and for which end user, without the words. Turning content off and naming a content source in the same update is refused: send the sources first, then --no-include-content.See lua drains.
Bound a log query by time, pick one environment, or keep the command open and watch entries arrive.
--since and --until take an ISO 8601 instant or a relative 15m, 2h, 7d, and are inclusive at both ends; a relative window is resolved by the platformโ€™s clock, so a skewed machine cannot shift the window it asked for. --follow polls about every two seconds, ends at --until, is refused together with --page, and under --json prints one complete { logs, nextCursor, pagination } envelope per poll, so a reader can consume it a document at a time.--type now accepts all eighteen log sources, adding trigger, model-resolver, workflow, workflow-step and workflow-script. --type calls reads voice call records and ignores all four of the new flags.See lua logs and Read logs and debug an agent.
Responses carry a nextCursor โ€” null once the window is exhausted โ€” so a script can page a moving log without skipping or repeating an entry, which page numbers cannot do while new entries keep arriving at the top.Above 10,000 matches the total is capped: pagination.totalCount reports 10000 with totalCountExact: false, and the page footer says so rather than quoting a page count derived from the cap.
--page still works and is deprecated in favour of the cursor.See lua logs.
Ready-made alerting, so โ€œlogs arrivedโ€ becomes โ€œsomeone was pagedโ€: four importable Datadog monitors (heartbeat absence, an error-rate spike per agent, dropped records, truncated executions), a Grafana dashboard for a Loki fed by OTLP, and a collector configuration with bearer auth and a worked exporter.See Datadog monitor pack, Grafana dashboard and OpenTelemetry Collector config.
Lua.request.conversation.access says whether the agent can read every message in this conversation, who its members are and the files shared in it, and names the provider permissions this conversation has not granted.In Microsoft Teams those permissions are granted per chat by whoever adds the app, while file access is a tenant-wide grant โ€” so a skill can branch on what it is allowed to see instead of inferring it from an empty result. The block is absent when the platform cannot determine it.See Lua and Teams permissions.

๐Ÿ”ง Improvements

The log read routes moved to logs:read, and log drains need logs:manage. logs:manage is sensitive โ€” it carries destination credentials โ€” so a wildcard grant does not satisfy it and a key holds it only when it was granted by exact name.The scope each log route used before (knowledge:read on an agentโ€™s logs, automations:read on a skillโ€™s logs, analytics:read on the organizationโ€™s logs and their CSV export) is accepted for two more releases. A request authorized that way still succeeds and answers with Deprecation: true, a Sunset date and a Warning header naming the scope it was authorized by, so a client can find out before the change bites.logs:read is not sensitive, so a key that already holds *:read has it and needs nothing re-minted. Only keys carrying knowledge:read, automations:read or analytics:read by exact name have to change.See Log drains REST and About credentials.
The log read routes answer a nextCursor now, and paging with cursor reads a moving log without skipping or repeating an entry โ€” which page numbers cannot do while new entries keep arriving at the top.
The page query parameter still works and is deprecated. A request that passes it explicitly answers with a Deprecation header and a Link: <โ€ฆ?cursor=โ€ฆ>; rel="next" pointing at the cursor form, so a client finds out before the parameter goes. This is the REST parameter; the CLIโ€™s lua logs --page is deprecated alongside it and still works, with the cursor the form to prefer.See Log drains REST.
Execution logs are kept for 90 days by default and then deleted. The docs previously listed execution-log retention among the things no verifiable mechanism was found for; it is now stated, in the limits tables and on the security page.To keep records longer than the window, copy them out with a drain.See Security and data.

๐Ÿ› Bug Fixes

trigger, model-resolver, workflow, workflow-step and workflow-script are valid log sources, but the CLI rejected them with a Valid values hint that did not list them. The accepted list is now the platformโ€™s own, so a source the platform adds is a --type the CLI takes.In the other direction, mastra is gone from the help text: it was advertised there but was never an accepted value.
The success line counted rewritten steps as an unreadable value, could never say that a workflow already asks for model classes everywhere it can, and always reported the published version as (new).It now prints the number of steps rewritten and the version that was published โ€” and, when a later version supersedes it, names the version that actually holds the pin. --json output is unchanged and was always correct.
A run that is not on the model-policy gate is refused by the platform โ€” clear-gate failed (NOT_GATED), exit 1 โ€” which is the documented contract. The CLIโ€™s unreachable โ€œnothing to clearโ€ exit-0 path is removed rather than made reachable.
Only the startup banner honoured NO_COLOR. lua logs, lua models, lua status, the sync and drift views and the setup prompts printed ANSI styling anyway; they now all follow the variable, so a CI log stays readable.Separately, LUA_WEBHOOK_URL is removed. It was defined but no command ever read it.See Environment variables.
lua marketplace template --help still advertised --skip-env-check as skipping env-contract validation, which it has not done since the check moved to the platform. The option line now reads as the deprecated no-op it is. The flag is still accepted, so existing scripts keep working.

๐Ÿ“ Notes

  • None. The three issues listed under v3.36.0 โ€” the recompose success line, clear-gate on a run that is not gated, and a quota park labelled as consent โ€” are all fixed in this release.

v3.37.0

Released: September 20, 2026

โœจ New Features

lua workflows status --steps now shows the model a Job-tier step ran on, how it was chosen โ€” the stepโ€™s own pin, the platform default, or your organizationโ€™s default โ€” and its cost multiplier. lua workflows logs shows the same when a step claims.lua push workflow and lua workflows deploy flag a Job step with no pinned model with a job-model-default advisory, naming the model it will run on before the first charge.See What a Job step costs.
A run priced per model call now prints a Tokens: line under Budget: โ€” for example 4,812 uncached in ยท 261,004 cache read ยท 1,024 cache write ยท 3,190 out โ€” and the --steps table gains Uncached, Cached and Output columns.A figure the run recorded nothing for is left out rather than printed as zero.See Operate workflow runs.
The sentence under Budget: now follows the runโ€™s own pricing instead of guessing from the unit shown, so it canโ€™t describe a seat run in legacy-plan terms or the other way around.A run whose last step completes past the cap now prints finished past the cap on that line, rather than reading as a plain success.See Operate workflow runs.
A run parking on its budget, being raised, and finishing past its cap now each print their own line โ€” paused on its run budget, raised, finished past the cap โ€” naming the spend against the cap.See Operate workflow runs.

๐Ÿ› Bug Fixes

The budget line now reads in the runโ€™s own unit โ€” actions, on a seat plan โ€” even before any step has priced a call, instead of quoting the legacy planโ€™s wording about credits at a seat customer.
The confirmation and every hint now say โ€œactionsโ€ on a seat plan and โ€œcreditsโ€ on the legacy plan, instead of always saying credits. The --credits flag keeps its name on both plans โ€” only the words around it changed.

v3.36.0

Released: September 18, 2026

โœจ New Features

In a group conversation (a Microsoft Teams group chat or channel thread) your code can read the participants, the current speaker and the providerโ€™s conversation id, and reply proactively into the same conversation.
Lua.request.conversation is undefined on 1:1 turns, jobs, webhooks and triggers. Teams group chats now run as one shared conversation per chat, so User.getChatHistory() there returns what the whole group said. See Lua and Microsoft Teams.
An agent step can now say what it does, ask for a model class instead of a fixed model, name the traits the model must have, record why, and set a reasoning effort. The platform resolves a class to a concrete model when the run starts, under your organizationโ€™s model policy.
lua workflows view shows what each step asked for and what it resolves to today; lua workflows status adds a per-model roll-up of the runโ€™s spend. A misspelled class, task class, trait or effort is refused by lua push before anything is sent.See Models and Choose a model per step.
A stepโ€™s effort is recorded and counted in the estimate but not sent to the model until you push with --apply-effort. Versions pushed before, and runs in flight, are unaffected; push without the flag to go back.
View and edit your organizationโ€™s workflow model policy โ€” administrators only.
Flags: --compose on|off (may the composer assign models), --max-class (the class ceiling), --consent-actions (the consent threshold), --allow (the allowed models), --class-fast / --class-balanced / --class-strong (per-class overrides), --class-map (task class to model class), --pins class-only|allowed (may a composed workflow pin a model) and --fallback same-class|same-provider (where a failed call may fall back). Only the flags you pass are changed.
Let your organization pre-consent to dynamic workflow starts inside an envelope, so an agent acting on its own no longer stops for a person on every composed start that fits.
get shows both what you set and what the platform actually applies. --clear restores a member to its default. lua workflows status shows Consent: auto (policy) with the envelope on a run that started this way. A start over any bound, or after the hourly allowance is spent, still asks.
lua workflows clear-gate <runId> --kind model_policy clears a run parked because a step asked for a model class the current policy cannot resolve.lua workflows recompose <workflow> rewrites a workflow composed before model classes existed so its steps ask for classes, and publishes the result as a new version.
The model catalog as a workflow step sees it: class, best for, speed, cost, and the reasoning efforts each model supports.
  • lua workflows logs shows which model each step resolved to, and a policy warning when a pinned model outside your organizationโ€™s allowed set was substituted.
  • lua workflows start prints the consent card โ€” the per-step plan and the cost line โ€” on a start that needs consent.
  • lua workflows status --steps explains an escalated approval: when an approval stepโ€™s approver would have been the person who started the run and the workflow excludes them, the request now goes to your organizationโ€™s admins instead of failing, and status says so.

๐Ÿ”ง Improvements

Previously a plain lua push skipped workflows entirely โ€” only lua push workflow sent them โ€” so a changed workflow could stay on an old version while the rest of the agent moved on. Workflows are versioned and activated on their own, so lua push now makes the workflow version it pushed live even without --auto-deploy, and says so.To roll back:
lua workflows deactivate <name> pauses a workflowโ€™s schedules and triggers without changing the live version.

๐Ÿ› Bug Fixes

A run waiting for consent no longer reads as still running in a polling loop; the --strict help text says so.

๐Ÿ“ Notes

  • The success line of lua workflows recompose prints the wrong figures; --json is correct.
  • lua workflows clear-gate on a run that is not gated is refused by the platform rather than answering โ€œnothing to clearโ€.
  • lua workflows watch and lua workflows logs label a run parked on a quota slot as gated for consent; the exit code is right either way.

v3.35.0

Released: September 15, 2026

โœจ New Features

Lua.request now exposes the turnโ€™s runtime context to your tools, preprocessors and postprocessors โ€” not just the model. Alongside Lua.request.channel and Lua.request.webhook, you can now read Lua.request.runtimeContext (whatever the caller passed as the requestโ€™s runtimeContext, e.g. via LuaPop.init({ runtimeContext })), Lua.request.threadId, Lua.request.timezone, Lua.request.userId and Lua.request.agentId.

๐Ÿ› Bug Fixes

lua push now makes clear that a pushed persona is served immediately โ€” the push publishes it and points the agent at it in the same step.
lua workflows no longer points to a recovery step that isnโ€™t available; the hints describe the supported path.

v3.34.0

Released: September 14, 2026

โœจ New Features

response is now optional on a block result. Return it with no response and the turn ends with nothing sent โ€” on every channel and in the web chat widget.
The userโ€™s message is still recorded in the agentโ€™s history, so when the agent next replies it has read everything said while it was quiet.This is what you want when something else is already handling the conversation โ€” a human support agent replying in a shared inbox, for example. Pair it with Data to remember when a person last replied, and Lua.request.webhook?.payload to see the incoming event.
An agent nodeโ€™s model may be a single placeholder instead of a fixed model, so one workflow can run a different model per stage, chosen when the run starts:
The form after | is the default, used when the run input doesnโ€™t supply a value. The whole model string must be the placeholder โ€” no prefix, suffix or second placeholder โ€” and initData is the only source it can read from.Job-tier steps must supply a default. Without one, lua push canโ€™t check the model and the step is judged when it runs.
Three additions to paramsMeta:
  • type: 'repositories' โ€” the installer picks repositories from an account theyโ€™ve already connected. Name the connection with connection: 'github'; max caps how many they can choose (1โ€“50, default 8).
  • advanced: true โ€” the parameter moves into a collapsed โ€œAdvanced settingsโ€ section. A value thatโ€™s been set still appears in review.
  • group: { id, title, description? } โ€” parameters sharing a group.id render together as one card, in the order you declare them.
Warnings now print beneath the success line:
The deploy still succeeds โ€” the warning tells you runs will be refused until the Job tier is switched on.

๐Ÿ› Bug Fixes

The selfAudit block in lua.skill.yaml was read but not sent with the publish, so published versions didnโ€™t carry it.Leaving the block out still leaves the previous versionโ€™s settings untouched โ€” it is not cleared.

v3.33.0

Released: September 10, 2026

โœจ New Features

lua auth logout now ends the session on this device only; your other devices and apps stay signed in. --all ends every session for your account.
See Authentication.
lua auth sessions lists every device and app signed in as you; --json for scripts. lua auth sessions revoke <id> signs one of them out once per-device sign-out is enabled for your account; until then it points you to lua auth logout --all.
A session created before this release becomes a per-device session on your next command, with no prompt.
Publishing a new version of a marketplace template pushes it to every install that opted into creator updates: at publish for a private template, once the version is approved for a public one. The command reports how many installs are updating.
A template parameter or persona variable can declare showIf so installers only see it when another parameter has one of the listed values. Hidden parameters are not required.
lua marketplace template show prints the condition beside each conditional entry.
An HTTP triggerโ€™s transform may return { startWorkflow: { name, input } } to start a workflow run instead of an agent turn. name accepts the workflowโ€™s name or id. lua triggers shows the started run, and a โ€œskipped, already in flightโ€ outcome when a run of that workflow is still going.
A workflow schedule frozen into a template runs as the person who installed it, so its runs show up in their inbox. Set schedule.runAs: 'system' on the workflow to keep a system run.
Job-tier steps can grant ripwire, a read-only code-search tool that stays available on ro steps. Read-only Job steps can no longer run shell commands.
lua workflows status prints the download route and the size or row count for a step or run output the engine stored out of band; --steps shows one line per offloaded step.

๐Ÿ”ง Improvements

lua workflows deploy on a workflow composed in chat is refused with WORKFLOW_DYNAMIC. Compose it again in chat under the same name to change what runs.
Integrations.passthrough under lua dev and lua test throws an error carrying code, statusCode, retryAfterSeconds, vendor and requestId beside the message, so skill code can branch on the code instead of the text.
A GitHub, Unified.to or Google fault behind a token mint or a passthrough call answers 503 VENDOR_UNAVAILABLE naming the vendor and whether a retry is safe. The CLI hint says so instead of โ€œthe Lua API could not be reachedโ€.
A read-only agent step whose declared tools are all dropped compiles to a text-only turn and now prints a warning naming the step.

๐Ÿ› Bug Fixes

When your CLI session has been signed out elsewhere or can no longer be refreshed, the next command removes it and tells you to sign in again (exit 9), instead of failing on every command until lua auth logout.
The session lock now works on Windows, and a lock left behind by a crashed process no longer blocks every command.
A request the model provider refused (a bad BYOK key, an unknown model id, a refused prompt) exits 12 provider_rejected with a hint that says what to fix, instead of an error that looks worth retrying.
Exported graphs keep the $self agent reference, get a title on every approval and place container arms. The command compiles its own output before exiting 0.
fromInit, fromStep and initData / stepResults references type-check. An unsupported source is refused with a named build error instead of a type error or a silently dropped page.
A startWorkflow that names the workflow no longer fails with WORKFLOW_NOT_FOUND.

Workflows

Released: September 5, 2026 Platform changes for Lua Workflows shipped on September 4 and 5, 2026.

โœจ New Features

Declare the connections a workflow acts through once, by key, and name the key where a connection id used to go:
The key is resolved on the agent that runs the workflow - an agent-scoped connection first, then an org-scoped one, never a personโ€™s - once per run, recorded as a connection.resolved event and returned in the run detail. A key with no match fails the step with credentials_unresolved and a notice naming the integration to connect; an undeclared key fails lua push with connection-key-undeclared. Literal connection ids and template installs are unchanged.Declaring keys in a lua-cli project (and the same check at lua compile) needs the next CLI release; agent templates already declare them.See Connections.
The agent can now list, inspect, pause, resume and close a workflowโ€™s goals (listWorkflowGoals, getWorkflowGoal, pauseWorkflowGoal, resumeWorkflowGoal, closeWorkflowGoal). setWorkflowGoal accepts a deterministic judge.predicate beside an agent judge, and maxRuns defaults to 3 when the user does not name one. unscheduleWorkflow refuses a goalโ€™s cadence (goal_schedule): a goal is stopped by pausing or closing it, never by removing its schedule.See Goals.
The agent recognizes requests that need steps which run unattended, wait on a person or an event, or outlive the reply - an approval in the middle, a parallel fan-out, a delay, a cadence, a specialist review - and composes a workflow for them instead of answering inline. composeWorkflow teaches the node grammar (promptTemplate on agent nodes; ${initData.<field>} and ${stepResults.<stepId>.text} placeholders), and the validator names an unknown member (unknown-node-key) or a missing prompt (agent-prompt-template-required) with a repair.See Workflows from chat.

๐Ÿ”ง Improvements

A failed agent or Job-tier step carries reason (provider_error, context_overflow, rate_limited, timeout, unknown), the providerโ€™s status, a scrubbed message, the model and the coding-turn index - on the step.failed event, in the run detail and in lua workflows status --steps --json. From the next CLI release the formatted table shows them too:
See Step failures.
Once a Job-tier attemptโ€™s container is gone, the step log endpoint - and lua workflows job-logs - returns the persisted log for that attempt with a note saying so. A finished attempt with nothing persisted is a 404 JOB_LOGS_NOT_PERSISTED rather than a control-plane error. The CLIโ€™s Note: line, the --follow stop on a finished attempt and the exit-3 handling follow in the next CLI release.
A sleepUntil node fails validation with node-type-unsupported-by-engine at push and in composed workflows alike (and at lua compile from the next CLI release). Use sleep with a duration in milliseconds.
lua workflows goals and lua workflows schedules are coming in the next CLI release.

v3.30.0

Released: September 2, 2026

โœจ New Features

Every Channels.send, template, reaction and email send now returns a deliveryId and a status (queued, accepted, sent, delivered, read, failed). Read the current state later, and the reason category for a failure:
Webhooks subscribed to message.sent, message.delivered, message.read and message.failed now receive them for SMS, email, Messenger, Instagram and the other channels, not only WhatsApp, with the full delivery in the payload.
Sends carry an idempotency key. Retrying a send with the same key returns the original outcome instead of sending twice.
Template env params can declare type: model so installers pick a model instead of typing a code.

๐Ÿ”ง Improvements

Send results keep the delivered flag; prefer status, which keeps updating as receipts arrive.

v3.29.1

Released: August 31, 2026

๐Ÿ› Bug Fixes

Email OTP setup now saves the renewable session instead of failing after verification.
Renewable sessions now load all current organizations and agents, including accounts with more than 100 organizations.

v3.29.0

Released: August 31, 2026

โœจ New Features

Email sign-in now creates a renewable user session that follows your current organizations and agents. Login no longer asks you to choose an organization, agents, or role.
Run lua init to select an agent for a project. The selection stays in that projectโ€™s lua.skill.yaml.
Signed-in users can create or duplicate agents when their current Lua permissions allow it.

๐Ÿ”ง Improvements

Email login no longer creates an agent-scoped API key. Existing API keys remain valid. Create and manage scoped keys in Settings โ†’ API Keys, or supply an existing key with direct API-key setup.
Scoped and legacy API keys remain supported for CI, plugins, integrations, and automation through LUA_API_KEY, .env, or direct API-key setup.

Scoped API keys

Released: August 27, 2026

โœจ New Features

Create personal API keys with an explicit role on specific organizations or agents, from Settings โ†’ API Keys in the admin dashboard โ€” instead of a key that always carries your full account permissions.A scoped key can never do more than its owner can: its role on a resource is capped at the ownerโ€™s current role there, and it loses access if the owner does (in that organization only โ€” it keeps working in the ownerโ€™s other organizations). An org admin can create a scoped key for another member, capped at that memberโ€™s role.Existing keys are unaffected โ€” they keep working exactly as before, with your full account permissions and no expiry. This is additive, not a migration.
See API Keys for the full picture: roles, expiry, rotate/suspend/revoke, and the new 403 response for a valid key whose role doesnโ€™t cover the action.

v3.28.0

Released: August 30, 2026

โœจ New Features

Email OTP setup can now create a credential limited to a selected organization, one or more agents, and an assignable role.
Add an optional signing key to LuaWebhook. When configured, Lua verifies an HMAC-SHA256 signature over the exact request body before running the webhook. Existing webhooks without a key behave as before.
Templates can run a bundled skill tool, an agent instruction, or both after installation and before uninstall cleanup. Installed trigger callback URLs are also available through LUA_TRIGGER_URL__<KEY>.
A webhook trigger can run a named skill tool after verification and filtering without starting an agent conversation. Existing transform-only triggers continue to work unchanged.
Organization administrators can list skills owned by their organization and transfer a skill to another organization they administer.

๐Ÿ”ง Improvements

lua integrations update now replaces credentials only after authorization succeeds. Existing triggers, tool access, labels, attachments, and memory settings remain intact. Personal connections support the same flow with --scope user.
User.send() now targets the authenticated user without a separate profile lookup. Existing code needs no changes.
An existing connected account can satisfy a template requirement without another selection step. Template-installed agents can also use active attached connections consistently.
Project initialization, agent listing, status, dashboard, and integration commands work cleanly with scoped credentials. CLI telemetry no longer stores account email or name.

๐Ÿ› Bug Fixes

Fixed an upload compatibility issue that could make lua push fail while uploading compiled bundles.
Canceling or failing an integration reauthorization no longer disconnects the working account.
Webhook-triggered turns now use the triggerโ€™s bound user, so user-specific actions reach the intended inbox.

v3.27.0

Released: August 25, 2026

โœจ New Features

lua integrations connect now asks who should own the connection.
This agent only is what the command has always done: the agent holds the credential and loses it when the agent goes.Me makes it yours. Every private agent you own can use it โ€” including agents you create later, which are added automatically. Publishing an agent removes its access.A non-interactive run without --scope still connects to the agent, so existing scripts keep their meaning.
Disconnecting removes it from every agent at once. What it already added to memory is kept unless you choose to forget it โ€” disconnecting and forgetting are separate decisions.
Already connected something to an agent and want it available everywhere? Convert it instead of reconnecting:
The agent you connected it to keeps access and its triggers keep firing. It asks for confirmation first, because there is no way to convert back.

๐Ÿ”ง Improvements

Connecting as yourself has no wake-up triggers, no account name and no sensitive-data toggle. Passing --triggers, --custom-webhook, --hook-url, --account-label or --hide-sensitive with --scope user now fails immediately, naming the flags โ€” rather than partway through a browser sign-in. The interactive prompts for those steps are skipped in that scope too.
Integrations that connect for a whole workspace, memory-source connectors, and types you have already connected are left out of the list. If the current agent already has its own connection of that type, you are warned before a second one is created โ€” it adds a credential, it does not replace the first.
The Channels.send typings now describe addressing a shared Teams conversation with conversationId, alongside the existing per-person options.

v3.26.0

Released: August 24, 2026

โœจ New Features

One command writes a reviewable template: section into lua.skill.yaml, inferred from your agent: the connections its skills use, {{variable}} slots detected in the persona, trigger presets with their current on/off state as recommended defaults, and display metadata for install-time parameters.
Re-running draft merges additively โ€” newly inferred entries are appended and your hand-edits are never modified โ€” and prints a per-section added/kept diff. Pass --force to replace the authored sections wholesale, and --source-version <n> to compose from a specific promoted agent version. See Publishing Templates.
When lua.skill.yaml carries a template: section, publish always serializes all four authored sections (connections, personaTemplate, triggerPresets, paramsMeta) โ€” an empty or absent section is an explicit clear, never โ€œinherit the previous versionโ€™sโ€. A publish that would clear or narrow a section prints a per-section consequence diff and asks for confirmation:
Template versions are validated server-side at publish: undeclared persona {{variables}}, invalid schedule intervals, unknown connection platforms or scopes, duplicate keys, and secrets in frozen code all reject with actionable per-field errors โ€” and a rejected publish burns no version number. Publishing from a source agent with voices or device triggers is rejected with a message saying why (voice agents canโ€™t be templated yet).
A template can now be deployed from the marketplace straight into a fresh agent: the persona templateโ€™s {{variables}} are filled in from your answers, connections youโ€™ve already granted are reused automatically, and automations only arm after an explicit confirmation. See Deploying Templates.
Call any endpoint a provider documents through the integration your agent is already connected to โ€” from tools, jobs, webhooks, and processors. The call is relayed server-side over the agentโ€™s own connection (your code never sees credentials), and the response comes back as a raw { status, headers, data } envelope: provider errors such as a 403 on a missing OAuth scope are relayed faithfully in status, never thrown. Every connected integration also gains a matching {integration}_passthrough agent tool.
Guardrails are built in: a per-integration enable switch admins can turn off, an audit log entry for every call, and a per-agent rate limit. See the Integrations API for the full request/response reference, the typed error codes, and Microsoft Graph + GitHub examples.

๐Ÿ”ง Improvements

The env-contract check on template install and apply is enforced server-side and can no longer be bypassed. The flag is still accepted so existing scripts donโ€™t break, but it does nothing beyond printing a deprecation warning โ€” satisfy the contract with lua env or --env-vars instead.

v3.25.0

Released: August 19, 2026

โœจ New Features

LuaAgent accepts an optional description โ€” a short capability summary that Spaces read when deciding which member agent to hand a request to:
Keep it to one or two sentences about what the agent can do. The persona still owns voice, tone and detailed instructions โ€” the description is only the routing signal.It travels with the rest of your configuration on lua push:
Ownership is opt-in. Omit description and a value set in the dashboard is left untouched, so existing projects are unaffected. Set it to an empty string to clear it.
Your agent can ask for an approval, flag a broken integration, or leave a notice in a userโ€™s inbox โ€” from a tool, a job, a webhook, or a local run:
Cards with options resolve in one click, reusing the same key lands on the existing card instead of knocking twice, and reaching the daily limit returns { outcome: 'capped' } rather than throwing.See the Inbox API reference for card kinds, limits and the full receipt contract.

v3.24.0

โœจ New Features

Declared indexes for the Data primitive โ€” filtered queries on large collections no longer degrade silently. Declare the fields you filter on where you store data, and the platform builds and maintains an agent-scoped database index for them:
  • Data.create() / Data.update() / patch() accept { searchText?, index? } (the legacy positional searchText string still works)
  • New Data.collections() lists your collections with entry counts and index status (pending | building | ready | failed | rejected + reasons)
  • Indexes build within minutes, stay alive while your agent uses them (writes or filtered reads), and are removed automatically ~14 days after all usage stops โ€” no cleanup code
  • Limits: 2 fields per index, 3 declarations per call, 5 indexes per agent; invalid declarations are rejected visibly, never silently trimmed
  • Filtered queries that exceed the time budget on an undeclared field now fail with an error naming the collection, the field, and the exact declaration to add โ€” instead of an opaque 502
See the Data API โ€” Indexes section for the full guide. Correction (August 26, 2026): the options-object third argument ({ searchText?, index? }) described above works in local development runs only. In a deployed agent, Data.create() and Data.update() accept a plain searchText string as the third argument, and passing an object fails with searchText must be a string. Keep deployed code on the string form until index declarations are supported in the deployed runtime; Data.create() tracks the current state.

v3.23.2

Released: August 14, 2026

๐Ÿ› Bug Fixes

Use --user with a user ID, email address, or mobile number when you have organization management permission for the agent:
The target must be a user associated with that agent. Without --user, the command continues to clear only your own conversation history.

v3.23.0

Released: August 7, 2026

โœจ New Features

A user resolved by ID, email, or phone now stays attached to that instance for every mutation:
update(), save(), and clear() are target-bound too. Existing User.get() calls without an identifier still use the current conversation user, and update() keeps its merge behavior.
Change and remove top-level fields without replacing an entire entry:
searchText: null also removes the entryโ€™s semantic-search text.

๐Ÿ› Bug Fixes

User instances resolved by user ID, email, or phone no longer fall back to the calling sessionโ€™s record when you update, save, patch, unset, or clear them.
LuaJob.timeout accepts whole seconds from 1 through 600 and defaults to 300 seconds.
Connection and reconnection attempts now verify that the browser callback belongs to the authorization attempt and intended agent.

v3.22.0

Released: July 28, 2026

โœจ New Features

LuaSkill now accepts the same optional condition as LuaTool, one level up:
When it returns false, the skillโ€™s tools canโ€™t be called and the skillโ€™s name, context, and tool names are left out of the agentโ€™s prompt entirely โ€” the agent doesnโ€™t know the capability exists. A tool-level condition only makes a tool uncallable; the skillโ€™s name and context stay in the prompt, so the agent can still say โ€œโ€ฆbut youโ€™re not enrolled, so I canโ€™t do thatโ€. Reach for the skill-level gate when the existence of a feature is itself sensitive: tiering, entitlements, per-customer capabilities.Evaluated per message, per user, with the full Platform API available. Fail-closed โ€” a condition that throws or times out hides the skill. Skills without a condition are unaffected. See Conditional Skills.
Template header values now accept a Meta media id as well as a public URL:
Upload the asset to Meta once and reuse the id across a campaign, instead of having the image re-fetched for every recipient. image_id, video_id and document_id sit alongside the existing image_url / video_url / document_url keys โ€” an id is used in preference to a URL when both are given, and document_filename still applies either way.

๐Ÿ› Bug Fixes

async condition() { ... } now behaves the same as condition: async () => { ... }. Previously the condition was dropped and the skill failed to build.
A skill defined as a class alongside its tools now builds correctly and stays small โ€” tool code no longer runs when the skillโ€™s condition is evaluated.

v3.21.0

Released: July 24, 2026

โœจ New Features

Publish a versioned snapshot of an agentโ€™s entire configuration (skills, webhooks, jobs, processors, triggers, model, plus a declared env-var contract) and install or roll it out to other agents. Includes fleet rollout across many agents with a per-target result table, an install ledger, consent-based creator updates, rollback via lua version promote, and manifest inspection.
Templates never touch a target agentโ€™s persona, environment variable values, or channels.
List a skill with --visibility private to make it visible and installable only within your organization.

๐Ÿ”ง Improvements

lua marketplace [skill|template] <action> replaces the previous create/install role menus with one flat action namespace per domain. Two actions were renamed: viewing your own listings is now mine, and editing listing metadata is now edit. See the marketplace command reference for the full old โ†’ new migration table.
Flags like lua marketplace template view --version 2 previously printed the CLIโ€™s own version instead of selecting a version. The CLIโ€™s version flag is now -V / --cli-version (bare lua --version still works).

๐Ÿ”„ Changes

Clearing another userโ€™s conversation history is no longer supported. The command now clears only your own history and fails with a clear message if --user is passed.

v3.20.0

Released: July 21, 2026

โœจ New Features

See, per primitive, which version is pinned by the active agent version versus whatโ€™s in your local project.
Mismatches are flagged with direction-aware guidance: primitives that are pushed but not yet live point you to lua version create + lua version promote (or lua deploy <type> for a single primitive), while local files older than whatโ€™s live get a lua sync recommendation โ€” with a warning that deploying older files would roll production back.
React to a WhatsApp message with an emoji from your agent code:

๐Ÿ”ง Improvements

For agents using agent versioning, lua deploy now automatically creates and promotes a new agent version scoped to the deployed primitive โ€” the deploy is live immediately and shows up in lua version list. The success output includes the promoted version, and if the live version canโ€™t be updated the deploy fails with a clear message instead of reporting success.
Connection listings now indicate when a connection has been paused.

v3.19.0

Released: July 3, 2026

โœจ New Features

Chat requests now accept an optional clientContext.timezone field โ€” an IANA timezone string. When provided, the agent uses it as the userโ€™s local timezone for date/time-aware responses; when omitted it falls back to the userโ€™s stored profile, country, or UTC.
Also available on Agents.invoke. See HTTP API and Agents API.
Set a default reasoning effort for your agent, normalized across every reasoning-capable provider (Claude, GPT/o-series, Gemini, Groq, DeepSeek, xAI, Qwen).
effort is one of 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'max'. Left unset, it defaults to adaptive reasoning where the model supports it and an explicit low effort otherwise, favoring lower cost/latency on turns that donโ€™t ask for deeper thinking. show controls whether the reasoning trace is surfaced to the caller (default true). A per-request reasoning override always takes precedence over this agent-level default. See LuaAgent โ†’ modelSettings and Model Selection โ†’ Reasoning Effort.
Resolve a colleague by name within your organization and get back the channel handles (WhatsApp, SMS, email) theyโ€™ve chosen to share. Pair it with Channels.send to message them directly โ€” no hard-coded phone numbers.
Returns a list of matches so an ambiguous name can be disambiguated โ€” each match includes its shareable targets[] (empty if the teammate hasnโ€™t opted in to sharing anything).
Turn on browser: true to give your agent browser tools like searchWeb, routed to the userโ€™s connected desktop browser when available, or a cloud fallback otherwise.
Pass an object instead of true for policy: engine ('auto' default, 'agent-browser' for local-only, 'browser-use' for cloud-only), allowedDomains, credentials (named vault entries โ€” never raw secrets), and maxSessionMinutes. Off by default โ€” a browser session costs money and carries risk, so itโ€™s opt-in.

๐Ÿ› Bug Fixes

version create could previously refuse with โ€œno staged changesโ€ even when you had real changes to snapshot, or silently accept a run with nothing to snapshot. It now diffs the actual content against the previous version โ€” including a rename with no other change. When thereโ€™s genuinely nothing new, it now tells you so plainly instead of throwing an error. lua version diff also shows voice changes, and the โ€œโ€”auto-deploy was ignoredโ€ notice now appears in the final push summary, not just at the very top.

v3.18.0

Released: June 22, 2026

โœจ New Features

Your agent can now reach out first โ€” send messages on any connected channel from tools, jobs, webhooks, and triggers. Every send is recorded to the userโ€™s conversation thread, so the agent picks up with full context when they reply.
See Channels API and Proactive Messaging.
Define a trigger with a verify โ†’ filter โ†’ transform pipeline and get a pasteable URL that wakes your agent on any external event โ€” no execute function required.
Manage triggers from the CLI:
Prefer no code? lua triggers create --name daily --instruction "Reply with today's date" creates a URL trigger with no SDK.
Let your agent send messages on its connected channels as part of a conversation by enabling the outboundChannels feature:
Scope it to current_user (message only the person itโ€™s talking to) or anyone (message recipients it specifies).
Drop specific tools from a voice agent, and transfer a live call to another in-room agent.
Send templates with media-rich headers. Create a template with format: 'IMAGE', 'VIDEO', or 'DOCUMENT' and provide a sample media URL for approval (must be publicly reachable HTTPS and within Metaโ€™s size/MIME limits).When sending, use values.header.image_url for IMAGE templates, values.header.video_url for VIDEO templates, or values.header.document_url (with optional document_filename) for DOCUMENT templates:
See the Templates API for creation details and size limits.

๐Ÿ”ง Improvements

Images embedded in an email body โ€” for example pasted or dragged into Gmail compose โ€” previously arrived as just a [image: โ€ฆ] text placeholder, with no image part. They are now forwarded to your agent as image parts, the same way paperclip attachments are.The rules:
  • The embedded image must be referenced in the emailโ€™s HTML body and be at least 1 KB (spacer pixels are filtered out)
  • Up to 10 embedded images per email are forwarded; regular attachments are unaffected
  • Remote-hosted images (e.g. Gmail signature images, which are hosted rather than embedded) are not fetched
  • Attachments mislabeled as embedded content by some clients are detected and forwarded as normal attachments
The [image: โ€ฆ] placeholder still appears in the message text โ€” use the image part, not the placeholder. See Email Channel โ†’ Attachments and Embedded Images.

v3.17.2

Released: June 1, 2026

๐Ÿ› Fixes

lua git auth github now links your GitHub account reliably using GitHubโ€™s device flow โ€” a code is shown in your terminal that you enter at github.com/login/device.
See the Git Command documentation for the full workflow.

โœจ New Features

lua git connect --auto-push enables pushing each auto-commit to your linked GitHub repository. It checks that a GitHub account is linked and that your origin is a GitHub HTTPS remote before turning auto-push on, so a missing link or remote is reported immediately.
The git block in lua.skill.yaml is managed by these commands โ€” you no longer need to edit it by hand. See the Git Command documentation for details.

v3.17.1

Released: May 29, 2026

โœจ New Features

Set sampling settings once on the agent instead of overriding them in every skill. Supports temperature, topP, topK, maxOutputTokens, presencePenalty, frequencyPenalty, stopSequences, and seed โ€” forwarded to the model on every chat turn.
Obviously-broken values (non-finite numbers, temperature outside 0..2, topP outside 0..1, non-positive maxOutputTokens, non-string stopSequences) are rejected at construction time. Provider-specific range checks are deferred to the provider.
Constrain the model response to a JSON Schema. The parsed object lands on result.output. No more โ€œrespond with JSON onlyโ€ prompts and no more JSON.parse boilerplate.
On Google models, the auto-injected google_search tool is suppressed when structuredOutput is set (Vertex does not allow mixing function-calling tools with google_search).
AiGenerateInput, AiGenerateOutput, AiGenerateStructuredOutput, AgentModelSettings, and sub-shapes (AiGenerateJsonSchema, AiGenerateSource, AiGenerateToolCall, AiGenerateToolResult) are now importable directly from lua-cli.

v3.5.0-alpha.2

๐Ÿ”ง Improvements

Lua.request.webhook.payload is now populated for inbound emails with a JMAP-aligned object containing parsed message metadata.
Access the Message-ID for deduplication, full RFC 5322 header list via headerLines, and all threading metadata (messageId, inReplyTo, references). See the API reference for the complete shape and AgentMail divergence note.

v3.16.0

Released: May 19, 2026

โœจ New Features

Define voice agents in TypeScript alongside your LuaAgent. Supports cascaded (STT/LLM/TTS) and realtime (speech-to-speech) shapes, with hooks for onEnter, onUserTurnCompleted, onExit, and voice-only tools.
Reference the voice from your LuaAgentโ€™s voices: [supportVoice]. lua push ships it through the same versioned-primitive flow as skills and webhooks.
Buy, bind, list, and release phone numbers from the CLI:
SMS-capable numbers route through Vonage; voice-only routes through LiveKit-native PSTN. The bind action wires the number directly to your agent for inbound calls.
Test voice agents with text input and event-stream assertions, runnable in CI:
Talk to your voice agent during development without going through a phone number:
View voice call logs from the CLI:
Also available as a choice in the interactive lua logs picker.
Re-exports of LiveKitโ€™s plugin namespaces from lua-cli so you donโ€™t need a direct LiveKit dependency in your project:
Factor shared tool / skill / webhook / etc. logic into a base class in a workspace package, then extend per-agent with field initializers:
Works for every primitive type. The compiler walks the full extends chain, including transitive extends through shared intermediates.

๐Ÿ”ง Improvements

Publishing a new voice version with lua push voice is enough โ€” the next call uses the new version. No need to re-push the agent.

๐Ÿ“ Notes

Voice agents require the latest server-side endpoints. Self-hosted Lua deployments must deploy the latest server before publishing voice agents via lua push.

v3.15.3

Released: May 12, 2026

โœจ New Features

After a primitive push, the CLI now also attaches the gzipped workspace archive to each skillโ€™s per-skill source store, so the Builder UIโ€™s source panel stays in sync with your CLI edits.
The attach is non-fatal โ€” if it fails, the push is still considered successful, because the primitive is already deployed and the agent-level backup already succeeded. The per-skill store is a denormalized projection for the Builder UI, not the canonical source store.

v3.15.2

Released: May 8, 2026 If youโ€™re upgrading from v3.15.1, upgrade directly to v3.15.2 (or later). v3.15.1 has a startup crash on fresh installs from npm.

๐Ÿ› Bug Fixes

v3.15.1 shipped with a missing runtime dependency that crashed every CLI command on a fresh npm install [email protected], not just the new lua source subcommands. v3.15.2 removes the dependency entirely โ€” the affected paths now use the same HTTP layer as the rest of the CLI, so install size and dependency footprint are unchanged from v3.15.0.If you were stuck on v3.15.0 because of this, you can upgrade safely now:

v3.15.1

Released: May 7, 2026 This release has a startup crash on fresh installs from npm. Upgrade directly to v3.15.2 or later.

โœจ New Features

Two new subcommands for working with your agentโ€™s backup version history.List versions:
The currently active version is starred in the output.Roll back to a past version:
Rollback downloads the chosen versionโ€™s files into your local workspace and then auto-pushes the rolled-back state as the next version. History is append-only โ€” the original v12 is never overwritten. After a rollback you have an explicit new version at the head representing โ€œwe returned to v12 on this date.โ€
lua push <primitive> now always runs a fresh-from-disk backup-push as the final step. If the backup fails, the command exits non-zero.No more silent partial success where the primitive landed on the server but your local source never reached the canonical store. This makes the CLI a reliable single source of truth for โ€œthis version was pushed from this machine in this state.โ€
Builds the backup manifest by walking your project directory directly from disk, instead of reading the compiled manifest.
Always-on for the auto-backup hook; opt-in for explicit lua push backup calls. Use this when files have been written by the Builder or by any other out-of-band path that bypassed lua compile.

๐Ÿ”ง Improvements

Initโ€™s restore step now hits the active manifest endpoint, which reflects every successful push from any platform โ€” CLI, Builder chat, dashboard edits.Previously, lua init against a Builder-managed agent could restore a backup days or weeks behind the actual runtime state because the Builderโ€™s writes werenโ€™t reaching the legacy backup manifest reliably.
After lua init and after every successful lua push, the CLI records the serverโ€™s active backup version into your local lua.skill.yaml. Foundation for future staleness warnings (โ€œlocal is behind serverโ€) and for cross-machine drift recovery.
The new fresh-from-disk walker enumerates source files directly, so files written by the Builder (or any out-of-band edit) make it into your backup. Per-file cap: 256 KB. Skipped: node_modules, .git, dist, dist-v2, .env, and lockfiles.Previously, the reconcile-only path re-hashed files already in the compiled manifest, missing anything written outside lua compile.

๐Ÿ› Bug Fixes

Network timeouts or missing blobs during the post-init backup restore are now caught and logged. lua init continues with an empty workspace and tells you what failed, instead of aborting with an unhandled exception.
The dist-v2 directory is no longer accidentally pulled into fresh backups. Previously, every fresh backup included the entire compiled tree (bundles, per-file source copies, the manifest), inflating per-push size and contradicting the intent that compiled artifacts stay separate.

v3.15.0

Released: May 6, 2026

โœจ New Features

Clone an entire agent into a new one in a single command:
The duplicated agent includes its persona, skills, MCP servers, env vars, and a full project backup that is restored locally so you can lua push immediately. The new agent inherits its sourceโ€™s LLM model โ€” lua init no longer prompts for a model.Opt-in flags add more buckets to the copy:
Cross-org duplicates are supported by adding --org-id <target-org-id>.The interactive lua init flow (no flags) now offers โ€œDuplicate an existing agentโ€ as a third choice alongside โ€œCreate a new agentโ€ and โ€œUse one of your existing agentsโ€.
Persona and skill context can now be split per channel using an object form. The previous string form keeps working โ€” adopt the object form only where you need it.
Useful when a voice agent needs different phrasing than the same agentโ€™s chat surface. The CLI pushes voice-only personas correctly (previously they could be skipped as empty), and lua sync --pull round-trips the object form back to your source file with all channel branches preserved.The new PersonaText type is exported from lua-cli for use in your own typings.

๐Ÿ”ง Improvements

When a command hits a 401 from the server, lua-cli now distinguishes between two very different conditions and tailors the remediation hint accordingly:
  • API key invalid or expired โ€” Authentication failed. Run \lua auth configure` to set a new API key.`
  • API key valid, but no access to this agent โ€” Access denied for this agent. Run \lua agents` to list agents you can access, or `lua init` to switch projects.`
Previous versions printed a single generic message for both, sending users to a dead-end remediation flow when their key was actually fine.
Setting priority on a LuaPostprocessor now correctly ships to production. In previous versions, the field was silently dropped during compile, so postprocessors always ran in their default order regardless of declared priority.No code change needed โ€” re-run lua push and your existing priority declarations now take effect.
Restoring a project backup from an older agent no longer aborts mid-restore with incorrect header check when one of its stored blobs was archived without compression. Uncompressed blobs are now passed through verbatim.Affects lua init --restore-sources, lua init --from-agent-id, and any other path that restores a project backup.
Two related fixes for the new channel-aware persona shape:
  • Voice-only personas now push. A persona of just { voice: '...' } (no base, no text) is correctly applied. Earlier this was silently skipped as empty.
  • lua sync --pull preserves all channel branches. Pulling an object-form persona from the server and writing it back to local source no longer drops voice or text.

๐Ÿ› Bug Fixes

lua production now correctly displays personas using the new object form, instead of crashing on .length / .substring() calls or printing the literal string [object Object].
Skills that donโ€™t define a context now push without error. Previously the CLI would send an empty string and the server would reject it under the new stricter persona/context validator.
The source-write path for string-form personas now produces a properly-quoted literal under all edit paths, instead of occasionally emitting unquoted text that broke compilation.

v3.14.0

Released: May 5, 2026

๐Ÿ”ง Improvements

lua push now reliably uploads agents of any size, including those with many tools or large code bundles.Previously, agents that exceeded the upload size limit would fail mid-push with a โ€œrequest too largeโ€ error. This no longer happens โ€” pushes succeed regardless of total bundle size.Applies to every primitive type:
  • skills (and their tools)
  • webhooks
  • jobs
  • preprocessors / postprocessors
  • devices and device triggers
No configuration or command change required. Just run lua push as usual.
Duplicate copies of code bundles have been removed from the push payload across all primitive types. Each push is now hundreds of KB to several MB smaller, which makes pushes faster โ€” especially on slow connections.Sandbox sessions launched by lua chat benefit from the same reduction.No action needed; the change is automatic with this release.

v3.13.0

Released: April 29, 2026

โœจ New Features

A new lua status command (alias: lua describe) dumps the full state of your agent in one shot โ€” no more running five separate commands to understand whatโ€™s going on.
What it shows:
  • Environment โ€” CLI version, install method, Node version, API base, env overrides
  • Updates โ€” current vs latest published version
  • Auth โ€” key source, email, user ID, org list, server reachability
  • Project โ€” config path, agent name/ID, manifest primitive count
  • Primitives โ€” per-type sync table: local version, server version, status (synced / ahead / behind / not deployed) for skills, webhooks, jobs, preprocessors, postprocessors, MCP servers, devices, device triggers
  • Persona โ€” synced / drifted
  • Backup โ€” synced / out-of-sync
  • Telemetry โ€” enabled/disabled
  • Next steps โ€” actionable hints based on current state
The --json flag outputs a stable JSON document (schemaVersion: 1) suitable for LLM agent consumption or CI dashboards. All progress output is suppressed in JSON mode.
Mistyped commands now show a โ€œDid you mean X?โ€ suggestion instead of a bare error:
Works for top-level commands and constrained argument values (log types, primitive kinds, environments) across all 22 commands.
Common aliases are now accepted everywhere constrained argument values are expected:For example: lua logs --type pp is equivalent to lua logs --type postprocessor.
Every push, deploy, chat, compile, sync, and test surface now provides contextual guidance after the operation:
  • Error paths โ€” ๐Ÿ’ก Diagnose: run \lua logs โ€”type X โ€”name Y โ€”limit 10โ€œ pointing at the right log stream for what just failed.
  • Success paths โ€” โœจ Tip: run \lua logs โ€”limit 10โ€œ nudges you to verify production execution.
  • lua chat โ€” after each conversation turn, silently probes for agent_error logs. If any fired during that turn, prints: โš ๏ธ N new agent error(s) โ€” run \lua logs โ€”type agent_error` to inspect.`
  • lua compile โ€” tip to run lua test after a successful compile.
  • lua sync --push โ€” tip to verify with lua logs after pushing.
To suppress all hints (for CI scripts): LUA_NO_HINTS=1 lua push all
Two improvements to lua test output:Shape headers โ€” the tool return value is now prefixed with its type and field names:
--json flag โ€” outputs pure JSON on stdout, with all progress and compile output redirected to stderr. Enables clean piping:

๐Ÿ› Bug Fixes

Primitive arrays defined outside the agent config object previously caused primitives to be silently dropped from the compiled manifest. All of the following patterns now resolve correctly:
Previously, using any of these patterns would cause the compile to succeed (โœ… Compiled N primitives) but with the primitives silently absent from the manifest โ€” resulting in a broken agent after push.
lua push all now automatically retries once when it encounters an โ€œalready existsโ€ version collision. It re-fetches the current highest server version, bumps to the next one, and retries โ€” resolving the most common lua push all failure without any user intervention.Additionally, sandbox versions (e.g. 1.0.21-sandbox) are now correctly excluded when calculating the next production version bump.
Primitives that exist on the server but not in your local YAML are now shown in interactive delete and trigger menus, clearly marked as [server only]. Previously these orphaned primitives were invisible in menus, making them impossible to delete or trigger interactively.Non-interactive delete (lua preprocessors delete --preprocessor-name X) also now falls back to server data for all primitive types.The orphan warning message now shows the exact delete command to use:
zod is now bundled with the CLI. Previously, if your node_modules/zod was corrupted โ€” by a partial install, a dependency conflict, or switching branches mid-install โ€” every tool got an empty inputSchema: {} and the compile printed โœ… Compiled N primitives anyway, shipping a broken agent silently.Now:
  • The bundled copy is used first and is immune to node_modules corruption.
  • A local node_modules fallback is tried second.
  • If both fail, compile aborts immediately with a clear reinstall hint before any primitive is processed.
  • Monorepo backup โ€” lua push backup now captures source files imported from cross-package paths in a monorepo and restores them to .lua/external/ on lua sync --accept.
  • Log type runtime โ€” The log source previously called mastra is now runtime. Use lua logs --type runtime (was --type mastra).
  • New log types โ€” lua logs --type rag and lua logs --type device-trigger are now valid filter values.
  • Startup warnings eliminated โ€” Extraneous warnings that appeared on every CLI command have been removed.
  • Skill sandbox stale ID โ€” Sandbox sessions that expire after 24 hours no longer show a misleading โ€œrun lua push firstโ€ message. The CLI now recovers automatically.
  • lua init template fixes โ€” Data.update and Products.search usage in the scaffolded template was corrected.

v3.12.3

Released: April 24, 2026

โœจ New Features

lua-cli now installs three binary aliases pointing to the same entry point: lua, heylua, and lua-ai. Users who have the Lua programming language interpreter installed (which also claims the lua command) can run heylua or lua-ai without any conflict or post-install workaround. Existing scripts using lua continue to work unchanged.

v3.12.2

Released: April 23, 2026

โœจ New Features

Attach images and documents to any chat message using @<path> syntax โ€” in both interactive and non-interactive mode.
Images are sent as vision inputs; documents and text files are sent as file parts. The @ token is stripped from the message text โ€” only the file is forwarded alongside any remaining text.Supported types include: .png, .jpg, .gif, .webp, .heic, .pdf, .docx, .xlsx, .ppt, .csv, .json, .html, .txt, .md, .eml, and more. Files with unsupported extensions are left in your message as plain text. Email addresses ([email protected]) are never mistaken for file paths.Maximum attachment size: 10 MB per file. Multiple attachments per message are supported.

v3.12.1

Released: April 22, 2026

๐Ÿ› Bug Fixes

Agent creation previously ended with a 30-second blind sleep. That wait has been removed โ€” lua init now completes immediately after the server responds. The agent persona is read directly from the create API response.
The chosen model is now sent in the initial create request instead of a follow-up PATCH call, eliminating a window where the agent could briefly exist without a model.

v3.12.0

Released: April 22, 2026

โœจ New Features

lua triggers is now a top-level command (alias for lua integrations webhooks) that lets you manage your integration triggers directly from the CLI.New pause and resume subcommands let you suspend or restore triggers individually or for an entire connection:
The trigger list now displays rich status icons โ€” โœ… active, โธ๏ธ paused, ๐Ÿ’ณ credit-suspended, ๐Ÿ”ด unhealthy โ€” so you can see trigger health at a glance.The connect flow is also updated: triggers are opt-in by default (none pre-selected), giving you explicit control over which events wake your agent.

๐Ÿ› Bug Fixes

Skills (defineSkill) were silently dropped from the compiled manifest because they were fed through esbuild like tools and webhooks. Skills are metadata-only (name, description, context, tool refs) and do not contain executable code. They now produce a JSON metadata artifact and flow through the full pipeline. Resolves the circular failure: lua push skill โ†’ โ€œno server ID, run lua compileโ€ โ†’ โ€œnot found in manifestโ€.
MCP server IDs are now written back to lua.skill.yaml immediately after a successful push, so the server is never treated as an orphan on its first deploy. Re-pushes are fully idempotent.

v3.11.0

Released: April 21, 2026

โœจ New Features

Added a reusable agent invocation surface callable from within a LuaSkill, LuaWebhook, LuaJob, or any other primitive:
Added support for device triggers as a first-class primitive decoupled from defineDevice, compiled and pushed like webhooks.
Added lua governance add and lua governance remove commands to configure runtime enforcement of governance policies for your agents.

๐Ÿ› Bug Fixes

Allowed underscores in device trigger names.
Push-backup refusal message now shows project hashes instead of misleading timestamps.

v3.10.0

Released: April 16, 2026

โœจ New Features

Manage the LLM model for your agent directly from the CLI.
lua models list shows all approved models grouped by provider, with the current model highlighted. lua models set writes the chosen model into your src/index.ts and syncs it to the server. lua models unset removes the model property and clears it on the server.
Full CLI for managing devices connected to your agent. Devices are a new first-class primitive type that enables your agent to send commands to physical or virtual hardware and receive trigger payloads from them.
Push device definitions with lua push device or include them in lua push all. Connect hardware or virtual devices using @lua/device-client.All actions support interactive mode โ€” omit --device-name to choose from a picker.
lua sync --accept now detects files you have modified locally since the last lua push backup and refuses to overwrite them.
After every successful lua push backup, a local cache of file hashes is stored in .lua/backup-manifest.json. The guard compares this cache against your current files and only flags files that were actually modified.If no backup has been run yet, the pull proceeds with a warning rather than failing.
New agents created via lua init now receive a structured persona template instead of "Placeholder persona". The template includes suggested sections โ€” identity, tone, audience, capabilities, boundaries, and guidelines โ€” each with guidance notes to help you write an effective persona. It is designed to be reshaped or replaced entirely.

๐Ÿ› Bug Fixes

The backup conflict detector used full 64-character SHA-256 hashes when reading files from disk, while the backup manifest stored 16-character truncated hashes (matching the compiler format). Every comparison failed, so every file appeared as a conflict regardless of whether anything had actually changed. Fixed by aligning the detector to use the same 16-char hash as the manifest.
When a backup restore failed (network error, missing manifest), the CLI printed โ€œSync completeโ€ and exited with code 0 โ€” silently masking the failure. When drift included source-bearing primitives but no backup was available, the missing count was also never incremented. Both failure paths now correctly propagate the error so CI/CD pipelines can detect a failed pull.

๐Ÿ”ง Improvements

Use lua sync --accept --force (or lua sync --force) to intentionally overwrite local changes when pulling from the server.

v3.9.3

Released: April 15, 2026

โœจ New Features

Choose your agentโ€™s LLM model during lua init.Interactive mode: a searchable, provider-grouped model list appears after org and name selection. Select a model or skip to use the server default.Non-interactive mode:
The selected model is written into your generated src/index.ts and synced to the server. Works correctly across fresh init, re-init, backup restore, and agent-switch flows.

๐Ÿ› Bug Fixes

lua sync --accept and interactive pull for MCP servers were silently no-ops โ€” servers created via the dashboard were never written to local YAML even when drift was detected. Fixed: MCP servers missing locally are now correctly added to YAML on pull.
During lua sync, the push suggestion for MCP servers was lua push mcpServer --name "X" (invalid command). Fixed to lua push mcp --name "X".

๐Ÿ”ง Improvements

The option to use an existing agent now reads โ€œUse one of your existing agentsโ€ instead of โ€œExtend one of your existing agentsโ€. The word โ€œextendโ€ implied inheritance; the actual behavior is source restore or template scaffold.

v3.9.0

Released: April 13, 2026

๐Ÿ”ง Improvements

keytar (OS keychain) has been removed. The CLI now works on headless Debian, Docker, and VMs without any native system dependencies.API key resolution order:
  1. LUA_API_KEY environment variable
  2. ~/.lua-cli/credentials file (written by lua auth configure)
  3. .env file values
Upgrading from v3.8.x or earlier: run lua auth configure once to store your key in the new location. Your previous key stored in the OS keychain is not migrated automatically.

v3.8.0

Released: April 8, 2026

โœจ New Features

Subscribe your webhooks to WhatsApp message lifecycle events dispatched when Meta sends status callbacks.
Supported events: sent, delivered, read, failed, played.
lua deploy now mirrors lua push [type] โ€” deploy any primitive interactively or directly:
New generic flags: --name (replaces --skill-name) and --set-version (replaces --skill-version). Deprecated aliases kept for backwards compatibility.

๐Ÿ› Bug Fixes

lua jobs deploy -i myJob -v latest now works correctly โ€” -i and -v are registered as short flags for --job-name and --job-version. The activate/deactivate selection list now shows the live server status badge next to each job.
Jobs, preprocessors, and postprocessors were comparing the wrong ID against activeVersionId, causing the active version to not be highlighted correctly. Fixed to use the versionโ€™s own ID in all cases.
Invalid event types passed to lua webhooks events unsubscribe now show a clear validation error instead of a misleading โ€œnot subscribedโ€ message.

v3.7.5

Released: March 30, 2026

โœจ New Features

Agents can now have individual batching configuration for message debouncing. New lua chat flags for testing:
Batching config is set per-agent with fallback to environment variables.

v3.7.4

Released: March 27, 2026

๐Ÿ› Bug Fixes

Fixed several issues that could cause API key saves to silently fail on macOS. The auth flow now correctly reports errors and clears stale data on re-authentication.
.env parsing now correctly strips inline comments: LUA_API_KEY=abc # my comment resolves to abc.

v3.7.3

Released: March 24, 2026

๐Ÿ”ง Improvements

Automatic retries with backoff for transient server failures (429, 500โ€“504). Max 3 retries, never retries client errors. Improves reliability for flaky network conditions.
Removed the lua dev web UI command and unused dependencies, significantly reducing install size.

๐Ÿ› Bug Fixes

Chat sessions now have a 5-minute timeout instead of hanging indefinitely on unresponsive connections.

v3.7.2

Released: March 24, 2026

โœจ New Features

New AI.generate API for running text generation from within tools, aligned with Vercel AI SDK generateText semantics.Simplified โ€” returns plain text:
Full options โ€” returns rich result:
Supported providers: google/* (Vertex AI), openai/*, anthropic/* โ€” with automatic fallback if the requested providerโ€™s API key is missing.Google Search grounding is automatically attached for Google models.

๐Ÿ”ง Improvements

Keytar is now loaded on demand and skipped entirely when LUA_SKIP_KEYCHAIN is set. Fixes MODULE_NOT_FOUND errors in StackBlitz WebContainers and browser sandboxes.

v3.7.0

Released: March 20, 2026

โœจ New Features

Use lua chat -t or --thread <id> for isolated sessions. lua chat clear --thread <id> clears one thread. Omit the thread ID with -t to auto-generate a UUID.
--clear / --clear-thread clear history when the session endsโ€”useful after testing without running lua chat clear separately.

๐Ÿ› Bug Fixes

Pushing after removing model from your agent now clears the server-side model (BAC-87).
Help text and examples reference current feature names (e.g. inquiry instead of deprecated tickets).

v3.6.7

Released: March 15, 2026

๐Ÿ› Bug Fixes

Connecting integrations defaults to exposing full MCP tool payloads (hide sensitive off). defer_tools is aligned with Unified.to expectations.
Compiling skills resolves tool references more reliably for imports and dependencies.

v3.6.6

Released: March 11, 2026

โœจ New Features

Added lua logs --type agent_error to filter logs for execution errors in tools, webhooks, and jobs.

๐Ÿ› Bug Fixes

Fixed timezone handling for cron schedules and improved error logging for failed cron jobs.

v3.6.5

Released: March 10, 2026

๐Ÿ”ง Improvements

Extended base URLs for internal service discovery.

v3.6.1

Released: March 2, 2026

๐Ÿ› Bug Fixes

interval and once job schedules now compile and push correctly. Previously, the seconds field (interval) and executeAt field (once) were silently dropped during compilation, causing lua push job to fail with:
All three schedule types now work as documented:
Agents that reference imported primitives using instantiation patterns in their config arrays (e.g. jobs: [new MyJob()]) now correctly resolve and compile.
The CLI now exits immediately after completing a command. Previously, a background timer kept the process alive for up to ~1s after all work was done.

v3.6.0

Released: February 27, 2026

โœจ New Features

LuaAgent now supports a model property to control which AI model your agent uses. Specify a static model string or a dynamic resolver function.
Supported providers: google/*, openai/*, anthropic/*. Default: google/gemini-2.5-flash.
lua-cli now collects usage data to help improve the developer experience. A new lua telemetry command lets you control data collection:
Or set LUA_TELEMETRY=false in your environment. See Telemetry for details.

๐Ÿ”ง Improvements

keytar (OS keychain access) is now optional. Fixes installation on CI/CD systems without native build tools. Falls back to environment variables or .env file.

v3.5.0

Released: February 20, 2026

โœจ New Features

The User.get() method now supports looking up users by email address or phone number, in addition to userId.
Useful for webhooks receiving contact info from external systems.
Connect your agent to 250+ third-party services via Unified.to. When you connect an account, an MCP server is automatically created to expose tools to your agent.
Key Features:
  • OAuth and API token authentication
  • Automatic MCP server creation and activation per connection
  • Event-driven webhook triggers that wake up your agent
  • Server-side finalization for reliable connection setup
See the Integrations Command documentation.
Event-driven triggers that wake up your agent when events occur in connected services.
  • Triggers pre-selected by default in interactive mode
  • Choose between โ€œAgent wake-upโ€ mode or custom webhook URLs
  • Friendly labels for OAuth scopes and webhook events
  • JSON output: lua integrations webhooks list --json
Back up your project source files to cloud storage and restore them on any machine.
Key Features:
  • Content-addressed storage with automatic deduplication
  • S3 direct upload โ€” no file size limits
  • Efficient incremental backups (only changed files uploaded)
  • Full project recovery on new machines
See the Skill Management documentation.
New global --ci flag makes the CLI fail loudly on missing required arguments instead of silently hanging in non-TTY environments.
See Automate releases in CI.
New lua update command for self-updating from npm, plus a background outdated version warning on every command.
  • Background version check with 24h file cache (zero latency impact)
  • Alpha users stay on alpha channel, stable users stay on latest
  • Boxed warning to stderr when outdated
New lua agents command lists all organizations and agents you have access to.
See the Utility Commands documentation.
You can now define primitive properties using variables and imports โ€” not just inline literals.
Supported for: job schedule/retry, skill tools arrays, MCP server resolver functions, and all agent config arrays.
lua push all now pushes everything including persona and backup:
Persona and backup failures are non-fatal โ€” the rest of the push completes normally.
The CLI now automatically adds dist-v2/ to your .gitignore: - New projects: Included in lua init template - Existing projects: Added after first successful compilation - Idempotent and safe to run multiple times
lua logs --type mcp # View MCP tool execution logs lua logs --type

๐Ÿ”ง Breaking Changes

These changes may affect existing scripts and workflows. Update accordingly.
Reason: Avoids confusion with the global --version flag that shows CLI version.
New flag: --verbose for detailed compilation output.

๐Ÿš€ Improvements

lua push --force now automatically checks the server for the highest existing version.Benefit: Prevents โ€œVersion already existsโ€ errors during automated deployments.
lua sync now runs compilation first for more accurate drift detection.
Chat now displays preprocessor block response text instead of showing empty responses.
All errors now go through standardized formatting for clearer, more actionable messages.

โšก Performance

  • Before: 6 sequential HTTP calls (~3-6s)
  • After: All fetched in parallel (~1s)
One HTTP call per primitive type instead of one per entity.
No upfront validation call. The first API call validates the key.

๐Ÿ› Bug Fixes

  • Job Creation: Fixed __exports naming collision when primitives called Jobs.create() at runtime
  • Backup Size Limit: S3 presigned uploads fix โ€œrequest entity too largeโ€ for large projects
  • MCP Server Duplicates: Intelligent URL merging handles concurrent MCP creation flows
  • Email Channels: Aligned with updated API schema (mode selection, displayName, response types)
  • Agent Config Arrays: Defining jobs: MY_JOBS via variables no longer silently empties the agent
  • Webhook headerSchema: Header validation schemas now correctly pushed to server
  • Tool Conditions: Tools without condition() no longer fail at runtime
  • MCP Server Push: Fixed detection, manifest metadata, and push pipeline (3 bugs)
  • Auth Errors: AuthenticationError properly propagated; 403 Forbidden handling added
  • Compile Sync: Handlers no longer overwrite each otherโ€™s IDs
  • Marketplace: Only shows skills with published and approved versions
  • Test Command: Fixed preprocessor test handling for object format

๐Ÿ“ Interface Updates

  • Added UserLookupOptions for email/phone lookup
  • Added EmailChannelMode, CreateGeneratedEmailChannelResponse, CreateExistingEmailChannelResponse
  • Added MCPServerSource enum for tracking MCP server origin
  • Updated User.get() return type to UserDataInstance | null
  • Added 'mcp' and 'mastra' to log type enums

v3.4.0

Released: January 22, 2026

โœจ New Features

The lua compile command now uses a safer, non-destructive sync pattern. Instead of automatically deleting primitives on the server that arenโ€™t in your local code, it now:
  • Warns about orphaned primitives (skills, webhooks, jobs, MCP servers)
  • Suggests using explicit delete commands
  • Filters warnings to CLI-sourced skills only (ignores marketplace and manual skills)
This prevents accidental data loss and gives you full control over what gets removed.
Explicit delete commands for managing primitives no longer in your local code:
# Delete a skill from the server lua skills delete --skill-name
Supported transports:
  • 'streamable-http' - Modern MCP standard (recommended)
  • 'sse' - Legacy Server-Sent Events transport
stdio transport removed: local MCP servers using stdio transport are not supported yet. Use remote servers with streamable-http or sse instead.

๐Ÿ› Bug Fixes

  • Push: Fixed crash when pushing primitives without a version field. First push now defaults to version 0.0.1 and shows โ€œ(none - first push)โ€ in prompts.

๐Ÿ“ Interface Updates

  • Added MCPStreamableHttpServerConfig interface for streamable-http transport
  • Updated MCPTransport type to 'sse' | 'streamable-http'
  • Added SkillSource type: 'cli' | 'marketplace' | 'manual'
  • Removed MCPStdioServerConfig (stdio not supported yet)

v3.3.0

Released: January 20, 2026

โœจ New Features

All CLI commands now support full non-interactive operation, enabling seamless automation for AI IDEs, CI/CD pipelines, and shell scripting.Design Patterns:
  • Consistent option naming: --<entity>-name and --<entity>-version
  • --force flag for skipping confirmation prompts
  • --json flag for machine-readable output
  • Action arguments for entity management (view, versions, deploy, activate, deactivate)
See Automate releases in CI for complete documentation.
The logs command now supports filtering by user messages and agent responses:
lua logs --type user_message --limit 20 lua logs --type agent_response
Also supported for SSE transport with url and headers resolver functions.
Added filter support to Products.get() with backward compatibility:

๐Ÿ› Bug Fixes

  • Chat: Allow lua chat to work without mandatory skills - agents can now have only webhooks, jobs, or processors
  • Commands: Normalized action handling for case-insensitive action comparisons

๐Ÿ”ง Improvements

  • Chat Command: Informational message when defaulting to sandbox environment
  • Chat Command: Improved visual separation between compile logs and chat response
  • Env Command: Proper error handling for save/delete operations
  • Push Command: Shared helper functions reduce code duplication

v3.2.0

Released: January 13, 2026

โœจ New Features

New lua sync command to detect drift between server and local code:
Features:
  • Compare agent name and persona between server state and local code
  • Fetch latest published persona version (excludes drafts and rollbacks)
  • Show colored line-by-line diff for easy comparison
  • Interactive resolution: update local from server or continue with local
  • Integrated into compile flow with --no-sync and --force-sync flags
New lua chat clear command to clear conversation history:
Accepts userId, email, or mobile number as the identifier.
New Lua namespace for runtime access:
Channel is typed as a union type: 'dev' | 'webchat' | 'whatsapp' | 'messenger' | 'voice' | 'api' | 'email'
Access raw webhook payloads in tool execute functions:
Browse marketplace skills now supports pagination:
  • Navigate through pages with Previous/Next options
  • Shows page info (Page X/Y) and total count
  • Configurable page size (default: 10 items)

๐Ÿ”ง Improvements

  • Simplified Agent Creation: Streamlined lua init flow with cleaner prompts
  • Better TypeScript Support: Improved handling of path aliases and variable references in your code

๐Ÿ› Bug Fixes

  • Fixed sync command occasionally showing false drift detection
  • Fixed skill publishing issues
  • Fixed compilation when skills are defined inline vs imported from separate files

v3.1.0

Released: December 7, 2025

โœจ New Features

New LuaMCPServer class for integrating Model Context Protocol servers with your agent:
CLI commands:
  • lua mcp - List, activate, deactivate, or delete MCP servers
  • lua push mcp - Push individual MCP servers
  • MCP servers included in lua push all --force
Tools can now have a condition function that determines if the tool is available:
Use conditions to dynamically enable/disable tools based on user subscription, verification status, feature flags, or region.
New CDN namespace for uploading and retrieving files:
New methods for job management:
New lua evals command opens the Evaluations Dashboard with your agent pre-configured. bash lua evals
New Templates namespace for WhatsApp template messaging:
New lua marketplace command for discovering, installing, and publishing skills: For Creators: - Publish skills to the global marketplace - Version management with semantic versioning - Environment variable configuration per version For Installers: - Browse and search for verified skills - Smart installation with dependency checks - Interactive environment variable configuration
The lua logs command now features: - Interactive filtering by primitive type (Skills, Jobs, Webhooks, etc.) - Live data from API including dynamically created jobs - Context-aware log display with detailed metadata
New user._luaProfile property for read-only core user data:

๐Ÿ’ฅ Breaking Changes

These changes may require updates to your existing code.
JobInstance now receives the full Job entity with activeVersion:
The welcomeMessage field has been removed from LuaAgent configuration: - For voice: use voiceConfig.welcomeMessage - For chat widgets: use WebchatChannelConfig.welcomeMessage
Webhook execute functions now receive a single event object:
PreProcessorResult now uses a discriminated union:
  • modifiedMessage is now ChatMessage[] (array)
  • Added priority field for execution order
  • Removed context field

๐Ÿ”ง Improvements

  • Data API Type Safety: searchText parameter added to Data.create() and Data.update(), data parameter type changed to Record<string, any>
  • PostProcessor Simplified: Return type now requires modifiedResponse: string, removed async field
  • Compilation: Handle .js extensions for Node16/NodeNext module resolution
  • Template: Minimal by default, use --with-examples flag for examples
  • Web UI: React Query, Sonner toasts, improved env panels, docs in toolbar

๐Ÿ› ๏ธ Refactoring

  • Removed context field from webhooks, jobs, and postprocessors
  • Removed version field from LuaSkill, LuaJob, and processor configurations
  • Improved push command: displays both webhookId and webhook-name URL formats
  • Rewritten interfaces/jobs.ts to match lua-api DTOs exactly

v3.0.3

Released: October 30, 2025

๐ŸŽฏ User API Enhancement

Enhanced User.get() method now accepts an optional userId parameter:
Use Cases:
  • Fetch data for specific users in admin tools
  • Access user information in webhooks/jobs
  • Multi-user data operations
  • User management features
Tools, webhooks, and jobs can now access any end userโ€™s data, which enables multi-user scenarios.

v3.0.2

Released: October 30, 2025

๐Ÿš€ Major Improvements to Compilation System

This release brings comprehensive dependency bundling, debug mode, enhanced validation, and critical bug fixes.
All components now properly bundle external dependencies:
  • โœ… LuaWebhooks bundle dependencies (e.g., Stripe, axios)
  • โœ… LuaJobs bundle dependencies
  • โœ… PreProcessors bundle dependencies (e.g., lodash)
  • โœ… PostProcessors bundle dependencies (e.g., date-fns)
  • โœ… Nested Jobs (Jobs.create()) independently bundle their own dependencies
Impact: All compiled components are now truly portable and self-contained, requiring no dependency installation on deployment targets.
Added --debug flag to lua compile command: bash lua compile --debug # or LUA_DEBUG=true lua compile Features: - Verbose step-by-step logging
  • Shows detected imports and dependencies - Displays bundle sizes (uncompressed and compressed) - Preserves temp files for inspection - Shows timing information for each component - Full error stack traces
  • tsconfig.json validation - Clear error if missing or invalid - Empty bundle detection - Warns about suspiciously small bundles (under 100 bytes)
  • Bundle output validation - Ensures esbuild creates valid output - Null config handling - Graceful compilation without lua.skill.yaml - Safe optional chaining - Fixed crash when agentData is null
Context-aware error messages with actionable hints: - Dependency resolution failures โ†’ โ€œRun npm installโ€ - TypeScript syntax errors โ†’ โ€œCheck syntax in filename.tsโ€ - Missing files โ†’ Shows expected path - Full stack traces in debug mode
Enhanced resolveImportPath() to support:
  • .ts, .tsx, .js, .jsx files
  • Directory imports (index.ts, index.tsx, index.js)
Critical Fix: Relative imports now work correctly in Jobs, Webhooks, and Processors:

๐Ÿ› Bug Fixes

  • Fixed null reference error when compiling without LuaAgent
  • Fixed crash when lua.skill.yaml is missing
  • Fixed compilation with empty agent name/persona
  • Critical: Fixed relative import resolution in all component types

๐Ÿงน Code Quality

  • Removed obsolete dynamic-job-bundler.ts
  • Extracted common helpers (extractRelevantImports, bundleAndCompressExecuteFunction)
  • Reduced bundling.ts from 1,149 to 1,036 lines (9.8% reduction)
  • Added 27 comprehensive tests for bundling, execution, validation, and relative imports

v3.0.0

Released: October 2025

๐ŸŽ‰ Major Release

Version 3.0.0 focuses on developer experience, deployment automation, and real-time chat capabilities.

โœจ New Features

The flagship feature: a single, intuitive way to configure your entire agent.Before (v2.x):
After (v3.0.0):
Benefits:
  • Single source of truth
  • Clearer organization
  • Automatic YAML synchronization
  • Better IDE support
Real-time chat responses with improved UX: bash lua chat - โœ… Animated typing indicator while waiting - โœ… Text streams character-by-character - โœ… Sandbox and production environment selection - โœ… Uses /chat/stream endpoint for real-time updates
New command for deploying all components without prompts: bash # Push all with auto-versioning lua push all --force # Push and deploy to production lua push all --force --auto-deploy What it does: 1. Compiles project 2. Reads all components from lua.skill.yaml 3. Increments patch versions automatically 4. Pushes all components to server 5. Deploys to production (if --auto-deploy) Features: - Auto-bumps patch versions (e.g., 1.0.0 โ†’ 1.0.1) - Perfect for CI/CD pipelines - Retry mechanism with exponential backoff
Flexible authentication with multiple sources (priority order): 1. System Keychain (macOS Keychain, Windows Credential Vault, Linux libsecret) 2. Environment Variable (LUA_API_KEY) 3. .env File (LUA_API_KEY=...) Usage in CI/CD: bash export LUA_API_KEY=your-key lua push all --force --auto-deploy
Bidirectional synchronization ensures consistency:On lua init:
  • Agent name, persona โ†’ YAML + index.ts LuaAgent
On lua compile:
  • LuaAgent persona โ†’ YAML
No manual synchronization needed!

๐Ÿ”ง Improvements

  • Excluded lua-cli internals from bundles
  • Reduced bundle sizes by 50-70%
  • Fixed relative import issues
  • Proper sandbox globals (tools use sandbox-provided APIs)
  • code field now properly compressed and included - Execute function properly converts to strings - Excludes lua-cli imports from job execute functions - Better metadata support for passing data
  • Comprehensive template with 30+ example tools
  • Quick Start Guide for new users
  • TypeScript examples with best practices
  • CI/CD integration examples

๐Ÿ› Bug Fixes

Bundling:
  • Fixed Cannot find module '../services/ApiService' in pre-bundled tools
  • Fixed process.cwd is not a function in sandbox execution
  • Fixed lua-cli API code being bundled into tools
Push & Deploy:
  • Fixed webhooks and jobs not found during push all
  • Fixed missing tools array causing validation errors
  • Fixed deployment timing issues with retry mechanism
Chat:
  • Fixed welcome message reading from lua.skill.yaml
  • Fixed streaming endpoint integration
  • Fixed typing indicator cleanup on errors

๐Ÿ’ฅ Breaking Changes

These changes require updates to your existing code.
Old Way:
New Way:
Migration:
  1. Wrap your existing skills in a LuaAgent
  2. Add name and persona fields
  3. Run lua compile to sync with YAML
  • Old: /chat/generate/:agentId - New: /chat/stream/:agentId No action needed - handled automatically by CLI.
Jobs must use metadata for data passing:

๐Ÿ“Š Statistics


Upgrade Guides

From v3.0.x to v3.1.0

Required changes:
  1. Update JobInstance access patterns (use activeVersion.schedule, id instead of jobId)
  2. Update webhook execute functions to use event object
  3. Update PreProcessor responses to use discriminated union
  4. Remove welcomeMessage from LuaAgent (configure on channel/voice instead)

From v2.x to v3.0.0

Required changes:
  1. Wrap skills in a LuaAgent configuration
  2. Update jobs to use metadata for data passing
  3. Run lua compile to sync with YAML