Semantic actions
Semantic actions
Section intitulée « Semantic actions »An action is a business operation with an id, a description and a JSON Schema for its parameters. One declaration serves the AI (function calling), macros, ui.json commands and buttons, and undo:
-
An id is
namespace/name, both identifiers (no hyphen: usemy_plugin/run); any other id, or a schema with an unknown JSON Schema keyword, is refused at registration. The contract comes from@scfloworg/actions. -
execute(id, params, context?)validates the parameters against their JSON Schema (draft 2020-12,defaultapplied — an error with the codeinvalid_parameterslists every problem), resolves the context (viewId= active tab,selection= current selection,docId= its document; overrides win) and runsrun(params, context)in one transaction: one undo step per document, everything rolled back ifrunthrows. Called from inside a running handler, it joins that transaction. -
kind: "read"actions run outside any transaction and need no approval. -
executeBatch([{ action, params, as? }])runs a macro in one transaction (one undo step); results come back in order, as plain JSON. A step can use an earlier step’s result:as: "taskA"names it, and"${taskA}"or"${taskA.id}"(any path) is replaced by that value before the step is validated, keeping its type when the placeholder is the whole string. A reference to an unknown or later name, or any refused step, stops the batch and rolls every step back. Actions should therefore return what later steps need (e.g.{ id }).await ctx.actions.executeBatch([{ action: "notes_tsx/create_task", params: { name: "A" }, as: "a" },{ action: "notes_tsx/create_task", params: { name: "B" }, as: "b" },{ action: "notes_tsx/link_tasks", params: { source: "${a.id}", target: "${b.id}" } },]); -
getAiTools()returns{ name, description, parameters, kind, actionId },namebeing the id with/ . : -replaced by_.subscribe()follows additions and removals; a disabled plugin’s actions disappear. -
runwrites state like a handler does (it runs inside the kernel’sactions/executehandler).
Declare them in ui.json and bind run in code:
"actions": [{ "id": "scflow/create_actor", "title": "action.createActor", "description": "Create an actor in the active model.", "parameters": { "type": "object", "properties": { "name": { "type": "string" }, "domain": { "type": "string", "enum": ["physical", "logical"] } }, "required": ["name"], "additionalProperties": false }, "run": "createActor"}],"commands": [{ "id": "new_actor", "title": "command.newActor", "event": "action:scflow/create_actor", "payload": { "domain": "physical" }}]ctx.ui.define({ actions: { createActor: (params, context) => { /* ... */ } } });// or in code: ctx.actions.register({ id, title, description, parameters, run })Anywhere an event name is accepted — commands, palette selectEvent /
addEvent, dropEvent, table createEvent, graph createEdgeEvent, map
createEvent/moveEvent, form events… — you may write "action:<id>"; no
handler is needed. A command’s payload gives its default parameters
("event": "action:<id>", "payload": { … }). When a required parameter is
still missing (or the payload has "$prompt": true) the shell opens a
dialog built from the schema. Notes shows the pattern: its task table
createEvent, its graph createEdgeEvent and its “New task…” command all
target actions.
Payload unpacking. For each property the action’s schema declares, the first defined value wins, in this order:
payload[key]— flat payloads (map clicks{ lat, lng }, graph links{ source, target }, commands);payload.values[key]— value dictionaries (table creation row, forms);payload.data[key]— item data (palette cards, transfers).
Undeclared keys are dropped, nothing is renamed or guessed. A docId in the
payload or its context becomes the context’s docId. When a gesture’s
payload does not fit an action (cell commits { rowId, key, value }, batch
deletes { rowIds }, cascades…), keep a handler and call
ctx.actions.execute from it.
Every run, whatever its channel (command, AI tool, macro, code), is reported
on the bus as actions/ran { actionId, kind, params, status, error?, durationMs, batch? } (informational, never opens a transaction); refused
runs (unknown id, invalid parameters) are reported too. The event inspector
shows them with a badge. actions/execute carries { label, actionId, params }
(or steps for a macro).
AI assistants opt in with useActions: true (or a list of action ids) in
their options or ui.json entry; action kinds wait for the user’s approval.
They also receive an actions_batch tool { summary, steps: [{ action, params, as? }] }
limited to their allowed actions: the model proposes several chained changes
at once, the user sees one card (the model’s summary in the user’s language,
then each step as its action title and name: value chips, references shown
as → taskA.id) and approves them all with one click, undone in one step.
Action titles are shown as written, so they need no translation keys.
Action IDs and function names are distinct: scflow/create_item is an action
ID; scflow_create_item is its provider-safe function name. Individual calls
use the function name; actions_batch.steps[].action uses the exact ID from
its schema enum. Tool descriptions include this mapping. The AI adapter also
accepts an unambiguous offered function name in a batch (or an offered action
ID in a single call), without guessing by replacing underscores. It resolves
all batch names before running any step, rejects unknown or ambiguous names,
and never expands the assistant’s useActions allowlist. Approval remains
mandatory for mutations. Read actions retain their immediate execution behavior.
Documentation sous licence CC BY 4.0 · © les contributeurs de SCFlow ·Retour à supplychainflow.org