- The file name and the hosted name - two names, one reference
- Descriptions and parameters are inherited - what the platform supplies
- What the block keeps - every key you write
- Attaching one - the same two lists
- Deploying to SLNG needs no mirror - what deploy checks
- What each target does with it - slng, livekit, pipecat
- Advanced - the legacy form and the mirrors
- Troubleshooting - the refusals, and their fixes
tools/check_order.yaml
slng: check_order is the whole reference. The scalar is the hosted tool’s
exact name, as your organisation holds it. No description, no schema, no
hash, no mirror, and no unmute pull are required to validate, compile, or
deploy this to SLNG.
Hosted reference fields
string or legacy object
required
The exact published tool name. No hosted name is inferred from a scalar reference. The
legacy object accepts
hash and resolves the hosted name from the tool file name; see
the legacy form below. A tool file must have exactly one execution block.The file name and the hosted name are two different things
The tool file’s name is still the package reference: it is what an agent’stools: list attaches, and what every local diagnostic calls the
tool. The slng: scalar is the name deployment resolves against your
organisation. They usually agree, and they do not have to:
tools/order_status.yaml
order_status; deployment resolves check_order. This
creates no tool and renames nothing in SLNG, it only lets your package call
the reference whatever reads best in the prompt and the tool list. Say the
hosted callable name out loud in a prompt that explicitly names the platform
function; the file name is a package reference, not a remote rename.
Descriptions and parameters are inherited
A hosted tool owns its own description and schema. Omitdescription: and
the attachment uses whatever the published tool says; write one and it
overrides the platform’s for this attachment only. Delete the field later
and inheritance comes back:
tools/check_order.yaml
unmute deploy completes rather than one unmute compile does.
input and output still have nowhere to go
A hosted tool owns its own schema, so these fields are unwritable rather than
silently ignored:
What the block keeps
These describe how your agent uses the tool, not what the tool is, so they stay yours to write, exactly as before:string
Required for local, webhook, and knowledge tools: explains when the model should call
the tool. Builtins use their registry description if omitted; hosted
slng tools
inherit their published description. Refused on MCP sources.list of one-key pairs
Hidden argument/value pairs. Values are scalars or strings with
{{variable}}
placeholders. Omit to inject nothing. Legal on local, webhook, and hosted slng tools;
builtin send_sms requires its literal from_number setting.string
Accepts
provider_default, continue, or cancel. Omitted means provider_default.
Refused on MCP sources. Target support is listed above.string
Accepts
returns_data or ends_conversation. Omitted means returns_data, except
builtins whose effect comes from the registry. Refused on MCP and knowledge tools.string
A fixed spoken sentence with no
{{variables}}. Omit for no announcement. Legal on
local, webhook, knowledge, and hosted slng tools.tools/search_places_text.yaml
hotel-concierge example. Declare
customer_name under variables: with type: str and source: call_start,
and supply it when starting a web session. For inbound phone calls, give it a
valid default or leave query to the model: carriers supply no session inputs.
Injected values keep their exact type, including false and 0, and are
never shown to the model. {{customer_name}} is resolved when a call starts;
a fixed value such as - limit: 0 is bound as written. Full rules for the
last three fields are on
the tools overview.
A variable does not have to land on a text parameter. SLNG stores the value as
text and converts it into the parameter’s declared type, so - limit: "{{how_many}}" works on an integer parameter as long as the stored value reads
as a number. That conversion happens when the call starts, so unmute deploy
checks the parameter and reports the value as deferred rather than claiming to
have validated it.
Attaching one
No different from any other tool. The same two lists:agent.yaml
Deploying to SLNG needs no mirror
- that a tool called
check_orderexists there, and which published version is the latest; - that every
injectargument is a parameter of that published version, and that a fixed value is the type it declares; - which Vault entries the published version needs.
build/slng/compile-report.json names these under deferred_checks, against
the references they apply to. A clean compile therefore reads as “nothing wrong
yet” rather than “everything is fine.” unmute deploy completes all three
before it changes the agent, and --dry-run reports them without changing
anything. See Deploy to SLNG for what that run checks and what
it prints.
What each target does with it
SLNG needs nothing from your package beyond the name. LiveKit and Pipecat are
different: they build and run the tool themselves, so they need a real copy
of its definition, and for a
code tool its module, sitting in your package.
That copy is what unmute pull fetches:
slng: check_order is the same on
all three targets, and nothing about the tool file changes between them.
What does not travel is one package selecting slng and a code target at
once. A code target needs a turn: binding to place its end-of-turn detector,
the slng target refuses one because SLNG runs its own turn taking, and a
per-target models: override cannot add a binding the agent does not define.
So the code-target build is a second package with the same tool files in it:
tools/<name>.slng.meta.json beside the
mirror, never into your tool file. Commit everything pull writes. A package
that targets slng alone never runs this command at all.
Two things pull may still write into files you wrote, so you can read the
diff knowing what to expect:
- A tool file written the older
slng:block form has itshash:restamped in place, because that form keeps its pin in the tool file. A scalar reference never gets touched. secrets:inagent.yamlgains any credential name a mirrored tool needs and your package does not declare yet, and only when a target builds the tool itself. LiveKit and Pipecat read that list to get the credential into the generated project. A package targeting slng alone gets nothing added: the platform reads the credential from its own vault, andunmute deployreports the same requirement from the published contract.
Reference-only
Unmute creates no tool on SLNG. A hosted reference only ever points at a tool your organisation already has; a name it does not hold is a refusal, not something a deploy creates for you. That is also whylocal: and
webhook: are refused on an slng target: SLNG owns a
tool’s code, version and gate pipeline, so there is nowhere in a package for
either block to write to. Both still work exactly as before on livekit and
pipecat. A brand new tool starts in the SLNG dashboard; slng: is how your
package reaches it once it exists there.
Which block for which job
The last two are the two that no longer reach slng.
Advanced
Neither of these comes up on a package that targets slng and writes the scalar reference.The legacy form still loads
tools/check_order.yaml
The mirrors are not yours to edit
tools/<name>.slng.json and, for a code tool, tools/<name>.slng.py are the
platform’s copy, not yours, whichever form the reference is written in. The
.slng. infix marks a file as mirrored rather than authored, so one glance
answers whether you may edit it. The answer is no.
Every compile of a code target checks the mirror against its pin, with no
network at all, so a hand edit is caught the next time anybody builds the
package:
unmute pull itself refuses the same way, before it overwrites anything,
naming every changed file at once. --force discards the edits; the honest
fix is to change the tool on the platform and pull again.
Troubleshooting
The build refuses a bare slng: block
Leave the block bare and validation refuses before anything else runs:
unmute deploy refuses an injected value
There are three cases, each naming the tool file and the published version:
- The parameter is not declared. An attachment pins a declared parameter. A schema that allows extra properties lets the model send more; it does not give an override anywhere to bind.
- The parameter’s type is not settled. A parameter that could be a string or an integer, or that declares no type, or that holds an object or a list, cannot take a pinned value. Leave it to the model.
- A constraint sits at the root of the schema. A published schema may
constrain a parameter from its root, under
allOfand friends. Checking one supplied value against a rule written for the whole call would reject the arguments the model fills in, so the value is not reported as checked.
inject: and let the model fill the parameter in,
or pin a different parameter whose published type is settled.
Two tool files resolve to one hosted tool
Two tool files resolving to the same hosted tool are refused, naming both. SLNG attaches a hosted tool once, so the two cannot carry separate descriptions orinject: values, and a push would silently keep one file’s
settings and drop the other’s.
Fix: keep one file per hosted tool, and attach that one file wherever both
were attached.
A code target says no mirror is committed
Skip the pull for a code target and the refusal names the fix, and the shortcut:unmute pull and commit everything it writes, or compile the
package to slng instead.
A hosted tool with Python dependencies is refused on livekit and pipecat
One limit, and it is the honest cost of “runs everywhere.” A hosted tool that declares Python dependencies compiles to slng, which installs a per-tool environment. It is refused on livekit and pipecat, in the same words an authoredlocal: tool’s dependencies: already gets there:
build/<target>/pyproject.toml after compiling, or
compile that tool’s package to slng.
Where to go next
Deploy to SLNG
What a real deploy resolves, checks and reports.
unmute pull
Optional, and only for livekit or pipecat: the command, its flags, and every refusal.
MCP servers
The other hosted-by-name block: a server SLNG already has.
Prebuilt tools
The other reference-only block: a capability SLNG curates.