Resident Workers vs Client-Owned Workers in the Prime Agent Daemon: Key Differences Explained
Resident workers are long-lived daemon processes that persist across client disconnections, while client-owned workers are ephemeral processes bound to the lifecycle of the creating client and automatically terminate when that client disconnects.
The Prime Agent daemon from PrimeIntellect-ai/prime-agent supports two distinct worker lifecycle models that determine process ownership, persistence, and shutdown behavior. Understanding the difference between resident and client-owned workers is essential for building reliable agent architectures that match your operational requirements.
Lifecycle Semantics and Ownership Model
The daemon distinguishes workers through the DaemonSessionLifecycle union type defined in packages/coding-agent/src/modes/daemon/daemon-protocol.ts. This type accepts two string literals: "resident" and "client_owned".
Resident Workers (Default)
Resident workers operate as daemon-global resources. When you create a session without explicitly requesting client ownership, the daemon constructs a descriptor with lifecycle: "resident" and leaves the ownerClientId field undefined. These workers survive individual client disconnections and continue running until the daemon itself shuts down or receives an explicit stop command.
Client-Owned Workers
Client-owned workers are strictly tied to the client connection that created them. When a session is created with lifecycle: "client_owned", the daemon captures the originating client’s identifier and stores it as ownerClientId in the worker descriptor. This binding ensures that when the owning client disconnects, the daemon automatically terminates the associated worker process.
How the Daemon Creates Each Worker Type
The entry point in packages/coding-agent/src/main.ts handles the branching logic around line 991. The createSessionDescriptor function checks the options.clientOwned boolean to determine which lifecycle to assign.
Resident worker creation (default):
// Resident workers are created when clientOwned is false or undefined
const sessionDescriptor = await createSessionDescriptor({
name: "background-agent",
// clientOwned defaults to false
});
// Results in: { lifecycle: "resident", ownerClientId: undefined }
Client-owned worker creation:
// Client-owned workers require explicit opt-in
const sessionDescriptor = await createSessionDescriptor({
name: "interactive-session",
clientOwned: true, // Explicitly request client ownership
});
// Results in: { lifecycle: "client_owned", ownerClientId: "<client-id>" }
Before accepting a client-owned request, the daemon validates that it advertises the client_owned_sessions capability. If a client attempts to create a client-owned session against a daemon that does not support this feature, the call raises a DaemonCapabilityUnavailableError.
Internal Implementation Details
The distinction between these worker types is enforced in packages/coding-agent/src/modes/daemon/daemon-supervisor.ts around line 2030. When processing a CreateSession command, the supervisor executes conditional ownership assignment:
// From daemon-supervisor.ts (~line 2030)
const ownerClientId = command.lifecycle === "client_owned"
? clientId
: undefined;
This single line determines the ownership model. For resident workers, the ownerClientId remains undefined, signaling to the daemon’s garbage collection and lifecycle management systems that the process should persist independently of any specific client connection.
The DaemonSessionLifecycle type definition in daemon-protocol.ts (around line 175) formalizes these values:
export type DaemonSessionLifecycle = "resident" | "client_owned";
Additionally, the supervisor retrieves resident worker collections for daemon-wide operations (such as update-restart checks) by iterating over workers.values() around line 4485 in the same file, filtering for processes that lack an ownerClientId or simply processing all resident entries.
Operational Behavior and Shutdown
The practical difference between these models emerges during connection failures and daemon maintenance events.
Resident worker characteristics:
- Survive client disconnections and network interruptions
- Ideal for background tasks, persistent LLM assistants, or long-running automation agents
- Require explicit shutdown via API call or daemon termination
- Listed in daemon-wide worker inventories for maintenance operations
Client-owned worker characteristics:
- Automatically terminate when the owning client disconnects (gracefully or otherwise)
- Suitable for interactive REPL sessions, temporary sub-agents, or ad-hoc tasks
- Cannot outlive the client session that spawned them
- Cleaned up immediately upon client socket closure to prevent resource leaks
Summary
- Resident workers use
lifecycle: "resident"with noownerClientId, persist beyond client disconnections, and serve as daemon-wide background processes. - Client-owned workers use
lifecycle: "client_owned", bind to a specificownerClientId, and automatically terminate when that client disconnects. - The daemon validates the
client_owned_sessionscapability before permitting client-owned creation. - Implementation logic resides primarily in
daemon-supervisor.ts(ownership assignment) andmain.ts(session descriptor construction). - Choose resident workers for long-running agents and client-owned workers for ephemeral, interactive tasks.
Frequently Asked Questions
What happens to client-owned workers if the network connection drops unexpectedly?
The daemon detects the client disconnection and immediately terminates any workers associated with that client’s ownerClientId. This prevents orphaned processes and ensures resource cleanup even during ungraceful disconnections.
Can I convert a resident worker to a client-owned worker after creation?
No, the lifecycle is immutable once the session descriptor is created. You must stop the existing resident worker and create a new session with the desired client_owned lifecycle to change ownership models.
Does using resident workers require special daemon configuration?
Resident workers are supported by default and require no capability flags. However, client-owned workers require the daemon to advertise the client_owned_sessions capability; attempting to create them against unsupported daemons results in a DaemonCapabilityUnavailableError.
Where is the worker ownership logic implemented in the source code?
The ownership assignment occurs in packages/coding-agent/src/modes/daemon/daemon-supervisor.ts around line 2030, where the code conditionally sets ownerClientId based on the command.lifecycle value.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →