{"slug": "deepseek-harness-everything-is-a-plugin-choose-the-right-cordis-extension-seam", "title": "DeepSeek Harness “Everything Is a Plugin”: choose the right Cordis extension seam", "summary": "DeepSeek Harness, an open-source agent framework from DeepSeek, treats every component—model adapter, tool registry, session log, and agent loop—as a Cordis plugin, according to a developer guide pinned to commit 141eb6f. The guide advises developers to choose plugin seams by ownership, lifetime, dependency, and replacement boundary rather than directory or YAML position, and details how services, events, and effects map to different integration needs.", "body_md": "DeepSeek Harness does not treat plugins as optional add-ons around a privileged core. At the source snapshot checked for this guide—default branch master, commit 141eb6fef83422698aef7a981029e843e8161534—the model adapter, tool registry, session log, and agent loop are all Cordis plugins. A plugin contributes services, typed events, and reversible effects to a shared context; profiles and bundles compose those plugins into a runnable product.\n\nThe practical rule is:\n\nDo not choose by directory name or YAML position. Choose by ownership, lifetime, dependency, and replacement boundary.\n\nDeepSeek Harness is still a **Developer Preview**. GitHub published the dsh-v0.1.0-rc.8 pre-release on August 19, while npm still reported @deepseek-ai/[dsh@0.1.0-rc.7](mailto:dsh@0.1.0-rc.7) when checked on August 20. This article therefore pins source claims to commit 141eb6f and does not imply that every rc.8 package is already installable from npm.\n\nThis guide is for agent-framework developers deciding where to add a tool, policy, adapter, observer, UI surface, or deployment composition in DeepSeek Harness. It assumes you already have a disposable evaluation environment; if not, start with the [DeepSeek Harness install and rollback checklist](https://dev.to/blogs/deepseek-harness-developer-preview-install-safety-checklist/).\n\nIt is an architecture map, not a stability guarantee. The source may change between preview candidates, so repeat the config and lifecycle checks after every upgrade.\n\n```\nprofile: $DSH_HOME/profiles//package.json\n  ordered dsh.profile.bundles\n    each package declares dsh.bundle.patch\n      patch rows mount plugin fibers\n        plugin -&gt; provides ctx.\n               -&gt; listens/emits typed events\n               -&gt; owns reversible effects\n  then profile cordis.patch.yml\n  then home-level patch\n  then --patch overlays\n\nruntime dependency: inject -&gt; PENDING until service exists\nlifecycle: PENDING -&gt; LOADING -&gt; ACTIVE -&gt; UNLOADING -&gt; DISPOSED\n                              \\-&gt; FAILED\n~~~\n\nA **plugin** is the lifecycle owner. Cordis accepts a function, an object with apply(ctx), or a Service subclass. A **context** is the repository through which plugins find stable service keys such as ctx.tools, ctx.llm, and ctx.sessions.\n\nA consumer declares hard requirements through inject. Cordis keeps it PENDING until every required service exists, unloads it if a dependency disappears, and reloads it when the service returns. That is why YAML list order is not a startup contract: entries start concurrently, and dependency declarations control readiness.\n\n## Plugin, service, event, or effect?\n\n| Need | Put it here | Why |\n|---|---|---|\n| A callable capability with replaceable providers | Service on a stable ctx key | Consumers depend on an interface, not one implementation |\n| A model-callable action | Register with ctx.tools | The schema joins prompt assembly and execution stays in the tool pipeline |\n| Passive observation | emit listener | Producer does not need to know the observer |\n| Policy, request rewriting, or veto | waterfall listener | Around-middleware can delegate, transform, or deliberately short-circuit |\n| Timer, connection, watcher, or external handle | ctx.effect() with a disposer | Unload and HMR release the resource predictably |\n| A durable model-visible fact | Session event | Reload, replay, fork, and model history can reconstruct it |\n\nEvents have different contracts. emit broadcasts synchronously; parallel awaits listeners together; serial awaits them in order; waterfall wraps a continuation. In a waterfall, an observer that forgets next() does not merely miss a callback—it can swallow the default behavior for every downstream plugin.\n\nRegistrations should also unwind. Cordis already treats ctx.on(), child plugins, service registrations, and Harness registry registrations as effects. A raw interval, watcher, socket, or file handle must be acquired inside ctx.effect() and return a disposer. If several asynchronous cleanup steps require ordering, keep them in one disposer and await them there; separate async disposers may run concurrently.\n\n## Profile, bundle, or local patch?\n\nA **profile** is the runnable composition stored under $DSH_HOME/profiles/&lt;name&gt;. Its package.json lists ordered bundles in dsh.profile.bundles, installs out-of-tree dependencies, and sits beside the user's cordis.patch.yml. Shipped templates compose:\n\n- web: @deepseek-ai/dsh-base + @deepseek-ai/dsh-web-app;\n- headless: @deepseek-ai/dsh-base + @deepseek-ai/dsh-headless.\n\nA **bundle** is an npm package whose manifest declares a patch file through dsh.bundle.patch. The base bundle inserts model adapters, tools, persistence, sandbox and approval policy, settings, credentials, telemetry, and core subagent providers. Later bundles replace or add rows for a surface such as Web or headless execution.\n\nUse the profile patch for a local override. Publish a bundle when the composition itself is reusable, versioned, and installable. Keep one crucial constraint in view: a patch that targets a row replaces that row's **whole config**; it is not a deep merge. An override must restate every field it intends to preserve.\n\nInspect what will actually boot instead of reasoning from package names:\n\n~~~sh\ndsh --profile web --dump-config\n~~~\n\nThe current rc.8 release notes reinforce this distribution boundary: the official Codex and Claude Code subagent providers can be installed on demand as Profile Bundles. That release fact does not change the placement rule—provider code owns the service implementation; the bundle owns how that provider is mounted.\n\n## Extension placement decision tree\n\n~~~text\nDoes another plugin need to call this capability directly?\n  yes -&gt; define or use a service; inject the stable key\n  no  -&gt; does it observe or intercept an existing operation?\n           observe -&gt; event listener with automatic disposal\n           intercept -&gt; waterfall; call next() unless intentionally vetoing\n           neither -&gt; does it own a timer/socket/watcher/external handle?\n                        yes -&gt; reversible effect with one complete disposer\n                        no  -&gt; ordinary plugin-local logic\n\nDoes the change select or replace several plugin rows?\n  one machine/deployment -&gt; profile cordis.patch.yml\n  reusable distribution -&gt; bundle with dsh.bundle.patch\n\nMust the fact survive reload, fork, or replay?\n  yes -&gt; append a typed session event; do not keep it only in memory\n~~~\n\nIf one agent needs a different capability set, do not automatically fork the global profile. The architecture guide points to an isolated agent-preset realm for per-session composition. Likewise, a filesystem provider, sandbox provider, and model-facing tool are different roles even when one package happens to combine them.\n\n## Seven lifecycle and HMR canaries\n\n| Canary | Pass evidence | Failure meaning |\n|---:|---|---|\n| 1. Effective tree | --dump-config shows the intended row IDs and full configs | A layer was resolved or patched differently than expected |\n| 2. Required dependency | Removing a test provider leaves the consumer PENDING, not half-active | Dependency was read without inject or diagnostics are missing |\n| 3. Service recovery | Restoring the provider unloads/reloads dependants into ACTIVE | Stale service references or incomplete effects remain |\n| 4. Disposal | HMR closes each timer, watcher, listener, and connection exactly once | A resource lives outside the plugin lifecycle |\n| 5. Stable identity | Editing one entry with a stable id remounts only that entry and dependants | Generated IDs caused removal-plus-addition churn |\n| 6. Waterfall delegation | An observer calls next(); a veto test alone short-circuits | A listener accidentally swallowed the operation |\n| 7. Bad configuration | Invalid schema or missing bundle metadata fails visibly before partial activation | The deployment can silently boot an incomplete tree |\n\nAlso test a misspelled module path. The tutorial notes a sharp edge: an unresolved entry can be reported through the Cordis logger rather than crash, and an early report may be lost before a console exporter is watching. A plugin that “does nothing” therefore needs both fiber-state inspection and module-resolution verification.\n\n## Common mistakes\n\n- **Treating YAML order as dependency order.** Use inject; sibling entries start concurrently.\n- **Putting every extension in a new service.** Observers and policies usually belong on an existing event contract.\n- **Forgetting a disposer.** HMR then duplicates timers, listeners, or connections.\n- **Forgetting next() in a waterfall.** That is an implicit veto, not harmless logging.\n- **Assuming patch configs deep-merge.** Row config replacement can silently discard required fields.\n- **Using a global service name casually.** Service names share one flat namespace; choose a distinctive key.\n- **Calling PENDING success.** A process can exit quietly while a consumer waits for a missing service.\n- **Equating a GitHub tag with npm availability.** Record repository, commit, release, and registry version separately.\n\n## FAQ\n\n### Does “everything is a plugin” mean every feature should be a separate package?\n\nNo. Plugin is the lifecycle and composition unit. A package may expose one or several plugins, combine service roles, or ship a bundle whose main artifact is a patch list. Split by ownership and replacement boundary, not by slogan.\n\n### When should I use a service instead of an event?\n\nUse a service when a consumer needs a direct capability call and a stable interface. Use an event when producers and consumers should remain decoupled, especially for observation, policy, interception, and lifecycle signals.\n\n### What is the difference between a profile and a bundle?\n\nA profile is a named runnable composition in Harness home. A bundle is a distributable patch layer that a profile stacks. The profile is the deployment choice; the bundle is reusable composition material.\n\n### Is HMR automatically safe?\n\nOnly when all registrations and external resources are reversible, dependencies are declared, entry IDs are stable, and the replacement path is tested. HMR exposes lifecycle mistakes; it does not repair them.\n\n### Is rc.8 available from npm?\n\nAt the August 20 check, GitHub had published the dsh-v0.1.0-rc.8 pre-release and the repository declared rc.8, but npm latest and next still reported rc.7. Recheck the registry before installing and pin the exact version you verify.\n\n## Sources\n\n- [DeepSeek Harness official repository and Developer Preview boundary](https://github.com/deepseek-ai/deepseek-harness)\n- [DeepSeek Harness dsh-v0.1.0-rc.8 pre-release](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.0-rc.8)\n- [npm: @deepseek-ai/dsh 0.1.0-rc.7](https://www.npmjs.com/package/@deepseek-ai/dsh/v/0.1.0-rc.7)\n- [Architecture at commit 141eb6f](https://github.com/deepseek-ai/deepseek-harness/blob/141eb6fef83422698aef7a981029e843e8161534/docs/architecture.md)\n- [Cordis primer at commit 141eb6f](https://github.com/deepseek-ai/deepseek-harness/blob/141eb6fef83422698aef7a981029e843e8161534/docs/cordis-primer.md)\n- [Lifecycle and reversible effects tutorial at commit 141eb6f](https://github.com/deepseek-ai/deepseek-harness/blob/141eb6fef83422698aef7a981029e843e8161534/docs/cordis-tutorial/02-lifecycle-and-effects.md)\n- [Services and inject tutorial at commit 141eb6f](https://github.com/deepseek-ai/deepseek-harness/blob/141eb6fef83422698aef7a981029e843e8161534/docs/cordis-tutorial/03-services.md)\n- [Events and waterfall tutorial at commit 141eb6f](https://github.com/deepseek-ai/deepseek-harness/blob/141eb6fef83422698aef7a981029e843e8161534/docs/cordis-tutorial/04-events.md)\n- [Composition and HMR tutorial at commit 141eb6f](https://github.com/deepseek-ai/deepseek-harness/blob/141eb6fef83422698aef7a981029e843e8161534/docs/cordis-tutorial/06-composition-and-hmr.md)\n- [Profile and bundle composition source at commit 141eb6f](https://github.com/deepseek-ai/deepseek-harness/blob/141eb6fef83422698aef7a981029e843e8161534/packages/boot/app-boot/src/profile.ts)\n- [Official bundle contract at commit 141eb6f](https://github.com/deepseek-ai/deepseek-harness/blob/141eb6fef83422698aef7a981029e843e8161534/packages/bundle/README.md)\n\nOriginally published on [IndieSeek](https://indieseek.co/blogs/deepseek-harness-everything-plugin-cordis-architecture-guide/).\n```\n\n", "url": "https://wpnews.pro/news/deepseek-harness-everything-is-a-plugin-choose-the-right-cordis-extension-seam", "canonical_source": "https://dev.to/ahab_indieseek/deepseek-harness-everything-is-a-plugin-choose-the-right-cordis-extension-seam-4773", "published_at": "2026-08-20 01:21:38+00:00", "updated_at": "2026-08-20 01:43:08.818460+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-infrastructure"], "entities": ["DeepSeek", "DeepSeek Harness", "Cordis", "GitHub", "npm"], "alternates": {"html": "https://wpnews.pro/news/deepseek-harness-everything-is-a-plugin-choose-the-right-cordis-extension-seam", "markdown": "https://wpnews.pro/news/deepseek-harness-everything-is-a-plugin-choose-the-right-cordis-extension-seam.md", "text": "https://wpnews.pro/news/deepseek-harness-everything-is-a-plugin-choose-the-right-cordis-extension-seam.txt", "jsonld": "https://wpnews.pro/news/deepseek-harness-everything-is-a-plugin-choose-the-right-cordis-extension-seam.jsonld"}}