Building a background task system with Swift actors
Ivan Magda demonstrates how to add non-blocking background task execution to an agent codebase using Swift's actor model, allowing long-running commands to execute in parallel while the agent loop remains responsive.
Score breakdown
Developers building agent systems can now execute long-running commands without blocking the agent loop, enabling true concurrent task execution and more responsive multi-step workflows.
- 01Agent loops block on slow commands like test suites and builds, preventing concurrent work
- 02BackgroundManager is implemented as a Swift actor to serialize access to shared state without explicit locks
- 03When background_run is called, the manager spawns a Task, returns a job ID immediately, and keeps the loop responsive
Agent loops built on synchronous tool handlers create a bottleneck: when the model calls bash to run a two-minute test suite, the entire loop blocks until completion. If the model requests parallel work — "run tests and create the config file" — the agent executes them sequentially. The solution is a background execution layer that hands slow commands to a worker, returns a job ID immediately, and keeps the loop moving.
Ivan Magda's guide introduces BackgroundManager, implemented as a Swift actor — the first and only actor in the entire codebase.
Ivan Magda's guide introduces BackgroundManager, implemented as a Swift actor — the first and only actor in the entire codebase. The actor keyword serializes all access to shared state (jobs, notifications, runningTasks) without explicit locks, preventing data races. When the model calls `background_run`, the manager creates a job record, spawns a Task to execute the command asynchronously, and returns a confirmation string. The spawned task executes the shell command, captures its exit code and output, handles timeouts and errors, and calls `complete()` to update the job status. Before each API call, the agent loop drains the notification queue and injects results as messages, allowing the model to see them on its next turn without ever blocking. Supporting types like BackgroundJob and BackgroundNotification are Sendable value types that cross the actor isolation boundary cleanly. The complete source code is available at the 08-background-tasks tag on GitHub.
Key facts
- 01Agent loops block on slow commands like test suites and builds, preventing concurrent work
- 02BackgroundManager is implemented as a Swift actor to serialize access to shared state without explicit locks
- 03When background_run is called, the manager spawns a Task, returns a job ID immediately, and keeps the loop responsive
- 04Results flow back via a notification queue that is drained before each API call, injecting results as messages
- 05BackgroundJob and BackgroundNotification are Sendable value types that safely cross actor isolation boundaries