Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

notices

Post an event record that surfaces to the callers it is addressed to, on the response of the tool call that posted it, or on each one's next tool call.

A notice reaches a caller without the caller knowing to look for it: each caller in the notice's audience is shown it once, attached to the response of the tool call that was running when it was posted, whether that call succeeded or failed. A notice posted while no call was running is attached to that caller's next tool call. Reading it marks it read for that caller alone; the other callers it is addressed to still receive it.

Content-posted notices are marked as coming from world content, and their values render as data rather than as instructions.

notices.post("wave complete", { wave = 3 })
notices.post("save slot corrupted, using defaults", { slot = id }, { severity = "warn" })

Notices with the same template collapse into one line with a count, so a loop that posts the same template many times renders once with the varying values summarized:

for _, path in ipairs(rebuilt) do
    notices.post("texture atlas rebuilt", { path = path })
end
-- surfaces as: texture atlas rebuilt (x950) { path="a.png", "b.png", ... } #5e0c2a91

A "warn" or "error" notice posted again with the same template and the same values after its reader was shown it is a repeat. A repeat is held for a while after that delivery and collects every further repeat, then reaches the reader as one line carrying this delivery's count and the total, and each delivery of a repeat doubles the next hold. A notice with other values, or another template, is delivered at once beside it, so an error raised over and over never buries a new one:

-- surfaces as: enemy spawn failed (x14 again, 15 in all) { wave="3" } #0b7d41e6

Keep the template a fixed sentence: it is the identity used for collapsing. Put every varying value in params. opts.severity is "info" (default), "warn", or "error"; opts.includeLocation attaches the emitting call site. Each line a reader is shown ends with the notice's handle (#5e0c2a91 above). An agent that has acknowledged a repeating notice mutes it by that handle through the notices toolbox (tools.use("notices", "suppress", { key = "5e0c2a91" }), or zero notices suppress 5e0c2a91 in bash); the mute is its own, and every other caller keeps receiving the key. On an engine bound to a live world the mute is kept in the signed-in user's store of that world, so it holds on the next engine that opens the world too. It lasts until the agent unmutes the key, or until the poster withdraws the notice because the condition it stated is over. A poster that refreshes a standing notice with new values withdraws the old one with notices.withdraw(template, { replacing = true }) and posts again: the callers that muted it stay muted.

opts.audience says who reads the notice. Left out, it is the caller whose code is running — the agent whose call, task or callback posted it — or every caller when no caller is running (a notice the engine raises on its own). "*" addresses every caller; a list addresses those callers only, each entry an agent's name (as the agent toolbox's roster lists it) or a caller key <kind>:<id> such as agent:<name>. A notice reaches the agents on this engine: an entry naming no agent on this world's roster, an entry naming an agent the roster lists on another engine of the world, or an empty list, raises an error.

notices.post("boss defeated", nil, { audience = "*" })

opts.actionable says whether the callers a notice is addressed to are expected to act on it, as opposed to taking it in as background. Left out, a notice addressed to particular callers — the default audience included, so the probe, task or callback an agent set up reaches that agent — is actionable, and a notice for every caller is background.

What a receiver does with that is its own decision. An agent working in the editor's agent panel is handed its notices as they arrive rather than on its next tool call: an actionable one starts a turn of its own when the agent is idle, and a background one joins the agent's context on its next prompt or tool call.

notices.post("build ready for review", nil, { audience = { "terrain" }, actionable = true })
notices.post("lighting rebaked", nil, { audience = "*" })  -- background

Ephemeral notices

opts.ephemeral = true posts a reminder rather than a stored notice. An agent whose harness has an ephemeral channel is shown it for a short window and it is never stored in the agent's history; an agent with no ephemeral channel gets it as a normal notice on its tool results.

notices.post("the build is not published", { uncommitted = 4 }, { ephemeral = true, audience = "*" })

An ephemeral notice is identified by its template and the code that posted it: posting the same template again from the same code replaces the entry. Keep the template a fixed sentence and put every varying value in params.

  • sticky = true keeps the entry visible on every read until it is replaced, withdrawn or expires. It expires 120 seconds after its last post unless it is posted again; identical text refreshes it. ttl raises or lowers that lifetime, at most 600 seconds.
  • renew = true shows an entry again that is posted with identical text.
  • audience is "*", or a list of agent names or caller keys; left out it is the caller whose code is running. An empty list raises, as for a stored notice, and a post the store refuses raises with the store's reason. The audience is not checked against the roster: a list entry naming no agent on it is kept for a key nobody reads. A bare agent name in a list is read as agent:<name>; a single string audience is used as given.
  • message, severity, includeLocation and actionable have no effect on an ephemeral post, and sticky, ttl and renew none on a stored one.
  • notices.withdraw(template) removes the ephemeral notices the calling chunk posted under the template, along with its stored ones. A failure to reach the ephemeral store is logged, and the stored notices are withdrawn all the same.

The notices toolbox's list tool shows the current entries as ephemeral.