A fast, minimal Python microkernel for composable agent harnesses.
Experimental, community-oriented implementation inspired by the public Cordis / DeepSeek Harness architecture. This project is not affiliated with or endorsed by DeepSeek or the Cordis maintainers, and does not claim API compatibility.
Cordis-Python keeps the core generic and small. LLM providers, tools, sessions, context management, agent loops, skills, workers and the web UI are plugins that communicate through stable services and scoped events.
Supported platforms: Windows, macOS and Linux. Python 3.10–3.13.
Cordis does not try to be one monolithic harness containing chat, tools, memory, browser, code, UI and telemetry.
It provides a small, working kernel—contexts, services, events, dependency reconciliation—and just enough base plugins to run a real agent. The agent can then read its own architecture skill and add specialized plugins through the same plugin boundary, instead of forcing every future capability into the core.
python -m venv .venvActivate the environment:
# Windows PowerShell
.venv\Scripts\Activate.ps1# macOS / Linux
source .venv/bin/activateInstall the full user extras and start the harness:
pip install -e ".[full]"
cordis run --profile profiles/user.yml --workspace .Open:
http://127.0.0.1:8000
The default provider is a fake offline provider, so the UI starts without any API key.
With profiles/user.yml, the UI starts even if SearXNG or Lightpanda are not running. Web tool calls that require a missing backend return a structured error until that backend is available.
profiles/user.yml is the recommended normal-user harness:
- the main agent sees only
runtime_status,list_skills,read_skill,worker_web_researchandworker_code - the main agent cannot directly call shell, file, raw web or raw code tools
- web research runs in an isolated
worker:webcontext - code work runs in an isolated
worker:codecontext - capability policy is deny-by-default
Use a workspace you are comfortable letting the code worker inspect. For real self-extension work, the workspace should be a clean Git repository.
cordis run --profile profiles/user.yml --dump-config
cordis run --profile profiles/user.yml --explain-configCordis-Python can connect to OpenAI-compatible endpoints such as LM Studio, llama.cpp, vLLM, Ollama and compatible servers:
cordis run \
--profile profiles/user.yml \
--workspace . \
--provider openai \
--base-url http://127.0.0.1:1234/v1 \
--model local-model \
--api-key lm-studio \
--context-window 131072On Windows PowerShell, use one line or PowerShell backticks instead of \ line continuations.
profiles/user.yml is provider-agnostic: it does not hard-code a provider, model, endpoint or API key.
| Profile | Intended use |
|---|---|
profiles/user.yml |
Recommended interactive user harness: UI, skills, isolated web/code workers, least-privilege main agent |
profiles/self-coding.yml |
Stricter code-worker composition: deny-by-default, no UI/shell/files/network by default |
profiles/research.yml |
Web-research-focused harness with a deny-by-default web capability gate |
profiles/coding.yml |
Developer file/shell harness without web UI |
profiles/minimal.yml |
Minimal agent loop + provider + conversation state |
Important distinction:
- Use
--profile profiles/user.ymlfor normal user work. - Running
cordis runwithout a profile is the developer/bootstrap harness. It mounts broader development plugins such asshell,developer-toolsandfilesystem-local. Treat it as a development configuration, not as the least-privilege user profile. - Only
profiles/user.ymlkeepsui-fastapienabled among the shipped profiles.research.yml,self-coding.yml,coding.ymlandminimal.ymldisable the UI; with the currentcordis runCLI, re-enableui-fastapiwith a patch or use a custom entrypoint.
The FastAPI dashboard is a real runtime inspector, not only a chat page:
- Chat — real tool-calling agent loop
- Runtime — services, fibers, effects and reconciliation diagnostics
- Plugins — plugin/fiber state, dependencies, provided services and enable/disable controls
- Skills — inspect skills callable by the agent
- Tools — inspect the tools currently exposed to the model
- Events — live lifecycle, agent and tool events
The UI does not depend on the agent service. If the provider disappears and the agent loop becomes pending, the dashboard remains alive so the user can inspect and repair the runtime.
Cordis-Python keeps an append-only SQLite record of every model-visible step:
- default path:
<workspace>/.cordis/session.sqlite - override:
--session-log PATH - storage: stdlib
sqlite3, WAL mode, no extra dependency - invariant: anything the model can see has a durable, ordered representation
- worker activity is logged in child sessions such as
web-<uuid>andcode-<uuid> - replay/fork/export APIs are available:
read,sessions,fork,derive_messages,export_jsonl
See docs/SESSION_LOG.md for schema, event mapping and replay semantics.
The context-manager plugin budgets every LLMRequest before dispatch:
- provider-owned token counting
- output and reasoning reserves
context/budgetemitted for prepared requests- deterministic compaction bands for prune / compress / checkpoint
- compaction bands never call the model
- the append-only session log is never mutated by compaction
See docs/ARCHITECTURE.md for the budgeting and compaction contract.
Cordis-Python separates visibility from authority:
- unauthorized tools are hidden from the LLM schema
- the same capability policy is checked again immediately before dispatch
- configuration layers control plugin composition only; they cannot bypass the dispatch-time permission gate
- shipped user/research/self-coding profiles use
default_allow: false - deny rules win over allow rules
- principals such as
main,worker:webandworker:codeare scoped separately
Minimal policy shape:
default_allow: false
principals:
main:
allow:
- "runtime_status"
- "list_skills"
- "read_skill"
- "worker_web.research"
- "worker_code.task"
"worker:web":
allow:
- "web.search"
- "web.fetch"
- "web.browser"
"worker:code":
allow:
- "code.search"
- "code.read"
- "code.write"
- "code.git.status"
- "code.git.diff"
- "code.test.run"
- "code.lint.run"
- "skill.read"
- "worker_web.research"The main agent sees one public web tool:
worker_web_research(task, sources=6, depth="low")
The web worker owns a private tool registry. Its internal tools are not exposed to the main agent:
web_search
web_fetch
web_browser
web_crawl
cordis run --profile profiles/user.yml starts the UI and runtime even if SearXNG or Lightpanda are not running.
| Capability | Required backend | Requirement |
|---|---|---|
web_search |
SearXNG | accessible SearXNG instance; default endpoint http://127.0.0.1:8080 |
web_fetch |
HTTPX | included by pip install -e ".[full]"; no Lightpanda required |
web_browser |
Lightpanda | lightpanda on PATH, or configure lightpanda_binary |
web_crawl |
Firecrawl | optional; configure firecrawl_url and enable Firecrawl explicitly |
web_searchcurrently uses SearXNG through thenetwork-webseam.web_fetchuses HTTPX for direct public page fetches and does not require Lightpanda.web_browserrequires Lightpanda for JavaScript rendering/navigation.web_crawl/ Firecrawl is optional and explicitly configured;profiles/user.ymldeniesweb.crawlby default.- Missing backends do not prevent startup; they cause the corresponding web tool call to return a structured error.
Untrusted target URLs are validated against SSRF-style abuse, including redirects. Point network-web.searxng_url at your own SearXNG instance if needed.
See docs/WORKER_WEB.md for depth modes, seams, backend behavior, configuration and isolation limits.
The main agent sees one public code tool:
worker_code(task, on_success="leave_dirty", on_failure="rollback",
commit_message="", fail_on_lint=true, max_steps=8)
The worker:code principal sees a private registry of code tools:
code_search
code_read
code_create_file
code_delete_file
code_apply_patch
code_git_status
code_git_diff
code_run_tests
code_run_lint
read_skill
research_web (only when the worker-web plugin is mounted)
When the web worker is mounted, research_web is the code worker's only web access: one narrow delegation to the isolated worker:web, never the raw web tools.
The worker does not receive the general shell plugin and cannot supply arbitrary shell argv. It runs only fixed, Cordis-built command operations:
gitfor checkpoint / status / diff / commit / rollbackpython -m pytest -qfor testspython -m ruff check .for lint- Python
compile()for syntax checks
Each attempt:
- checkpoints on a
cordis/attempt-*branch - edits files inside the configured workspace
- runs syntax, test and lint validation
- emits a
code/manifestevent in a childcode-<uuid>session - leaves changes dirty, commits, or rolls back according to the harness
- reports structured failure semantics and retry guidance
The retry budget is bounded per user request.
See docs/CODE_WORKER.md for the full manifest, Git workflow and profile details.
Cordis-Python's default answer to "the agent must do something risky" is not to expose more low-level tools to the main agent, but to isolate the work behind a specialized worker with a narrow boundary. This is a Cordis architectural choice, not a generic multi-agent framework:
- the main agent receives small, intentional high-level capabilities (
worker_code,worker_web_research), never the underlying tools - each specialized worker owns a private tool registry and a restricted capability scope
worker:codeperforms filesystem/code/test/git operations inside its isolated code capability boundary- when code work needs external information,
worker:codedoes not receive rawweb_search,web_fetch,web_browser,web_crawl; it receives the narrowresearch_webdelegation, which invokes the isolatedworker:web - web research results cross the boundary as untrusted reference data, never instructions
- delegation is deliberately asymmetric:
worker:code→worker:webexists, butworker:web→worker:codedoes not - the asymmetry prevents an accidental generic recursive worker graph: delegation stays bounded to depth 1 and remains auditable
- the session log preserves the hierarchy
main → code-* → web-*
The rule: isolate risky capabilities behind narrow worker boundaries, compose those capabilities explicitly through the capability policy, and keep the microkernel unaware of application-specific worker orchestration.
The bundled worker-web, worker-code, browser, memory and other reference plugins are not claimed to be the best possible implementations. They are functional reference plugins intended to show that the Cordis model works end-to-end, and that implementations can be replaced without moving their complexity into the microkernel.
See docs/ARCHITECTURE.md, docs/CODE_WORKER.md and docs/WORKER_WEB.md for implementation details.
Conceptual flow for:
User: Add a weather plugin.
main:
1. read_skill("cordis-architecture")
2. worker_code(
task="Add a weather plugin",
on_success="leave_dirty",
on_failure="rollback"
)
worker:code:
3. code_search / code_read to inspect existing plugin patterns
4. code_create_file / code_apply_patch to add the weather plugin
5. code_run_tests
6. code_run_lint
7. success → code/manifest + leave dirty or commit
failure → rollback + code/manifest with retry guidance
The point is not that the core understands weather plugins. The point is that the agent can extend the harness through the same plugin, permission, test and audit boundaries already exposed by Cordis.
Cordis-Python ships a built-in skill:
cordis_py/skills/cordis-architecture/SKILL.md
The skills plugin exposes it through ordinary tools:
list_skills
read_skill
Before modifying Cordis-Python itself, the agent is instructed to read cordis-architecture, inspect the relevant source/runtime state, preserve the core/plugin boundary, make minimal edits and run tests.
Project skills can also be loaded:
my-skills/
└── my-project/
└── SKILL.md
cordis run --profile profiles/user.yml --skills ./my-skillsReal protections:
- deny-by-default capability profiles
- private tool registries for workers
- tool schema hiding plus dispatch-time permission re-check
- workspace confinement for file tools and code tools
- no arbitrary shell argv in the code worker
- fixed pytest / ruff / git command surface for code attempts
- SSRF validation for untrusted web targets
- append-only session log for model-visible steps
- structured
code/manifestaudit data for code attempts
Explicit limits:
- Cordis-Python is not an OS security sandbox.
- Plugins run inside the Cordis Python process.
- The
shellplugin, when enabled, is an allow-listed subprocess helper, not an OS sandbox. - The network guard is an SSRF/URL validation layer, not a full egress proxy.
- For untrusted workloads, run Cordis inside a container, VM, micro-VM or dedicated OS sandbox.
CORDIS-PY CORE
Context / scope / isolation / services
│
events: emit / serial / parallel / waterfall
│
Runtime
dependency reconciliation
│
reversible Effect cleanup
│
↓
PLUGINS
provider ─────────────┐
tools ────────────────┤
sessions ─────────────┼──→ agent-loop
context-manager ──────┤
skills ───────────────┘
│
└── read_skill("cordis-architecture")
developer-tools ──→ workspace file tools
shell ────────────→ allow-listed subprocess helper
worker-web ───────→ isolated web research
worker-code ──────→ isolated code worker
session-log ──────→ append-only SQLite audit
ui-fastapi ───────→ live runtime inspection + chat
The core remains ignorant of LLMs, tools, skills, agents, browsers, code workers and UI.
Plugins depend on capabilities and contracts, not on plugin implementations.
Good:
class ContextManagerPlugin:
inject = ("provider",)Bad:
from cordis_py.plugins.provider_openai import OpenAIProviderPluginA provider owns model transport, token counting and context-window knowledge. Context compaction remains separate because its strategy can change independently.
cordis_py/
├── context.py
├── composition.py
├── contracts.py
├── errors.py
├── events.py
├── fiber.py
├── loader.py
├── netguard.py
├── permissions.py
├── runtime.py
├── textutils.py
├── tooling.py
├── cli.py
│
├── plugins/
│ ├── provider_fake.py
│ ├── provider_openai.py
│ ├── tools.py
│ ├── agent_loop.py
│ ├── session.py
│ ├── session_log.py
│ ├── context_manager.py
│ ├── skills.py
│ ├── developer_tools.py
│ ├── shell.py
│ ├── filesystem_local.py
│ ├── network_httpx.py
│ ├── browser_lightpanda.py
│ ├── worker_web.py
│ ├── code_tools.py
│ ├── worker_code.py
│ ├── subagents.py
│ ├── capabilities.py
│ └── ui_fastapi.py
│
└── skills/
└── cordis-architecture/
└── SKILL.md
pip install -e ".[full,dev]"
pytest -q
ruff check .On current main (v0.11.0), the suite reports:
177 passed
Benchmarks are regression signals, not scientific measurements:
python bench.py
python bench_stress.pyDeep details live in docs/:
docs/ARCHITECTURE.mddocs/CONFIGURATION.mddocs/SESSION_LOG.mddocs/WORKER_WEB.mddocs/CODE_WORKER.mddocs/PLUGIN_CONFIG.md
MIT.