Skip to main content
A simple agent is the most basic type of agent in the HasteKit SDK. It executes in-process without durability, making it perfect for stateless interactions, testing, and simple use cases that don’t require crash recovery or long-running workflows.

Overview

Simple agents provide:
  • System instructions: Define the agent’s behavior and personality
  • LLM integration: Use any supported LLM provider (OpenAI, Anthropic, Gemini, etc.)
  • Tool support: Optional tools for function calling
  • Conversation history: Optional memory across interactions
  • Streaming responses: A live channel of response chunks delivered through the run handle
  • Stop signal: Cancel an in-flight run cleanly via handle.Stop, including mid-stream and mid-tool-call
  • Hooks: Intercept tool calls and model calls for auth, budgets, and audit
Unlike durable agents, simple agents execute in-process and don’t persist state between runs across restarts. They’re ideal for stateless applications, quick prototypes, and scenarios where you don’t need crash recovery.

Creating a Simple Agent

To create a simple agent, use hastekit.NewAgent() with AgentConfig:

AgentConfig Fields

Executing an Agent

Execute() is non-blocking — it generates a stream id, subscribes to the broker, and returns an *AgentHandle. There are two valid ways to consume the handle:
  • handle.Result() — drains the chunk channel internally and returns the aggregated AgentOutput. Use this when you only care about the final output.
  • for chunk := range handle.Chunks + handle.Wait() — observe chunks as they arrive (e.g. to forward to a UI or SSE stream), then collect the aggregated output.
Call handle.Stop(ctx) at any point to stop the run — including in the middle of a streaming model call or a running tool.
Or if you want to consume chunks live:

AgentInput Fields

AgentHandle

Execute() returns a handle:
  • Stop records a stop request on the broker and unwinds the run — see Stopping an In-Flight Run.
  • EnqueueMessage pushes a message onto the run’s queue. The agent drains it at iteration boundaries, so a follow-up or an approval decision reaches a run that is still in flight. For a run that has already paused and exited, the next agent.Execute on the same thread is the right entry instead.
  • Wait blocks until the run finishes and returns the aggregated output. Safe to call only after Chunks has been drained.
  • Result drains Chunks internally and returns the aggregated output — equivalent to for range Chunks {}; Wait().
Calling Wait without draining Chunks will deadlock once the broker’s per-subscriber buffer fills, because the agent’s publisher back-pressures. Use Result if you don’t intend to consume chunks yourself.

AgentOutput Structure

handle.Wait() returns the aggregated AgentOutput:

Complete Example

Here’s a complete working example:

Streaming Responses

Streaming is built into the handle — every run delivers chunks on handle.Chunks as they arrive:

Stopping an In-Flight Run

handle.Stop(ctx) records a stop request on the broker. The run records a "Cancelled by user" assistant turn in history and emits run.completed cleanly — the chunk stream stays open while the agent winds down, so you’ll still see that final chunk before the channel closes.
Stop does not wait for an iteration boundary. It reaches work already in flight:
  • Mid-stream — the model call is cut off where it is, rather than waited out. Text that had already streamed still reached the client, but the turn is recorded as cancelled rather than as a half-answer the model never finished. Cancelling also reaches the provider’s own request, which is what actually stops the tokens being generated and billed.
  • Mid-tool-call — a running tool has its context cancelled. A tool that ignores cancellation is abandoned after a grace period (agents.DefaultCancelGrace, 2s) so the run still ends; it keeps running in the background, unobserved.
Either way the loop’s invariant holds: every function_call in history is answered — a cancelled call gets a synthetic result saying so — so a stopped thread is still a valid thread to resume from.
Reaching a running tool or an in-flight stream needs a broker implementing agents.StopWatcher; the built-in memory and Redis brokers both do. With a plain broker the stop still lands, at the loop’s next iteration boundary.

Single-Turn Execution

SingleTurn ends the run as soon as the model has responded, before any tool is executed. The AgentOutput carries exactly what the model emitted — assistant text and/or tool calls — with status completed. This is for evaluating a single decision rather than an outcome: offline single-turn evals score “given this conversation, what does the model do next?”, where executing the tool (or faking its result) would replace the thing under test with an improvised continuation. It lives on agents.AgentOptions rather than AgentConfig, so set it with an AgentOption:
This is not the same as MaxLoops: 1, which still executes the first round of tools and then fails the run with “exceeded maximum loops”.

Helper Functions

The SDK provides convenient helper functions for creating messages:
  • responses.UserMessage(msg string): Creates a user message from a string

Next Steps