# 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`
