Services/Observability

Sessions & Debugging

Debug agents using session IDs, CLI commands, and trace timelines

Every request to your agents gets a unique session ID (sess_...). Sessions link logs, traces, and state, making them essential for debugging.

Sessions vs Threads

ScopeLifetimeID PrefixUse For
SessionSingle requestsess_Debugging, request-scoped state
Thread1 hour (conversation)thrd_Chat history, user preferences

A thread contains multiple sessions. When a user has a multi-turn conversation, each message creates a new session within the same thread.

Quick Mental Model

Threads "wrap" sessions. Think of a thread as a conversation and a session as one message in that conversation.

Accessing Session ID

import { createAgent } from '@agentuity/runtime';
 
const agent = createAgent('SessionExample', {
  handler: async (ctx, input) => {
    // In agents
    ctx.logger.info('Processing request', { sessionId: ctx.sessionId });
 
    // Thread ID for conversation tracking
    ctx.logger.info('Thread context', { threadId: ctx.thread.id });
 
    return { sessionId: ctx.sessionId };
  },
});

In routes:

import { createRouter } from '@agentuity/runtime';
import myAgent from '@agent/my-agent';
 
const router = createRouter();
 
router.post('/', myAgent.validator(), async (c) => {
  const input = c.req.valid('json');
 
  // In routes
  c.var.logger.info('Route called', { sessionId: c.var.sessionId });
 
  const result = await myAgent.run(input);
 
  return c.json({ ...result, sessionId: c.var.sessionId });
});
 
export default router;

Viewing Session Logs

Use the CLI to view logs for a specific session:

agentuity cloud session logs sess_abc123xyz

See CLI Reference for additional session commands.

Including Session ID in Responses

For easier debugging, include the session ID in error responses:

const agent = createAgent('ErrorHandler', {
  handler: async (ctx, input) => {
    try {
      const result = await processRequest(input);
      return { success: true, data: result };
    } catch (error) {
      ctx.logger.error('Request failed', {
        sessionId: ctx.sessionId,
        error: error.message,
      });
 
      return {
        success: false,
        error: 'Processing failed',
        sessionId: ctx.sessionId,  // Helpful for debugging
      };
    }
  },
});

Linking External Logs

If you use external logging services, include the session ID so you can connect logs across systems:

const agent = createAgent('WebhookHandler', {
  handler: async (ctx, input) => {
    // Create a logger with session context for external services
    const requestLogger = ctx.logger.child({
      sessionId: ctx.sessionId,
      threadId: ctx.thread.id,
      service: 'webhook-handler',
    });
 
    requestLogger.info('Processing webhook', { eventType: input.event });
 
    // External service call with session context
    await externalApi.process({
      ...input,
      metadata: { agentuitySessionId: ctx.sessionId },
    });
 
    return { success: true };
  },
});

Session State

Use session state for request-scoped data that doesn't persist:

const agent = createAgent('TimingExample', {
  handler: async (ctx, input) => {
    // Track timing within this request
    ctx.session.state.set('startTime', Date.now());
 
    const result = await processRequest(input);
 
    const duration = Date.now() - (ctx.session.state.get('startTime') as number);
    ctx.logger.info('Request completed', { durationMs: duration });
 
    return result;
  },
});

Session state is cleared after the response. For persistent data, use thread state or KV storage.

Thread and Session Metadata

Store unencrypted metadata for filtering and querying. Unlike state (which is encrypted), metadata is stored as-is with database indexes for efficient lookups.

import { createAgent } from '@agentuity/runtime';
 
const agent = createAgent('UserContext', {
  handler: async (ctx, input) => {
    // Thread metadata persists across sessions within a thread
    ctx.thread.metadata.userId = input.userId;
    ctx.thread.metadata.department = 'sales';
    ctx.thread.metadata.plan = 'enterprise';
 
    // Session metadata is request-scoped
    ctx.session.metadata.requestType = 'chat';
    ctx.session.metadata.clientVersion = input.clientVersion;
    ctx.session.metadata.source = 'web';
 
    ctx.logger.info('Request context', {
      threadId: ctx.thread.id,
      userId: ctx.thread.metadata.userId,
      requestType: ctx.session.metadata.requestType,
    });
 
    return { success: true };
  },
});
 
export default agent;

Metadata vs State

AspectMetadataState
StorageUnencrypted, indexedEncrypted
Use caseFiltering, querying, analyticsSensitive data, conversation history
Accessctx.thread.metadata / ctx.session.metadatactx.thread.state / ctx.session.state
Best forUser IDs, request types, feature flagsMessages, preferences, tokens

When to Use Metadata

Use metadata for values you might filter or query on later, like user IDs, request types, or client versions. Use state for data that should remain private, like conversation history or user preferences.

Best Practices

  • Include session ID in logs and error responses: Makes it easy to trace issues back to specific requests
  • Use structured logging: Add context to logs for easier filtering
  • Create child loggers: Add session context to component-specific loggers

Next Steps

Need Help?

Join our DiscordCommunity for assistance or just to hang with other humans building agents.

Send us an email at hi@agentuity.com if you'd like to get in touch.

Please Follow us on

If you haven't already, please Signup for your free account now and start building your first agent!