Running Agents Without HTTP — Agentuity Documentation

Running Agents Without HTTP

Execute agents programmatically for cron jobs, bots, CLI tools, and background workers

Sometimes your agent logic needs to run without an incoming HTTP request. createAgentContext() gives standalone code the same infrastructure that HTTP handlers get automatically: tracing, sessions, and storage access.

Basic Usage

import { createAgentContext } from '@agentuity/runtime';
import chatAgent from '@agent/chat';
 
const ctx = createAgentContext();
const result = await ctx.run(chatAgent, { message: 'Hello' });

The run() method executes your agent with full infrastructure support: tracing, session management, and access to all storage services.

For agents that don't require input:

const result = await ctx.run(statusAgent);

Options

OptionTypeDescription
sessionIdstringCustom session ID. Auto-generated from trace context if not provided
triggerstringTrigger type for telemetry: 'discord', 'cron', 'websocket', 'manual'
threadThreadCustom thread for conversation state
sessionSessionCustom session instance
parentContextContextParent OpenTelemetry context for distributed tracing

External Cron Job Example

For scheduled tasks managed outside of Agentuity:

import { createApp, createAgentContext } from '@agentuity/runtime';
import cron from 'node-cron';
import cleanupAgent from '@agent/cleanup';
 
await createApp();
 
// Run cleanup every hour
cron.schedule('0 * * * *', async () => {
  const ctx = createAgentContext({ trigger: 'cron' });
  await ctx.run(cleanupAgent, { task: 'expired-sessions' });
});

Multiple Agents in Sequence

Run multiple agents in sequence with the same context:

const ctx = createAgentContext();
 
// First agent analyzes the input
const analysis = await ctx.run(analyzeAgent, { text: userInput });
 
// Second agent generates response based on analysis
const response = await ctx.run(respondAgent, {
  analysis: analysis.summary,
  sentiment: analysis.sentiment,
});

Each ctx.run() call gets its own session and tracing span, while sharing the logger, tracer, and app state.

Reusing Contexts

Create a context once and reuse it for multiple invocations:

const ctx = createAgentContext({ trigger: 'websocket' });
 
// Each run gets its own session and tracing span
websocket.on('message', async (data) => {
  const result = await ctx.run(messageAgent, data);
  websocket.send(result);
});

Using invoke() Directly

ctx.run() is syntactic sugar over ctx.invoke(). Use invoke() when you need more control:

  • Run arbitrary async functions (not just agents) within agent context
  • Execute multiple agents in a single invocation with a shared session and tracing span
  • Set a custom OpenTelemetry span name

Running Arbitrary Functions

const ctx = createAgentContext({ trigger: 'cron' });
 
await ctx.invoke(async () => {
  // Any async code runs with full agent infrastructure
  const data = await ctx.kv.get('config', 'settings');
  await ctx.stream.create('audit', {
    contentType: 'text/plain',
  });
  ctx.logger.info('Cron task complete');
});

Multiple Agents in One Invocation

When agents share a session and tracing span:

const ctx = createAgentContext();
 
const result = await ctx.invoke(async () => {
  const analysis = await analyzeAgent.run({ text: userInput });
  const response = await respondAgent.run({
    analysis: analysis.summary,
  });
  return response;
});

Both agent calls share the same OpenTelemetry span, session, and thread. Compare this with calling ctx.run() twice, where each call gets its own session and span.

Custom Span Names

const result = await ctx.invoke(() => agent.run(input), { spanName: 'daily-cleanup' });

invoke() Options

OptionTypeDefaultDescription
spanNamestring'agent-invocation'Span name for this invocation

Invocation Lifecycle

Each call to ctx.run() or ctx.invoke() follows this lifecycle:

If background tasks are pending (waitUntil), session save and the complete event happen after all tasks finish. Errors during execution set the span status to ERROR and include the error message in the session complete event.

What's Included

Detecting Context

Use inAgentContext() to check if code is running inside an agent handler with ambient context available:

import { inAgentContext, createAgentContext } from '@agentuity/runtime';
import myAgent from '@agent/my-agent';
 
async function processRequest(data: unknown) {
  if (inAgentContext()) {
    // Inside an agent handler, ambient context is available
    // so agent.run() works directly without explicit context
    return myAgent.run(data);
  }
 
  // Outside agent handler, create context first
  const ctx = createAgentContext();
  return ctx.run(myAgent, data);
}

This is useful for writing utility functions that work both inside agent handlers and in standalone scripts.

To check if the Agentuity runtime is initialized (but not necessarily inside a handler), use isInsideAgentRuntime() instead.

Next Steps