How Prime Agent Maintains Backward Compatibility with Its Daemon Protocol
Prime Agent guarantees backward compatibility through a versioned daemon protocol that uses protocol version stamps, per-command compatibility matrices, and runtime negotiation.
The prime-agent repository implements a sophisticated versioning system for its persistent coding agent daemon. This architecture allows independent evolution of client and daemon components without breaking existing deployments.
Protocol Versioning and Schema Revision
The foundation of compatibility lies in explicit version advertisement. In packages/coding-agent/src/modes/daemon/daemon-protocol.ts, the daemon exports two critical constants:
export const DAEMON_PROTOCOL_VERSION = 7;
export const DAEMON_SCHEMA_REVISION = 16;
These values serve as the contract between components. Protocol version indicates major compatibility requirements, while schema revision tracks incremental field additions that older daemons might not parse.
The Per-Command Compatibility Matrix
Every command type carries structured compatibility requirements through the DaemonCommandCompatibility interface (lines 24-32):
interface DaemonCommandCompatibility {
minProtocol: number;
minSchemaRevision?: number; // optional, for field-level changes
capability?: string; // optional feature flag
}
The complete mapping lives in DAEMON_COMMAND_COMPATIBILITY (lines 53-52), which the daemon consults before executing any command. This design isolates breaking changes to specific command types rather than forcing global version bumps.
Runtime Negotiation Flow
The compatibility check operates through a six-step pipeline:
1. Client Envelope Construction
Clients stamp their supported protocol version using createDaemonCommandEnvelope:
const envelope = createDaemonCommandEnvelope(
command,
id,
clientId,
DAEMON_PROTOCOL_VERSION // defaults to 7
);
The helper implementation (lines 773-785) embeds version metadata into every command envelope.
2. Daemon Compatibility Resolution
Upon receiving a command, the daemon calls getDaemonCommandCompatibilities() (lines 754-666). This function returns an array of compatibility entries and dynamically injects additional requirements for:
- Telemetry policy features
- Prompt admission capabilities
3. Version Comparison
The daemon validates hello.protocol.version from the client against each entry's minProtocol. A client sending version 6 against a command requiring version 7 triggers rejection.
4. Schema Revision Validation
For commands evolving their payload structure—such as mutate_queued_message requiring schema ≥ 15—the daemon checks minSchemaRevision before deserialization.
5. Capability Gating
Certain advanced features require explicit capability advertisement. The session_input_admission capability, for example, must be present before the daemon accepts related commands.
6. Graceful Error Handling
Incompatible commands receive explicit rejection:
// Daemon side rejection
throw new Error(`Unknown daemon command: ${command.type}`);
// Client side detection
if (isUnknownDaemonCommandError(err, envelope.command.type)) {
// Implement fallback strategy
}
The isUnknownDaemonCommandError() helper (lines 998-1000 in daemon-client.ts) enables clients to distinguish protocol errors from other failure modes.
Code Examples
Creating a Command Envelope
import { createDaemonCommandEnvelope, DAEMON_PROTOCOL_VERSION } from "./daemon-protocol";
const cmd = {
type: "prompt",
activeSessionId: "sess-1",
message: "Refactor this function"
};
const envelope = createDaemonCommandEnvelope(
cmd,
"cmd-123", // command ID
"client-42" // client identifier
);
Implementing Compatibility Checks
import { getDaemonCommandCompatibilities } from "./daemon-protocol";
function handleCommand(cmd: DaemonCommand, clientHello: HelloMessage): void {
const compatList = getDaemonCommandCompatibilities(cmd);
for (const { minProtocol, minSchemaRevision, capability } of compatList) {
if (clientHello.protocol.version < minProtocol) {
throw new Error(
`Protocol version ${clientHello.protocol.version} below required ${minProtocol}`
);
}
if (minSchemaRevision && clientHello.schemaRevision < minSchemaRevision) {
throw new Error(`Schema revision incompatible`);
}
if (capability && !clientHello.capabilities.includes(capability)) {
throw new Error(`Missing required capability: ${capability}`);
}
}
// Execute command with validated compatibility
}
Handling Unknown Command Errors
import { isUnknownDaemonCommandError } from "./daemon-client";
async function sendWithFallback(envelope: DaemonCommandEnvelope): Promise<void> {
try {
await daemon.send(envelope);
} catch (err) {
if (isUnknownDaemonCommandError(err, envelope.command.type)) {
// Degrade to legacy command format or notify user
console.warn(`Daemon doesn't support ${envelope.command.type}, using fallback`);
await sendLegacyCommand(envelope.command);
return;
}
throw err; // Re-throw non-compatibility errors
}
}
Evolution Policy
The AGENTS.md documentation (line 39) establishes strict rules for protocol changes:
"Bump
DAEMON_PROTOCOL_VERSIONfor incompatible changes"
This policy ensures:
- Patch changes: Schema revision increments for additive fields
- Minor changes: Backward-compatible capability additions
- Major changes: Protocol version bump forcing coordinated upgrades
Key Implementation Files
| File | Responsibility |
|---|---|
packages/coding-agent/src/modes/daemon/daemon-protocol.ts |
Constants, interfaces, createDaemonCommandEnvelope, getDaemonCommandCompatibilities |
packages/coding-agent/src/modes/daemon/daemon-client.ts |
isUnknownDaemonCommandError, runtime version negotiation (line 304) |
packages/coding-agent/test/daemon-protocol.test.ts |
Unit tests for version bump scenarios and compatibility edge cases |
AGENTS.md |
Protocol evolution policy documentation |
Summary
- Explicit versioning:
DAEMON_PROTOCOL_VERSIONandDAEMON_SCHEMA_REVISIONconstants establish baseline compatibility - Command-level granularity: Per-command compatibility matrices isolate breaking changes
- Dynamic negotiation:
getDaemonCommandCompatibilities()adapts requirements based on feature context - Clear error contracts:
isUnknownDaemonCommandError()enables client-side graceful degradation - Documented evolution policy:
AGENTS.mdmandates protocol version bumps for incompatible changes
Frequently Asked Questions
What happens when a newer client connects to an older daemon?
The daemon rejects unsupported commands with an "unknown command" error. The client detects this via isUnknownDaemonCommandError() and can either retry with reduced functionality or prompt for daemon upgrade.
When should developers bump DAEMON_PROTOCOL_VERSION versus DAEMON_SCHEMA_REVISION?
Bump schema revision for additive changes that older daemons can safely ignore (new optional fields). Bump protocol version for structural changes that would cause parsing failures or semantic misinterpretation.
How does the daemon handle commands requiring capabilities the client hasn't advertised?
The compatibility check fails at the capability validation stage. The command is rejected before execution, preventing partial or corrupted state modifications.
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 →