# Workflow Tool Steps This note documents the `Tool_*` workflow extension implemented in VLCode-Lite on top of the JS `VL-Workflow-Engine` vendor copy. Canonical spec note: - DocCenter Path `3` is now `VL Workflow Spec 3.18` - Current VLCode-Lite runtime still emits/accepts workflow JSON `version: "3.16"` during the transition - `Tool_*`, `tool_message`, `clientRunToken`, and checkpoint rerun semantics are documented as part of the 3.17 extension profile - `Subflow_*` is supported as an editor/runtime alias that lowers to `WorkflowRun` without introducing a separate execution primitive ## What It Adds - Direct workflow access to the local VLCode tool registry - Optional `Subflow_*` node alias for child-workflow orchestration - `Tool_*` node rendering and editing in `public/workflow-editor.html` - Runtime events: `tool_start`, `tool_message`, `tool_done`, `tool_error` - Support in normal execution, `WorkflowRun`, pause/resume, and `executeFrom()` rerun - Automatic run attribution for tool events through `runID` and `clientRunToken` ## Step Shape ```json { "id": "Tool_010_ReadSpec", "tool": "ReadFile", "input": { "file_path": "=\"docs/spec.md\"" }, "out": { "$specText": "=_result", "$rawTool": "=_toolResult" }, "next": "Stop_End" } ``` Supported fields: - `tool` or `toolName`: explicit tool name - `input` or `in`: tool arguments - `timeout`: forwarded into tool input when the input object does not already carry `timeout` - `allowError` or `continueOnError`: continue after tool failure - `out`: normal workflow output mapping If `tool` is omitted, VLCode derives it from the step id: - `Tool_010_ReadFile` → `ReadFile` - `Tool_120_VLCompile` → `VLCompile` ## Runtime Variables During `Tool_*` output mapping, two temporary variables are available: - `_result`: normalized primary tool payload - `_toolResult`: full raw tool envelope Normalization rule: - If a tool returns `{ result: ... }`, `_result` is auto-unwrapped to that inner payload - Use `_toolResult` when the workflow needs metadata like `diff`, `undoId`, or the original nested return shape Example: ```json { "id": "Tool_020_EditFile", "tool": "EditFile", "input": { "file_path": "=\"Apps/Main.vx\"", "old_string": "=\"old\"", "new_string": "=\"new\"" }, "out": { "$editSummary": "=_result", "$editDiff": "=_toolResult.diff", "$undoId": "=_toolResult.undoId" } } ``` ## Tool Runtime Hooks VLCode tools may accept a second `runtime` argument: ```js execute: async (input, runtime = {}) => { runtime.info?.('Reading spec', { file_path: input.file_path }); runtime.progress?.('Loaded first chunk', { bytes: 4096 }); return { result: '...' }; } ``` Available helpers: - `runtime.emit(type, payload)` - `runtime.text(message, meta)` - `runtime.info(message, data)` - `runtime.warn(message, data)` - `runtime.error(message, data)` - `runtime.progress(message, data)` These are surfaced by the workflow runtime as `tool_message` events, so the outer window, detail log, SSE clients, and `workflow-editor.html` can display them while the tool is still running. ## Error Handling Default behavior: - tool failure emits `tool_error` - the step fails - workflow execution stops unless a parent parallel strategy absorbs it With `allowError: true`: ```json { "id": "Tool_030_Compile", "tool": "VLCompile", "allowError": true, "input": {}, "out": { "$compileOk": "=_result.ok", "$compileError": "=_result.error" }, "next": "Branch_040_HandleCompile" } ``` In this mode: - `tool_error` still emits - `_result` becomes `{ "ok": false, "error": "...", "tool": "VLCompile" }` - the step reaches `step_done` ## Events `Tool_*` steps emit: ```txt step_start → tool_start → tool_message(0..N) → tool_done | tool_error → var_changed(0..N) → step_done | step_error ``` `tool_message` payload shape: ```json { "type": "tool_message", "stepId": "Tool_010_ReadSpec", "name": "ReadFile", "level": "info", "message": "Reading file", "data": { "file_path": "docs/spec.md" }, "runID": "wf_1770000000000", "clientRunToken": "client:1770000000000:1" } ``` Server-side broadcasts now include `runID` and `clientRunToken` for all tool events, so parallel runs can be disambiguated in the editor and in the main UI. ## Editor Support The workflow editor now treats `Tool` as a first-class node type: - icon: `TL` - dedicated node body renderer - modal editor for `tool`, `input`, `timeout`, `allowError` - selected-run event panel shows `tool_message`, `checkpoint`, pause/resume, rerun, and other non-token events - works with the multi-run session bar and rerun-from-step flow ## Workflow-of-Workflows Pattern `WorkflowRun` now supports `mode: "sync"` for supervisor-style orchestration: ```json { "id": "Tool_010_RunWorker", "tool": "WorkflowRun", "input": { "mode": "sync", "workflow_path": "=\"examples/workflows/vlclaw-worker-spec.json\"", "params": { "goal": "=goal", "artifactPath": "=$specPath" } }, "out": { "$workerRun": "=_result" }, "next": "RETURN" } ``` `sync` mode returns a structured object with: - `status` - `runID` - `filesWritten` - `variables` - `checkpoint` - `completedSteps` - `events` This is the recommended primitive for “workflow managing child workflows”, which is the most practical landing path for a VLClaw-style AI engineer manager. ## Current Limits - `Tool_*` is a VLCode extension, not a pure core-spec node - the target tool must already be registered in `ToolRegistry` - pause is still run-level, not branch-level - rerun from a downstream node still depends on checkpoint completeness for variables, loop progress, and completed branches ## Verified Paths Validated locally on 2026-03-14: - `node test-workflow-executor.js` - `node test-workflow-run-tool-step.js` - `node test-workflow-editor.js` - `node test-workflow-parallel-codegen.js`