A shared runtime for concurrent agentsTesting concurrent work and human review in a simulated office.
Building Idyllic - Prior Iterations
November-December 2025
Several agents working on one document need somewhere to keep the document while it changes. They may have different prompts and responsibilities, but they are contributing to the same piece of work. A person also needs to inspect that work, intervene, and return to it later.
I was building an Idyllic runtime around that shared session. I started with a working chat so I could test the path from the browser to the model and back. Then I could change the application model behind the chat and see what each experiment actually enabled.
Choosing what one object represents
I wanted an application author to express the session as a TypeScript class. Its properties would hold the current work, and its methods would describe the operations that changed it. For a task board, that meant keeping the tasks and their status alongside the code that assigned work or responded to review.
The important choice was what one instance of the class represented. Giving every agent a separate runtime object would also give each one a boundary across which shared information had to travel. Three agents editing the same document would need some way to coordinate their versions of it.
I chose one instance for the whole session. Agents could run as functions inside it, with their own prompts and context, while reading and updating common state. Ordinary variables could hold an intermediate result. Functions could describe repeatable procedures, and promises could coordinate activities that ran at the same time. I could use familiar code structures to express the arrangement before deciding what a visual builder should expose.
A module would add a configured resource to that application. A document collection, for example, would bring a place to keep documents together with operations for working on them. A messaging module would bring the conversation data its functions needed. I wanted installing a capability to supply the state behind it, so every application author would not have to reconstruct that state separately.
Making the class survive and communicate
Cloudflare's Durable Objects gave me a way to associate a session with a named object and attached storage. I explored the platform through small tests, including a counter and a tic-tac-toe application. The counter retained its value between some requests and reset in later tests. I could not treat an ordinary in-memory property as durable state: important values had to be saved and restored when the runtime recreated the object.
The framework therefore had more work to do than keep a class instance around. The authoring model could make state look like ordinary properties, but the running system needed to connect the relevant changes to storage. The game also needed a connection through which clients could make moves and see the current board. Writing raw WebSocket messages at each call site was too low-level for the application model I wanted.

The deployment sketch traces the machinery around the class. An idyllic/ directory sits beside the frontend. A deployment command bundles its server code and uploads it, and routing information lets an incoming request find the appropriate deployed program and session. The red notes mark questions I was still working through about bundling and live connections.
I wanted to generate that machinery from the application's source. The author would write a TypeScript class; a source transform would produce the Worker and Durable Object code needed to run it. The browser would call the session through a client interface, with Idyllic handling the routing and synchronization underneath.
The prototype eventually transformed system.ts into that runtime and connected it to React. It supported several parallel streams, so I could test more than one model response progressing inside the same session. The detailed source model is described in Idyllic v4.
Streaming several activities into one interface
A research application gave the parallelism a purpose. An explorer, a critic and a synthesizer could have separate outputs while contributing to shared work. Each output needed a place in the session's state and a corresponding place in the interface.
When server code changed a synchronized field, the framework sent the change to connected clients. Streaming output needed a more specific lifecycle: text could be appended to a field, and the stream could eventually complete. Several generations could therefore update their own fields without the application author having to route every token through one combined response.
Completion mattered because a partial output can be visible before it is ready for the next operation. A growing paragraph and a finished result may contain the same text at a particular instant, but they permit different next steps. The runtime needed to express that difference alongside the content.
This also gave the interface something more useful to show than an overall loading indicator. A person could watch the separate outputs and the shared state while work continued, rather than waiting for the entire application to resolve into one answer.
Testing intervention in a small shared world
I built a Virtual Office as another test application. It contained a task board, two simulated employees named Alice and Bob, and an activity log. Each employee had a current assignment and a panel displaying generated work.

In the first capture, Alice is working on a login form and Bob on a landing page. Documentation remains in the backlog. The board describes the work at the session level while the employee panels show the individual activities underway.
Those views need to agree. Assigning a task affects both its board status and the worker's current assignment. Producing a result makes work available for review. If a person rejects the result, that feedback has to enter the same state the agent uses to continue, or the board becomes a display disconnected from the process it supposedly controls.

The later capture shows documentation marked done, the landing page in review, and the login form back in progress. The activity log records the human rejecting the login form after it entered review. Alice and Bob are taking breaks, a behavior in the toy office simulation; the energy bars are not measurements of model capability.
The demonstration exercised concurrent work, review and changing shared state. It did not establish that the generated login form or landing page was useful finished software. What I could inspect was the application's behavior as those tasks moved through the process.
The same connection helped me debug the framework. A coding agent could send WebSocket requests to the running session while I watched the browser update. Both were clients of the same object, so I could see the effects of a test through the application itself. Calling an operation, changing state and displaying the change already belonged to the runtime; a second kind of caller could reuse that path.
I wanted to use this runtime to explore the module system and more complex agent patterns. The small applications gave me a way to investigate those questions with concrete state and behavior: write the session, run several activities inside it, and observe what happened when another client intervened.