JarvisHub Deep Dive: Canvas-Native State Management and Agent Orchestration for Long-Horizon Multimodal Creation

Introduction

On August 2, 2026, the JarvisX Team publicly released JarvisHub — an open-source, canvas-native Agent Harness designed for long-horizon multimodal creative workflows. The project has generated significant interest in the technical community, and for good reason: its core insight is deceptively simple yet profoundly impactful — existing AI creative systems, whether prompt-to-output tools, chatbot agents, or node-based workflows, all fail to properly address project state management in long-horizon creative work.

The JarvisHub paper is published on arXiv (arXiv:2607.23588), the project homepage is at https://www.jarvishub.site/, and the source code is hosted on https://github.com/LYL1015/JarvisHub (Apache 2.0 licensed). Core contributors include Yunlong Lin, Zixu Lin, and Zhaohu Xing, with academic advisors Tianyu Pang and Xiangyu Yue.

This article provides a deep technical analysis of JarvisHub, exploring how its canvas-as-state design philosophy redefines the interaction paradigm between agents and project state in long-horizon multimodal creation.


Part I: Why Canvas-Native? — The State Management Dilemma

1.1 The Fundamental Challenge of Long-Horizon Creation

In real-world creative workflows, multimodal creation is never a linear “one prompt, one output” process. Creators need to:

  • Collect and organize reference materials
  • Plan layouts or shots
  • Generate multiple candidates
  • Revise local details
  • Compare different versions
  • Incorporate feedback
  • Assemble intermediate results into a final deliverable

These intermediate artifacts — prompts, reference images, drafts, candidates, failed attempts, version relations, feedback — are not incidental byproducts of creation. They are the evolving state of a creative project. For an agent to make its next decision, it must know: what materials already exist? How are they related? Which candidates were accepted or rejected? Which parts of the project need updating?

1.2 Limitations of Existing Approaches

ApproachExamplesCore Limitation
Prompt-to-Output ToolsMidjourney, DALL·E, Stable DiffusionHides intermediate decisions, failed attempts, and version history
Chatbot AgentsClaude Design, GPT-4 with ToolsLinear conversation poorly represents spatial layouts, asset dependencies, version branches
Node-Based WorkflowsComfyUI, PromptChainerRelies on manually predefined pipelines, agents cannot continuously inspect, repair, and extend

JarvisHub’s solution is direct: treat the canvas as the shared project state for both agents and humans. The canvas is not merely a visual interface — it is the agent’s observable external memory, a protocol-constrained action space, and persistent state for artifacts, dependencies, versions, and feedback.


Part II: Three-Layer Architecture Deep Dive

JarvisHub’s architecture can be summarized in one sentence: Canvas State is “what exists”, Protocol Bridge is “what can be done and how”, Agent Runtime is “what to do and when”.

┌──────────────────────────────────────────────────────────────────┐
│                  JarvisHub Three-Layer Architecture               │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    Agent Runtime                          │   │
│  │  ┌───────────┐  ┌──────────┐  ┌───────────┐  ┌────────┐ │   │
│  │  │  Skills   │  │  Memory  │  │   Tools   │  │Subagents│ │   │
│  │  └───────────┘  └──────────┘  └───────────┘  └────────┘ │   │
│  │                                                          │   │
│  │   Observe Canvas → Plan Action → Execute Tools →         │   │
│  │   → Return Observations → Commit Updates                 │   │
│  └────────────────────────┬─────────────────────────────────┘   │
│                           │ Protocol Verification                │
│  ┌────────────────────────▼─────────────────────────────────┐   │
│  │                   Protocol Bridge                         │   │
│  │  ┌───────────────────────────────────────────────────┐   │   │
│  │  │  Capability Manifest (Γ_t)                        │   │   │
│  │  │  Execution Grant (Ω_t)                            │   │   │
│  │  │  Action Validation → State Transition (F)         │   │   │
│  │  └───────────────────────────────────────────────────┘   │   │
│  └────────────────────────┬─────────────────────────────────┘   │
│                           │ Read/Write Operations               │
│  ┌────────────────────────▼─────────────────────────────────┐   │
│  │                   Canvas State                            │   │
│  │  ┌───────────────────────────────────────────────────┐   │   │
│  │  │  Artifact Graph (G_t) = (V_t, E_t)               │   │   │
│  │  │  ┌──────┐ ──Edge Types──→ ┌──────┐               │   │   │
│  │  │  │Node1 │  Reference/     │Node2 │               │   │   │
│  │  │  │      │  Version/       │      │               │   │   │
│  │  │  └──────┘  Dependency/    └──────┘               │   │   │
│  │  │            Group/Continuation                     │   │   │
│  │  │  X_t: Content Payloads   M_t: Metadata            │   │   │
│  │  │  U_t: User Interactions  L_t: Spatial Layouts    │   │   │
│  │  └───────────────────────────────────────────────────┘   │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

2.1 Canvas State Layer

2.1.1 Formal Definition

At turn $t$, JarvisHub formalizes a creative project as:

$$C_t = (G_t, X_t, M_t, U_t, L_t), \quad G_t = (V_t, E_t)$$

Where:

  • $G_t$: Typed Artifact Graph
  • $V_t$: Set of canvas nodes, $|V_t| = N_t$
  • $E_t \subseteq V_t \times R \times V_t$: Directed typed relations
  • $X_t$: Editable content and artifact payloads
  • $M_t$: Provenance, execution metadata, and runtime status
  • $U_t$: User selections, edits, and feedback records
  • $L_t$: Spatial positions and group layouts

2.1.2 Node Structure

Each node $v_i$ is represented as:

$$v_i = (id_i, k_i, p_i, x_i, y_i, m_i, s_i), \quad k_i \in \mathcal{K}_{\text{node}}$$

Field breakdown:

FieldTypeDescription
$id_i$Stable identifierGlobally unique, addressable reference
$k_i$Node kindText, image, video, audio, UI component, storyboard, etc.
$p_i$Position and local layoutCanvas coordinates, dimensions, grouping
$x_i$Editable inputsPrompts, configuration fields
$y_i$Generated outputs / artifact handlesAsset references
$m_i$Provenance and diagnosticsCall chain, model, parameters, timestamps
$s_i$Runtime statusPlanned, running, completed, failed, accepted, rejected

2.1.3 Edge Types

JarvisHub defines five directed edge types:

Edge TypeSemanticsExample
ReferenceReferences external or internal materialsStoryboard node → Reference image
VersionVersion lineageCandidate A → Candidate A_v2
DependencyDownstream generation dependencyStoryboard → Video clip
GroupLogical groupingScene 1 → Shot 1, Shot 2, Shot 3
ContinuationWorkflow control flowStoryboard complete → Enter generation phase

2.1.4 Three Key Properties

  1. Addressable: The agent can precisely reference a specific candidate image, video clip, webpage render, or slide, rather than relying on ambiguous conversational phrases.
  2. Reusable: References, drafts, rejected candidates, and intermediate results can serve as input to subsequent generation or editing steps.
  3. Inspectable: Users and agents can trace which materials influenced a result, which version was selected, and which downstream artifacts depend on it.

2.1.5 Code Implementation: Canvas State Management

Below is the Go implementation of the core canvas state data structures, showing how JarvisHub organizes project state in practice:

// canvas/state.go — Canvas State Core Implementation

package canvas

import (
	"fmt"
	"sync"
	"time"
)

// NodeKind defines canvas node types
type NodeKind string

const (
	NodeKindText        NodeKind = "text"
	NodeKindImage       NodeKind = "image"
	NodeKindVideo       NodeKind = "video"
	NodeKindAudio       NodeKind = "audio"
	NodeKindStoryboard  NodeKind = "storyboard"
	NodeKindWebPage     NodeKind = "webpage"
	NodeKindSlide       NodeKind = "slide"
	NodeKindCharacter   NodeKind = "character"
	NodeKindScene       NodeKind = "scene"
	NodeKindReference   NodeKind = "reference"
	NodeKindCandidate   NodeKind = "candidate"
	NodeKindFeedback    NodeKind = "feedback"
)

// RuntimeStatus node runtime status
type RuntimeStatus string

const (
	StatusPlanned       RuntimeStatus = "planned"
	StatusRunning       RuntimeStatus = "running"
	StatusCompleted     RuntimeStatus = "completed"
	StatusFailed        RuntimeStatus = "failed"
	StatusAccepted      RuntimeStatus = "accepted"
	StatusRejected      RuntimeStatus = "rejected"
	StatusPendingReview RuntimeStatus = "pending_review"
)

// EdgeKind edge type
type EdgeKind string

const (
	EdgeReference    EdgeKind = "reference"
	EdgeVersion      EdgeKind = "version"
	EdgeDependency   EdgeKind = "dependency"
	EdgeGroup        EdgeKind = "group"
	EdgeContinuation EdgeKind = "continuation"
)

// Node canvas node
type Node struct {
	ID        string                 `json:"id"`
	Kind      NodeKind               `json:"kind"`
	Position  Position               `json:"position"`
	Input     map[string]interface{} `json:"input,omitempty"`
	Output    map[string]interface{} `json:"output,omitempty"`
	Metadata  Metadata               `json:"metadata"`
	Status    RuntimeStatus          `json:"status"`
	CreatedAt time.Time              `json:"created_at"`
	UpdatedAt time.Time              `json:"updated_at"`
}

// Position node position
type Position struct {
	X       float64 `json:"x"`
	Y       float64 `json:"y"`
	Width   float64 `json:"width,omitempty"`
	Height  float64 `json:"height,omitempty"`
	GroupID string  `json:"group_id,omitempty"`
}

// Metadata provenance and execution metadata
type Metadata struct {
	Provenance   string            `json:"provenance,omitempty"`
	ModelName    string            `json:"model_name,omitempty"`
	ModelParams  map[string]string `json:"model_params,omitempty"`
	ToolCallID   string            `json:"tool_call_id,omitempty"`
	ErrorMessage string            `json:"error_message,omitempty"`
	Duration     time.Duration     `json:"duration,omitempty"`
	Version      int               `json:"version"`
}

// Edge directed edge
type Edge struct {
	SourceID string   `json:"source_id"`
	TargetID string   `json:"target_id"`
	Kind     EdgeKind `json:"kind"`
	Label    string   `json:"label,omitempty"`
}

// CanvasState canvas state
type CanvasState struct {
	mu       sync.RWMutex
	Nodes    map[string]*Node   `json:"nodes"`
	Edges    []Edge             `json:"edges"`
	UserData map[string]interface{} `json:"user_data,omitempty"`
	Version  int                `json:"version"`
}

// NewCanvasState creates a new canvas state
func NewCanvasState() *CanvasState {
	return &CanvasState{
		Nodes:    make(map[string]*Node),
		Edges:    make([]Edge, 0),
		UserData: make(map[string]interface{}),
		Version:  0,
	}
}

// AddNode adds a node to the canvas
func (cs *CanvasState) AddNode(n *Node) error {
	cs.mu.Lock()
	defer cs.mu.Unlock()
	if _, exists := cs.Nodes[n.ID]; exists {
		return fmt.Errorf("node %s already exists", n.ID)
	}
	n.CreatedAt = time.Now()
	n.UpdatedAt = time.Now()
	cs.Nodes[n.ID] = n
	cs.Version++
	return nil
}

// GetNode retrieves a node by ID
func (cs *CanvasState) GetNode(id string) (*Node, error) {
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	n, exists := cs.Nodes[id]
	if !exists {
		return nil, fmt.Errorf("node %s not found", id)
	}
	return n, nil
}

// UpdateNode applies an update function to a node
func (cs *CanvasState) UpdateNode(id string, updateFn func(*Node)) error {
	cs.mu.Lock()
	defer cs.mu.Unlock()
	n, exists := cs.Nodes[id]
	if !exists {
		return fmt.Errorf("node %s not found", id)
	}
	updateFn(n)
	n.UpdatedAt = time.Now()
	cs.Version++
	return nil
}

// AddEdge adds a directed edge between two nodes
func (cs *CanvasState) AddEdge(sourceID, targetID string, kind EdgeKind, label string) error {
	cs.mu.Lock()
	defer cs.mu.Unlock()
	if _, ok := cs.Nodes[sourceID]; !ok {
		return fmt.Errorf("source node %s not found", sourceID)
	}
	if _, ok := cs.Nodes[targetID]; !ok {
		return fmt.Errorf("target node %s not found", targetID)
	}
	cs.Edges = append(cs.Edges, Edge{
		SourceID: sourceID, TargetID: targetID,
		Kind: kind, Label: label,
	})
	cs.Version++
	return nil
}

// GetDependents returns all downstream nodes depending on nodeID
func (cs *CanvasState) GetDependents(nodeID string) []*Node {
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	var dependents []*Node
	for _, e := range cs.Edges {
		if e.SourceID == nodeID && e.Kind == EdgeDependency {
			if n, ok := cs.Nodes[e.TargetID]; ok {
				dependents = append(dependents, n)
			}
		}
	}
	return dependents
}

// GetUpstreamNodes returns all upstream dependencies of nodeID
func (cs *CanvasState) GetUpstreamNodes(nodeID string) []*Node {
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	var upstream []*Node
	for _, e := range cs.Edges {
		if e.TargetID == nodeID && e.Kind == EdgeDependency {
			if n, ok := cs.Nodes[e.SourceID]; ok {
				upstream = append(upstream, n)
			}
		}
	}
	return upstream
}

// Snapshot creates an immutable checkpoint for recovery
func (cs *CanvasState) Snapshot() *CanvasState {
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	snap := &CanvasState{
		Nodes:    make(map[string]*Node),
		Edges:    make([]Edge, len(cs.Edges)),
		UserData: make(map[string]interface{}),
		Version:  cs.Version,
	}
	for k, v := range cs.Nodes {
		cp := *v
		snap.Nodes[k] = &cp
	}
	copy(snap.Edges, cs.Edges)
	for k, v := range cs.UserData {
		snap.UserData[k] = v
	}
	return snap
}
# canvas_state.py — Python Canvas State Implementation

from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, List, Optional
from datetime import datetime
import copy
import threading


class NodeKind(str, Enum):
    TEXT = "text"
    IMAGE = "image"
    VIDEO = "video"
    AUDIO = "audio"
    STORYBOARD = "storyboard"
    WEBPAGE = "webpage"
    SLIDE = "slide"
    CHARACTER = "character"
    SCENE = "scene"
    REFERENCE = "reference"
    CANDIDATE = "candidate"
    FEEDBACK = "feedback"


class RuntimeStatus(str, Enum):
    PLANNED = "planned"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"
    ACCEPTED = "accepted"
    REJECTED = "rejected"
    PENDING_REVIEW = "pending_review"


class EdgeKind(str, Enum):
    REFERENCE = "reference"
    VERSION = "version"
    DEPENDENCY = "dependency"
    GROUP = "group"
    CONTINUATION = "continuation"


@dataclass
class Position:
    x: float
    y: float
    width: Optional[float] = None
    height: Optional[float] = None
    group_id: Optional[str] = None


@dataclass
class Metadata:
    provenance: Optional[str] = None
    model_name: Optional[str] = None
    model_params: Dict[str, str] = field(default_factory=dict)
    tool_call_id: Optional[str] = None
    error_message: Optional[str] = None
    duration_ms: Optional[float] = None
    version: int = 1


@dataclass
class Node:
    id: str
    kind: NodeKind
    position: Position
    input: Dict[str, Any] = field(default_factory=dict)
    output: Dict[str, Any] = field(default_factory=dict)
    metadata: Metadata = field(default_factory=Metadata)
    status: RuntimeStatus = RuntimeStatus.PLANNED
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None

    def __post_init__(self):
        now = datetime.now()
        self.created_at = self.created_at or now
        self.updated_at = self.updated_at or now


@dataclass
class Edge:
    source_id: str
    target_id: str
    kind: EdgeKind
    label: Optional[str] = None


class CanvasState:
    """Thread-safe typed artifact graph for canvas state management"""

    def __init__(self):
        self._lock = threading.RLock()
        self.nodes: Dict[str, Node] = {}
        self.edges: List[Edge] = []
        self.user_data: Dict[str, Any] = {}
        self.version: int = 0

    def add_node(self, node: Node) -> None:
        with self._lock:
            if node.id in self.nodes:
                raise ValueError(f"Node {node.id} already exists")
            node.created_at = datetime.now()
            node.updated_at = datetime.now()
            self.nodes[node.id] = node
            self.version += 1

    def get_node(self, node_id: str) -> Optional[Node]:
        with self._lock:
            return self.nodes.get(node_id)

    def update_node(self, node_id: str, **updates) -> None:
        with self._lock:
            node = self.nodes.get(node_id)
            if node is None:
                raise KeyError(f"Node {node_id} not found")
            for key, value in updates.items():
                if hasattr(node, key):
                    setattr(node, key, value)
            node.updated_at = datetime.now()
            self.version += 1

    def add_edge(self, source_id: str, target_id: str,
                 kind: EdgeKind, label: Optional[str] = None) -> None:
        with self._lock:
            if source_id not in self.nodes:
                raise KeyError(f"Source node {source_id} not found")
            if target_id not in self.nodes:
                raise KeyError(f"Target node {target_id} not found")
            self.edges.append(Edge(source_id, target_id, kind, label))
            self.version += 1

    def get_dependents(self, node_id: str) -> List[Node]:
        with self._lock:
            return [
                self.nodes[e.target_id]
                for e in self.edges
                if e.source_id == node_id and e.kind == EdgeKind.DEPENDENCY
                and e.target_id in self.nodes
            ]

    def get_upstream(self, node_id: str) -> List[Node]:
        with self._lock:
            return [
                self.nodes[e.source_id]
                for e in self.edges
                if e.target_id == node_id and e.kind == EdgeKind.DEPENDENCY
                and e.source_id in self.nodes
            ]

    def get_nodes_by_status(self, status: RuntimeStatus) -> List[Node]:
        with self._lock:
            return [n for n in self.nodes.values() if n.status == status]

    def get_nodes_by_kind(self, kind: NodeKind) -> List[Node]:
        with self._lock:
            return [n for n in self.nodes.values() if n.kind == kind]

    def snapshot(self) -> 'CanvasState':
        """Create immutable checkpoint for recovery"""
        with self._lock:
            snap = CanvasState()
            snap.nodes = copy.deepcopy(self.nodes)
            snap.edges = copy.deepcopy(self.edges)
            snap.user_data = copy.deepcopy(self.user_data)
            snap.version = self.version
            return snap

2.2 Protocol Bridge Layer

The Protocol Bridge is one of JarvisHub’s most innovative design components. It acts as a contract layer between the agent and the canvas, ensuring every turn’s interaction is explicit, verifiable, and recoverable.

2.2.1 Capability Manifest

At turn $t$, the bridge provides the agent with a capability manifest $\Gamma_t$, containing:

  • Node types available in the current project (text, image, video, audio, UI components, etc.)
  • Allowed mutation operations (create, update, delete, connect, group, select, branch)
  • Invocable tool sets (Canvas Tools, Generation Tools, Native Tools, etc.)
  • Accessible artifact handles (references to existing assets)

2.2.2 Execution Grant

The bridge further derives an execution grant $\Omega_t$, limiting the scope of operations the agent may perform in the current turn. This grant mechanism ensures:

  • The agent cannot execute unauthorized operations
  • Every canvas mutation is validated before committing
  • All operations are recorded in the trajectory log

2.2.3 State Transition Function

With the participation of feedback signal $f_t$ and repair decision $r_t$, the canvas state evolves through:

$$C_{t+1} = \mathcal{F}(C_t, a_t, o_t, f_t, r_t)$$

Where $\mathcal{F}$ is the state transition operator implemented by the bridge, responsible for:

  • Writing accepted actions and returned evidence
  • Creating, updating, or deleting nodes
  • Connecting dependency relationships
  • Attaching assets
  • Recording failures
  • Creating checkpoints
  • Requesting human correction

2.2.4 Code Implementation: Protocol Bridge

// bridge/protocol.go — Protocol Bridge Implementation

package bridge

import (
	"fmt"
	"time"

	"jarvishub/canvas"
)

// OperationType canvas operation type
type OperationType string

const (
	OpCreateNode  OperationType = "create_node"
	OpUpdateNode  OperationType = "update_node"
	OpDeleteNode  OperationType = "delete_node"
	OpConnectEdge OperationType = "connect_edge"
	OpGroupNodes  OperationType = "group_nodes"
	OpSelectNode  OperationType = "select_node"
	OpBranchNode  OperationType = "branch_node"
)

// ToolFamily tool family
type ToolFamily string

const (
	ToolCanvas     ToolFamily = "canvas"
	ToolGeneration ToolFamily = "generation"
	ToolNative     ToolFamily = "native"
	ToolRecovery   ToolFamily = "recovery"
	ToolMCP        ToolFamily = "mcp"
)

// CapabilityManifest capability manifest
type CapabilityManifest struct {
	AllowedNodeKinds []canvas.NodeKind `json:"allowed_node_kinds"`
	AllowedOps       []OperationType   `json:"allowed_ops"`
	AvailableTools   []ToolFamily      `json:"available_tools"`
	ArtifactHandles  []string          `json:"artifact_handles"`
	Constraints      map[string]string `json:"constraints,omitempty"`
}

// ExecutionGrant execution grant
type ExecutionGrant struct {
	MaxNodes       int                     `json:"max_nodes,omitempty"`
	AllowedActions []OperationType         `json:"allowed_actions"`
	ToolGrants     map[ToolFamily]bool     `json:"tool_grants"`
	MaxTokens      int                     `json:"max_tokens,omitempty"`
	TimeoutMs      int64                   `json:"timeout_ms,omitempty"`
}

// Action agent action
type Action struct {
	Type      OperationType          `json:"type"`
	Payload   map[string]interface{} `json:"payload"`
	ToolCalls []ToolCall             `json:"tool_calls,omitempty"`
	Timestamp time.Time              `json:"timestamp"`
}

// ToolCall tool invocation
type ToolCall struct {
	Family    ToolFamily             `json:"family"`
	Name      string                 `json:"name"`
	Params    map[string]interface{} `json:"params"`
	RequestID string                 `json:"request_id"`
}

// ToolObservation tool observation result
type ToolObservation struct {
	ToolCallID string                 `json:"tool_call_id"`
	Success    bool                   `json:"success"`
	Output     map[string]interface{} `json:"output,omitempty"`
	Artifacts  []string               `json:"artifacts,omitempty"`
	Error      string                 `json:"error,omitempty"`
	DurationMs int64                  `json:"duration_ms"`
}

// FeedbackSignal feedback signal
type FeedbackSignal struct {
	Source    string                 `json:"source"`
	NodeID    string                 `json:"node_id,omitempty"`
	Verdict   string                 `json:"verdict"`
	Comments  string                 `json:"comments,omitempty"`
	Timestamp time.Time              `json:"timestamp"`
	Edits     map[string]interface{} `json:"edits,omitempty"`
}

// RepairDecision repair decision
type RepairDecision struct {
	Action      string                 `json:"action"`
	TargetNodes []string               `json:"target_nodes,omitempty"`
	Strategy    string                 `json:"strategy,omitempty"`
	Params      map[string]interface{} `json:"params,omitempty"`
}

// StateTransition state transition record
type StateTransition struct {
	Turn        int                  `json:"turn"`
	BeforeState *canvas.CanvasState  `json:"before_state"`
	Action      *Action              `json:"action"`
	Observation *ToolObservation     `json:"observation,omitempty"`
	Feedback    *FeedbackSignal      `json:"feedback,omitempty"`
	Repair      *RepairDecision      `json:"repair,omitempty"`
	AfterState  *canvas.CanvasState  `json:"after_state"`
	Timestamp   time.Time            `json:"timestamp"`
}

// ProtocolBridge protocol bridge
type ProtocolBridge struct {
	state       *canvas.CanvasState
	manifest    *CapabilityManifest
	grant       *ExecutionGrant
	transitions []StateTransition
	turnCount   int
}

// NewProtocolBridge creates a new protocol bridge
func NewProtocolBridge(state *canvas.CanvasState) *ProtocolBridge {
	return &ProtocolBridge{
		state:       state,
		transitions: make([]StateTransition, 0),
	}
}

// BuildManifest builds the capability manifest for the current turn
func (pb *ProtocolBridge) BuildManifest(projectType string) *CapabilityManifest {
	manifest := &CapabilityManifest{
		AllowedOps: []OperationType{
			OpCreateNode, OpUpdateNode, OpConnectEdge,
		},
		AvailableTools: []ToolFamily{
			ToolCanvas, ToolGeneration, ToolNative, ToolRecovery,
		},
		ArtifactHandles: make([]string, 0),
	}
	for id := range pb.state.Nodes {
		manifest.ArtifactHandles = append(manifest.ArtifactHandles, id)
	}
	switch projectType {
	case "narrative_media":
		manifest.AllowedNodeKinds = []canvas.NodeKind{
			canvas.NodeKindText, canvas.NodeKindImage,
			canvas.NodeKindVideo, canvas.NodeKindAudio,
			canvas.NodeKindStoryboard, canvas.NodeKindCharacter,
			canvas.NodeKindScene, canvas.NodeKindReference,
			canvas.NodeKindCandidate,
		}
	case "web_development":
		manifest.AllowedNodeKinds = []canvas.NodeKind{
			canvas.NodeKindText, canvas.NodeKindImage,
			canvas.NodeKindWebPage, canvas.NodeKindReference,
			canvas.NodeKindCandidate,
		}
		manifest.AvailableTools = append(manifest.AvailableTools, ToolMCP)
	case "presentation":
		manifest.AllowedNodeKinds = []canvas.NodeKind{
			canvas.NodeKindText, canvas.NodeKindImage,
			canvas.NodeKindSlide, canvas.NodeKindReference,
			canvas.NodeKindCandidate,
		}
	}
	pb.manifest = manifest
	return manifest
}

// DeriveGrant derives the execution grant for the current turn
func (pb *ProtocolBridge) DeriveGrant(userQuery string) *ExecutionGrant {
	grant := &ExecutionGrant{
		MaxNodes: 100,
		AllowedActions: []OperationType{
			OpCreateNode, OpUpdateNode, OpConnectEdge,
		},
		ToolGrants: map[ToolFamily]bool{
			ToolCanvas:     true,
			ToolGeneration: true,
			ToolNative:     true,
			ToolRecovery:   true,
		},
		TimeoutMs: 300000,
	}
	pb.grant = grant
	return grant
}

// ValidateAction validates an action against the current grant
func (pb *ProtocolBridge) ValidateAction(action *Action) error {
	allowed := false
	for _, op := range pb.grant.AllowedActions {
		if op == action.Type {
			allowed = true
			break
		}
	}
	if !allowed {
		return fmt.Errorf("operation %s is not in current execution grant", action.Type)
	}
	for _, tc := range action.ToolCalls {
		if granted, ok := pb.grant.ToolGrants[tc.Family]; !ok || !granted {
			return fmt.Errorf("tool family %s is not granted", tc.Family)
		}
	}
	return nil
}

// CommitTransition commits a state transition to the trajectory log
func (pb *ProtocolBridge) CommitTransition(
	before *canvas.CanvasState, action *Action,
	observation *ToolObservation, feedback *FeedbackSignal,
	repair *RepairDecision, after *canvas.CanvasState,
) StateTransition {
	pb.turnCount++
	transition := StateTransition{
		Turn: pb.turnCount, BeforeState: before, Action: action,
		Observation: observation, Feedback: feedback, Repair: repair,
		AfterState: after, Timestamp: time.Now(),
	}
	pb.transitions = append(pb.transitions, transition)
	return transition
}

// GetTrajectory returns the full execution trajectory
func (pb *ProtocolBridge) GetTrajectory() []StateTransition {
	return pb.transitions
}

2.3 Agent Runtime Layer

The Agent Runtime is JarvisHub’s execution engine, responsible for converting user requests into protocol-checked canvas updates.

2.3.1 Runtime Loop

The core loop of each turn:

Observe Canvas → Interpret User Input → Select Permitted Action →
→ Invoke Capability → Return Observations → Write to Canvas

2.3.2 Five Tool Families

Tool FamilyRuntime RoleRepresentative Operations
Canvas ToolsUpdate project stateRead, create, update, connect, group, select, branch
Generation ToolsProduce canvas artifactsImage, video, audio, composed-media generation
Native ToolsUse external executionBrowser, file, code, search, document, presentation
Recovery ToolsInspect and repairStructured feedback, verification, checkpointing, local repair
MCP ToolsExtend external servicesMCP-provided capabilities

2.3.3 Three Higher-Level Supports

  1. Skills: Reusable creative procedure templates, such as storyboarding, reference-guided generation, web reconstruction, video prompting, and deck construction.
  2. Memory: Preserves user preferences, prior decisions, and procedural knowledge across turns.
  3. Subagents: Handles independently explorable subtasks, with the parent agent integrating useful results.

2.3.4 Code Implementation: Agent Runtime

# runtime/agent_runtime.py — Agent Runtime Core Implementation

from __future__ import annotations
from dataclasses import dataclass, field
from typing import Callable, Dict, List, Optional, Any
from enum import Enum
import time
import logging

from canvas_state import (CanvasState, Node, NodeKind, RuntimeStatus, EdgeKind)
from protocol_bridge import (ProtocolBridge, CapabilityManifest, ExecutionGrant,
                             Action, OperationType, ToolCall, ToolObservation,
                             FeedbackSignal, RepairDecision, ToolFamily)


logger = logging.getLogger(__name__)


@dataclass
class RuntimeContext:
    """Runtime execution context"""
    user_query: str
    canvas_state: CanvasState
    manifest: CapabilityManifest
    grant: ExecutionGrant
    skills: Dict[str, 'Skill']
    memory: Dict[str, Any]
    subagents: List['SubAgent']


class AgentRuntime:
    """Agent execution runtime"""

    def __init__(self, bridge: ProtocolBridge, state: CanvasState):
        self.bridge = bridge
        self.state = state
        self.skills: Dict[str, Skill] = {}
        self.memory: Dict[str, Any] = {}
        self.subagents: List[SubAgent] = []
        self.tool_handlers: Dict[ToolFamily, ToolHandler] = {}
        self.turn_count = 0

    def register_tool_handler(self, family: ToolFamily, handler: ToolHandler):
        self.tool_handlers[family] = handler

    def register_skill(self, name: str, skill: Skill):
        self.skills[name] = skill

    def execute_turn(self, user_query: str) -> Dict[str, Any]:
        """Execute one turn of the runtime loop"""
        self.turn_count += 1
        logger.info(f"=== Turn {self.turn_count} ===")

        # Step 1: Build capability manifest
        manifest = self._build_manifest()

        # Step 2: Derive execution grant
        grant = self._derive_grant(user_query)

        # Step 3: Create runtime context
        ctx = RuntimeContext(
            user_query=user_query,
            canvas_state=self.state.snapshot(),
            manifest=manifest,
            grant=grant,
            skills=self.skills,
            memory=self.memory,
            subagents=self.subagents,
        )

        # Step 4: Select action (LLM-driven in production, simulated here)
        action = self._select_action(ctx)

        # Step 5: Validate action
        validation_error = self._validate_action(action, grant)
        if validation_error:
            return {"error": validation_error, "turn": self.turn_count}

        # Step 6: Execute action
        before_state = self.state.snapshot()
        observation = self._execute_action(action)

        # Step 7: Collect feedback
        feedback = self._collect_feedback(observation, ctx)

        # Step 8: Determine repair decision
        repair = self._decide_repair(feedback, before_state)

        # Step 9: Apply repair
        if repair and repair.action == "repair":
            self._apply_repair(repair, before_state)
        elif repair and repair.action == "stop":
            logger.warning("Agent stopped due to insufficient evidence")
            return {"status": "stopped", "reason": repair.strategy}

        # Step 10: Commit state transition
        after_state = self.state.snapshot()
        transition = self.bridge.commit_transition(
            before=before_state, action=action,
            observation=observation, feedback=feedback,
            repair=repair, after=after_state,
        )

        return {
            "turn": self.turn_count,
            "action": action,
            "observation": observation,
            "feedback": feedback,
            "repair": repair,
            "transition_recorded": True,
        }

    def _build_manifest(self) -> CapabilityManifest:
        project_type = self.memory.get("project_type", "general")
        return self.bridge.build_manifest(project_type)

    def _derive_grant(self, query: str) -> ExecutionGrant:
        return self.bridge.derive_grant(query)

    def _select_action(self, ctx: RuntimeContext) -> Action:
        """Select action. In production, LLM-driven."""
        for skill_name, skill in self.skills.items():
            if skill.should_apply(ctx):
                logger.info(f"Applying skill: {skill_name}")
                return skill.generate_action(ctx)
        return Action(
            type=OperationType.CREATE_NODE,
            payload={"kind": "image", "prompt": ctx.user_query},
            tool_calls=[ToolCall(
                family=ToolFamily.GENERATION, name="generate_image",
                params={"prompt": ctx.user_query},
                request_id=f"req_{self.turn_count}",
            )],
        )

    def _validate_action(self, action: Action, grant: ExecutionGrant) -> Optional[str]:
        try:
            return self.bridge.validate_action(action)
        except Exception as e:
            return str(e)

    def _execute_action(self, action: Action) -> ToolObservation:
        start = time.time()
        all_results = []
        for tc in action.tool_calls:
            handler = self.tool_handlers.get(tc.family)
            if handler is None:
                return ToolObservation(
                    tool_call_id=tc.request_id, success=False,
                    error=f"No handler for tool family: {tc.family}",
                    duration_ms=int((time.time() - start) * 1000),
                )
            try:
                result = handler.execute(tc)
                all_results.append(result)
            except Exception as e:
                all_results.append(ToolObservation(
                    tool_call_id=tc.request_id, success=False, error=str(e),
                    duration_ms=int((time.time() - start) * 1000),
                ))

        if action.type == OperationType.CREATE_NODE and all_results:
            result = all_results[0]
            if result.success:
                node = Node(
                    id=f"node_{self.turn_count}_{int(time.time())}",
                    kind=NodeKind(action.payload.get("kind", "text")),
                    position={"x": 100, "y": 100 * self.turn_count},
                    input=action.payload, output=result.output or {},
                    status=RuntimeStatus.COMPLETED,
                )
                self.state.add_node(node)

        merged = ToolObservation(
            tool_call_id=action.tool_calls[0].request_id if action.tool_calls else "",
            success=all(r.success for r in all_results),
            output={}, artifacts=[], duration_ms=int((time.time() - start) * 1000),
        )
        for r in all_results:
            if r.output: merged.output.update(r.output)
            if r.artifacts: merged.artifacts.extend(r.artifacts)
        return merged

    def _collect_feedback(self, obs: ToolObservation, ctx: RuntimeContext) -> Optional[FeedbackSignal]:
        if not obs.success:
            return FeedbackSignal(source="evaluator", verdict="reject",
                                  comments=f"Tool execution failed: {obs.error}")
        return None

    def _decide_repair(self, feedback: Optional[FeedbackSignal],
                       state: CanvasState) -> Optional[RepairDecision]:
        if feedback is None:
            return RepairDecision(action="continue")
        if feedback.verdict == "reject":
            failed_nodes = state.get_nodes_by_status(RuntimeStatus.FAILED)
            if failed_nodes:
                return RepairDecision(action="repair",
                    target_nodes=[n.id for n in failed_nodes],
                    strategy="regenerate_with_feedback",
                    params={"feedback": feedback.comments})
            return RepairDecision(action="clarify", strategy="ask_user_for_guidance")
        return RepairDecision(action="continue")

    def _apply_repair(self, repair: RepairDecision, state: CanvasState):
        for node_id in repair.target_nodes:
            node = state.get_node(node_id)
            if node:
                state.update_node(node_id, status=RuntimeStatus.PLANNED)
                logger.info(f"Repair scheduled for node {node_id}: {repair.strategy}")
// runtime/runtime.go — Agent Runtime Go Implementation

package runtime

import (
	"context"
	"fmt"
	"log"
	"time"

	"jarvishub/bridge"
	"jarvishub/canvas"
)

// ToolHandler tool handler function type
type ToolHandler func(ctx context.Context, call bridge.ToolCall) (*bridge.ToolObservation, error)

// RuntimeConfig runtime configuration
type RuntimeConfig struct {
	MaxTurns       int
	DefaultTimeout time.Duration
	ModelName      string
}

// AgentRuntime agent execution runtime
type AgentRuntime struct {
	config       RuntimeConfig
	bridge       *bridge.ProtocolBridge
	state        *canvas.CanvasState
	skills       map[string]Skill
	memory       map[string]interface{}
	toolHandlers map[bridge.ToolFamily]ToolHandler
	turnCount    int
}

// Skill interface for reusable creative procedures
type Skill interface {
	Name() string
	ShouldApply(ctx context.Context, query string, state *canvas.CanvasState) bool
	Execute(ctx context.Context, query string, state *canvas.CanvasState) (*bridge.Action, error)
}

// NewAgentRuntime creates a new agent runtime
func NewAgentRuntime(config RuntimeConfig, b *bridge.ProtocolBridge, s *canvas.CanvasState) *AgentRuntime {
	return &AgentRuntime{
		config:       config,
		bridge:       b,
		state:        s,
		skills:       make(map[string]Skill),
		memory:       make(map[string]interface{}),
		toolHandlers: make(map[bridge.ToolFamily]ToolHandler),
	}
}

// RegisterToolHandler registers a tool handler
func (rt *AgentRuntime) RegisterToolHandler(family bridge.ToolFamily, handler ToolHandler) {
	rt.toolHandlers[family] = handler
}

// RegisterSkill registers a skill
func (rt *AgentRuntime) RegisterSkill(skill Skill) {
	rt.skills[skill.Name()] = skill
}

// ExecuteTurn executes one turn of the runtime loop
func (rt *AgentRuntime) ExecuteTurn(ctx context.Context, userQuery string) (map[string]interface{}, error) {
	rt.turnCount++
	log.Printf("=== Turn %d ===\n", rt.turnCount)

	projectType := "general"
	if pt, ok := rt.memory["project_type"].(string); ok {
		projectType = pt
	}
	manifest := rt.bridge.BuildManifest(projectType)
	grant := rt.bridge.DeriveGrant(userQuery)

	action, err := rt.selectAction(ctx, userQuery, manifest, grant)
	if err != nil {
		return nil, fmt.Errorf("action selection failed: %w", err)
	}
	if err := rt.bridge.ValidateAction(action); err != nil {
		return nil, fmt.Errorf("action validation failed: %w", err)
	}

	beforeState := rt.state.Snapshot()
	observation, err := rt.executeAction(ctx, action)
	if err != nil {
		observation = &bridge.ToolObservation{
			ToolCallID: action.ToolCalls[0].RequestID, Success: false, Error: err.Error(),
		}
	}

	feedback := rt.collectFeedback(observation)
	repair := rt.decideRepair(feedback, beforeState)
	if repair != nil && repair.Action == "repair" {
		rt.applyRepair(ctx, repair, beforeState)
	}

	afterState := rt.state.Snapshot()
	rt.bridge.CommitTransition(beforeState, action, observation, feedback, repair, afterState)

	return map[string]interface{}{
		"turn": rt.turnCount, "action": action, "observation": observation,
	}, nil
}

func (rt *AgentRuntime) selectAction(ctx context.Context, query string, manifest *bridge.CapabilityManifest, grant *bridge.ExecutionGrant) (*bridge.Action, error) {
	for name, skill := range rt.skills {
		if skill.ShouldApply(ctx, query, rt.state) {
			log.Printf("Applying skill: %s\n", name)
			return skill.Execute(ctx, query, rt.state)
		}
	}
	return &bridge.Action{
		Type: bridge.OpCreateNode,
		Payload: map[string]interface{}{"kind": "image", "prompt": query},
		ToolCalls: []bridge.ToolCall{{
			Family: bridge.ToolGeneration, Name: "generate_image",
			Params: map[string]interface{}{"prompt": query},
			RequestID: fmt.Sprintf("req_%d", rt.turnCount),
		}},
	}, nil
}

func (rt *AgentRuntime) executeAction(ctx context.Context, action *bridge.Action) (*bridge.ToolObservation, error) {
	start := time.Now()
	for _, tc := range action.ToolCalls {
		handler, ok := rt.toolHandlers[tc.Family]
		if !ok {
			return nil, fmt.Errorf("no handler for tool family: %s", tc.Family)
		}
		if _, err := handler(ctx, tc); err != nil {
			return nil, err
		}
	}
	return &bridge.ToolObservation{
		ToolCallID: action.ToolCalls[0].RequestID, Success: true,
		DurationMs: time.Since(start).Milliseconds(),
	}, nil
}

func (rt *AgentRuntime) collectFeedback(obs *bridge.ToolObservation) *bridge.FeedbackSignal {
	if !obs.Success {
		return &bridge.FeedbackSignal{Source: "evaluator", Verdict: "reject",
			Comments: fmt.Sprintf("Tool execution failed: %s", obs.Error)}
	}
	return nil
}

func (rt *AgentRuntime) decideRepair(feedback *bridge.FeedbackSignal, state *canvas.CanvasState) *bridge.RepairDecision {
	if feedback == nil {
		return &bridge.RepairDecision{Action: "continue"}
	}
	if feedback.Verdict == "reject" {
		return &bridge.RepairDecision{Action: "repair", Strategy: "regenerate_with_feedback",
			Params: map[string]interface{}{"feedback": feedback.Comments}}
	}
	return &bridge.RepairDecision{Action: "continue"}
}

func (rt *AgentRuntime) applyRepair(ctx context.Context, repair *bridge.RepairDecision, state *canvas.CanvasState) {
	for _, nodeID := range repair.TargetNodes {
		node, err := state.GetNode(nodeID)
		if err == nil && node != nil {
			state.UpdateNode(nodeID, func(n *canvas.Node) { n.Status = canvas.StatusPlanned })
			log.Printf("Repair scheduled for node %s: %s\n", nodeID, repair.Strategy)
		}
	}
}

Part III: Feedback-Driven Local Repair Mechanism

3.1 Repair Flow

JarvisHub’s feedback-driven repair mechanism is the key enabler of its long-horizon creation capability. The core insight: in long-horizon creation, failure is normal; local repair is vastly more efficient than global regeneration.

Feedback-Driven Repair Flow:

Input: Canvas State C_t, Action a_t, Observation o_t
Output: Repaired Canvas State C_{t+1}

1. Execute action a_t, obtain observation o_t
2. Collect feedback signal f_t:
   - User feedback: select/reject candidates, modify prompts, mark defects
   - Model evaluator: consistency check, visual quality, task constraints
   - Subagent critique: independent assessment
3. Determine repair decision r_t:
   ├─ continue: proceed to next step
   ├─ repair: localize failed node → local repair
   │    ├─ Mark failed node as planned
   │    ├─ Preserve its upstream dependencies
   │    ├─ Use feedback signal as correction context
   │    └─ Regenerate
   ├─ clarify: request user clarification
   └─ stop: insufficient evidence, halt
4. Apply state transition C_{t+1} = F(C_t, a_t, o_t, f_t, r_t)

3.2 Local Repair vs. Global Regeneration

Consider a video creation project with 10 shots, where shot 5 fails:

  • Global regeneration (typical of Chatbot Agents): Regenerate the entire prompt, losing all accepted choices and context
  • Local repair (JarvisHub): Repair only shot 5, preserving the state and dependencies of the other 9 shots
# repair/local_repair.py — Local Repair Algorithm

from typing import List, Optional, Set
from dataclasses import dataclass

from canvas_state import CanvasState, Node, NodeKind, RuntimeStatus, EdgeKind


@dataclass
class RepairPlan:
    """Repair plan"""
    failed_node_id: str
    affected_downstream: List[str]
    preserved_upstream: List[str]
    repair_strategy: str
    feedback_context: str


class LocalRepairEngine:
    """Local repair engine"""

    def __init__(self, state: CanvasState):
        self.state = state

    def analyze_failure(self, feedback: str, failed_node_id: str) -> RepairPlan:
        """
        Analyze failure and formulate repair plan.

        Core algorithm:
        1. Locate the failed node and its downstream dependencies
        2. Preserve upstream dependencies (no regeneration needed)
        3. Only mark affected downstream nodes for repair
        """
        node = self.state.get_node(failed_node_id)
        if not node:
            raise ValueError(f"Node {failed_node_id} not found")

        affected = self._find_affected_downstream(failed_node_id)
        preserved = self._find_preserved_upstream(failed_node_id)
        strategy = self._determine_strategy(node, feedback)

        return RepairPlan(
            failed_node_id=failed_node_id,
            affected_downstream=affected,
            preserved_upstream=preserved,
            repair_strategy=strategy,
            feedback_context=feedback,
        )

    def _find_affected_downstream(self, node_id: str) -> List[str]:
        """BFS traversal of downstream dependency graph"""
        affected: Set[str] = set()
        queue = [node_id]
        while queue:
            current = queue.pop(0)
            dependents = self.state.get_dependents(current)
            for dep in dependents:
                if dep.id not in affected:
                    affected.add(dep.id)
                    queue.append(dep.id)
        return list(affected)

    def _find_preserved_upstream(self, node_id: str) -> List[str]:
        """Find upstream dependencies that can be preserved"""
        preserved: Set[str] = set()
        queue = [node_id]
        while queue:
            current = queue.pop(0)
            upstream = self.state.get_upstream(current)
            for up in upstream:
                if up.id not in preserved and up.status == RuntimeStatus.COMPLETED:
                    preserved.add(up.id)
                    queue.append(up.id)
        return list(preserved)

    def _determine_strategy(self, node: Node, feedback: str) -> str:
        strategy_map = {
            NodeKind.IMAGE: "regenerate_with_feedback",
            NodeKind.VIDEO: "regenerate_segment",
            NodeKind.STORYBOARD: "revise_layout",
            NodeKind.TEXT: "revise_with_feedback",
            NodeKind.WEBPAGE: "patch_element",
            NodeKind.SLIDE: "revise_slide",
        }
        return strategy_map.get(node.kind, "regenerate_with_feedback")

    def apply_repair(self, plan: RepairPlan) -> int:
        """Apply the repair plan. Returns count of repaired nodes."""
        repair_count = 0

        self.state.update_node(
            plan.failed_node_id,
            status=RuntimeStatus.PLANNED,
            metadata={
                "repair_strategy": plan.repair_strategy,
                "feedback_context": plan.feedback_context,
                "repair_attempts": 1,
            },
        )
        repair_count += 1

        for node_id in plan.affected_downstream:
            node = self.state.get_node(node_id)
            if node and node.status in (RuntimeStatus.COMPLETED, RuntimeStatus.ACCEPTED):
                self.state.update_node(node_id, status=RuntimeStatus.PLANNED)
                repair_count += 1

        logger.info(
            f"Repair plan: {repair_count} nodes to repair, "
            f"{len(plan.preserved_upstream)} upstream nodes preserved"
        )
        return repair_count

Part IV: Deep Comparison with Existing Approaches

4.1 Prompt-to-Output Tools

Representatives: Midjourney, DALL·E, Stable Diffusion, FLUX

DimensionPrompt-to-OutputJarvisHub
State PersistenceNone, each generation independentCanvas persists all intermediate state
Version ManagementManualAutomatic version edges
Local EditingInpainting/OutpaintingPrecise node addressing on canvas
Failure RecoveryRegenerateLocal repair
Multimodal OrchestrationNoneTyped artifact graph

4.2 Chatbot Agents

Representatives: Claude Design, GPT-4 with Tools, various Agent frameworks

DimensionChatbot AgentJarvisHub
Context StorageLinear chat historyStructured canvas graph
State VisibilityHidden inside LLMCanvas fully visible
Local Edit TargetingVague text descriptionPrecise node addressing
Dependency TrackingImplicitExplicit edges
Parallel ExplorationDifficultSubagents

4.3 Node-Based Workflows

Representatives: ComfyUI, PromptChainer, StoryNodes

DimensionNode-Based WorkflowJarvisHub
Pipeline ConstructionManual presetAgent dynamically builds
State EvolutionFixed topologyDynamic graph
Agent InvolvementNone, manual executionFully automated
Feedback LoopNoneFeedback-driven repair
Use CaseFixed pipelinesLong-horizon open creation

Part V: Technical Analysis of Three Validation Cases

5.1 Narrative Media Generation

Process: Story Brief → Character Design → Scene Design → Storyboard → Shot Planning → Image Sequence → Video Clip → Animatic

JarvisHub’s Unique Value:

  • Character Consistency: Reference edges ensure consistent character appearance across shots
  • Version Lineage: Each candidate shot retains version history for easy backtracking
  • Cross-Shot Continuity: Scene node serves as shared context, maintaining narrative coherence

5.2 Interactive Web Development

Process: Design Brief → Visual References → Layout Draft → Frontend Code → Rendered Preview → Iterative Revision

JarvisHub’s Unique Value:

  • Responsive Verification: Preview nodes correspond to different breakpoints, linked via edges
  • Component Reuse: UI component nodes can be referenced across pages
  • Visual Consistency: Design reference nodes serve as upstream dependencies constraining all generation

5.3 Presentation Deck Generation

Process: Topic → Content Planning → Diagram Generation → Slide Layout → Format Check → Export

JarvisHub’s Unique Value:

  • Cross-Slide Consistency: Slide nodes share theme and style references
  • Content Organization: Group edges organize chapter structure
  • Progressive Construction: Content outline first, details filled in later

Part VI: Future Outlook and Research Significance

6.1 Open Research Infrastructure

JarvisHub’s open design provides three key capabilities for future research:

  1. Project-State Benchmarks: Each task specifies an initial canvas, reference materials, available tools, constraints, feedback events, and expected checkpoints, rather than only a prompt and target answer.
  2. Process-Level Evaluation: Combines final artifact quality with process metrics — context preservation, tool-use appropriateness, dependency correctness, feedback adherence, repair success rate.
  3. Data Flywheel: Each run produces structured trajectory data for training future agents in planning, tool selection, multimodal state tracking, local repair, and feedback-guided revision.

6.2 Limitations and Boundaries

  • Current experiments are qualitative demonstrations rather than a completed benchmark
  • Final artifact quality still depends on external models and tools
  • The Protocol Bridge cannot guarantee semantic correctness of creative decisions
  • Trajectory data requires quality filtering, user consent, anonymization, and copyright review before use

Conclusion

The release of JarvisHub marks a significant shift in AI creative agents — from black-box rendering to replayable canvas. It elevates the canvas from a visual interface to a shared project state space for both agents and humans, achieving a systematic solution for state management in long-horizon multimodal creation through its three-layer architecture: Canvas State, Protocol Bridge, and Agent Runtime.

For AI agent researchers, JarvisHub provides a rare open experimental platform. For creators, it means a creative partner that can be inspected, intervened upon, and recovered from failure. As the project homepage states: “Stop shipping black-box renders. Ship a canvas you can replay.”


This article is based on the JarvisX Team paper “JarvisHub: An Open Harness for Canvas-Native Multimodal Creative Agents” (arXiv:2607.23588) and the open-source project.