# DeepSeek Harness, Cordis, and the Case for Spatiotemporal Agent Architectures

> Source: <https://kondasamy.com/blog/2026/deepseek-harness-cordis-kernel-architecture/>
> Published: 2026-08-14 00:00:00+00:00

## On this page

# DeepSeek Harness, Cordis, and the Case for Spatiotemporal Agent Architectures

DeepSeek AI and Peking University formalized agent modularity in a recent paper on spatiotemporal composability. Here is an engineering look at the Cordis kernel, revertible effects, and what this pattern means for self-modifying agents.

Most agent frameworks claim they are modular. In practice, they offer surface-level customization.

They give you a tool registry, a prompt template, and a model router. Then they hardcode the execution loop, the session state, the sandbox runtime, and the user interface into a fixed core. If you want to change how the loop handles retries, swap the session storage engine, or replace the sandbox environment, you end up forking the repository or hacking internal classes.

When DeepSeek released [DeepSeek Harness](https://deepseek.com/harness/en/) (`dsh`

), they took a different approach. Alongside the codebase, DeepSeek AI and Peking University published a research paper: [ A Programming Paradigm for Spatiotemporal Composability](https://github.com/cordiverse/paper/blob/main/paper.pdf).

The paper formalizes the architecture behind [Cordis](https://deepseek-harness.github.io/deepseek-harness/en/develop/cordis-tutorial/), the open-source TypeScript micro-kernel underneath DeepSeek Harness. Instead of building a monolithic agent runtime with hooks, they built an engine where models, tools, sandboxes, session stores, and even the agent loop itself are hot-swappable plugins.

```
%%{init: {"layout": "dagre"}}%%
flowchart TD
    subgraph Monolith["Typical Agent Framework (Fixed Core)"]
        M1[Hardcoded Agent Loop] --> M2[Built-in Session State]
        M1 --> M3[Built-in Terminal UI]
        M1 --> M4[Hardwired Sandbox]
        M1 -.->|Only customizable layer| M5[Custom Tools & Prompts]
    end

    subgraph CordisHarness["DeepSeek Harness on Cordis (Micro-Kernel)"]
        K[Cordis Kernel: Context + Events + Lifecycle]
        K --- P1["ctx.llm: Model Adapter"]
        K --- P2["ctx.agentLoop: Agent Driver"]
        K --- P3["ctx.sessions: Append-Only Log"]
        K --- P4["ctx.sandbox: Container / MicroVM"]
        K --- P5["ctx.tools: Tool Registry & MCP"]
        K --- P6["ctx.agents: Live Agent Registry"]
    end
```

This design did not start in the AI lab. Cordis ran in production for four years powering the Koishi chatbot ecosystem across more than 4,000 community plugins before DeepSeek applied it to agent harnesses.

I am still reading through the paper myself, working through the formal theory of effects and coeffects. Rather than declaring this pattern superior to simpler agent loops, let us examine how the mechanics work, what trade-offs they introduce, and what we can learn from this direction.

## The Theory: Spatial and Temporal Composability

The core argument of the DeepSeek and Peking University paper is that dynamic software systems fail at composability in two distinct dimensions: time and space.

```
%%{init: {"layout": "dagre"}}%%
flowchart LR
    subgraph Spatial["Spatial Composability (Coeffects)"]
        A[Plugin declares required services] --> B[Cordis resolves DAG]
        B --> C[Plugin activates only when context is ready]
    end

    subgraph Temporal["Temporal Composability (Revertible Effects)"]
        D[Plugin executes side effect] --> E[Runtime tracks inverse action]
        E --> F[Plugin unloads: effect unwinds with zero leftovers]
    end
```

### 1. Temporal Composability via Revertible Effects

In standard software, installing a capability is easy; removing it cleanly is hard. When you disable a plugin, it often leaves active interval timers, dangling event listeners, open network sockets, or stale prompt injections in memory.

The paper formalizes **revertible effects**: every transformation applied to the shared execution context must have a mathematically defined inverse that the runtime tracks. When a plugin unloads, the runtime executes that inverse.

``` js
import { Context, Service } from 'cordis'

export class MonitoringService extends Service {
  constructor(ctx: Context) {
    super(ctx, 'monitoring', true)
  }

  protected start() {
    // Revertible effect: the returned closure is the tracked inverse
    this.ctx.effect(() => {
      const timer = setInterval(() => this.collectMetrics(), 10000)
      return () => clearInterval(timer)
    })

    // Event listener: automatically unbound on plugin unload
    this.ctx.on('tool/execute', (event) => {
      this.recordLatency(event)
    })
  }
}
```

When this plugin unloads, Cordis runs the cleanup closure and unbinds the event listener. The process returns to its exact prior state.

### 2. Spatial Composability via Reactive Coeffects

In type theory, **effects** describe what a program *produces* (e.g. logs, network calls, state mutations). **Coeffects** describe what a program *requires* from its environment to execute (e.g. specific services, configurations, credentials).

Cordis treats plugin dependencies as reactive coeffects. A plugin declares what it demands:

```
export class ToolExecutionPlugin extends Service {
  // Coeffect requirements: requires both ctx.tools and ctx.sandbox
  static inject = ['tools', 'sandbox']

  constructor(ctx: Context) {
    super(ctx, 'toolExecution', true)
  }
}
```

Cordis monitors the context tree. When `ctx.tools`

and `ctx.sandbox`

become available, the plugin activates automatically. If the sandbox plugin crashes or unloads, downstream plugins pause or deactivate until the dependency returns. There is no manual boot order to configure.

## How DeepSeek Harness Implements the Model

In DeepSeek Harness, the Cordis kernel contains zero AI logic. It does not know what an LLM token is. Instead, it exposes a typed `Context`

where packages register services and listen to events.

| Package | Responsibility | Context Key |
|---|---|---|
`core/session` | Append-only `SessionEvent` log and store | `ctx.sessions` |
`core/system-prompt` | Dynamic prompt-section and tool-schema assembly | `ctx.systemPrompt` |
`core/tools` | Scoped tool registry and guarded execution pipeline | `ctx.tools` |
`core/agent` | Live agent registry and lifecycle events | `ctx.agents` |
`core/agent-loop` | Default execution driver implementing step turns | `ctx.agentLoop` |
`llm/llm` | Model stream abstraction and provider adapters | `ctx.llm` |
`core/sandbox` | Process isolation, container, and microVM boundaries | `ctx.sandbox` |

### Four Typed Dispatch Modes

Communication across plugins relies on an event bus with four explicit dispatch modes:

| Mode | Awaited? | Execution Order | Return Value? | Behavioral Semantic |
|---|---|---|---|---|
`emit` | No | Registration order | No | Fire-and-forget notifications (telemetry, background logging). |
`waterfall` | No | Around-middleware chain | Yes | Interception middleware (`(...args, next)` ). Can mutate, delegate, or short-circuit. |
`parallel` | Yes | Concurrent (`Promise.all` ) | No | Awaited broadcast across independent listeners. |
`serial` | Yes | Registration order | Yes | Decisive sequential gates (e.g. `agent/turn-stopping` ). |

The **waterfall** mode allows plugins to wrap core decisions:

```
// Intercepting prompt messages before the model sees them
ctx.waterfall('agent/pre-step', async (session, messages, next) => {
  if (violatesPolicy(messages)) {
    // Short-circuit: stop the turn before calling the model
    return { status: 'rejected', reason: 'Blocked by policy' }
  }
  const sanitized = sanitize(messages)
  return next(session, sanitized)
})
```

## The Turn and Step Lifecycle

DeepSeek Harness defines a structured execution pipeline:

```
turn/start
  │
  ├─ Claim queued input from inbox
  ├─ Assemble prompt sections + tool schemas (ctx.systemPrompt)
  │
  ├─► agent/pre-step (waterfall) ──► [reject / enter(messages)]
  │     └─ If rejected: close turn immediately (0 steps spent, logged)
  │
  ├─► step/start
  │     ├─ Append entered messages to session log as user/message
  │     ├─ deriveMessages(): Project model history from the immutable log
  │     ├─ agent/request (waterfall)
  │     ├─ llm/stream (waterfall) ──► assistant/chunk* ──► assistant/message
  │     ├─ tool/call* ──► tools/pre-execute ──► tools/execute ──► tools/post-execute ──► tool/result*
  │     └─ step/end
  │
  ├─ Check continuation: tools owe another request OR queued input arrived?
  │     ├─ If YES: jump back to step/start (next step)
  │     └─ If NO: proceed
  │
  ├─► agent/turn-stopping (serial gate)
turn/end
```

## Capability Seams and Append-Only Logs

Two additional engineering choices stand out in the architecture:

### 1. Capability Seams

A **seam** separates a Service Definition (interface), a Service Provider (implementation), and Consumers (tools).

Because file access (`ctx.fs`

), subprocess execution (`ctx.subprocess`

), and terminals (`ctx.terminals`

) share a common seam, changing the provider from local Node.js to a remote microVM moves all tools, file reads, and language servers into the container simultaneously. Consumer code remains untouched.

### 2. “Model-Visible Means Logged”

The harness enforces an invariant: **anything that reaches the LLM must be reconstructable from the append-only event log**.

- The runtime does not mutate an in-memory chat array. The
`deriveMessages()`

function computes context directly from immutable`SessionEvent`

records. - Sub-agents branch cleanly via
`ctx.sessions.fork(parentSessionId, boundaryEventId)`

without cloning process heaps. - Deterministic replay allows developers to step through historical execution runs event by event.

## Why This Pattern Matters: The Self-Modifying Agent

Why go through the trouble of building formal spatiotemporal composability into an AI agent harness?

The most compelling answer is **runtime self-modification**.

In a traditional agent framework, if an agent writes a new tool or scripts an integration during a long-running task, it cannot mount that tool without restarting its process. Restarting wipes ephemeral memory, drops open network sessions, and resets execution state.

Under Cordis:

- The agent writes a new TypeScript tool plugin.
- The harness loads the plugin into the live context at runtime.
- Cordis checks the plugin’s coeffects, mounts the tool, and automatically updates the system prompt assembly for the next step.
- If the tool fails or finishes its purpose, the agent unloads the plugin. Cordis unwinds the effect tree with zero leftover memory or socket leaks.

The agent modifies its own runtime in-flight while preserving active session history.

## Open Questions and Engineering Trade-Offs

While the architecture is elegant on paper, it introduces real trade-offs that teams should weigh:

| Dimension | Monolithic Loop (e.g. Pi / Minimalist Harness) | Spatiotemporal Micro-Kernel (Cordis / dsh) |
|---|---|---|
| Conceptual overhead | Low: read one linear loop file | High: understand contexts, seams, and dispatch modes |
| Debuggability | Simple stack traces and breakpoints | Non-linear event graphs across waterfall chains |
| Dynamic safety | Compile-time static guarantees | Runtime dependency resolution in TypeScript |
| Extensibility | Fork or subclass internal code | Declarative plugin mounting via config patches |
| Self-modification | Difficult without process restart | Native support for in-flight tool mounting and unwinding |

Three practical questions remain as this pattern encounters production adoption:

**Debugging Indirection:** When behavior is distributed across multiple waterfall listeners, tracing why a prompt was altered or a tool call was rejected requires dedicated event-graph tooling.**Language Boundaries:** Cordis is written in TypeScript. Bringing this level of dynamic effect unwinding to environments like Python or Rust requires different runtime primitives (e.g. explicit RAII guards or actor systems).**Complexity Budget:** For focused coding agents with fixed tool sets, a 200-line linear loop like HuggingFace’s Tau remains significantly easier to audit and reason about.

## The Bottom Line

DeepSeek Harness and the Cordis paper represent a deliberate shift in agent system design: treating agent harnesses not as static scripts around an LLM API, but as dynamic operating systems for hot-swappable capabilities.

I am still working my way through the mathematical details and operational calculus in the paper. We do not need to rush to declare this architecture superior or inferior to simpler, linear agent loops. Instead, we should wait and watch where these patterns head: whether dynamic plugin trees become the standard foundation for self-modifying agents, or if linear simplicity remains the preferred choice for production stability.

For now, the paper gives us a clear vocabulary for understanding what true modularity in agent runtimes requires.

*Observing how agent architectures evolve, or experimenting with plugin runtimes for autonomous systems? I would love to hear your perspective. Reach out on LinkedIn.*
