nebulaflow

Protocol API Reference

Overview

The NebulaFlow protocol defines the message contract between the VS Code extension (host) and the webview (React UI). All messages are typed for type safety and follow a strict schema. The protocol is defined in workflow/Core/Contracts/Protocol.ts.

Message Flow

Webview (React UI) ←→ Extension (VS Code)

Direction

Message Types

Commands (Webview → Extension)

Commands are requests from the webview to the extension to perform actions.

Workflow Management

Command Description Payload
save_workflow Save workflow to disk { nodes, edges, state }
load_workflow Load workflow from disk -
load_last_workflow Load last opened workflow -
execute_workflow Start workflow execution { nodes, edges, state, resume }
abort_workflow Stop all workflow execution -
pause_workflow Pause running workflow -
clear_workflow Clear current workflow -
reset_results Clear execution results -

Node Operations

Command Description Payload
execute_node Execute single node { node, inputs, runId, variables }
llm_node_chat Chat with LLM node { node, threadID, message, mode }
node_approved Approve node execution { nodeId, modifiedCommand }
node_rejected Reject node execution { nodeId, reason }
calculate_tokens Calculate token count { text, nodeId }

Custom Nodes

Command Description Payload
save_customNode Save custom node definition { node: WorkflowNodeDTO }
delete_customNode Delete custom node { nodeId: string }
rename_customNode Rename custom node { oldNodeTitle, newNodeTitle }
get_custom_nodes Request custom nodes -

Subflows

Command Description Payload
create_subflow Create new subflow { subflow: SubflowDefinitionDTO }
get_subflow Get subflow by ID { id: string }
get_subflows List all subflows -
duplicate_subflow Duplicate subflow { id, nodeId }

Storage & Configuration

Command Description Payload
get_storage_scope Request storage scope info -
toggle_storage_scope Toggle storage scope -
get_models Request available LLM models -

Clipboard

Command Description Payload
copy_selection Copy selected nodes/edges { nodes, edges }
paste_selection Paste clipboard content -

External

Command Description Payload
open_external_link Open URL in external browser { url: string }

Events (Extension → Webview)

Events are notifications from the extension to the webview, typically containing data or status updates.

Workflow State

Event Description Payload
workflow_loaded Workflow data loaded from disk { nodes, edges, state }
workflow_saved Workflow successfully saved { path? }
workflow_save_failed Workflow save failed { error? }

Execution Status

Event Description Payload
execution_started Workflow execution started -
execution_completed Workflow execution completed { stoppedAtNodeId? }
execution_paused Workflow execution paused { stoppedAtNodeId? }
node_execution_status Node execution status update { nodeId, status, result, command }

Content Streaming

Event Description Payload
node_assistant_content LLM assistant content stream { nodeId, threadID?, content, mode? }
node_output_chunk CLI output stream { nodeId, chunk, stream }
token_count Token count result { count, nodeId }

Models & Configuration

Event Description Payload
models_loaded Available LLM models loaded { models: Model[] }
provide_custom_nodes Custom nodes loaded { nodes: WorkflowNodeDTO[] }
storage_scope Storage scope information { scope, basePath? }

Subflow Events

Event Description Payload
subflow_saved Subflow saved successfully { id }
provide_subflow Subflow data provided { subflow: SubflowDefinitionDTO }
provide_subflows List of subflows provided { id, title, version }[]
subflow_copied Subflow duplicated { nodeId, oldId, newId }

Subflow-Scoped Events

When viewing a subflow, these events are forwarded from the inner workflow:

Event Description Payload
subflow_node_execution_status Node execution status in subflow { subflowId, payload }
subflow_node_assistant_content Assistant content in subflow { subflowId, nodeId, threadID?, content, mode? }

Clipboard Events

Event Description Payload
clipboard_paste Clipboard paste result { nodes, edges }

Data Types

Basic Types

Model

interface Model {
    id: string
    title?: string
}

Represents an available LLM model.

Node & Edge Types

WorkflowNodeDTO

interface WorkflowNodeDTO {
    id: string
    type: string
    data: Record<string, unknown>
    position: { x: number; y: number }
    selected?: boolean
}

Transport-friendly workflow node DTO (neutral shape).

Fields:

EdgeDTO

interface EdgeDTO {
    id: string
    source: string
    target: string
    sourceHandle?: string
    targetHandle?: string
}

Transport-friendly edge DTO.

Fields:

Execution Types

NodeExecutionPayload

interface NodeExecutionPayload {
    nodeId: string
    status: 'running' | 'completed' | 'error' | 'interrupted' | 'pending_approval'
    result?: string
    multi?: string[]
    command?: string
}

Execution status payload for node updates.

Fields:

NodeSavedState

interface NodeSavedState {
    status: 'completed' | 'error' | 'interrupted'
    output: string
    error?: string
    tokenCount?: number
}

Saved state for a single node after execution.

Fields:

Workflow State

WorkflowStateDTO

interface WorkflowStateDTO {
    nodeResults: Record<string, NodeSavedState>
    ifElseDecisions?: Record<string, 'true' | 'false'>
    nodeAssistantContent?: Record<string, AssistantContentItem[]>
    nodeThreadIDs?: Record<string, string>
}

Workflow state envelope persisted across save/load.

Fields:

WorkflowPayloadDTO

interface WorkflowPayloadDTO {
    nodes?: WorkflowNodeDTO[]
    edges?: EdgeDTO[]
    state?: WorkflowStateDTO
    resume?: ResumeDTO
}

Common workflow payload for commands/events.

Fields:

Assistant Content

AssistantContentItem

type AssistantContentItem =
    | { type: 'text'; text: string }
    | { type: 'user_message'; text: string }
    | { type: 'thinking'; thinking: string }
    | { type: 'tool_use'; id: string; name: string; inputJSON?: string }
    | { type: 'tool_result'; toolUseID: string; resultJSON?: string }
    | { type: 'server_tool_use'; name: string; inputJSON?: string }
    | { type: 'server_web_search_result'; query?: string; resultJSON?: string }

Assistant/user/tool content blocks streamed during LLM node execution.

Types:

Subflow Types

SubflowPortDTO

interface SubflowPortDTO {
    id: string
    name: string
    index: number
}

Input/output port for subflows.

Fields:

SubflowGraphDTO

interface SubflowGraphDTO {
    nodes: WorkflowNodeDTO[]
    edges: EdgeDTO[]
}

Subflow graph definition.

Fields:

SubflowDefinitionDTO

interface SubflowDefinitionDTO {
    id: string
    title: string
    version: string
    inputs: SubflowPortDTO[]
    outputs: SubflowPortDTO[]
    graph: SubflowGraphDTO
}

Complete subflow definition.

Fields:

Resume Types

ResumeDTO

interface ResumeDTO {
    fromNodeId?: string
    seeds?: {
        outputs?: Record<string, string>
        decisions?: Record<string, 'true' | 'false'>
        variables?: Record<string, string>
    }
}

Resume metadata for paused/interrupted workflows.

Fields:

Message Envelope

BaseWorkflowMessage

interface BaseWorkflowMessage {
    type: string
}

Base interface for all workflow messages. Every message must have a type field.

Protocol Implementation Notes

Type Safety

All messages are typed using TypeScript interfaces. This ensures:

  1. Compile-time validation: Type errors are caught during development
  2. IntelliSense support: IDE provides autocomplete and type hints
  3. Runtime safety: Message validation can be added for production

Message Validation

In development mode, messages are validated strictly. In production, validation may be relaxed for performance.

Message Flow Patterns

  1. Request-Response: Webview sends command → Extension processes → Extension sends event
  2. Streaming: Extension sends multiple events for a single operation (e.g., LLM streaming)
  3. Broadcast: Extension sends events to all listeners (e.g., execution status updates)

Error Handling

Usage Examples

Sending a Command from Webview

import type { ExecuteWorkflowCommand } from './Protocol'

const message: ExecuteWorkflowCommand = {
    type: 'execute_workflow',
    data: {
        nodes: workflowNodes,
        edges: workflowEdges,
        state: workflowState,
    },
}

// Send to extension
vscode.postMessage(message)

Receiving an Event in Webview

import type { NodeExecutionStatusEvent } from './Protocol'

window.addEventListener('message', (event) => {
    const message = event.data as NodeExecutionStatusEvent
    
    if (message.type === 'node_execution_status') {
        const { nodeId, status, result } = message.data
        // Update UI based on node execution status
    }
})

Type-Safe Message Handling

import type { ExtensionToWorkflow } from './Protocol'

function handleMessage(message: ExtensionToWorkflow) {
    switch (message.type) {
        case 'node_execution_status':
            // TypeScript knows message.data is NodeExecutionPayload
            console.log(message.data.nodeId, message.data.status)
            break
        
        case 'node_assistant_content':
            // TypeScript knows message.data has content array
            message.data.content.forEach(item => {
                console.log(item.type, item.text)
            })
            break
        
        // ... other message types
    }
}