> ## Documentation Index
> Fetch the complete documentation index at: https://docs.standardagents.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Utilities

> Common utility functions for working with ThreadState

## Overview

Standard Agents provides utility functions that wrap common ThreadState operations. While you can call ThreadState methods directly, these utilities are exported from `@standardagents/builder` for convenience and backwards compatibility.

> [!NOTE]
>
> Most utilities are now methods on ThreadState. You can use either the standalone functions or call methods directly on state.

## queueTool

Queue a tool call for execution in the current thread.

```typescript theme={null}
// Using the utility function
import { queueTool } from "@standardagents/builder";
queueTool(state, 'tool_name', { arg: 'value' });

// Or call directly on state
state.queueTool('tool_name', { arg: 'value' });
```

**Parameters:**

* `state`: The ThreadState context
* `toolName`: The name of the tool to call
* `args`: Arguments to pass to the tool

**Example:**

```typescript theme={null}
import { defineTool } from "@standardagents/spec";
import { z } from "zod";

export default defineTool({
  description: "Process user order and send confirmation",
  args: z.object({
    orderId: z.string(),
    userId: z.string(),
  }),
  execute: async (state, args) => {
    // Validate the order
    const order = await validateOrder(args.orderId);

    if (!order.valid) {
      return {
        status: "error",
        error: "Order validation failed",
      };
    }

    // Queue additional tools to execute after this one
    state.queueTool("charge_payment", {
      orderId: args.orderId,
      amount: order.total,
    });

    state.queueTool("send_confirmation_email", {
      userId: args.userId,
      orderId: args.orderId,
    });

    state.queueTool("update_inventory", {
      items: order.items,
    });

    return {
      status: "success",
      result: `Order ${args.orderId} queued for processing`,
    };
  },
});
```

***

## invokeTool

Invoke a tool directly and wait for the result.

```typescript theme={null}
const result = await state.invokeTool('tool_name', { arg: 'value' });

if (result.status === 'success') {
  console.log(result.result);
}
```

**Parameters:**

* `toolName`: The name of the tool to invoke
* `args`: Arguments to pass to the tool

**Returns:** `Promise<ToolResult>`

***

## injectMessage

Inject a message into the thread conversation.

```typescript theme={null}
const message = await state.injectMessage({
  role: 'user',
  content: 'Additional context...',
  silent: true,
  metadata: { source: 'tool' },
});
```

**Options:**

```typescript theme={null}
interface InjectMessageInput {
  role: 'user' | 'assistant' | 'system';
  content: string;
  silent?: boolean;          // Hide from UI
  metadata?: Record<string, unknown>;
}
```

`injectMessage()` persists the record and runs `before_create_message` and
`after_create_message` in active and at-rest contexts. During execution, later
tools and hooks immediately see it in `state.execution.messageHistory`.
`silent` controls ordinary UI presentation; silent records still participate in
the request context. A context assembly uses one stable history snapshot,
so injection from `filter_messages` or `prefilter_llm_history` becomes eligible
for the following assembly.

**Examples:**

> [tab] System Context
>
> ```typescript theme={null}
> export default defineTool({
>   description: "Load user context",
>   args: z.object({ userId: z.string() }),
>   execute: async (state, args) => {
>     const user = await fetchUser(args.userId);
>
>     await state.injectMessage({
>       role: "system",
>       content: `User context loaded: ${user.name} (${user.email})
>         - Account tier: ${user.tier}
>         - Preferences: ${JSON.stringify(user.preferences)}`,
>     });
>
>     return {
>       status: "success",
>       result: "User context injected",
>     };
>   },
> });
> ```

> [tab] Silent Message
>
> ```typescript theme={null}
> export default defineTool({
>   description: "Add internal note",
>   args: z.object({ note: z.string() }),
>   execute: async (state, args) => {
>     await state.injectMessage({
>       role: "system",
>       content: args.note,
>       silent: true,  // Hidden from ordinary UI lists
>     });
>
>     return {
>       status: "success",
>       result: "Internal note added",
>     };
>   },
> });
> ```

***

## getMessages

Retrieve message history from the thread.

```typescript theme={null}
const { messages, total, hasMore } = await state.getMessages({
  limit: 50,
  offset: 0,
  order: 'desc',
});
```

**Options:**

| Option | Type | Default | Description |
| - | - | - | - |
| `limit` | `number` | - | Max messages to return |
| `offset` | `number` | 0 | Messages to skip |
| `order` | `'asc' \| 'desc'` | `'desc'` | Sort order |
| `includeSilent` | `boolean` | false | Include silent messages |
| `maxDepth` | `number` | - | Max sub-prompt nesting |

**Example:**

```typescript theme={null}
export default defineTool({
  description: "Analyze recent conversation tone",
  execute: async (state) => {
    // Get last 10 messages
    const { messages } = await state.getMessages({ limit: 10 });

    // Filter user and assistant messages only
    const conversationMessages = messages.filter(
      (m) => m.role === "user" || m.role === "assistant"
    );

    const tone = analyzeTone(conversationMessages);

    return {
      status: "success",
      result: `Conversation tone: ${tone}`,
    };
  },
});
```

***

## emit (Custom Events)

Send custom events to WebSocket clients.

```typescript theme={null}
state.emit('event_type', {
  key: 'value',
  progress: 50,
});
```

**Parameters:**

* `event`: Event type name
* `data`: Event payload (must be JSON-serializable). When it is `undefined`, clients receive `"data": null`.

**Event Structure (received by clients):**

```json theme={null}
{
  "type": "event",
  "eventType": "<your-type>",
  "data": <your-data>,
  "timestamp": 1234567890
}
```

**Examples:**

> [tab] Progress Updates
>
> ```typescript theme={null}
> export default defineTool({
>   description: "Process large dataset",
>   args: z.object({ datasetId: z.string() }),
>   execute: async (state, args) => {
>     const dataset = await loadDataset(args.datasetId);
>     const total = dataset.length;
>
>     for (let i = 0; i < total; i++) {
>       await processItem(dataset[i]);
>
>       // Emit progress event every 10 items
>       if (i % 10 === 0) {
>         state.emit("progress", {
>           current: i + 1,
>           total,
>           percentage: ((i + 1) / total) * 100,
>           message: `Processing item ${i + 1} of ${total}`,
>         });
>       }
>     }
>
>     return {
>       status: "success",
>       result: `Processed ${total} items`,
>     };
>   },
> });
> ```

> [tab] Status Updates
>
> ```typescript theme={null}
> export default defineTool({
>   description: "Upload file",
>   args: z.object({ fileUrl: z.string() }),
>   execute: async (state, args) => {
>     state.emit("upload_status", {
>       status: "started",
>       message: "Starting download...",
>     });
>
>     const file = await downloadFile(args.fileUrl);
>
>     state.emit("upload_status", {
>       status: "processing",
>       message: "Processing file...",
>     });
>
>     const result = await processFile(file);
>
>     state.emit("upload_status", {
>       status: "complete",
>       message: "Upload complete!",
>     });
>
>     return {
>       status: "success",
>       result: "File processed",
>     };
>   },
> });
> ```

### Frontend Integration

Listen for events on the frontend using `@standardagents/react`:

```tsx theme={null}
import {
  StandardAgentsProvider,
  ThreadProvider,
  useThread,
} from "@standardagents/react";

function App() {
  return (
    <StandardAgentsProvider config={{ endpoint: "https://api.example.com" }}>
      <ThreadProvider threadId="123e4567-e89b-12d3-a456-426614174000">
        <OrderTracker />
      </ThreadProvider>
    </StandardAgentsProvider>
  );
}

function OrderTracker() {
  const { onCustomEvent } = useThread();

  // Listen for custom events
  useEffect(() => {
    const unsubscribe = onCustomEvent('progress', (data) => {
      console.log('Progress:', data.percentage);
    });
    return unsubscribe;
  }, [onCustomEvent]);

  return <div>...</div>;
}
```

***

## forceTurn (Execution Control)

Force the next execution turn to a specific side (for dual\_ai agents).

```typescript theme={null}
// Only available during execution
if (state.execution) {
  state.execution.forceTurn('b');
}
```

**Parameters:**

* `side`: The side to force (`'a'` or `'b'`)

> [!NOTE]
>
> This is primarily used for dual\_ai agents to control turn order.

***

## stop (Execution Control)

Stop the current execution after the current operation completes.

```typescript theme={null}
if (state.execution) {
  state.execution.stop();
}
```

***

## scheduleEffect

Schedule an effect for delayed or background execution.

```typescript theme={null}
const effectId = await state.scheduleEffect(
  'send_reminder_email',
  { to: 'user@example.com', subject: 'Reminder' },
  30 * 60 * 1000  // 30 minutes
);
```

**Parameters:**

* `name`: Effect name (file in `agents/effects/`)
* `args`: Arguments passed to effect handler
* `delay`: Delay in milliseconds (default: 0)

**Returns:** Effect ID (UUID) for later cancellation

***

## Complete Example

Here's a comprehensive example showing multiple utilities working together:

```typescript theme={null}
import { defineTool } from "@standardagents/spec";
import { z } from "zod";

export default defineTool({
  description: "Complete order processing workflow",
  args: z.object({
    orderId: z.string(),
    userId: z.string(),
    notifyUser: z.boolean().default(true),
  }),
  execute: async (state, args) => {
    // Emit initial status event
    state.emit("order_status", {
      orderId: args.orderId,
      status: "started",
    });

    // Inject system message for context
    await state.injectMessage({
      role: "system",
      content: `Processing order ${args.orderId} for user ${args.userId}`,
    });

    // Queue validation tool
    state.queueTool("validate_order", {
      orderId: args.orderId,
    });

    // Queue payment processing
    state.queueTool("process_payment", {
      orderId: args.orderId,
      userId: args.userId,
    });

    // Queue inventory update
    state.queueTool("update_inventory", {
      orderId: args.orderId,
    });

    // Conditionally queue notification
    if (args.notifyUser) {
      state.queueTool("send_notification", {
        userId: args.userId,
        type: "order_confirmation",
        orderId: args.orderId,
      });
    }

    // Get conversation history for audit
    const { messages } = await state.getMessages({ limit: 50 });
    const orderMessages = messages.filter(
      (m) => m.content?.includes(args.orderId)
    );

    // Emit completion event
    state.emit("order_status", {
      orderId: args.orderId,
      status: "queued",
      steps: 4,
      relatedMessages: orderMessages.length,
    });

    return {
      status: "success",
      result: `Order ${args.orderId} workflow initiated`,
    };
  },
});
```

## Best Practices

> [accordion] Error Handling
>
> Always wrap async operations in try-catch blocks:
>
> ```typescript theme={null}
> try {
>   await state.injectMessage({
>     role: "system",
>     content: "Context updated",
>   });
> } catch (error) {
>   console.error("Failed to inject message:", error);
>   return {
>     status: "error",
>     error: `Message injection failed: ${error.message}`,
>   };
> }
> ```

> [accordion] Throttle Event Emissions
>
> Avoid excessive event emissions that could overwhelm WebSocket clients:
>
> ```typescript theme={null}
> // Good: Throttle emissions
> for (let i = 0; i < 10000; i++) {
>   if (i % 100 === 0) {
>     state.emit("progress", {
>       current: i,
>       total: 10000,
>     });
>   }
> }
>
> // Avoid: Emitting inside tight loop
> for (let i = 0; i < 10000; i++) {
>   state.emit("progress", { value: i });
> }
> ```

> [accordion] Message Pagination
>
> When retrieving large message histories, use pagination:
>
> ```typescript theme={null}
> const pageSize = 50;
> let offset = 0;
> let hasMore = true;
>
> while (hasMore) {
>   const result = await state.getMessages({ limit: pageSize, offset });
>   await processMessages(result.messages);
>   hasMore = result.hasMore;
>   offset += pageSize;
> }
> ```

> [accordion] Check Execution State
>
> When using execution-specific features, check if execution is active:
>
> ```typescript theme={null}
> if (state.execution) {
>   console.log(`Step: ${state.execution.stepCount}`);
>   state.execution.forceTurn('b');
> }
> ```

## Troubleshooting

> [accordion] Events not received by frontend
>
> **Solution:** Ensure you're using `ThreadProvider` and that you've subscribed to the correct event type name.

> [accordion] Tool not executing after queueTool
>
> **Solution:** Tools are queued for sequential execution. Ensure the current tool completes successfully and doesn't stop execution.

> [accordion] getMessages returns empty
>
> **Solution:** Check that messages exist in the thread and that limit/offset parameters are correct.

## Next Steps

> [card] [ThreadState](https://docs.standardagents.ai/core-concepts/flowstate.md)
>
> Understand the ThreadState interface

> [card] [Tools](https://docs.standardagents.ai/core-concepts/tools.md)
>
> Create custom tools

> [card] [Hooks](https://docs.standardagents.ai/core-concepts/hooks.md)
>
> Use utilities in lifecycle hooks

> [card] [React Integration](https://docs.standardagents.ai/user-interfaces/react.md)
>
> Connect frontend to backend events


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.