Skip to main content

@tokenring-ai/workflow

Overview​

The @tokenring-ai/workflow package provides a comprehensive system for defining and executing multi-step agent workflows within the TokenRing AI ecosystem. It enables users to configure sequences of commands that can be executed either on the current agent or in a newly spawned agent with specific agent types. The package integrates seamlessly with the agent system and provides both chat commands and JSON-RPC endpoints for workflow management.

Workflows are configuration-driven, defined in your application's configuration file with Zod schema validation. Each workflow consists of a sequence of commands that are executed in order, allowing for automated multi-step tasks that can span research, analysis, content generation, and other agent operations.

Key Features​

  • Multi-step Workflow Execution: Execute sequential command chains with any agent commands
  • Agent Spawning: Create new agents of specified types to run workflows independently
  • Configuration-driven: Workflows defined in configuration files with Zod schema validation
  • JSON-RPC API: Remote workflow management via WebSocket API endpoints
  • Interactive Commands: Three separate chat commands (/workflow list, /workflow run, /workflow spawn)
  • Headless Support: Run workflows in background agents without user interaction
  • Workflow Listing: Display available workflows with names, descriptions, and step counts
  • Abort Support: Workflow execution can be aborted via agent abort signal
  • Comprehensive Testing: Unit tests for all command implementations

Core Components​

WorkflowService​

The primary service managing workflow execution within the TokenRing application.

Properties:

  • name: The service identifier ("WorkflowService")
  • description: Human-readable service description ("Manages multi-step agent workflows")
  • workflows: Map of workflow configurations keyed by workflow name

Methods:

constructor(app: TokenRingApp, workflows: ParsedWorkflowConfig)​

Initializes the workflow service with the application instance and workflow configurations.

Parameters:

  • app: The TokenRing application instance
  • workflows: Parsed workflow configuration object

getWorkflow(name: string): WorkflowItem | undefined​

Retrieves a workflow by its configuration key name.

Parameters:

  • name: The workflow identifier key from the configuration

Returns: The WorkflowItem configuration or undefined if not found

listWorkflows(): Array<{ key: string; workflow: WorkflowItem }>​

Lists all available workflows from the configuration.

Returns: Array of workflow entries containing the configuration key and WorkflowItem object

spawnWorkflow(workflowName: string, { headless }: { headless: boolean }): Promise<Agent>​

Spawns a new agent of the specified type and runs the workflow on it.

Parameters:

  • workflowName: The name key of the workflow to run
  • headless: Whether to run in headless mode

Returns: Promise resolving to the spawned Agent instance

WorkflowItem Type​

The configuration structure defining a workflow's properties:

export type WorkflowItem = z.infer<typeof WorkflowItemSchema>;
// Which resolves to:
{
name: string; // Human-readable workflow name
description: string; // Detailed description of workflow purpose
agentType: string; // Required agent type for execution
steps: string[]; // Sequential commands to execute in order
}

All properties are required and validated through Zod schema validation at application startup.

Services​

WorkflowService​

The workflow package implements the TokenRingService interface:

export default class WorkflowService implements TokenRingService {
readonly name = "WorkflowService";
description = "Manages multi-step agent workflows";
workflows: Map<string, WorkflowItem>;

constructor(private app: TokenRingApp, workflows: ParsedWorkflowConfig)

getWorkflow(name: string): WorkflowItem | undefined
listWorkflows(): Array<{ key: string; workflow: WorkflowItem }>
async spawnWorkflow(workflowName: string, { headless }: { headless: boolean }): Promise<Agent>
}

Service Registration:

The service is automatically registered when the plugin is installed. The workflow service integrates with:

  • AgentCommandService: Registers chat commands for workflow interaction
  • RpcService: Registers JSON-RPC endpoints for remote workflow management
  • AgentManager: Handles agent spawning for workflow execution

Provider Documentation​

This package does not implement a provider architecture.

RPC Endpoints​

The workflow package provides JSON-RPC endpoints under /rpc/workflow:

listWorkflows​

Lists all available workflows.

Type: query

Input:

{
// No parameters required
}

Output:

Array<{
key: string; // Workflow identifier
name: string; // Human-readable workflow name
description: string; // Workflow description
agentType: string; // Agent type for execution
steps: string[]; // List of workflow steps
}>

Example:

const workflows = await rpcClient.listWorkflows({});
// Returns: [
// { key: "morning-article", name: "MarketMinute Morning Article", description: "...", agentType: "contentWriter", steps: [...] },
// { key: "daily-report", name: "Daily Report Generator", description: "...", agentType: "reportGenerator", steps: [...] }
// ]

getWorkflow​

Retrieves a specific workflow by name.

Type: query

Input:

{
name: string; // The workflow identifier
}

Output:

{
key: string; // Workflow identifier
name: string; // Human-readable workflow name
description: string; // Workflow description
agentType: string; // Agent type for execution
steps: string[]; // List of workflow steps
}

Errors: Throws error if workflow is not found

Example:

const workflow = await rpcClient.getWorkflow({ name: "morning-article" });
// Returns: { key: "morning-article", name: "MarketMinute Morning Article", description: "...", agentType: "contentWriter", steps: [...] }

spawnWorkflow​

Spawns a new agent and runs the specified workflow.

Type: mutation

Input:

{
workflowName: string; // The name of the workflow to run
headless?: boolean; // Whether to run in headless mode (default: false)
}

Output:

{
id: string; // Spawned agent ID
name: string; // Spawned agent name
description: string; // Spawned agent description
}

Example:

const result = await rpcClient.spawnWorkflow({ workflowName: "morning-article", headless: true });
// Returns: { id: "agent-123", name: "MarketMinute Morning Article", description: "..." }

Chat Commands​

The workflow package provides three separate chat commands for workflow management:

/workflow list​

Lists all available workflows with their names, descriptions, and step counts.

Syntax:

/workflow list

Help Text:

# /workflow list

List all available workflows with their names, descriptions, and step counts.

## Example

/workflow list

Examples:

/workflow list

Example Output:

Available workflows:

**morning-article**: MarketMinute Morning Article Generator (9AM EST)
Automatically write and publish the 9AM EST morning market minute articles
Steps: 4

**daily-report**: Daily Report Generator
Generate and send daily reports
Steps: 3

Implementation Details:

  • Retrieves workflows from WorkflowService.listWorkflows()
  • Displays workflow key, name, description, and step count
  • Returns "Workflow service is not running." if service is unavailable

/workflow run​

Executes a workflow by name on the current agent.

Syntax:

  • <name> - Required workflow identifier

Help Text:

# /workflow run <name>

Run a workflow by name on the current agent.

## Example

/workflow run myWorkflow

Errors: Throws CommandFailedError if workflow not found, if no name is provided, or if workflow execution fails

Examples:

/workflow run content-pipeline

Implementation Details:

  • Uses AgentCommandService.executeAgentCommand to execute each step
  • Processes steps sequentially with abort signal support
  • Checks for abort signal before each step execution
  • Returns completion message when all steps are executed
  • Returns "Workflow was aborted." if abort signal is triggered

/workflow spawn​

Spawns a new agent and runs the specified workflow on it.

Syntax:

  • <name> - Required workflow identifier

Help Text:

# /workflow spawn <name>

Spawn a new agent and run a workflow on it.

## Example

/workflow spawn myWorkflow

Errors: Throws CommandFailedError if workflow not found, if no name is provided, or if workflow service is unavailable

Examples:

/workflow spawn morning-article

Implementation Details:

  • Uses SubAgentService.runSubAgent() to spawn a new agent with the workflow's specified agent type
  • Sends /workflow run <name> command to the spawned agent to execute workflow steps
  • Output is forwarded back to the parent agent
  • Respects the parent agent's headless mode setting
  • The spawned agent runs independently with its own context and state

Configuration​

Configuration Schema​

The workflow configuration uses Zod schema validation:

import { z } from 'zod';

const WorkflowItemSchema = z.object({
name: z.string(),
description: z.string(),
agentType: z.string(),
steps: z.array(z.string()),
});

const WorkflowConfigSchema = z.record(z.string(), WorkflowItemSchema);

Plugin Configuration​

The workflow plugin automatically loads workflows from your application configuration:

export default {
workflows: {
"my-workflow": {
name: "My Workflow",
description: "A sample workflow",
agentType: "contentWriter",
steps: ["/chat Do something"]
}
}
};

Configuration Validation​

At application startup, all workflow configurations are validated against the schema. Invalid configurations will prevent the application from starting with descriptive error messages indicating which workflows are misconfigured.

Full Configuration Example​

export default {
workflows: {
"morning-article": {
name: "MarketMinute Morning Article Generator (9AM EST)",
description: "Automatically write and publish the 9AM EST morning market minute articles",
agentType: "contentWriter",
steps: [
"/tools enable @tokenring-ai/research/research",
"/tools enable @tokenring-ai/agent/runAgent",
"/tools enable @tokenring-ai/websearch/searchNews",
"/chat Write morning market analysis"
]
},
"daily-report": {
name: "Daily Report Generator",
description: "Generate and send daily reports",
agentType: "reportGenerator",
steps: [
"/tools enable @tokenring-ai/database/query",
"/chat Generate daily metrics report",
"/chat Send report to team"
]
},
"content-pipeline": {
name: "Content Creation Pipeline",
description: "Research, write, and publish content",
agentType: "contentWriter",
steps: [
"/tools enable @tokenring-ai/research/research",
"/tools enable @tokenring-ai/websearch/searchNews",
"/chat Research latest trends in AI",
"/chat Write article based on research",
"/chat Publish to blog"
]
}
}
};

Integration​

Plugin Integration​

The workflow package integrates with TokenRing applications through the plugin system:

import {AgentCommandService} from "@tokenring-ai/agent";
import {TokenRingPlugin} from "@tokenring-ai/app";
import {RpcService} from "@tokenring-ai/rpc";
import {z} from "zod";
import agentCommands from "./commands.ts";
import packageJSON from "./package.json" with {type: "json"};
import workflowRPC from "./rpc/workflow";
import {WorkflowConfigSchema} from "./schema.ts";
import WorkflowService from "./WorkflowService";

const packageConfigSchema = z.object({
workflows: WorkflowConfigSchema.prefault({})
});

export default {
name: packageJSON.name,
version: packageJSON.version,
description: packageJSON.description,
install(app, config) {
app.waitForService(AgentCommandService, agentCommandService =>
agentCommandService.addAgentCommands(agentCommands)
);
const workflowService = new WorkflowService(app, config.workflows);
app.addServices(workflowService);

app.waitForService(RpcService, rpcService => {
rpcService.registerEndpoint(workflowRPC);
});
},
config: packageConfigSchema
} satisfies TokenRingPlugin<typeof packageConfigSchema>;

Service Dependencies​

The workflow package requires these services to be available:

ServicePurpose
AgentCommandServiceRegisters and handles chat commands for workflow interaction
RpcServiceExposes JSON-RPC endpoints for remote workflow management
AgentManagerHandles agent spawning and lifecycle management
SubAgentServiceManages sub-agent creation and command execution

Command Registration​

The workflow package exports three separate commands via commands.ts:

import list from './commands/workflow/list.ts';
import spawn from './commands/workflow/spawn.ts';
import run from './commands/workflow/run.ts';

export default [list, spawn, run];

These commands are registered with the AgentCommandService when the plugin is installed.

Usage Examples​

Chat Commands​

List Available Workflows​

/workflow list

Displays all configured workflows with their names, descriptions, and step counts.

Run Workflow on Current Agent​

/workflow run <name>

Executes all steps in the specified workflow sequentially on the current agent. Each step is processed through the AgentCommandService.executeAgentCommand method.

Example:

/workflow run content-pipeline

This runs each step in sequence on the current agent, with the output displayed in the chat interface.

Spawn Agent and Run Workflow​

/workflow spawn <name>

Creates a new agent of the type specified in the workflow configuration, then executes all workflow steps on that new agent. Output is forwarded back to the parent agent.

Example:

/workflow spawn morning-article

The spawned agent runs independently and its output (chat, reasoning, human requests, and system output) is forwarded to the parent agent.

Programmatic Workflow Execution​

import { WorkflowService } from '@tokenring-ai/workflow';

const workflowService = app.getService(WorkflowService);

// List all workflows
const workflows = workflowService.listWorkflows();
console.log('Available workflows:', workflows.map(w => w.key));
// Output: ['morning-article', 'daily-report', 'content-pipeline']

// Get specific workflow
const workflow = workflowService.getWorkflow('morning-article');
if (workflow) {
console.log('Workflow:', workflow.name);
console.log('Steps:', workflow.steps);
console.log('Agent Type:', workflow.agentType);
}

// Spawn workflow on dedicated agent
const spawnedAgent = await workflowService.spawnWorkflow('morning-article', {
headless: true
});

console.log('Spawned agent ID:', spawnedAgent.id);
console.log('Agent name:', spawnedAgent.displayName);

Workflow Step Types​

Workflow steps can include any valid agent commands:

Tool Commands:

steps: [
"/tools enable @tokenring-ai/research/research",
"/tools enable @tokenring-ai/websearch/searchNews"
]

Chat Commands:

steps: [
"/chat Write morning market analysis",
"/chat Generate daily metrics report"
]

Mixed Commands:

steps: [
"/tools enable @tokenring-ai/database/query",
"/chat Generate daily metrics report",
"/chat Send report to team"
]

RPC Client Usage​

// Using the RPC client to interact with workflow endpoints
const workflows = await rpcClient.listWorkflows({});
const specificWorkflow = await rpcClient.getWorkflow({ name: "morning-article" });
const agent = await rpcClient.spawnWorkflow({ workflowName: "morning-article", headless: true });

Best Practices​

Workflow Design​

  1. Keep workflows focused: Design workflows for specific tasks rather than complex multi-step processes
  2. Use descriptive names: Choose clear, self-documenting workflow names and keys
  3. Test workflows incrementally: Test each step before adding more complexity
  4. Consider error handling: Include appropriate error handling in workflow steps
  5. Document workflows: Add detailed descriptions for complex workflows

Agent Spawning​

  1. Choose appropriate agent types: Select agent types that match the workflow's requirements
  2. Use headless mode for background tasks: Set headless: true for automated workflows
  3. Monitor spawned agents: Track spawned agent status and output

Configuration​

  1. Validate workflow schemas: Ensure all workflows conform to the schema before deployment
  2. Use consistent naming: Follow naming conventions for workflow keys (kebab-case recommended)
  3. Group related workflows: Organize workflows by function or domain in configuration

Error Handling​

  1. Handle workflow not found errors: Check if workflow exists before execution
  2. Monitor step execution: Track individual step success/failure
  3. Implement abort handling: Support workflow cancellation via abort signals

Testing​

Testing Setup​

The package includes comprehensive unit and integration tests using Vitest:

# Run all tests
bun test

# Run tests in watch mode
bun test --watch

# Run tests with coverage
bun test --coverage

Test Coverage​

The test file commands/workflow.test.ts includes:

  • List Command: Tests for /workflow list command output formatting
  • Run Command: Tests for sequential workflow step execution, error handling for non-existent workflows
  • Spawn Command: Tests for agent spawning with correct agent type, headless mode handling, error handling
  • Integration Tests: Full workflow execution and spawn flow scenarios
  • Abort Signal Handling: Tests for workflow cancellation via abort signals

Example Test​

import {Agent, AgentCommandService, SubAgentService} from '@tokenring-ai/agent';
import createTestingAgent from '@tokenring-ai/agent/test/createTestingAgent';
import TokenRingApp from '@tokenring-ai/app';
import createTestingApp from '@tokenring-ai/app/test/createTestingApp';
import {describe, expect, it, vi} from 'vitest';
import WorkflowService from '../WorkflowService';
import workflowRunCommand from './workflow/run.js';
import workflowSpawnCommand from './workflow/spawn.js';
import workflowListCommand from './workflow/list.js';

describe('workflow list command', () => {
let app: TokenRingApp;
let agent: Agent;
let workflowService: WorkflowService;

const mockWorkflows = {
testWorkflow: {
name: 'Test Workflow',
description: 'A test workflow',
agentType: 'test-agent',
steps: ['step1', 'step2', 'step3'],
},
};

beforeEach(() => {
app = createTestingApp();
workflowService = new WorkflowService(app, mockWorkflows);
app.addServices(workflowService);
agent = createTestingAgent(app);
});

it('should list all workflows', async () => {
const result = await workflowListCommand.execute({ agent });

expect(result).toContain('Available workflows:');
expect(result).toContain('**testWorkflow**: Test Workflow');
expect(result).toContain('A test workflow');
expect(result).toContain('Steps: 3');
});
});

describe('workflow run command', () => {
it('should execute workflow steps', async () => {
// Mock AgentCommandService
vi.spyOn(agentCommandService, 'executeAgentCommand').mockImplementation(() => Promise.resolve());

const result = await workflowRunCommand.execute({
positionals: { workflowName: 'testWorkflow' },
args: {},
agent
});

expect(result).toContain('Workflow "testWorkflow" completed');
expect(agentCommandService.executeAgentCommand).toHaveBeenCalledWith(agent, 'step1');
expect(agentCommandService.executeAgentCommand).toHaveBeenCalledWith(agent, 'step2');
expect(agentCommandService.executeAgentCommand).toHaveBeenCalledWith(agent, 'step3');
});

it('should show error for non-existent workflow', async () => {
await expect(workflowRunCommand.execute({
positionals: { workflowName: 'nonExistentWorkflow' },
args: {},
agent
})).rejects.toThrow('Workflow "nonExistentWorkflow" not found.');
});
});

describe('workflow spawn command', () => {
it('should spawn agent and run workflow', async () => {
const mockRunSubAgent = vi.fn().mockResolvedValue({
id: 'spawned-agent-123',
name: 'Spawned Agent',
config: { description: 'Spawned agent description' },
});

vi.spyOn(subAgentService, 'runSubAgent').mockImplementation(mockRunSubAgent);

const result = await workflowSpawnCommand.execute({
positionals: { workflowName: 'testWorkflow' },
args: {},
agent
});

expect(result).toContain('Spawned agent for workflow: Test Workflow');
expect(subAgentService.runSubAgent).toHaveBeenCalledWith(
expect.objectContaining({
agentType: 'test-agent',
input: {
from: 'Workflow testWorkflow',
message: '/workflow run testWorkflow'
},
headless: false,
parentAgent: agent
})
);
});
});

Example Test​

import {Agent, AgentCommandService} from '@tokenring-ai/agent';
import createTestingAgent from '@tokenring-ai/agent/test/createTestingAgent';
import TokenRingApp from '@tokenring-ai/app';
import createTestingApp from '@tokenring-ai/app/test/createTestingApp';
import WorkflowService from '../WorkflowService';
import listCommand from './commands/workflow/list.ts';
import runCommand from './commands/workflow/run.ts';
import spawnCommand from './commands/workflow/spawn.ts';

describe('workflow list command', () => {
let app: TokenRingApp;
let agent: Agent;
let workflowService: WorkflowService;

const mockWorkflows = {
testWorkflow: {
name: 'Test Workflow',
description: 'A test workflow',
agentType: 'test-agent',
steps: ['step1', 'step2', 'step3'],
},
};

beforeEach(() => {
app = createTestingApp();
workflowService = new WorkflowService(app, mockWorkflows);
app.addServices(workflowService);
agent = createTestingAgent(app);
});

it('should list workflows', async () => {
const result = await listCommand.execute('', agent);
expect(result).toContain('Available workflows');
expect(result).toContain('testWorkflow');
expect(result).toContain('Test Workflow');
});
});

describe('workflow run command', () => {
// Test implementation for run command
});

describe('workflow spawn command', () => {
// Test implementation for spawn command
});

Dependencies​

Production Dependencies​

  • @tokenring-ai/app (0.2.0) - Base application framework
  • @tokenring-ai/agent (0.2.0) - Agent orchestration and management
  • @tokenring-ai/chat (0.2.0) - Chat service integration
  • @tokenring-ai/rpc (0.2.0) - JSON-RPC endpoint management
  • @tokenring-ai/utility (0.2.0) - Utility functions and helpers
  • zod (^4.3.6) - Schema validation

Development Dependencies​

  • vitest (^4.1.1) - Testing framework
  • typescript (^6.0.2) - TypeScript compiler
ComponentPurpose
@tokenring-ai/agentAgent system and command execution
@tokenring-ai/appBase application framework and service management
@tokenring-ai/rpcJSON-RPC endpoint management
@tokenring-ai/utilityUtility functions and helpers
@tokenring-ai/researchResearch tools for workflow steps
@tokenring-ai/websearchWeb search tools for workflow steps
@tokenring-ai/databaseDatabase query tools for workflow steps
@tokenring-ai/chatChat service for workflow step commands

Development​

Package Structure​

pkg/workflow/
├── index.ts # Main exports (WorkflowService, WorkflowItem)
├── plugin.ts # Plugin definition for TokenRing integration
├── package.json # Dependencies and scripts
├── README.md # Package-level documentation
├── schema.ts # Zod schema definitions
├── WorkflowService.ts # Core service implementation
├── vitest.config.ts # Vitest configuration
├── commands.ts # Command registry (exports list, run, spawn commands)
├── commands/
│ └── workflow/
│ ├── list.ts # /workflow list command implementation
│ ├── run.ts # /workflow run command implementation
│ └── spawn.ts # /workflow spawn command implementation
├── rpc/
│ ├── schema.ts # JSON-RPC schema definition
│ └── workflow.ts # RPC endpoint implementation
└── commands/
└── workflow.test.ts # Unit tests for chat commands

Building​

TypeScript compilation verification:

bun build

Testing Commands​

# Run all tests
bun test

# Run tests in watch mode
bun test --watch

# Run tests with coverage
bun test --coverage

Error Types​

The workflow commands use the following error types:

  • CommandFailedError: Thrown when workflow execution fails (e.g., workflow not found, missing arguments)

Example Error Handling:

import {CommandFailedError} from '@tokenring-ai/agent/AgentError';

try {
await runCommand.execute('run nonexistent', agent);
} catch (error) {
if (error instanceof CommandFailedError) {
console.error('Workflow execution failed:', error.message);
}
}

License​

MIT License - see LICENSE for details.