Orply.

DeepSeek Harness Makes the Agent Loop and Tools Replaceable Plugins

Alejandro AOHugging FaceMonday, November 9, 20268 min read

DeepSeek Harness (DSH) treats the agent loop, tools, system prompt and execution layer as plugins rather than fixed parts of an agent application. In a technical walkthrough, Alejandro Ao argues that this design, built on the Cordis plugin system, lets developers replace or extend those components without modifying a monolithic core. He demonstrates plugins that add a greeting tool and block a potentially destructive bash command, loaded through DSH configuration.

The harness has no fixed agent loop

DeepSeek Harness (DSH) is an agent harness, not just a coding-agent interface. As Alejandro AO explains, it supplies an agent loop, tools, system prompts, session files, and a user interface, but is intended to support other agentic applications too. The desktop application is an Electron wrapper around the web application; the project also offers TypeScript and Python SDKs and ACP. A command-line interface, he notes, is not part of the implementation he is discussing.

The design principle is “Everything is a plugin.” In DSH, that includes components many applications treat as fixed infrastructure: the agent loop, tool registry, individual tools, system prompt, tool-execution layer, model providers, skills, session storage, and interface. The desktop UI is itself a Cordis application, communicating with a separate Cordis application on the backend.

Everything, including the agent loop, the tools, the system prompt, everything. The tool execution layer, everything itself plugs to the same plugin surface.
Alejandro AO

That makes the agent loop replaceable. A user could install a plugin that never calls a model or invokes tools and instead returns the user’s query as an echo. The point is not that this would be useful in normal operation; it is that the loop is not privileged code that must be patched to change the agent’s behavior.

The same modularity applies to tool execution. DSH comes with bash and filesystem sandboxes and a subprocess plugin, but a different execution plugin could send tool work to a remote machine. Model integrations, system prompts, and session persistence are also plugins. These parts can be exchanged or extended through a shared mechanism rather than by modifying a monolithic core.

The application tour distinguishes several agent modes. Standard mode is the general-purpose coding-agent experience. PTC mode runs tool calls through scripts, allowing calls to be batched or parallelized rather than handled only one at a time. Minimal mode supplies less context. Creator mode adds the skills and context needed for the agent to modify DSH itself by writing plugins. Alejandro also demonstrates configuring model providers in settings, including third-party providers and custom model APIs.

Cordis connects plugins through services and context

Alejandro describes Cordis as the plugin system that powers DSH, while distinguishing the two projects. The Cordis repository is not owned by DeepSeek, he says, although the relationship is close: DSH identifies Cordis as its underlying architecture, the Cordis project links to the DSH site, and the design paper is published under DeepSeek-AI. He says Cordis’s first commit predates DeepSeek as a company by about a year. His understanding is that Cordis’s creator later joined DeepSeek and that Cordis became the foundation for the harness. Cordis and DSH, in other words, are closely linked but are not the same project.

Cordis is not limited to coding agents. Alejandro presents it as a way to compose applications more generally, though he sees its composability as especially useful for coding agents. For a more formal account, he recommends the first pages of the design paper as an accessible introduction before its technical material becomes more demanding.

At runtime, plugins connect through a shared context, written as ctx. Some provide services on that context; others listen to events emitted through it. DSH’s session, tool, and model-provider plugins can provide capabilities. A compaction plugin or a headless interface can listen for events. The context is the common surface through which these pieces interact.

Alejandro describes four things a plugin can do. They are not sequential steps every plugin must follow: a plugin may provide a service, depend on one, register something with one, or listen for events.

OperationWhat it doesExample from the walkthrough
Provide a serviceClaim a name on the context and make a capability availableThe tools service
Inject a dependencyWait for a named service to be mountedA tool plugin depends on tools
Register into a serviceAdd an item to a serviceRegister the greet tool with tools
Listen to eventsReact when an event is emittedLog tools/result or intercept tools/pre-execute
Four ways plugins participate in Cordis; a plugin need not use them in this order.

A service is an object mounted at a named key on the context, commonly with methods such as register, resolve, or execute. It may own the events associated with the actions it performs. The service model also creates a seam between an abstract interface, its provider, and its consumers. Alejandro’s example uses a shell service: a tool can call that service without depending directly on whether it is provided by a bash sandbox, a local implementation, or another provider.

Plugins can therefore cooperate without importing one another directly. In Alejandro’s example, a greet-tool plugin injects tools and registers a greet tool. A separate logger also injects tools, but listens for the tools/result event and logs which tool ran. The logger does not need to know that greet-tool exists; the service and its event connect them.

Cordis also tracks what a plugin registers so it can clean up when that plugin is disabled or unmounted. Alejandro says the system tracks which features to unregister, which names to release, and the order in which dependent plugins should be removed. Plugins can provide additional cleanup behavior themselves, which he recommends as good practice.

Events can observe, wrap, or stop work

Cordis events have different dispatch modes, chosen according to what the event needs to do. An ordinary emit is fire-and-forget; session/event and tools/result are examples. A waterfall event behaves like middleware: a listener can inspect or alter an operation, pass control onward, or veto it. The examples shown include agent/pre-step, llm/stream, and tools/execute. In a waterfall handler, calling next() continues the normal flow; returning a denial can interrupt it.

Other events are serial, with handlers awaited in order. Alejandro gives agent/created and agent/turn-stopping as examples. The slide also distinguishes parallel dispatch, which fans out, from bail dispatch, where the first answer wins. The service that owns an action fires its event; in a waterfall, the innermost next() reaches the underlying work.

Dispatch modeBehaviorExamples shown
EmitFire and forgetsession/event; tools/result
WaterfallMiddleware can wrap, rewrite, or veto workagent/pre-step; llm/stream; tools/execute
SerialHandlers run in order and are awaitedagent/created; agent/turn-stopping
Parallel / bailFan out to listeners / use the first answerNo specific event named on the slide
Cordis event modes and the behaviors and examples shown in the walkthrough.

The plugin that owns an action can declare new events for others to consume; the compaction plugin, for example, adds events under compaction/*. Events are also scoped to the context where they are emitted. A listener in an agent’s context sees events for that agent, rather than events from every agent.

Cordis plugins can be nested. A child can see services from its ancestors, and an isolated subtree can have its own instance of a service. Alejandro describes this as useful for configurations such as different agent presets or per-agent registrations. But nesting is not a reason to place every plugin beneath another: in general, plugins can share a context and express their relationships through dependency injection. Nesting is useful when the scope itself matters.

A plugin can add a tool or intercept one

The code examples make the abstractions concrete. Alejandro says a minimal plugin needs an exported apply function that receives the Cordis context. His simplest example just logs that it has loaded. A plugin that adds a tool also exports a name and an inject list; because it needs the tool registry, it declares tools as a dependency. In apply, it uses the tools service to register a tool called greet, which accepts a name and returns a personalized greeting.

The example registers the tool through ctx.tools.register(defineTool(...)), declares a name parameter, and implements execution by returning a greeting. The plugin also logs that it registered the tool. It adds a capability through the service the agent already uses rather than replacing the tool system.

The third example is a guard plugin. It listens for tool results and logs when a tool finishes. It also listens to tools/pre-execute, a waterfall event, and checks whether the requested tool is bash and its command includes rm -rf. If so, it returns a denial instructing the agent to ask the user to run the command themselves. Otherwise, it calls next() so execution continues normally.

The distinction is that the logger observes a result after a tool has run, while the guard intervenes before execution. The logger uses an ordinary event; the guard uses a waterfall so it can either let the action proceed or block it.

Although Alejandro shows the code directly, he says most users would not normally write plugins by hand. Creator mode supplies the agent with DSH-specific skills and context so that it can create and modify plugins through conversation. The code walkthrough is meant to show what the agent is building and how the pieces fit together.

Configuration determines which plugins are mounted

In Cordis, plugins are loaded through configuration rather than by embedding plugin code in the configuration itself. A cordis.patch.yml file can insert plugins by package name or by file path. Each inserted plugin has an ID and a name; the name can point to an npm package or directly to a local plugin file.

For the web profile, Alejandro runs dsh web --patch with his custom YAML file. The patch adds his plugins to the web bundle’s existing configuration. That bundle already has its own cordis.patch.yml, listing the plugins that make up the base application. His patch layers additional entries on top.

The live test loads three plugins: a hello plugin that logs when it loads, a greet tool, and the bash guard. Alejandro asks the agent to greet Ada and then run a specific rm -rf command. The agent calls the greet tool; the guard blocks the bash command. The terminal logs show the hello plugin loading, the greet tool registering and running, and the guard blocking the command. The test demonstrates that the custom plugins mounted and that the guard acted before execution.

The desktop workflow is related but distinct. Alejandro says packaged plugins are added through the desktop helper; users do not normally launch the desktop app with --patch. Separately, user-level configuration lives under .dsh/profiles, with a configuration for each profile. The desktop profile’s cordis.patch.yml can set configuration values for existing plugins—for example, the displayed settings include a default model provider and model. Those model settings are not themselves plugin entries. The helper adds packaged plugins, while profile configuration can also modify settings for the running application.

He demonstrates the greet-and-block test in the desktop app as well. In both workflows, the code defines what a plugin provides or listens for, while the profile or bundle configuration determines which plugins are mounted. Creator mode can help author the plugin; packaging and configuration make it available to the running harness.

The frontier, in your inbox tomorrow at 08:00.

Sign up free. Pick the industry Briefs you want. Tomorrow morning, they land. No credit card.

Sign up free