> ## Documentation Index
> Fetch the complete documentation index at: https://docs.osmedeus.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Step Types

> Available step types for workflow execution

Osmedeus supports 8 step types for different execution needs.

## Overview

| Type | Description | Primary Use |
| - | - | - |
| `bash` | Execute shell commands | Run tools, file operations |
| `function` | Run utility functions | Conditions, logging, file checks |
| `foreach` | Iterate over file lines | Process lists |
| `parallel-steps` | Run steps concurrently | Parallel tool execution |
| `remote-bash` | Per-step Docker/SSH | Mixed environments |
| `http` | Make HTTP requests | API calls, webhooks |
| `llm` | AI-powered processing | Analysis, summarization |
| `agent` | Agentic LLM execution | Autonomous tool-calling agents |

## bash

Execute shell commands.

### Basic Command

```yaml theme={null}
- name: run-subfinder
  type: bash
  command: subfinder -d {{target}} -o {{Output}}/subs.txt
```

### Multiple Commands (Sequential)

```yaml theme={null}
- name: setup
  type: bash
  commands:
    - mkdir -p {{Output}}/scans
    - echo "Starting scan for {{target}}"
    - date > {{Output}}/start-time.txt
```

### Parallel Commands

```yaml theme={null}
- name: run-tools
  type: bash
  parallel_commands:
    - subfinder -d {{target}} -o {{Output}}/subfinder.txt
    - amass enum -passive -d {{target}} -o {{Output}}/amass.txt
    - assetfinder {{target}} > {{Output}}/assetfinder.txt
```

### Structured Arguments

```yaml theme={null}
- name: nuclei-scan
  type: bash
  command: nuclei
  input_args:
    - name: target-list
      flag: -l
      value: "{{Output}}/live.txt"
  output_args:
    - name: output
      flag: -o
      value: "{{Output}}/nuclei.txt"
  config_args:
    - name: templates
      flag: -t
      value: "{{Data}}/templates/cves"
  speed_args:
    - name: rate-limit
      flag: -rl
      value: "150"
```

### Save Output to File

```yaml theme={null}
- name: scan
  type: bash
  command: nmap -sV {{target}}
  std_file: "{{Output}}/nmap-output.txt"
```

## function

Execute utility functions via Goja JavaScript VM.

### Single Function

```yaml theme={null}
- name: log-start
  type: function
  function: log_info("Starting scan for {{target}}")
```

### Multiple Functions

```yaml theme={null}
- name: check-files
  type: function
  functions:
    - log_info("Checking prerequisites")
    - fileExists("{{Output}}/targets.txt")
    - log_info("Ready to proceed")
```

### Parallel Functions

```yaml theme={null}
- name: parallel-checks
  type: function
  parallel_functions:
    - fileLength("{{Output}}/subs.txt")
    - fileLength("{{Output}}/urls.txt")
    - fileLength("{{Output}}/live.txt")
```

### Use in Conditions

```yaml theme={null}
- name: run-if-exists
  type: bash
  pre_condition: 'fileExists("{{Output}}/targets.txt")'
  command: nuclei -l {{Output}}/targets.txt
```

## foreach

Iterate over lines in a file with parallel execution using a worker pool.

### Basic Loop

```yaml theme={null}
- name: probe-subdomains
  type: foreach
  input: "{{Output}}/subdomains.txt"
  variable: subdomain
  threads: 10
  step:
    name: httpx-probe
    type: bash
    command: echo [[subdomain]] | httpx -silent >> {{Output}}/live.txt
```

### With Nested Variables

```yaml theme={null}
- name: scan-hosts
  type: foreach
  input: "{{Output}}/hosts.txt"
  variable: host
  threads: 5
  step:
    name: nuclei-scan
    type: bash
    command: nuclei -u [[host]] -t {{templates}} -o {{Output}}/nuclei-[[host]].txt
```

### Bounded Concurrency

The foreach executor uses a worker pool pattern:

```
┌───────────────────────────────────────────────────┐
│                  Foreach Executor                  │
│                                                    │
│   Input File: subdomains.txt (1000 lines)         │
│                      │                             │
│                      ▼                             │
│   ┌────────────────────────────────────────────┐  │
│   │            Worker Pool (threads: 10)        │  │
│   │  ┌────┐ ┌────┐ ┌────┐ ... ┌────┐          │  │
│   │  │ W1 │ │ W2 │ │ W3 │     │ W10│          │  │
│   │  └────┘ └────┘ └────┘     └────┘          │  │
│   └────────────────────────────────────────────┘  │
│                      │                             │
│                      ▼                             │
│   Results collected after all items processed      │
│                                                    │
└───────────────────────────────────────────────────┘
```

* Workers pull items from a shared queue
* Maximum `threads` items processed concurrently
* Memory-efficient: doesn't spawn all goroutines upfront
* Graceful cancellation on context timeout

### Fields

| Field | Required | Description |
| - | - | - |
| `input` | Yes | Path to file with items (one per line) |
| `variable` | Yes | Variable name for current item |
| `threads` | No | Maximum concurrent iterations (default: 1) |
| `step` | Yes | Step to execute for each item |
| `variable_pre_process` | No | Transform each input line before storing (e.g., `trim([[line]])`) |

### Pre-Processing Input

Transform each input line before it is stored in the loop variable:

```yaml theme={null}
- name: scan-cleaned-hosts
  type: foreach
  input: "{{Output}}/hosts.txt"
  variable: host
  variable_pre_process: "trim([[line]])"
  threads: 10
  step:
    name: probe
    type: bash
    command: httpx -u [[host]] -silent
```

### Variable Syntax

Use `[[variable]]` (double brackets) for loop variables to avoid conflicts with `{{templates}}`:

```yaml theme={null}
step:
  name: scan
  type: bash
  # [[subdomain]] - replaced per iteration
  # {{Output}} - resolved once before loop
  command: nuclei -u [[subdomain]] -o {{Output}}/result-[[subdomain]].txt
```

### Nested Foreach

Foreach steps can contain other foreach steps:

```yaml theme={null}
- name: scan-ports-per-host
  type: foreach
  input: "{{Output}}/hosts.txt"
  variable: host
  threads: 5
  step:
    name: scan-ports
    type: foreach
    input: "{{Output}}/ports.txt"
    variable: port
    threads: 10
    step:
      name: probe
      type: bash
      command: nc -zv [[host]] [[port]]
```

## parallel-steps

Run multiple steps concurrently.

```yaml theme={null}
- name: parallel-recon
  type: parallel-steps
  parallel_steps:
    - name: subfinder
      type: bash
      command: subfinder -d {{target}} -o {{Output}}/subfinder.txt

    - name: amass
      type: bash
      command: amass enum -passive -d {{target}} -o {{Output}}/amass.txt

    - name: findomain
      type: bash
      command: findomain -t {{target}} -o {{Output}}/findomain.txt
```

Nested steps can be any type:

```yaml theme={null}
- name: parallel-checks
  type: parallel-steps
  parallel_steps:
    - name: check-dns
      type: bash
      command: dig {{target}}

    - name: log-check
      type: function
      function: log_info("Parallel check running")

    - name: probe-hosts
      type: foreach
      input: "{{Output}}/subs.txt"
      variable: sub
      threads: 5
      step:
        type: bash
        command: echo [[sub]] | httpx
```

## remote-bash

Execute commands in Docker or SSH without module-level runner.

### Docker Execution

```yaml theme={null}
- name: docker-nuclei
  type: remote-bash
  step_runner: docker
  step_runner_config:
    image: projectdiscovery/nuclei:latest
    volumes:
      - "{{Output}}:/output"
    environment:
      - "API_KEY={{api_key}}"
  command: nuclei -u {{target}} -o /output/nuclei.txt
```

### SSH Execution

```yaml theme={null}
- name: ssh-nmap
  type: remote-bash
  step_runner: ssh
  step_runner_config:
    host: "{{ssh_host}}"
    port: 22
    user: "{{ssh_user}}"
    key_file: ~/.ssh/scanner_key
  command: nmap -sV {{target}} -oN /tmp/nmap.txt
  step_remote_file: /tmp/nmap.txt
  host_output_file: "{{Output}}/nmap-result.txt"
```

### Fields

| Field | Required | Description |
| - | - | - |
| `step_runner` | Yes | `docker` or `ssh` |
| `step_runner_config` | Yes | Runner configuration |
| `command` | Yes | Command to execute |
| `step_remote_file` | No | Remote file to copy back |
| `host_output_file` | No | Local destination for remote file |

## http

Make HTTP requests with automatic retries and connection pooling.

### Supported Methods

| Method | Description |
| - | - |
| `GET` | Retrieve data (default) |
| `POST` | Create/submit data |
| `PUT` | Update/replace resource |
| `PATCH` | Partial update |
| `DELETE` | Remove resource |

### GET Request

```yaml theme={null}
- name: fetch-api
  type: http
  url: "https://api.example.com/data/{{target}}"
  method: GET
  headers:
    Authorization: "Bearer {{api_token}}"
  exports:
    api_response: "{{fetch_api_http_resp.response_body}}"
    status: "{{fetch_api_http_resp.status_code}}"
```

### POST Request

```yaml theme={null}
- name: submit-scan
  type: http
  url: "https://scanner.example.com/api/scan"
  method: POST
  headers:
    Content-Type: application/json
  request_body: |
    {
      "target": "{{target}}",
      "scan_type": "full"
    }
```

### PUT Request

```yaml theme={null}
- name: update-config
  type: http
  url: "https://api.example.com/config/{{target}}"
  method: PUT
  headers:
    Content-Type: application/json
    Authorization: "Bearer {{api_token}}"
  request_body: '{"enabled": true}'
```

### PATCH Request

```yaml theme={null}
- name: patch-status
  type: http
  url: "https://api.example.com/scan/{{scan_id}}"
  method: PATCH
  headers:
    Content-Type: application/json
  request_body: '{"status": "completed"}'
```

### DELETE Request

```yaml theme={null}
- name: remove-entry
  type: http
  url: "https://api.example.com/entries/{{entry_id}}"
  method: DELETE
  headers:
    Authorization: "Bearer {{api_token}}"
```

### Auto-Exported Variables

After HTTP step execution, variables are exported with the pattern `<step_name>_http_resp`:

```yaml theme={null}
# Access as: {{step_name_http_resp.field}}
# Fields available:
#   status_code      - HTTP status code (int)
#   response_body    - Response body (string)
#   response_headers - Response headers (map)
#   content_length   - Response size in bytes (int)
#   response_time_ms - Request duration in ms (int)
#   error            - Error message if failed (string or null)
#   message          - Status message (string)
```

### HTTP Features

* **Connection Pooling**: Reuses connections for efficiency
* **Automatic Retries**: Retries on network errors and 5xx responses (up to 3 attempts)
* **Timeout**: Configurable via step `timeout` field (default: 30s)
* **Template Support**: Headers and request body support `{{variable}}` interpolation

## llm

AI-powered processing using LLM APIs (OpenAI-compatible).

### Chat Completion

```yaml theme={null}
- name: analyze-findings
  type: llm
  messages:
    - role: system
      content: You are a security analyst. Analyze the findings and provide a summary.
    - role: user
      content: |
        Analyze these vulnerability findings:
        {{readFile("{{Output}}/vulnerabilities.txt")}}
  exports:
    analysis: "{{analyze_findings_content}}"
```

### Message Roles

| Role | Description |
| - | - |
| `system` | System prompt defining behavior |
| `user` | User input/question |
| `assistant` | Model's previous response |
| `tool` | Tool/function call result |

### With Tool Calling

Define tools the LLM can invoke (OpenAI-compatible function calling):

```yaml theme={null}
- name: intelligent-scan
  type: llm
  messages:
    - role: system
      content: You are a security scanner assistant.
    - role: user
      content: Analyze {{target}} and suggest next steps.
  tools:
    - type: function
      function:
        name: run_scan
        description: Execute a security scan
        parameters:
          type: object
          properties:
            scan_type:
              type: string
              enum: [port, vuln, web]
            target:
              type: string
              description: Target to scan
          required:
            - scan_type
            - target
  tool_choice: auto  # auto, none, or {"type": "function", "function": {"name": "run_scan"}}
```

Tool calls are available in exports as `{{step_name_llm_resp.tool_calls}}`.

### Embeddings

Generate vector embeddings for text:

```yaml theme={null}
- name: generate-embeddings
  type: llm
  is_embedding: true
  embedding_input:
    - "{{readFile('{{Output}}/finding1.txt')}}"
    - "{{readFile('{{Output}}/finding2.txt')}}"
  exports:
    embeddings: "{{generate_embeddings_llm_resp.embeddings}}"
```

### Multimodal Content (Vision)

Include images in messages:

```yaml theme={null}
- name: analyze-screenshot
  type: llm
  messages:
    - role: user
      content:
        - type: text
          text: "Analyze this application screenshot for security issues"
        - type: image_url
          image_url:
            url: "{{Output}}/screenshot.png"
```

### Structured Output (JSON Schema)

Force structured JSON responses:

```yaml theme={null}
- name: structured-analysis
  type: llm
  messages:
    - role: system
      content: You are a security analyst.
    - role: user
      content: Analyze {{target}} and return structured findings.
  llm_config:
    response_format:
      type: json_schema
      json_schema:
        name: security_findings
        schema:
          type: object
          properties:
            severity:
              type: string
              enum: [critical, high, medium, low, info]
            findings:
              type: array
              items:
                type: object
                properties:
                  title:
                    type: string
                  description:
                    type: string
          required:
            - severity
            - findings
```

### Configuration Override

Override global LLM settings per step:

```yaml theme={null}
- name: custom-llm
  type: llm
  llm_config:
    model: gpt-4-turbo
    max_tokens: 4000
    temperature: 0.3
    top_p: 0.95
    timeout: 5m
    max_retries: 5
    custom_headers:
      X-Custom-Header: value
  messages:
    - role: user
      content: Analyze {{target}}
```

### Auto-Exported Variables

After LLM step execution:

```yaml theme={null}
# Access as: {{step_name_llm_resp.field}} or {{step_name_content}}
# Fields available in step_name_llm_resp:
#   id             - Response ID
#   model          - Model used
#   content        - Response text (first choice)
#   finish_reason  - Why generation stopped
#   role           - Message role
#   tool_calls     - Tool calls if any (array)
#   all_contents   - All choices if n > 1
#   usage          - Token usage (prompt_tokens, completion_tokens, total_tokens)
#   embeddings     - Embedding vectors (for embedding mode)
#
# Shorthand: {{step_name_content}} directly contains the response text
```

### Provider Rotation

If multiple LLM providers are configured, the executor automatically:

* Rotates to next provider on rate limits or errors
* Retries up to `max_retries * provider_count` times
* Records rate limit metrics for monitoring

## agent

Agentic LLM execution with an autonomous tool-calling loop. The agent receives a task, plans its approach, calls tools iteratively, and produces a final answer.

### Basic Usage

```yaml theme={null}
- name: analyze-target
  type: agent
  query: "Enumerate subdomains of {{Target}} and summarize findings."
  system_prompt: "You are a security reconnaissance agent."
  max_iterations: 10
  agent_tools:
    - preset: bash
    - preset: read_file
    - preset: save_content
  exports:
    findings: "{{agent_content}}"
```

### Preset Tools

The following preset tools are available via the `preset` field:

| Preset | Description |
| - | - |
| `bash` | Execute shell commands |
| `read_file` | Read entire file contents |
| `read_lines` | Read specific line range from a file |
| `file_exists` | Check if a file exists |
| `file_length` | Count non-empty lines in a file |
| `append_file` | Append content to a file |
| `save_content` | Write content to a file |
| `glob` | Find files matching a glob pattern |
| `grep_string` | Search for literal string in files |
| `grep_regex` | Search for regex pattern in files |
| `http_get` | Make HTTP GET request |
| `http_request` | Make HTTP request (any method) |
| `jq` | Query JSON with jq expressions |
| `exec_python` | Execute inline Python code |
| `exec_python_file` | Execute a Python file |
| `exec_ts` | Execute inline TypeScript code |
| `exec_ts_file` | Execute a TypeScript file |
| `run_module` | Run an Osmedeus module |
| `run_flow` | Run an Osmedeus flow |

```yaml theme={null}
agent_tools:
  - preset: bash
  - preset: read_file
  - preset: grep_regex
  - preset: save_content
  - preset: http_get
```

### Custom Tool Handlers

Define custom tools with a handler expression:

```yaml theme={null}
agent_tools:
  - preset: bash
  - name: lookup_whois
    description: "Look up WHOIS information for a domain"
    parameters:
      type: object
      properties:
        domain:
          type: string
          description: "Domain to query"
      required: [domain]
    handler: "exec('whois ' + args.domain)"
```

### Multi-Goal Queries

Use `queries` for multiple goals evaluated in sequence:

```yaml theme={null}
- name: multi-task
  type: agent
  queries:
    - "Enumerate subdomains of {{Target}}"
    - "Probe discovered hosts for HTTP services"
    - "Summarize all findings"
  max_iterations: 20
  agent_tools:
    - preset: bash
    - preset: save_content
```

### Sub-Agents

Spawn specialized sub-agents from the main agent:

```yaml theme={null}
- name: coordinator
  type: agent
  query: "Perform full reconnaissance of {{Target}}"
  max_iterations: 15
  agent_tools:
    - preset: bash
    - preset: read_file
  sub_agents:
    - name: dns-expert
      description: "DNS enumeration specialist"
      system_prompt: "You are a DNS enumeration expert."
      agent_tools:
        - preset: bash
        - preset: save_content
      max_iterations: 5
    - name: web-scanner
      description: "Web application scanner"
      system_prompt: "You are a web security scanner."
      agent_tools:
        - preset: bash
        - preset: http_get
      max_iterations: 5
```

The main agent can invoke sub-agents via the auto-generated `spawn_agent` tool.

### Memory Configuration

Control conversation memory for long-running agents:

```yaml theme={null}
- name: long-task
  type: agent
  query: "Perform deep analysis of {{Target}}"
  max_iterations: 50
  memory:
    max_messages: 30
    summarize_on_truncate: true
    persist_path: "{{Output}}/agent/conversation.json"
    resume_path: "{{Output}}/agent/conversation.json"
  agent_tools:
    - preset: bash
    - preset: save_content
```

| Field | Description |
| - | - |
| `max_messages` | Sliding window size for conversation history |
| `summarize_on_truncate` | Summarize old messages before removing them |
| `persist_path` | Save conversation to file after completion |
| `resume_path` | Resume from a previous conversation file |

### Planning Stage

Run a planning prompt before the main execution loop:

```yaml theme={null}
- name: planned-recon
  type: agent
  query: "Execute the reconnaissance plan for {{Target}}"
  plan_prompt: "Create a step-by-step reconnaissance plan for {{Target}}. Consider subdomain enumeration, port scanning, and service fingerprinting."
  max_iterations: 15
  agent_tools:
    - preset: bash
    - preset: save_content
  exports:
    plan: "{{agent_plan}}"
    results: "{{agent_content}}"
```

### Structured Output

Enforce a JSON schema on the agent's final output:

```yaml theme={null}
- name: structured-recon
  type: agent
  query: "Analyze {{Target}} and return structured findings."
  max_iterations: 10
  output_schema:
    type: object
    properties:
      target:
        type: string
      subdomains:
        type: array
        items:
          type: string
      open_ports:
        type: array
        items:
          type: integer
      summary:
        type: string
    required: [target, subdomains, summary]
  agent_tools:
    - preset: bash
    - preset: read_file
```

### Model Selection

Specify preferred models (tried in order before falling back to default):

```yaml theme={null}
- name: smart-agent
  type: agent
  query: "Analyze {{Target}}"
  models:
    - claude-sonnet-4-20250514
    - gpt-4-turbo
  max_iterations: 10
  agent_tools:
    - preset: bash
```

### Tool Tracing Hooks

Add JavaScript hooks for tool call monitoring:

```yaml theme={null}
- name: traced-agent
  type: agent
  query: "Scan {{Target}}"
  max_iterations: 10
  on_tool_start: "log_info('Calling tool: ' + tool_name)"
  on_tool_end: "log_info('Tool ' + tool_name + ' returned: ' + tool_result.substring(0, 100))"
  agent_tools:
    - preset: bash
    - preset: save_content
```

### Stop Condition

Evaluate a JS expression after each iteration to stop early:

```yaml theme={null}
- name: conditional-agent
  type: agent
  query: "Find vulnerabilities in {{Target}}"
  max_iterations: 20
  stop_condition: "file_exists('{{Output}}/critical-finding.txt')"
  agent_tools:
    - preset: bash
    - preset: save_content
```

### Parallel Tool Calls

Control whether the agent can execute multiple tool calls in parallel (enabled by default):

```yaml theme={null}
- name: sequential-agent
  type: agent
  query: "Carefully analyze {{Target}}"
  max_iterations: 10
  parallel_tool_calls: false  # Force sequential tool execution
  agent_tools:
    - preset: bash
```

### Auto-Exported Variables

After agent step execution, these variables are automatically available:

| Variable | Description |
| - | - |
| `{{agent_content}}` | Final agent response text |
| `{{agent_history}}` | Full conversation history (JSON) |
| `{{agent_iterations}}` | Number of iterations used |
| `{{agent_total_tokens}}` | Total tokens consumed |
| `{{agent_prompt_tokens}}` | Prompt tokens consumed |
| `{{agent_completion_tokens}}` | Completion tokens consumed |
| `{{agent_tool_results}}` | All tool call results (JSON) |
| `{{agent_plan}}` | Planning stage output (if `plan_prompt` was set) |
| `{{agent_goal_results}}` | Per-goal results (for multi-goal `queries`) |

### Fields Reference

| Field | Required | Description |
| - | - | - |
| `query` | Yes\* | Task prompt for the agent |
| `queries` | Yes\* | Multiple task prompts (alternative to `query`) |
| `max_iterations` | Yes | Maximum tool-calling loop iterations (must be > 0) |
| `agent_tools` | No | List of preset or custom tools |
| `system_prompt` | No | System prompt for the agent |
| `sub_agents` | No | Inline sub-agents spawnable via `spawn_agent` tool |
| `memory` | No | Sliding window config (`max_messages`, `summarize_on_truncate`, `persist_path`, `resume_path`) |
| `models` | No | Preferred models tried in order before falling back to default |
| `output_schema` | No | JSON schema enforced on final output |
| `plan_prompt` | No | Planning stage prompt run before the main loop |
| `stop_condition` | No | JS expression evaluated after each iteration |
| `on_tool_start` | No | JS hook expression run before each tool call |
| `on_tool_end` | No | JS hook expression run after each tool call |
| `parallel_tool_calls` | No | Enable/disable parallel tool execution (default: true) |

\* Either `query` or `queries` is required.

## Common Step Fields

All steps support these fields:

```yaml theme={null}
- name: step-name              # Required: unique name
  type: bash                   # Required: step type

  depends_on:                  # Step dependencies (DAG execution)
    - previous-step-1
    - previous-step-2

  pre_condition: 'expr'        # Skip if false
  timeout: 30m                 # Step timeout (e.g., 30s, 30m, 1h, 1d)
  log: "{{Output}}/step.log"   # Log file path

  exports:                     # Export values
    var_name: "{{value}}"

  on_success:                  # Success handlers
    - action: log
      message: "Done"

  on_error:                    # Error handlers
    - action: continue

  decision:                    # Conditional routing
    switch: "{{value}}"
    cases:
      "match": { goto: other-step }
    default: { goto: fallback }
```

### Field Reference

| Field | Description |
| - | - |
| `name` | Required. Unique step identifier |
| `type` | Required. Step type (bash, function, foreach, parallel-steps, remote-bash, http, llm, agent) |
| `depends_on` | Array of step names this step depends on (enables parallel execution) |
| `pre_condition` | Expression to evaluate; step skipped if false |
| `timeout` | Maximum execution time (default varies by step type) |
| `log` | Path to write step output log |
| `exports` | Map of variable names to values to export |
| `on_success` | Actions to execute on successful completion |
| `on_error` | Actions to execute on failure |
| `decision` | Conditional routing based on variable values |

## Next Steps

* [Variables](variables) - Exports and propagation
* [Control Flow](control-flow) - Conditions and routing
* [Functions Reference](../functions/reference) - Available functions


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.