How DeepSeek-Reasonix Implements Extension Protocol v2 for Runtime Event Interception

DeepSeek-Reasonix implements Extension Protocol v2 as a stdio-based RPC contract between the host runtime and an out-of-process sidecar, using method constants like MethodExtensionIntercept and DTOs such as InterceptParams and InterceptResult to route events through registered interceptor functions.

The esengine/DeepSeek-Reasonix repository provides a robust mechanism for extending AI reasoning workflows through Extension Protocol v2. This protocol enables secure, language-agnostic runtime event interception by spawning sidecar processes that communicate with the host over stdio. By leveraging generated DTOs and a transport-neutral RPC wire format, the system allows extensions to observe, modify, or block critical runtime events without compiling into the core binary.

Architecture of Extension Protocol v2

Transport and Connection Management

The protocol relies on internal/extension/rpcwire/conn.go to manage transport-neutral connections between the host and sidecar. According to the DeepSeek-Reasonix source code, this layer abstracts the stdio pipe into a framed RPC connection capable of handling request-response cycles asynchronously.

Sidecar Process Model

Extensions run as separate OS processes spawned by the host via sdk/go/sdk.go. The host initializes these sidecars using a versioned handshake defined in the protocol specification, ensuring both sides agree on supported method names and DTO schemas before processing events.

Handshake and Interceptor Registration

Protocol Initialization

Before intercepting events, the host performs a handshake using MethodExtensionInitialize (defined in sdk/go/types_generated.go). This method establishes the protocol version and capability negotiation between the Reasonix host and the extension sidecar.

Configuring the Interceptor Map

The host builds an interceptor registry in internal/extension/uihub/hub.go using an Options struct containing an Interceptors map. Keys are event names (e.g., tool.before, session.start) or the wildcard "*" for catch-all handlers. Each entry maps to an InterceptorFunc that receives the event string and raw JSON payload.

Runtime Event Interception Implementation

Host-Side Event Triggering

When the Reasonix runtime reaches a hook point, the host constructs an InterceptParams DTO containing the Event enum and payload. The hub implementation in internal/extension/uihub/hub.go then dispatches a JSON-RPC request using the method constant MethodExtensionIntercept.

Request Structure and Wire Format

The generated code in sdk/go/types_generated.go defines the wire format precisely. The InterceptParams struct includes the event type and payload, while InterceptResult contains the Decision enum and optional replacement data. These DTOs are generated by internal/extension/protocolgen/sdkgo.go to ensure type safety across language boundaries.

Sidecar Request Processing

Upon receiving the request, the sidecar client in internal/extension/sidecar/client.go unmarshals the parameters and routes them to the appropriate handler. The lookup follows a priority order: exact event name match, wildcard "*", then default behavior. The handler returns an InterceptResult serialized back to the host.

Decision Application

The host evaluates the Decision field from the sidecar response. As implemented in internal/extension/uihub/hub.go, valid decisions include:

  • continue: Forward the original payload unchanged.
  • replace: Substitute the payload with the Replacement field from the result.
  • block: Abort the current operation immediately.
  • allow/deny: Grant or revoke permission for protected operations.

Error Handling and Timeouts

The protocol defines specific error constants in sdk/go/types_generated.go, including ErrUnknownMethod and ErrInterceptTimeout. The host enforces per-intercept deadlines; if the sidecar fails to respond within the timeout window, the host defaults to a conservative fallback decision to prevent pipeline stalls.

Implementation Example

The following example demonstrates registering interceptors and handling decisions in a Go-based extension:

// Initialize host with interceptors
host, err := NewHost(Options{
    Interceptors: map[string]InterceptorFunc{
        "tool.before": func(ctx context.Context, event string, payload json.RawMessage) (*InterceptResult, error) {
            // Inspect tool invocation
            return &InterceptResult{Decision: DecisionContinue}, nil
        },
        "*": func(ctx context.Context, event string, payload json.RawMessage) (*InterceptResult, error) {
            // Catch-all logging or policy enforcement
            return &InterceptResult{Decision: DecisionAllow}, nil
        },
    },
})
// Host-side dispatch (simplified from internal/extension/uihub/hub.go)
params := InterceptParams{
    Event:   InterceptEvent("tool.before"),
    Payload: json.RawMessage(`{"tool":"search","query":"data"}`),
}
// Send via rpcwire
resp, err := host.Request(MethodExtensionIntercept, params)
// Apply decision based on InterceptResult

Summary

  • Extension Protocol v2 uses stdio-based RPC via internal/extension/rpcwire/conn.go to communicate with out-of-process sidecars.
  • The host registers interceptors in internal/extension/uihub/hub.go using an Options map keyed by event names or the "*" wildcard.
  • Runtime events trigger MethodExtensionIntercept requests with InterceptParams DTOs defined in sdk/go/types_generated.go.
  • Sidecars process requests in internal/extension/sidecar/client.go and return InterceptResult containing decisions: continue, block, replace, allow, or deny.
  • Timeout handling and error constants like ErrInterceptTimeout ensure robust production operation.

Frequently Asked Questions

What transport mechanism does Extension Protocol v2 use?

Extension Protocol v2 communicates over stdio pipes managed by internal/extension/rpcwire/conn.go. This transport-neutral design allows the same protocol to run over alternative transports if needed, though stdio is the default for sidecar isolation.

How does the host route events to the correct interceptor?

The host maintains an interceptor map in internal/extension/uihub/hub.go. When an event occurs, it looks up handlers by exact event name first, falls back to the wildcard "*" handler if no specific match exists, and finally applies default behavior if neither is registered.

What happens if a sidecar interceptor times out?

If the sidecar does not return an InterceptResult within the configured deadline, the host raises ErrInterceptTimeout (defined in sdk/go/types_generated.go) and applies a conservative fallback decision—typically continue or block depending on the event type—to prevent blocking the Reasonix runtime.

Can extensions modify event payloads or only block them?

Extensions can modify payloads by returning DecisionReplace in the InterceptResult struct. The host then substitutes the original payload with the Replacement field contents before continuing execution, enabling transformation of tool arguments or session data.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →