Worker mode allows an agent to dispatch complex, long-running tasks to background "copy" agents while the main agent stays fully interactive. When a worker finishes, its result is automatically surfaced back to the user through the main agent's conversation.
This mode is ideal when users submit tasks that take a while (research, code generation, multi-step analysis) and you want them to continue chatting or submit more tasks without blocking.
See the Worker Mode Example for a complete, runnable implementation.
import { Agent, TAgentMode } from '@smythos/sdk';
const agent = new Agent({
name: 'Research Assistant',
behavior: 'You are a research assistant capable of handling complex tasks.',
model: 'gpt-4o',
mode: TAgentMode.WORKER,
});
When the user asks for something complex, the main agent's LLM evaluates the request:
_sre_Worker_Dispatch skill.The main agent immediately confirms the dispatch and remains available for new prompts.
Each worker is a "copy" of the main agent — it shares the same skills and model but runs independently in its own chat session. The copy agent:
_sre_* skills)<worker_result> and <worker_question> tags for communicationWorkers communicate back to the main agent through two tag-based protocols:
<worker_result>...</worker_result>: The worker wraps its final output in this tag when the task is complete.<worker_question>...</worker_question>: The worker wraps a follow-up question in this tag if it needs clarification from the user.When a worker completes or has a question, the result is automatically injected into the main agent's active chat session. The main agent's LLM then presents it naturally to the user — no manual polling needed.
The injection happens via an internal queue mechanism:
CHECK_WORKERS_RESULTS_QUEUE or CHECK_WORKERS_QUESTIONS_QUEUE) is sent to the main agentTLLMEvent.Content events — visible to the consumer like any other responseUp to 3 workers can run simultaneously. Additional dispatches are queued and start automatically when a running worker completes.
Worker mode emits events on the agent object for real-time visibility into background task progress.
Fired when a task is dispatched to a background worker.
agent.on('WorkerDispatched', ({ jobId, task }) => {
console.log(`Task dispatched: ${jobId}`);
console.log(`Description: ${task}`);
});
| Field | Type | Description |
|---|---|---|
jobId |
string |
Unique identifier for the job |
task |
string |
The task description sent to the worker |
Fired when a worker successfully completes its task.
agent.on('WorkerCompleted', ({ jobId, result }) => {
console.log(`Completed: ${jobId}`);
console.log(`Result: ${result}`);
});
| Field | Type | Description |
|---|---|---|
jobId |
string |
The job identifier |
result |
string |
The worker's final result text |
Fired when a worker encounters an error.
agent.on('WorkerFailed', ({ jobId, error }) => {
console.error(`Failed: ${jobId} — ${error}`);
});
| Field | Type | Description |
|---|---|---|
jobId |
string |
The job identifier |
error |
string |
Error message |
Fired when a worker needs clarification from the user.
agent.on('WorkerQuestion', ({ jobId, questionId, question }) => {
console.log(`Worker ${jobId} asks: ${question}`);
});
| Field | Type | Description |
|---|---|---|
jobId |
string |
The job identifier |
questionId |
string |
Unique identifier for the question |
question |
string |
The question text |
Fired when a user's answer is relayed back to the worker.
agent.on('WorkerAnswered', ({ jobId, answer }) => {
console.log(`Answer sent to ${jobId}: ${answer}`);
});
| Field | Type | Description |
|---|---|---|
jobId |
string |
The job identifier |
answer |
string |
The user's answer |
Fired when a job is cancelled.
agent.on('WorkerCancelled', ({ jobId }) => {
console.log(`Cancelled: ${jobId}`);
});
| Field | Type | Description |
|---|---|---|
jobId |
string |
The job identifier |
A catch-all event that fires on every status transition, alongside the more specific events above.
agent.on('WorkerStatusChanged', ({ jobId, status }) => {
console.log(`${jobId}: ${status}`);
});
| Field | Type | Description |
|---|---|---|
jobId |
string |
The job identifier |
status |
string |
New status: 'running', 'waiting_for_input', 'completed', 'failed', or 'cancelled' |
| Event | Payload | When |
|---|---|---|
WorkerDispatched |
{ jobId, task } |
Task sent to a background worker |
WorkerCompleted |
{ jobId, result } |
Worker finished successfully |
WorkerFailed |
{ jobId, error } |
Worker encountered an error |
WorkerQuestion |
{ jobId, questionId, question } |
Worker needs user clarification |
WorkerAnswered |
{ jobId, answer } |
User's answer relayed to worker |
WorkerCancelled |
{ jobId } |
Job was cancelled |
WorkerStatusChanged |
{ jobId, status } |
Any status transition (fires with every event above) |
Dispatched ──→ Running ──→ Completed
│ ↑ │
│ │ (auto-surface)
↓ │
Waiting for Input
│
↓
(answered) ──→ Running ──→ ...
Any state ──→ Cancelled (via user request)
Running ──→ Failed (on error)
import { Agent, TAgentMode, TLLMEvent } from '@smythos/sdk';
import * as readline from 'readline';
const agent = new Agent({
name: 'Research Assistant',
behavior: `You are a helpful assistant. For simple questions, answer directly.
For complex research tasks, dispatch them to a background worker.`,
model: 'gpt-4o',
mode: TAgentMode.WORKER,
});
// Add skills that workers can use
agent.addSkill({
name: 'WebSearch',
description: 'Search the web for information',
process: async ({ query }) => {
// Your search implementation here
return { results: [{ title: `Result for: ${query}` }] };
},
});
// Track worker activity
agent.on('WorkerDispatched', ({ jobId, task }) => {
console.log(`\n⚡ Worker started: ${task.substring(0, 60)}...`);
});
agent.on('WorkerCompleted', ({ jobId }) => {
console.log(`\n✅ Worker ${jobId} completed`);
});
// Create a chat session
const chat = agent.chat({ persist: false });
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'You: ',
});
// Listen on the Chat object for all output (user-initiated + auto-surfaced)
let first = true;
chat.on(TLLMEvent.Content, (content) => {
if (first) {
process.stdout.write('\n🤖 Assistant: ');
first = false;
}
process.stdout.write(content);
});
chat.on(TLLMEvent.End, () => {
if (!first) {
first = true;
console.log('\n');
rl.prompt();
}
});
// Handle user input
rl.on('line', (input) => {
if (input.trim() === '') return rl.prompt();
chat.prompt(input).stream();
});
rl.prompt();
Note: We listen on
chat.on(TLLMEvent.Content, ...)rather than on the per-prompt emitter returned by.stream(). This ensures we see both user-initiated responses and auto-surfaced worker results, sinceChatCommand.stream()emits events on the Chat object for every prompt — including internally injected ones.
Worker mode pairs naturally with Planner mode. The agent plans the work and dispatches complex subtasks to background workers:
const agent = new Agent({
name: 'Research Assistant',
model: 'gpt-4o',
mode: [TAgentMode.PLANNER, TAgentMode.WORKER],
});
See the Combined Example for a full implementation.
For advanced use cases, the WorkerJob interface is exported from the SDK:
import { WorkerJob } from '@smythos/sdk';
| Field | Type | Description |
|---|---|---|
id |
string |
Unique job identifier |
task |
string |
The dispatched task description |
status |
string |
Current status |
createdAt |
number |
Timestamp of creation |
completedAt |
number? |
Timestamp of completion |
result |
string? |
Final result (when completed) |
error |
string? |
Error message (when failed) |
pendingQuestion |
object? |
{ questionId, text } when waiting for input |
interactions |
array |
Full log of prompts and responses |
partialResult |
string |
Accumulated response so far (while running) |
currentStep |
string? |
Description of current tool call |