# 2044 Workflow Nodes 81a3fb81

> A Comprehensive Guide to WilmerAI Workflow Nodes

- Skill: `tools-only/2044-workflow-nodes-81a3fb81` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/2044-workflow-nodes-81a3fb81`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/2044-workflow-nodes-81a3fb81/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/tools-only/2044-workflow-nodes-81a3fb81

---

## A Comprehensive Guide to WilmerAI Workflow Nodes

This document provides an exhaustive reference for all available non-memory-related nodes within the WilmerAI workflow
system. It is designed to be a complete guide for an LLM to understand the purpose, configuration, and proper usage of
each node.

-----

### **Core LLM Interaction: The `Standard` Node**

The **`Standard` Node** (also referred to as `Conversational`) is the fundamental building block for all direct
interactions with a Large Language Model (LLM). Its primary purpose is to assemble a prompt from various sources of
context (conversation history, previous node outputs, static text), send it to a specified LLM backend, and process the
response.

#### **Properties**

| Property                                    | Type    | Required | Default    | Description                                                                                                                           |
|:--------------------------------------------|:--------|:---------|:-----------|:--------------------------------------------------------------------------------------------------------------------------------------|
| **`type`**                                  | String  | Yes      | `Standard` | The node type. Best practice is to always include it for clarity.                                                                     |
| **`title`**                                 | String  | No       | `""`       | A descriptive name for the node, used for logging and debugging.                                                                      |
| **`endpointName`**                          | String  | Yes      | N/A        | The name of the LLM endpoint configuration. **Supports LIMITED variables: only `{agent#Input}` and static workflow variables, NOT `{agent#Output}`.**  |
| **`preset`**                                | String  | Yes      | N/A        | The name of the generation preset to use. If omitted, the endpoint's default is used. **Supports LIMITED variables like endpointName.** |
| **`returnToUser`**                          | Boolean | No       | `false`    | If `true`, this node's output is sent to the user. Only one node per workflow can be a responder.                                     |
| **`systemPrompt`**                          | String  | Yes      | N/A        | The system prompt or initial instruction set for the LLM. Supports variable substitution.                                             |
| **`prompt`**                                | String  | Yes      | N/A        | The main user-facing prompt. If this is empty, the node will use `lastMessagesToSendInsteadOfPrompt`. Supports variable substitution. |
| **`lastMessagesToSendInsteadOfPrompt`**     | Integer | No       | `5`        | If `prompt` is empty, this specifies how many recent conversational turns to use as the prompt.                                       |
| **`maxResponseSizeInTokens`**               | Integer/String | No       | `400`      | Overrides the maximum number of tokens the LLM can generate for this node. **Supports LIMITED variables like endpointName.**         |
| **`maxContextTokenSize`**                   | Integer | No       | `4096`     | Overrides the maximum context window size (in tokens) for this node.                                                                  |
| **`nMessagesToIncludeInVariable`**           | Integer | No       | `5`        | Controls how many messages are included in the `{chat_user_prompt_n_messages}` and `{templated_user_prompt_n_messages}` variables.    |
| **`estimatedTokensToIncludeInVariable`**    | Integer | No       | `2048`     | Controls the estimated token budget for the `{chat_user_prompt_estimated_token_limit}` and `{templated_user_prompt_estimated_token_limit}` variables. Messages are included from most recent backwards until the budget is reached. At least one message is always included. |
| **`minMessagesInVariable`**                 | Integer | No       | `5`        | Used with `maxEstimatedTokensInVariable`. Sets the minimum message count for the `{chat_user_prompt_min_n_max_tokens}` and `{templated_user_prompt_min_n_max_tokens}` variables. These messages are always included regardless of the token budget. |
| **`maxEstimatedTokensInVariable`**          | Integer | No       | `2048`     | Used with `minMessagesInVariable`. Sets the token budget for expansion beyond the minimum message count. After the minimum messages are included, older messages are added until this budget would be exceeded. |
| **`jinja2`**                                | Boolean | No       | `false`    | If `true`, enables Jinja2 templating for the `systemPrompt` and `prompt` fields.                                                      |
| **`addDiscussionIdTimestampsForLLM`**       | Boolean | No       | `false`    | If `true`, automatically injects timestamps into the `messages` payload sent to the LLM.                                              |
| **`useRelativeTimestamps`**                 | Boolean | No       | `false`    | If `addDiscussionIdTimestampsForLLM` is `true`, this uses relative timestamps (e.g., "5 minutes ago").                                |
| **`useGroupChatTimestampLogic`**            | Boolean | No       | `false`    | Activates special timestamping logic for group chat-style generation prompts.                                                         |
| **`addUserTurnTemplate`**                   | Boolean | No       | `false`    | Manually wraps the final prompt content in the user turn template defined by the endpoint.                                            |
| **`addOpenEndedAssistantTurnTemplate`**     | Boolean | No       | `false`    | Appends the start of an assistant turn template to the end of the final prompt.                                                       |
| **`forceGenerationPromptIfEndpointAllows`** | Boolean | No       | `false`    | Forces the addition of a generation prompt even if other settings would normally suppress it.                                         |
| **`blockGenerationPrompt`**                 | Boolean | No       | `false`    | Explicitly blocks the addition of any automatic generation prompt.                                                                    |

#### **Limitations and Key Usage Notes**

* **Variable Support:** Full variables are supported in the `systemPrompt` and `prompt` fields. The `endpointName`,
  `preset`, and `maxResponseSizeInTokens` fields support LIMITED variables (only `{agent#Input}` from parent workflows
  and static variables defined in the workflow JSON, NOT `{agent#Output}` which doesn't exist yet). For
  `maxResponseSizeInTokens`, the variable must resolve to a valid integer string (e.g., `"5000"`).
* **Responder Node:** Only one node in a workflow can have `returnToUser` set to `true`. If no node is designated, the
  last node in the workflow automatically becomes the responder.
* **Prompt Fallback:** The node prioritizes the `prompt` field. If it's empty, it will fall back to using the
  conversation history as defined by `lastMessagesToSendInsteadOfPrompt`.

#### **Full Syntax Example**

This example shows a non-responder "thinking" node that synthesizes information from previous nodes and the conversation
history.

```json
{
  "title": "LLM Thinking Over to User Request",
  "type": "Standard",
  "systemPrompt": "System Information: Today is {todays_date_pretty}. The user is {human_persona_name}.\n\n<your_profile>\n{agent3Output}\n</your_profile>\n\n<user_profile>\n{agent2Output}\n</user_profile>",
  "prompt": "Please consider the most recent twenty messages of your online conversation with {human_persona_name}:\n\n<recent_conversation>\n{chat_user_prompt_last_twenty}\n</recent_conversation>\n\nPlease think carefully about all of this by answering the following questions:\n- A) How long has it been since the last message?\n- B) What did {human_persona_name} mean in their last message to you?\n- C) Carefully consider what the best way to respond might be.",
  "endpointName": "Thinker-Endpoint",
  "preset": "Thinker-Preset",
  "maxResponseSizeInTokens": 8000,
  "addUserTurnTemplate": true,
  "returnToUser": false,
  "addDiscussionIdTimestampsForLLM": true,
  "useRelativeTimestamps": true
}
```

#### **Variable Substitution for Child Workflows**

When calling a child workflow via `CustomWorkflow`, you can pass dynamic values for `endpointName`, `preset`, and
`maxResponseSizeInTokens` using `scoped_variables`. This allows the parent workflow to control which endpoint, preset,
and response size the child uses.

**Parent Workflow (calls the child with dynamic config):**

```json
{
  "title": "Run Analysis with Custom Settings",
  "type": "CustomWorkflow",
  "workflowName": "General_Workflow_Replaceable_Endpoint",
  "workflowUserFolderOverride": "_common",
  "scoped_variables": [
    "MyDynamicEndpoint",
    "MyDynamicPreset",
    "6000"
  ]
}
```

**Child Workflow (receives the values as `{agent#Input}`):**

```json
{
  "title": "Responding Agent",
  "type": "Standard",
  "endpointName": "{agent1Input}",
  "preset": "{agent2Input}",
  "maxResponseSizeInTokens": "{agent3Input}",
  "systemPrompt": "...",
  "prompt": ""
}
```

In this example, the child workflow will use endpoint `MyDynamicEndpoint`, preset `MyDynamicPreset`, and generate up to
`6000` tokens.

-----

### **Logic & Control Flow: The `Conditional` Node**

The **`Conditional`** node is a control flow utility that evaluates a complex logical expression and returns the string
`"TRUE"` or `"FALSE"`. Its output is designed to be used by a `ConditionalCustomWorkflow` node to make branching
decisions. It supports comparisons, logical operators (`AND`, `OR`), and parentheses `()` for grouping.

#### **Properties**

| Property           | Type    | Required | Default | Description                                                                                                     |
|:-------------------|:--------|:---------|:--------|:----------------------------------------------------------------------------------------------------------------|
| **`type`**         | String  | Yes      | N/A     | Must be `"Conditional"`.                                                                                        |
| **`title`**        | String  | No       | `""`    | A descriptive name for the node, used for logging and debugging.                                                |
| **`condition`**    | String  | Yes      | N/A     | The logical expression to evaluate (e.g., `({val} >= 100 AND '{status}' != 'ERROR') OR {is_override} == TRUE`). |
| **`returnToUser`** | Boolean | No       | `false` | This node is designed for internal logic; its output is not intended for the end-user.                          |

#### **Limitations and Key Usage Notes**

* **Variable Support:** The `condition` property supports all available workflow variables.
* **Type Inference:** The node intelligently infers types. Values in quotes (e.g., `'complete'`) are strings. Unquoted
  `TRUE` or `FALSE` are booleans. Other unquoted values are treated as numbers if possible, otherwise as strings.
* **IMPORTANT:** Be careful when doing comparisons. The node distinguishes between a boolean and a string. The output of
  a `Conditional` node is a boolean `TRUE` or `FALSE`. If `agent3Output` comes from a `Conditional` node and is `TRUE`,
  the comparison `"{agent3Output} == 'TRUE'"` will be **FALSE** because `(boolean) TRUE` is not equal to
  `(string) 'TRUE'`. The correct comparison is `"{agent3Output} == TRUE"`.

#### **Full Syntax Example**

This example demonstrates complex logic with `OR` and parentheses, checking for multiple valid states or an override
condition.

```json
{
  "title": "Check for valid state or admin override",
  "type": "Conditional",
  "condition": "({agent1Output} == 'Admin') OR ({agent2Output} == 'Approved' AND {agent3Output} == TRUE)"
}
```

-----

### **In-Workflow Routing: The `ConditionalCustomWorkflow` Node**

The **`ConditionalCustomWorkflow` Node** provides powerful branching logic. It dynamically selects and executes a
sub-workflow based on the value of a conditional variable (e.g., the output from a `Conditional` node). It also supports
a default content fallback, preventing the need for an extra workflow file for a simple default response.

#### **Properties**

| Property                                 | Type             | Required | Default | Description                                                                                                                          |
|:-----------------------------------------|:-----------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------|
| **`type`**                               | String           | Yes      | N/A     | Must be set to `"ConditionalCustomWorkflow"`.                                                                                        |
| **`title`**                              | String           | No       | `""`    | A descriptive title for the node shown in logs.                                                                                      |
| **`is_responder`**                       | Boolean          | No       | `false` | Determines if the output provides the final user-facing response. Renamed from `returnToUser` for clarity.                           |
| **`conditionalKey`**                     | String           | Yes      | N/A     | A variable placeholder (e.g., `{agent1Output}`) whose resolved value determines which workflow to execute.                           |
| **`conditionalWorkflows`**               | Object           | Yes      | N/A     | A dictionary mapping possible values of `conditionalKey` to workflow filenames. A special `"Default"` key can be used as a fallback. |
| **`UseDefaultContentInsteadOfWorkflow`** | String           | No       | `null`  | A string (supports variables) to return as output if no condition is met. This takes precedence over the `"Default"` workflow.       |
| **`scoped_variables`**                   | Array of Strings | No       | `[]`    | A list of values to pass into whichever child workflow is chosen.                                                                    |
| **`routeOverrides`**                     | Object           | No       | `{}`    | A dictionary specifying prompt overrides for each potential route. Keys should correspond to keys in `conditionalWorkflows`.         |
| **`workflowUserFolderOverride`**         | String           | No       | `null`  | Specifies a user folder from which to load the selected workflow. Use `_common` for shared workflows.                                |

#### **Limitations and Key Usage Notes**

* **IMPORTANT:** Only `scoped_variables`, `UseDefaultContentInsteadOfWorkflow`, and `conditionalKey` support workflow
  variables.
* **You cannot set a variable as a workflow name.** Configuration keys like workflow names inside `conditionalWorkflows`
  and `routeOverrides` must be static, hardcoded strings.
* **Matching Logic:** The match for `conditionalWorkflows` is **case-insensitive**. However, the match for
  `routeOverrides` keys is **case-sensitive** and expects the key to be **Capitalized**.

#### **Full Syntax Example**

This node routes to a specialized coding workflow based on the language detected in a previous step.

```json
{
  "title": "Route to a Specific Coding Model",
  "type": "ConditionalCustomWorkflow",
  "is_responder": true,
  "conditionalKey": "{agent1Output}",
  "workflowUserFolderOverride": "_common",
  "conditionalWorkflows": {
    "Python": "PythonCodingWorkflow",
    "JavaScript": "JavaScriptCodingWorkflow",
    "Default": "GeneralCodingWorkflow"
  },
  "UseDefaultContentInsteadOfWorkflow": "I'm sorry, I can only assist with Python and JavaScript at the moment.",
  "scoped_variables": [
    "{agent2Output}"
  ],
  "routeOverrides": {
    "Python": {
      "systemPromptOverride": "You are an expert Python programmer. The user's request is: {agent1Input}"
    },
    "JavaScript": {
      "systemPromptOverride": "You are a master JavaScript developer. The user's request is: {agent1Input}"
    }
  }
}
```

> Values sent in as scoped_variables are accessible within the child workflow that is called as agentXInputs. So
> the first scoped_variable is agent1Input, second scoped_variable is agent2Input, etc.

-----

### **Modular Logic: The `CustomWorkflow` Node**

The **`CustomWorkflow` Node** allows you to execute a separate workflow from within the current one. This is essential
for encapsulating reusable logic and breaking down complex processes. Child workflows run in an isolated context; data
must be passed in explicitly via `scoped_variables`.

#### **Properties**

| Property                            | Type             | Required | Default | Description                                                                                                         |
|:------------------------------------|:-----------------|:---------|:--------|:--------------------------------------------------------------------------------------------------------------------|
| **`type`**                          | String           | Yes      | N/A     | Must be set to `"CustomWorkflow"`.                                                                                  |
| **`title`**                         | String           | No       | `""`    | A descriptive title for the node shown in logs.                                                                     |
| **`workflowName`**                  | String           | Yes      | N/A     | The filename of the child workflow to execute (without the `.json`).                                                |
| **`is_responder`**                  | Boolean          | No       | `false` | Determines if this node provides the final user-facing response. Renamed from `returnToUser`.                       |
| **`scoped_variables`**              | Array of Strings | No       | `[]`    | A list of values to pass from the parent to the child workflow. These become `{agent1Input}`, `{agent2Input}`, etc. |
| **`workflowUserFolderOverride`**    | String           | No       | `null`  | Specifies a user folder to load the workflow from. Use `_common` for shared workflows.                              |
| **`firstNodeSystemPromptOverride`** | String           | No       | `null`  | **(Legacy)** Overrides the `systemPrompt` for the first node in the child workflow. Use `scoped_variables` instead. |
| **`firstNodePromptOverride`**       | String           | No       | `null`  | **(Legacy)** Overrides the `prompt` for the first node in the child workflow. Use `scoped_variables` instead.       |

#### **Limitations and Key Usage Notes**

* **Variable Support:** Variable substitution is supported within the `scoped_variables` array and the legacy prompt
  override fields.
* **Static Configuration:** The `workflowName` and `workflowUserFolderOverride` fields **do not** support variables and
  must be static strings.

#### **Full Syntax Example**

This example calls a reusable child workflow to perform a search and summarize the findings.

```json
{
  "title": "Custom Wiki Search: Initial Search",
  "type": "CustomWorkflow",
  "workflowName": "Util_Workflow_Wiki_Search_And_Summarize",
  "workflowUserFolderOverride": "_common",
  "is_responder": false,
  "scoped_variables": [
    "{agent1Output}",
    "{agent3Output}"
  ]
}
```

> Values sent in as scoped_variables are accessible within the child workflow that is called as agentXInputs. So
> the first scoped_variable is agent1Input, second scoped_variable is agent2Input, etc.

-----

### **Data Manipulation: The `ArithmeticProcessor` Node**

The **`ArithmeticProcessor`** node performs a basic mathematical calculation. It takes a string expression containing
two numbers and one operator (`+`, `-`, `*`, `/`), resolves any variables, computes the result, and returns it as a
string. If the expression is invalid, it returns `"-1"`.

#### **Properties**

| Property           | Type    | Required | Default | Description                                                              |
|:-------------------|:--------|:---------|:--------|:-------------------------------------------------------------------------|
| **`type`**         | String  | Yes      | N/A     | Must be `"ArithmeticProcessor"`.                                         |
| **`title`**        | String  | No       | `""`    | A descriptive name for the node.                                         |
| **`expression`**   | String  | Yes      | N/A     | The mathematical expression to evaluate (e.g., `{agent1Output} * 1.07`). |
| **`returnToUser`** | Boolean | No       | `false` | Unlikely to be used, as this node is for internal data processing.       |

#### **Limitations and Key Usage Notes**

* **Variable Support:** The `expression` property supports all available workflow variables. These variables are
  expected to resolve to numeric values.

#### **Full Syntax Example**

This node calculates a final price by adding a 7% tax to a subtotal from a previous node.

```json
{
  "title": "Calculate Final Price with Tax",
  "type": "ArithmeticProcessor",
  "expression": "{agent1Output} * 1.07",
  "returnToUser": false
}
```

-----

### **Data Manipulation: The `StringConcatenator` Node**

The **`StringConcatenator`** is a utility for combining multiple strings into one. It takes a list of strings, resolves
any variables, and joins them with a specified delimiter. It can also act as a streaming responder.

#### **Properties**

| Property           | Type            | Required | Default | Description                                                                       |
|:-------------------|:----------------|:---------|:--------|:----------------------------------------------------------------------------------|
| **`type`**         | String          | Yes      | N/A     | Must be `"StringConcatenator"`.                                                   |
| **`title`**        | String          | No       | `""`    | A descriptive name for the node.                                                  |
| **`strings`**      | List of Strings | Yes      | N/A     | A JSON array of strings to be joined. Each string supports variable substitution. |
| **`delimiter`**    | String          | No       | `""`    | The character(s) to insert between each string. `"\n"` is common for new lines.   |
| **`returnToUser`** | Boolean         | No       | `false` | If `true`, this node's output is sent to the user, with support for streaming.    |

#### **Limitations and Key Usage Notes**

* **Variable Support:** Every string within the `strings` list supports full variable substitution.

#### **Full Syntax Example**

This example assembles a multi-line user profile from various data sources.

```json
{
  "title": "Assemble User Profile Block",
  "type": "StringConcatenator",
  "strings": [
    "--- User Profile ---",
    "Name: {user_name}",
    "Key Trait: {agent1Output}",
    "Report Generated: {todays_date_pretty}"
  ],
  "delimiter": "\n",
  "returnToUser": false
}
```

-----

### **Data Extraction: The `JsonExtractor` Node**

The **`JsonExtractor`** node extracts a specific field from a JSON string. It parses a JSON object (optionally wrapped
in markdown code blocks), resolves workflow variables, and returns the value of the specified field as a string. This
is useful for parsing structured LLM outputs.

#### **Properties**

| Property                | Type   | Required | Default | Description                                                                                          |
|:------------------------|:-------|:---------|:--------|:-----------------------------------------------------------------------------------------------------|
| **`type`**              | String | Yes      | N/A     | Must be `"JsonExtractor"`.                                                                           |
| **`title`**             | String | No       | `""`    | A descriptive name for the node.                                                                     |
| **`jsonToExtractFrom`** | String | Yes      | N/A     | The JSON string to extract from. Supports variables. Handles markdown code blocks automatically.     |
| **`fieldToExtract`**    | String | Yes      | N/A     | The name of the field to extract. Supports variables.                                                |

#### **Limitations and Key Usage Notes**

* **Variable Support:** Both `jsonToExtractFrom` and `fieldToExtract` support full variable substitution.
* **Markdown Handling:** The node automatically strips ` ```json ` and ` ``` ` code block wrappers before parsing.
* **Return Types:** Strings are returned as-is; numbers/booleans become strings; nested objects/arrays become JSON
  strings; `null` returns an empty string.

#### **Full Syntax Example**

This node extracts the "name" field from a JSON object returned by a previous LLM node.

```json
{
  "title": "Extract Character Name from LLM Response",
  "type": "JsonExtractor",
  "jsonToExtractFrom": "{agent1Output}",
  "fieldToExtract": "name"
}
```

**Example Input (from `agent1Output`):**
```json
{"name": "Socg", "file": "Socg.txt"}
```

**Example Output:** `Socg`

-----

### **Data Extraction: The `TagTextExtractor` Node**

The **`TagTextExtractor`** node extracts content from XML/HTML-style tags within a text string. It searches for a
specified tag (e.g., `<answer>...</answer>`) and returns the content between the opening and closing tags. This is
useful for parsing structured LLM outputs where the model wraps content in custom tags.

#### **Properties**

| Property              | Type   | Required | Default | Description                                                                    |
|:----------------------|:-------|:---------|:--------|:-------------------------------------------------------------------------------|
| **`type`**            | String | Yes      | N/A     | Must be `"TagTextExtractor"`.                                                  |
| **`title`**           | String | No       | `""`    | A descriptive name for the node.                                               |
| **`tagToExtractFrom`** | String | Yes      | N/A     | The text string to search within. Supports variables.                          |
| **`fieldToExtract`**    | String | Yes      | N/A     | The name of the tag to search for (without angle brackets). Supports variables.|

#### **Limitations and Key Usage Notes**

* **Variable Support:** Both `tagToExtractFrom` and `fieldToExtract` support full variable substitution.
* **Case Sensitivity:** Tag matching is case-sensitive (`<Answer>` and `<answer>` are different).
* **First Match:** If multiple instances of the tag exist, only the first match is extracted.
* **Whitespace:** Leading/trailing whitespace is stripped from the extracted content.

#### **Full Syntax Example**

This node extracts the content within `<answer>` tags from an LLM response.

```json
{
  "title": "Extract Answer from LLM Response",
  "type": "TagTextExtractor",
  "tagToExtractFrom": "{agent1Output}",
  "fieldToExtract": "answer"
}
```

**Example Input (from `agent1Output`):**
```
I've analyzed your question. Here is my response:

<answer>
The capital of France is Paris.
</answer>

I hope this helps.
```

**Example Output:** `The capital of France is Paris.`

-----

### **Utility: The `GetCustomFile` Node**

The **`GetCustomFile`** node loads the content of a local text file into the workflow as a string. This allows you to
inject large blocks of static text (like instructions or lore) without cluttering the workflow JSON.

#### **Properties**

| Property                    | Type   | Required | Default | Description                                                                                           |
|:----------------------------|:-------|:---------|:--------|:------------------------------------------------------------------------------------------------------|
| **`type`**                  | String | Yes      | N/A     | Must be `"GetCustomFile"`.                                                                            |
| **`title`**                 | String | No       | `""`    | An optional, human-readable name for the node.                                                        |
| **`filepath`**              | String | Yes      | N/A     | The full path to the text file to load. Supports variables including `{Discussion_Id}` and `{YYYY_MM_DD}`. |
| **`delimiter`**             | String | No       | `\n`    | An optional string to search for and replace within the file's content.                               |
| **`customReturnDelimiter`** | String | No       | `\n`    | An optional string that will replace every instance of the `delimiter`.                               |

#### **Limitations and Key Usage Notes**

* **Variable Support:** The `filepath` field supports full variable substitution, including `{Discussion_Id}` for
  per-conversation files and `{YYYY_MM_DD}` for date-based files.
* **File Not Found:** If the file doesn't exist, the node returns `"Custom instruction file did not exist"`.
* **IMPORTANT:** Do not set a delimiter or custom delimiter if you want the file to be pulled as it was originally
  written.

#### **Full Syntax Example**

This node loads a character sheet and replaces a simple `---` separator with a more decorative one.

```json
{
  "title": "Load Character Sheet",
  "type": "GetCustomFile",
  "filepath": "C:\\Users\\User\\Desktop\\character_sheet.txt",
  "delimiter": "---",
  "customReturnDelimiter": "\n**********\n"
}
```

#### **Dynamic Filepath Example**

This node loads session-specific notes using the conversation's unique identifier.

```json
{
  "title": "Load Session Notes",
  "type": "GetCustomFile",
  "filepath": "/data/sessions/{Discussion_Id}_notes.txt"
}
```

-----

### **Utility: The `SaveCustomFile` Node**

The **`SaveCustomFile`** node writes string content to a local text file. This is useful for saving data generated
during a workflow, such as an LLM's analysis, a conversation summary, or a report.

#### **Properties**

| Property       | Type   | Required | Default | Description                                                                                             |
|:---------------|:-------|:---------|:--------|:--------------------------------------------------------------------------------------------------------|
| **`type`**     | String | Yes      | N/A     | Must be `"SaveCustomFile"`.                                                                             |
| **`title`**    | String | No       | `""`    | An optional, human-readable name for the node.                                                          |
| **`filepath`** | String | Yes      | N/A     | The full path where the file will be saved. Supports variables including `{Discussion_Id}` and `{YYYY_MM_DD}`. |
| **`content`**  | String | Yes      | N/A     | The string content to be written to the file. Supports variables.                                       |

#### **Limitations and Key Usage Notes**

* **Variable Support:** Both `filepath` and `content` fields support full variable substitution, including
  `{Discussion_Id}` for per-conversation files and `{YYYY_MM_DD}` for date-based files.
* **Error Handling:** The node returns a status message indicating success or failure (e.g., due to permissions).
* **Directory Creation:** If parent directories don't exist, the node will attempt to create them.

#### **Full Syntax Example**

This node saves a character bio generated by a previous node to a file.

```json
{
  "title": "Save Character Bio to File",
  "type": "SaveCustomFile",
  "filepath": "D:\\WilmerAI\\Characters\\jax_the_pirate.txt",
  "content": "CHARACTER PROFILE\n-----------------\nName: Jax\nBio: {agent1Output}"
}
```

#### **Dynamic Filepath Example**

This node saves a daily report using the date variable.

```json
{
  "title": "Save Daily Report",
  "type": "SaveCustomFile",
  "filepath": "/data/reports/{YYYY_MM_DD}_report.txt",
  "content": "Report for {todays_date_pretty}:\n\n{agent1Output}"
}
```

-----

### **Utility: The `StaticResponse` Node**

The **`StaticResponse`** node returns a hardcoded string. It's versatile for debugging, providing static instructions,
or delivering canned responses without an LLM call. When designated as a responder, it can simulate a streaming
response.

#### **Properties**

| Property           | Type    | Required | Default | Description                                                           |
|:-------------------|:--------|:---------|:--------|:----------------------------------------------------------------------|
| **`type`**         | String  | Yes      | N/A     | Must be `"StaticResponse"`.                                           |
| **`title`**        | String  | No       | `""`    | An optional, human-readable name for the node.                        |
| **`content`**      | String  | Yes      | N/A     | The static text content that the node will output.                    |
| **`returnToUser`** | Boolean | No       | `false` | If `true`, the `content` is sent to the user, with streaming support. |

#### **Limitations and Key Usage Notes**

* **Variable Support:** The `content` field does not support variable substitution; it is treated as a literal string.
* **Streaming:** If `returnToUser` is `true` and the request is for streaming, the content is delivered word-by-word.

#### **Full Syntax Example**

This node sends a pre-written message directly to the user as a final, streaming response.

```json
{
  "title": "Return System Status Message",
  "type": "StaticResponse",
  "content": "Affirmative. All systems are operating within nominal parameters. This is a pre-recorded message.",
  "returnToUser": true
}
```

-----

### **Vision: The `ImageProcessor` Node**

The **`ImageProcessor`** node is the bridge between user-provided images and text-based nodes. It calls a vision-capable
LLM to generate text descriptions of images in the user's latest message. These descriptions are then made available to
subsequent nodes.

#### **Properties**

| Property               | Type    | Required | Default | Description                                                                                                       |
|:-----------------------|:--------|:---------|:--------|:------------------------------------------------------------------------------------------------------------------|
| **`type`**             | String  | Yes      | N/A     | Must be `"ImageProcessor"`.                                                                                       |
| **`endpointName`**     | String  | Yes      | N/A     | The name of the vision-capable LLM endpoint. **Supports LIMITED variables: only `{agent#Input}` and static workflow variables, NOT `{agent#Output}`.**  |
| **`systemPrompt`**     | String  | Yes      | N/A     | The system prompt for the vision LLM, instructing it on how to describe the image. Supports variables.            |
| **`prompt`**           | String  | Yes      | N/A     | The user prompt for the vision LLM, guiding what to focus on. Supports variables.                                 |
| **`preset`**           | String  | Yes      | N/A     | The generation preset for the vision LLM. **Supports LIMITED variables like endpointName.**                       |
| **`addAsUserMessage`** | Boolean | No       | `false` | If `true`, injects the aggregated image description into the conversation history as a new user message.          |
| **`message`**          | String  | No       | N/A     | A template string for the injected message. **Must contain the `[IMAGE_BLOCK]` placeholder.** Supports variables. |

#### **Limitations and Key Usage Notes**

* **Limited Variable Support:** `endpointName` and `preset` support LIMITED variables (only `{agent#Input}` from parent
  workflows and static variables, NOT `{agent#Output}` which doesn't exist yet).
* **Sequential Processing:** The node processes images one by one and aggregates their descriptions into a single string
  output, separated by `\n-------------\n`.
* **`[IMAGE_BLOCK]` Placeholder:** This special keyword is **only valid** inside the `message` property of this node. It
  marks where the aggregated descriptions will be inserted.

#### **Full Syntax Example**

This node analyzes all user images and injects the description into the chat history for a subsequent text-only node to
use.

```json
{
  "title": "Analyze and Describe All User Images",
  "type": "ImageProcessor",
  "endpointName": "Vision-Endpoint",
  "preset": "Vision-Preset",
  "addAsUserMessage": true,
  "message": "[SYSTEM: An image analysis module has processed the user's recent image(s). The detailed description is below:\n\n[IMAGE_BLOCK]\n\nThis is now part of our conversation.]",
  "systemPrompt": "You are a world-class visual analysis AI. Describe the image in meticulous detail for a text-only assistant.",
  "prompt": "Based on our recent conversation:\n{chat_user_prompt_last_five}\n\nDescribe the image's contents in extreme detail. Transcribe any text you see."
}
```

-----

### **External Tools: The `PythonModule` Node**

The **`PythonModule`** node executes a custom Python script, allowing you to extend workflow capabilities with custom
logic, API calls, or file access. The script must contain an `Invoke(*args, **kwargs)` function that returns a single
string value.

#### **Properties**

| Property          | Type   | Required | Default | Description                                                                                   |
|:------------------|:-------|:---------|:--------|:----------------------------------------------------------------------------------------------|
| **`type`**        | String | Yes      | N/A     | Must be `"PythonModule"`.                                                                     |
| **`title`**       | String | No       | `""`    | A descriptive name for the node.                                                              |
| **`module_path`** | String | Yes      | N/A     | The full, absolute file path to the Python (`.py`) script.                                    |
| **`args`**        | Array  | No       | `[]`    | A list of positional arguments to pass to the `Invoke` function. Values support variables.    |
| **`kwargs`**      | Object | No       | `{}`    | A dictionary of keyword arguments to pass to the `Invoke` function. Values support variables. |

#### **Limitations and Key Usage Notes**

* **Variable Support:** All values within the `args` array and `kwargs` object support variable substitution.
* **Script Requirement:** The target Python file must define a function `Invoke(*args, **kwargs)` that returns a single
  value, which will be converted to a string.
* **Output Variable:** The string returned by the `Invoke` function becomes the node's output.

#### **Full Syntax Example**

This node executes a script to process data, passing arguments from workflow variables.

```json
{
  "title": "My Custom Python Tool",
  "type": "PythonModule",
  "module_path": "C:/WilmerAI/Public/Scripts/process_data.py",
  "args": [
    "A static string argument",
    "{agent1Output}"
  ],
  "kwargs": {
    "api_key": "your-secret-key",
    "user_id": "{userName}"
  }
}
```

-----

### **External Tools: The Offline Wikipedia Nodes**

The **Offline Wikipedia** nodes query a local `OfflineWikipediaTextApi` service to retrieve factual information from a
Wikipedia database. This is used for Retrieval-Augmented Generation (RAG) by providing context to an LLM. The
`promptToSearch` field supports variabl

…(truncated)
