How the HookServer Feeds Control Signals to Agent Sessions via the ControlRegistry
The HookServer queries the ControlRegistry on every hook payload to determine whether to block, redirect, or allow agent actions based on real-time control flags like paused, gated, or halted.
In the chaitanyagiri/munder-difflin repository, the HookServer acts as the central bridge between operator commands and running agent sessions. By leveraging the ControlRegistry—a per-agent state container defined in src/main/control.ts—it dynamically feeds control signals that determine execution flow without modifying the core agent logic.
ControlRegistry: The Per-Agent State Store
The ControlRegistry maintains a dedicated state snapshot for each active session. Located in src/main/control.ts, this class stores boolean flags that represent the operator's current control intent.
Key flags tracked per session include:
paused– Blocks all hook delivery until explicitly clearedgated– Requires human-in-the-loop (HITL) approval before proceedingsteering– Redirects or modifies payload contenthalted– Terminates the session entirely
The registry exposes methods like getSessionState(sessionId) to retrieve the current flag map and setPause(sessionId, boolean) to update control states. This centralization ensures that control logic remains decoupled from the HookServer's delivery mechanism.
HookServer Initialization and Dependency Injection
The HookServer receives its ControlRegistry reference during instantiation in src/main/index.ts. This dependency injection pattern ensures that both components share a singleton state instance across the application lifecycle.
// src/main/index.ts
const control = new ControlRegistry(); // Per-agent control state
const hookServer = new HookServer({ // Receives registry reference
control,
// …other dependencies
});
By passing the registry through the constructor, the HookServer gains read access to all session control flags without directly managing state mutations.
Runtime Signal Processing in the HookServer
When the HookServer receives a hook payload, it inspects the ControlRegistry before delivering the event to the agent. In src/main/hooks.ts, the handleHookPayload method implements this evaluation logic:
// src/main/hooks.ts (excerpt)
private handleHookPayload(payload: HookPayload) {
const ctrl = this.control?.getSessionState(payload.sessionId);
if (ctrl?.paused) {
// Block delivery until unpaused
return this.blockDelivery(payload);
}
if (ctrl?.gated) {
// Apply gating logic
return this.applyGate(payload);
}
// Normal processing continues...
}
This check occurs synchronously on every incoming hook, ensuring that control signals take effect immediately when the operator toggles a flag in the registry.
Feeding Control Signals to Agent Sessions
Once the HookServer detects an active control flag, it feeds the signal to the target session through one of three pathways:
1. Direct Delivery Blocking
When ctrl.paused returns true, the server invokes blockDelivery(payload), queuing the hook until the operator clears the pause state. The session receives no data during this interval, effectively freezing agent execution.
2. Human-in-the-Loop Gating
For gated sessions, the HookServer calls applyGate(payload), which redirects the request to the UI layer via showPermissionPrompt(payload). The operator approves or rejects the action through the interface, and the registry updates accordingly before the HookServer resumes normal delivery.
3. Session Control API Integration
When the registry state changes (e.g., an operator calls control.setPause("agent-42", true)), the next hook evaluation cycle picks up the new flag. The HookServer translates this state change into concrete session behavior without requiring the agent to implement pause logic internally.
Summary
- The ControlRegistry in
src/main/control.tsstores per-session flags (paused,gated,steering,halted) that represent operator intent. - HookServer instantiation in
src/main/index.tsinjects the registry as a shared dependency. - At runtime,
handleHookPayloadinsrc/main/hooks.tsqueriesgetSessionState()to read current control flags before delivering hooks. - Control signals feed into agent sessions through delivery blocking, HITL prompts, or direct state evaluation on each hook cycle.
Frequently Asked Questions
What is the ControlRegistry responsible for in munder-difflin?
The ControlRegistry is a state management class defined in src/main/control.ts that maintains a map of control flags for each active agent session. It tracks whether a session is paused, gated, steering, or halted, providing the single source of truth that the HookServer consults before processing lifecycle hooks.
How does the HookServer detect when an agent should pause?
The HookServer checks the paused flag by calling this.control?.getSessionState(payload.sessionId) inside the handleHookPayload method. If the flag is true, it immediately invokes blockDelivery(payload), preventing the hook from reaching the agent until the operator clears the pause state in the registry.
Where does the ControlRegistry get initialized in the codebase?
The registry initializes in src/main/index.ts as a singleton instance: const control = new ControlRegistry(). This instance gets passed into the HookServer constructor, ensuring both components share the same state reference throughout the application lifecycle.
How does gating work with human approval?
When the ControlRegistry indicates gated: true for a session, the HookServer's applyGate method redirects the payload to showPermissionPrompt(payload), which renders a UI prompt for operator approval. The registry updates after the human decision, allowing the HookServer to either proceed with delivery or discard the hook based on the response.
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 →