Skip to main content
Lifecycle hooks are the onInstall and onUninstall sections of the template manifest: what the installed agent does right after an install and right before an uninstall. sharedData names the organization’s shared collections the installed agent joins. installPolicy limits how many installs one organization may hold. All four are authored by hand; lua marketplace template draft never composes them, and keeps a sharedData section already in the file. Verified against lua-cli 3.33.0.

Shape

Both hooks take the same object with at least one field; any other field, or a section that is not an object, is refused. {{var}} tokens are refused in a hook; nothing is substituted.
string
Bare name of a tool in this version’s frozen skills, executed directly with input {} and no model involved. A name that does not resolve against the frozen skills is refused at publish.
string
Plain text, 1 to 2000 characters after trimming, sent as one background agent turn. Served on the manifest so the installer reads it before consenting.
{ workflow: string, input?: object }
onInstall only. Starts one run of a workflow frozen in this version, as the installer. input is at most 8 KB and is validated against the workflow’s input schema.
lua.skill.yaml
The recommended split: the tool does the work and the instruction is the conversation. Make the tool idempotent; a retry runs it again.

onInstall timing

After a successful install or apply, on the first install and on every apply whose tool name or instruction differs from the last one that ran.
  • The tool runs first, as the installer, with a 180-second budget. The instruction then fires as one background turn on channel template-install, prefixed [Install: <displayName>] ; when the tool failed, the turn is told with [Setup step "<tool>" failed: <error>].
  • The install never waits on either half and never fails because of one. The response returns as soon as both are claimed; failures are recorded on the install.
  • Idempotent per install: the tool is keyed by name and the turn by a hash of the instruction, so a re-run of the same version, a retry, or an update with an unchanged hook does not run it again. A changed tool name or instruction runs once more.
  • A fleet apply or platform provisioning has no installer, so the hook runs in system scope. A one-call deploy defers both halves until the deployer’s access to the deployed agent is registered; the install response then says onInstallDeferred: true.

onUninstall timing

At the start of an uninstall, before any primitive, env key, subscription, or connection binding is removed, so the tool still has the skills, LUA_TRIGGER_URL__* values, and connections it needs.
  • The tool is awaited: teardown waits for it, within its budget.
  • The instruction fires on channel template-uninstall, prefixed [Uninstall: <displayName>] , and is awaited for at most 30 seconds; teardown then proceeds regardless. Cleanup that must happen belongs in tool.
  • Both run as the removing user. With no acting user (an internal removal), the tool is skipped and the turn runs in system scope.
  • The outcome { ran, ok?, error? } is recorded on the install and returned as onUninstall on the uninstall response. Cleanup never fails the uninstall; the CLI prints ✅ Template uninstalled from this agent. either way.

Install result fields

lua marketplace template install --json prints these beside the fields on the agent templates API.
boolean
true when this install claimed and fired the instruction turn; absent otherwise.
{ toolRan: boolean, ok?: boolean }
toolRan is true when the tool was claimed. It runs in the background; read the result back as setup.
boolean
true on a one-call deploy: the hooks fire after the deployer grant, so the outcomes come from that later call.
string
The run startWorkflow started, or the run an earlier same-version install already started.
{ name, access, outcome, reason? }[]
One entry per declared shared collection: joined, unchanged (the agent was already on the list), or needs-approval with a reason. Absent on a one-call deploy, where the join runs with the deferred hooks.
{ code, message }
Why startWorkflow started no run: workflow-not-materialized, control-plane-unavailable, control-plane-dark, start-refused, or start-failed. The install itself succeeded.

Setup status and retry

GET /agents/:agentId/templates reports setup for each installed template that declares a tool, startWorkflow or sharedData: { status: 'running' | 'ok' | 'failed' | 'needs-approval', error?, code?, workflowRunId?, sharedData? }. failed wins over needs-approval, which wins over running, which wins over ok. needs-approval means a declared shared collection was not joined; sharedData lists each one with its outcome. A tool marker with no result after about 220 seconds reports Setup step did not finish; a deferred hook whose fire never arrived reports Setup step never started. POST /agents/:agentId/templates/:templateId/setup/retry re-runs the tool when its last run failed or died, re-attempts startWorkflow, joins any shared collection still at needs-approval (as the person retrying), and never re-fires the instruction turn. Its answer, whose startWorkflow.outcome is started, already-started, failed, or not-declared, is on the agent templates API. No CLI action calls either route.

Shared data

sharedData lists the organization’s shared collections an installed copy of the template needs, so its code can call Data.shared(name) with no API key. Available in lua-cli 3.52.0 and later.
string
required
The shared collection’s name: 1 to 128 of A-Z a-z 0-9 _ . -. Each name once.
'read' | 'write'
required
read joins the collection’s readers, write joins its writers.
lua.skill.yaml
Up to 20 entries. A template never creates a collection or decides who may join it. An org admin creates the collection and lists the template in its installableBy, up to a maxAccess.
  • Install. Right after a successful install, the installed agent joins each declared collection the organization allows it into. On a one-call deploy this happens with the deferred onInstall hooks. It never fails or blocks the install.
  • Needs approval. An entry that cannot join is reported as needs-approval with a reason: collection-missing, not-installable (the template is not in installableBy), access-exceeds-max, writer-needs-assign-role (write needs the person installing to hold members:assign-role), acl-full, agent-not-live, unavailable (try again), or not-attempted. Once an admin fixes it, setup/retry joins it.
  • Uninstall. After the agent’s managed pieces are removed, it leaves only the collections this install joined. A place an admin gave the agent by hand, or one another installed template on the same agent still needs, stays.
  • Updates. Adding, removing or widening an entry is a change installers consent to again (Update consent). A fleet apply or auto-update does not join or leave collections; after the installer consents to the new version, setup/retry joins what it adds.
  • Testing. Joining needs a deployed agent in an organization. lua test and other local runs cannot reach shared collections at all.
On the publish API an omitted sharedData inherits the previous version’s and null or [] clears it.

installPolicy

'single' | 'multiple'
required
single allows one installed row of the template per organization. multiple, the behavior when the section is absent, sets no limit.
lua.skill.yaml
With single, a second install or deploy into an organization that already holds an installed row is refused with HTTP 409 SINGLE_INSTALL_PER_WORKSPACE before anything is created; the response names the agent that holds it. The holding agent may re-install or upgrade; an apply_failed row holds no slot; an uninstall frees it; an agent outside any organization is exempt. Installers see This template can only be installed once per workspace, and this workspace already has it. Use the agent that has it, or uninstall it there first. draft drops the section, so re-add it after every draft. On the publish API an omitted installPolicy inherits the previous version’s and null clears it; from the CLI, removing the section clears it and the consequence prompt prints installPolicy: CLEARING the section — new installs are no longer limited to one per workspace.

Lints

See also