Infinity: A runtime for extreme agent concurrency AWS and UC Berkeley released Infinity, an Apache 2.0 open-source agent runtime in the Hydro project that suspends agents during tool calls and resumes them from persisted state, fitting 75,000 concurrent agents in the 8 GB of a Raspberry Pi 5. Infinity executes each agent as bounded slices that load history from a pluggable store, run one model completion, persist the output, and dispatch at most one tool call, letting serverless functions end while a tool call runs for days. The runtime also uses synthetic tool calls to deliver asynchronous events to existing models and for agent-to-agent communication; AWS described Infinity as an early research release shared for feedback. AWS Open Source Blog https://aws.amazon.com/blogs/opensource/ Introducing Infinity: a runtime for extreme agent concurrency Agents spend much of their time waiting on tools. A coding agent may start a build that runs for twenty minutes, a triage agent may wait hours for the next pull request on a repository, and an assistant may wait until the next morning for an approval. Most agent harnesses run the agent loop as a sequence of blocking steps: the harness calls the model, invokes a tool, waits for the tool to return, and repeats. While the agent waits, it holds a process and its memory, and it cannot respond to events that arrive in the meantime. Today, we’re releasing Infinity https://infinity.hydro.run under the Apache 2.0 open source license as part of the Hydro https://hydro.run project, a collaborative effort between AWS and UC Berkeley. Infinity is a research project that re-imagines agent runtimes, based on the techniques that programming languages use to suspend and resume asynchronous functions. When an agent calls a tool, Infinity dispatches the call, saves the conversation, and suspends the agent. When the tool result arrives, the runtime resumes the agent from its saved state. With Infinity, concurrent agents are extremely lightweight. For example, we can fit 75,000 concurrent agents in the 8 GB of a Raspberry Pi 5. Infinity also makes it possible to deploy concurrent agents on standard serverless platforms. When Infinity suspends an agent during a tool call, the serverless function can end. So even if a tool call takes days, nothing needs to run while the agent waits. Infinity is an early research release, and we are sharing it to get early feedback. The rest of this post describes the ideas behind Infinity and its architecture. We start an overview of how languages suspend asynchronous functions with stackless coroutines, and why an agent can be suspended in the same way. We’ll explore how Infinity uses this to run many agents concurrently, even on serverless platforms. We then describe synthetic tool calls, which Infinity uses to deliver asynchronous events to existing models, and we use synthetic events for agent-to-agent communication. Finally, we discuss some open questions that we hope to explore further. Agents as stackless coroutines Many languages, including Rust, JavaScript, and Python, let developers write asynchronous logic with async and await . When a function awaits an async operation, such as a network request, the function is suspended, and the thread that was running it moves on to other work. When the operation completes, the runtime resumes the function from the point where it was suspended. To make this possible, the runtime has to save the function’s continuation , which is the state it needs in order to resume. Languages save this state in one of two ways. Operating system threads and stackful coroutines keep the task’s entire call stack in memory while it waits. Stackless coroutines, which Rust uses to implement async fn , instead have the compiler turn each function into a state machine that holds only the relevant values at each await . This approach results in a smaller memory footprint, so an executor can efficiently run a large number of stackless coroutines. An LLM agent can be suspended in the same way as a stackless coroutine. At the moment an agent calls a tool, its conversation history contains everything needed to continue the agent once the result is available. If the runtime persists this history, it can discard all in-memory state, and any process with access to the store can resume it later. The Infinity Runtime uses this idea to suspend and resume long-running agents. It executes each agent as a sequence of bounded slices . In a slice, the runtime loads the agent’s history from a pluggable store, runs one model completion, persists the output, and dispatches at most one tool call. The slice then ends. Tool calls play the role of await points: a slice never blocks for a tool, a timer, or any other external operation. Everything that can resume an agent arrives as a message on a shared input queue, including user text and tool results. Because the queue absorbs pending events, the runtime does not need to keep an agent alive while it waits. When the Infinity Runtime dequeues a message, it launches a slice that reloads the agent state and processes the message. Running agents concurrently Since a suspended agent occupies no memory, the runtime can manage a large number of agents and schedule them as messages arrive. Agents run inside an agent system , which is a pool of agent threads that share stores, tools, and a model. The runtime organizes the agent threads in a way similar to an actor system. Each conversation thread has its own input queue, threads do not share mutable state, and threads communicate only by sending messages to each other’s queues. Each message carries a thread id that identifies its destination thread. The runtime guarantees that messages for the same thread are processed in order, one slice at a time, while slices for different threads run in parallel. Ordering within a thread is required for correctness, because two slices that read and write the same history at the same time would overwrite each other’s turns. Different threads have nothing to coordinate. In a long-running process, the agent system gives each active thread a channel and a lightweight task, and schedules the pool in the same way that an async executor schedules futures. When a thread has no queued messages and no pending tool results, its task exits, and the runtime starts a new one when the next message for that thread arrives. Because a suspended agent consists only of its history, the memory needed to keep agents resident is close to the size of their histories. To measure this, we wrote a benchmark that launches waves of agents in one agent system, backed by a simulated model. Each agent runs twenty turns, and each turn makes a full round trip through the runtime: the model streams a completion of about 1.2 KB that calls a tool, the tool result returns through the input queue, and a second completion ends the turn. We ran the benchmark with 80,000 agents, for a total of 3.2 million completions, on a single thread of an AMD EPYC 9R14 with in-memory stores. Resident memory grew linearly, at about 100 KB per idle agent. At this rate, about 75,000 agents fit in 8 GB, which is the memory of a Raspberry Pi 5. Most of this memory is taken up by the conversation histories, since an idle agent has no task, stack, or connection associated with it. Example: a tool that does not block Let’s look at how this appears to a developer who embeds the runtime in local in-memory mode. The following program builds an agent system with one custom tool, launches one agent, and prints the events that the agent produces: js let system = AgentSystemBuilder::new local InMemoryConversationStore::new , InMemoryStateStore::new , StaticModel::new provider, "global.anthropic.claude-sonnet-4-6" .await?, .tool Box::new GetBuildStatus { builds } .start ; let mut thread = system.thread builder .launch .await; thread.send user text "Is build 42 green yet?" .await?; while let Some event = thread.recv .await { println "{event:?}" ; } The difference from most agent frameworks is in how tools are written. Rather than returning its result, a tool’s execute method starts the work and returns immediately. When the work completes, the tool sends the result to the agent’s input queue: async trait impl Tool