@helix-agents/runtime-js
JavaScript runtime for in-process agent execution. Non-durable execution suitable for development, testing, and single-process deployments.
Installation
npm install @helix-agents/runtime-jsJSAgentExecutor
The main executor class for running agents in-process.
Constructor
import { JSAgentExecutor } from '@helix-agents/runtime-js';
const executor = new JSAgentExecutor(
stateStore, // StateStore implementation
streamManager, // StreamManager implementation
llmAdapter // LLMAdapter implementation
);execute
Start executing an agent.
const handle = await executor.execute(MyAgent, 'Hello, agent!');
// Or with initial state
const handle = await executor.execute(MyAgent, {
message: 'Hello',
state: { userId: 'user-123' },
});Parameters:
agent- Agent configuration fromdefineAgent()input- String message or{ message: string, state?: Partial<TState> }
Returns: AgentExecutionHandle<TOutput>
getHandle
Get a handle to an existing execution.
const handle = await executor.getHandle(MyAgent, 'run-123');
if (handle) {
const result = await handle.result();
}Parameters:
agent- Agent configurationrunId- Run identifier
Returns: AgentExecutionHandle | null
canResume
Check if an execution can be resumed.
const result = await executor.canResume(MyAgent, 'run-123');
if (result.canResume) {
// Can continue execution
} else {
console.log('Cannot resume:', result.reason);
}Returns:
interface CanResumeResult {
canResume: boolean;
reason?: string;
state?: AgentState;
}resume
Resume a paused or interrupted execution.
const handle = await executor.resume(MyAgent, 'run-123', {
additionalMessage: 'Continue with this context',
});AgentExecutionHandle
Handle returned by execute() for interacting with a running agent.
Properties
handle.runId; // Unique run identifier (readonly)stream
Get a stream of events from the execution. Returns null if streaming is not available.
const stream = await handle.stream();
if (stream) {
for await (const chunk of stream) {
switch (chunk.type) {
case 'text_delta':
process.stdout.write(chunk.delta);
break;
case 'tool_start':
console.log(`Tool: ${chunk.toolName}`);
break;
case 'tool_end':
console.log(`Result: ${JSON.stringify(chunk.result)}`);
break;
}
}
}result
Wait for the execution to complete and get the result.
const result = await handle.result();
if (result.status === 'completed') {
console.log('Output:', result.output);
} else if (result.status === 'failed') {
console.error('Error:', result.error);
}Returns:
interface AgentResult<TOutput> {
status: 'running' | 'completed' | 'failed' | 'paused' | 'waiting_tool';
output?: TOutput;
error?: string;
}abort
Cancel the execution.
await handle.abort('User requested cancellation');Usage Example
import { JSAgentExecutor } from '@helix-agents/runtime-js';
import { InMemoryStateStore, InMemoryStreamManager } from '@helix-agents/store-memory';
import { VercelAIAdapter } from '@helix-agents/llm-vercel';
import { defineAgent, defineTool } from '@helix-agents/core';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
// Define agent
const MyAgent = defineAgent({
name: 'my-agent',
systemPrompt: 'You are a helpful assistant.',
outputSchema: z.object({ response: z.string() }),
tools: [
defineTool({
name: 'greet',
description: 'Generate a greeting',
inputSchema: z.object({ name: z.string() }),
outputSchema: z.object({ greeting: z.string() }),
execute: async ({ name }) => ({ greeting: `Hello, ${name}!` }),
}),
],
llmConfig: { model: openai('gpt-4o-mini') },
});
// Create executor
const executor = new JSAgentExecutor(
new InMemoryStateStore(),
new InMemoryStreamManager(),
new VercelAIAdapter()
);
// Execute agent
const handle = await executor.execute(MyAgent, 'Greet John');
// Stream results
for await (const chunk of (await handle.stream()) ?? []) {
if (chunk.type === 'text_delta') {
process.stdout.write(chunk.delta);
}
}
// Get final result
const result = await handle.result();
console.log('\nOutput:', result.output);Behavior
Parallel Tool Execution
The JS runtime executes independent tool calls in parallel:
// If LLM requests multiple tool calls, they run concurrently
const tools = [searchTool, fetchTool, analyzeTool];
// All three execute in parallelSub-Agent Handling
Sub-agents are executed recursively in the same process:
// When a sub-agent tool is invoked:
// 1. New execution context is created
// 2. Sub-agent runs to completion
// 3. Result is returned to parentAbort Handling
Abort signals are propagated to tool executions:
defineTool({
execute: async (input, context) => {
// Check abort signal
if (context.abortSignal?.aborted) {
throw new Error('Execution aborted');
}
// Long-running operation
await doWork();
},
});Limitations
- No crash recovery - If the process dies, execution state is lost
- Single process - No distribution across workers
- No timeout isolation - Tool timeouts must be handled manually
For production workloads requiring durability, use @helix-agents/runtime-temporal.