Server integration
Introduction
Section titled “Introduction”@pilotoo/server mounts the workflow runtime. WorkflowService builds one runtime on the checkpointer port your agents use, with coreNodeKinds plus your kinds, and stores workflows, runs and run events through the workflowStorage port. This page covers the module options, the routes, the visual builder and the agent tools. See Assembling a server for the module itself.
Enabling workflows
Section titled “Enabling workflows”Set the roles that may use the feature, then pass the options you need to the module.
WORKFLOW_ROLES=member,ownerimport { createQuickJsSandbox } from '@pilotoo/workflow/quickjs';
PilotooModule.forRoot({ workflowKinds: [myKind], // added to coreNodeKinds; one with a core type replaces it workflowSandbox: createQuickJsSandbox(), workflowTools: true, // workflow_list, workflow_run (opt-in)});| Setting | Default |
|---|---|
WORKFLOW_ROLES (environment) |
Comma-separated roles that may list kinds, validate, and create and run workflows. Unset means nobody. See Authentication |
workflowStorage |
PostgreSQL (tables created on first use; see Storage) when the module uses it. null without a database: /workflows answers 501 while /workflows/kinds and /workflows/validate still answer |
workflowKinds |
None: coreNodeKinds alone |
workflowSandbox |
None: a code node fails its run with “The host lends no sandbox port” |
workflowTools |
false |
The fetch port is the platform’s. The tool, llm and agent kinds are registered too and use the server’s tool registry, model profiles and agent runner.
The local backend
Section titled “The local backend”The local backend keeps workflows in its SQLite file and gives the local owner the workflow role, so the feature is on by default on the desktop. See Local backend.
Letting an agent run workflows
Section titled “Letting an agent run workflows”With workflowTools: true the module registers two tools. They are opt-in: an agent gets them only by naming them in its definition.
tools: [workflow_list, workflow_run]The user can then ask “run the nightly report” and the agent calls workflow_list to find the id, then workflow_run. Both act as the caller and answer “Not done: …” for a caller without a workflow role or for an unknown workflow. See Tools and Agents.
| Tool | Input | Answer |
|---|---|---|
workflow_list |
none | Each workflow’s id, name, description? and trigger ({ id, type }) |
workflow_run |
{ workflowId, input?, waitSeconds? } (wait defaults to 60, at most 300) |
Starts the run and waits. finished: the output as JSON. paused: the run id and each pending interrupt (nodeId, interruptId, payload) for the agent to put to the user. failed: “Not done: the workflow failed: …”. Still running when the wait ends: its run id and that it is still running |
The visual builder
Section titled “The visual builder”@pilotoo/ui/workflows ships a drag-and-drop builder over the WorkflowsApi client from @pilotoo/chat-core. The demo web app uses useWorkflowEditor to load, validate (debounced) and save a workflow, and renders WorkflowBuilder with what it returns.
import { createWorkflowsClient } from '@pilotoo/chat-core';import { useWorkflowEditor, WorkflowBuilder } from '@pilotoo/ui/workflows';
const api = createWorkflowsClient(apiUrl, fetch, { credentials });
function Editor({ id }: { id: string }) { const { state, setDefinition, save } = useWorkflowEditor(api, id); if (state.status !== 'ready') return null;
return ( <> <button disabled={!state.dirty} onClick={() => void save()}>Save</button> <WorkflowBuilder definition={state.definition} kinds={state.kinds} issues={state.issues} ports={state.ports} onChange={setDefinition} /> </> );}Load @pilotoo/ui/tokens.css and @xyflow/react/dist/style.css once. The builder takes the node kinds from GET /workflows/kinds, so your custom kinds appear in its palette with a config form generated from their schema.
REST routes
Section titled “REST routes”All routes require a caller with a workflow role, except the hook.
| Route | Purpose |
|---|---|
GET /workflows/kinds |
{ kinds }: each kind’s type, category, label, description?, configSchema (JSON Schema) and ports. outputs is 'dynamic' when they depend on the config |
POST /workflows/validate |
A definition, with or without id. 200 with { issues, ports }; ports[nodeId] lists the output ports of every node whose kind is known |
GET /workflows |
The caller’s own, oldest first |
POST /workflows |
{ name, description?, nodes, edges }; mints the id; 201 |
GET, PATCH, DELETE /workflows/:id |
The caller’s own; another owner’s answers 404. PATCH takes any create field. DELETE stops running runs and removes the workflow, its runs, events and checkpoints |
POST /workflows/:id/runs |
{ input }; 201 with the run as running. 400 if the workflow does not validate |
GET /workflows/:id/runs |
Runs, newest first |
GET /workflows/:id/runs/:runId |
{ id, workflowId, threadId, status, input, output, error, interrupts, startedAt, finishedAt } |
GET /workflows/:id/runs/:runId/events |
Every event the run reported, in order, each with at |
POST /workflows/:id/runs/:runId/resume |
{ interruptId, answer }; 200 with the run as running. 400 for a run that is not paused or an interrupt it does not have |
POST /workflows/:id/webhook |
Mint or rotate the webhook token; 201 { webhookToken } |
DELETE /workflows/:id/webhook |
Clear the token; 204 |
POST /workflows/:id/hooks/:token |
Anonymous; the token is the credential. 202 { runId }. See Triggers |
The request and response shapes are the workflow* schemas in @pilotoo/chat-protocol (Protocol).
Real-time events
Section titled “Real-time events”Every engine event is kept as a run event and also emitted on the socket as workflow:event with { run, event }, to the room of the run’s thread (thread:<threadId>). run is the run as it is after that event.
A socket joins a run’s room with the command workflow:watch { workflowId, runId }. It is answered { ok: true, threadId }, or { ok: false, error: { code: 'RUN_NOT_FOUND' } } for a run the caller cannot see, whatever the reason.
Ownership
Section titled “Ownership”Workflows and runs belong to their owner (tenant and subject). A caller with a workflow role sees their own only. There is no admin reach.
Next steps
Section titled “Next steps”- Workflows: the complete example.
- Building workflows: custom node kinds.
- Tools: how an agent selects the workflow tools.