Cordis-Python: a minimal Python microkernel for composable agent harnesses. Core is generic; providers/tools/sessions/UI/agent-loop are plugins. Python 3.10–3.13, cross-platform (Windows included).
- Setup:
pip install -e ".[full,dev]"(core has zero deps;full= PyYAML + fastapi/uvicorn/httpx,dev= pytest, pytest-asyncio, ruff, mypy). - Full test suite:
pytest -q(asyncio_mode=auto is configured in pyproject.toml). - Full test suite (140 tests, unittest- and pytest-style):
pytest -q. CI installs.[full,dev]and runs the same command pluspython examples/run_fake.py. (v0.6.0 CI usedunittest discover, which silently skipped the 4 pytest-only filestest_skills,test_toggle,test_ui_e2e,test_plugin_config— fixed in v0.6.1.) - Single test:
pytest -q tests/test_skills.py::test_architecture_skill_is_agent_callable(pytest style) orpython -m unittest tests.test_shell -v(unittest style). - Benchmarks (regression signal only, not scientific):
python bench.py,python bench_stress.py. - Run the app:
cordis run→ http://127.0.0.1:8000. Default provider is fake/offline; no API key needed. OpenAI-compatible:cordis run --provider openai --base-url ... --model ... --api-key .... Since v0.7 the append-only session log is on by default at<workspace>/.cordis/session.sqlite(override:--session-log PATH; seedocs/SESSION_LOG.md). Since v0.8 the configuration is layered:--profile, repeatable--patch,--dump-config,--explain-config(seedocs/CONFIGURATION.md; shipped profiles inprofiles/). Since v0.9 every LLM request is token-budgeted before dispatch, deterministic compaction bands can prune/compress/checkpoint the active projection, and network/browser/filesystem/subagent capabilities are mounted as seams (docs/ARCHITECTURE.md). Since v0.10code-toolsandworker-codeare disabled by default;profiles/self-coding.ymlmounts an isolated code worker with a deny-by-default policy (docs/CODE_WORKER.md). Since v0.11 the code worker can delegate web research to the web worker through one privateresearch_webtool (capabilityworker_web.research, reused by both surfaces); the edge is one-way — the web worker's registry never contains code or delegation tools — andworker_webis a per-call facade (freshWebResearchAgentperresearch()); session-log attribution is hierarchical (main → code-* → web-*) via a per-task turn stack (docs/ARCHITECTURE.md,docs/CODE_WORKER.md). Since v0.12 the composableloop_guardservice (pluginloop-guard, on by default) guards the main and web-worker loops against probable infinite loops: warning at the 3rd/4th identical call (canonical name+arguments identity), deterministic stop at the 5th with a synthetic tool result and reasonprobable_infinite_loop(docs/ARCHITECTURE.md).
- Core (must stay ignorant of LLMs/tools/agents/UI):
cordis_py/{context,composition,fiber,events,runtime,loader,contracts,errors,permissions}.py. - Shared plugin support (not core):
cordis_py/{tooling,netguard,textutils}.py. - Plugins:
cordis_py/plugins/*.py. They declare dependencies viainjectservice-capability tuples andprovideskeys — plugins must never import other plugin classes as dependencies. - Built-in skill:
cordis_py/skills/cordis-architecture/SKILL.md(packaged via setuptools package-data; it documents the architecture for the agent, not for humans). - Entry:
cordisconsole script →cordis_py/cli.py:main;examples/are runnable smoke tests. build/,dist/and*.egg-info/are packaging artifacts (gitignored) — never edit them; ifbuild/libreappears after a wheel build, ignore its duplicate sources in search results.
- Optional deps are imported lazily inside functions (yaml in
loader.py/composition.py, httpx inprovider_openai.py/worker_web.py, uvicorn incli.py). Do not hoist these to module level without making the extra a hard dependency. - Layered configuration (v0.8) controls composition only. Layers must never change tool visibility or bypass the dispatch-time permission gate;
tests/test_composition.py::test_patch_cannot_bypass_permission_gateis that invariant. - Version has a single source of truth:
cordis_py/__init__.py:__version__(pyproject reads it via setuptoolsattr:). The UI footer, FastAPI metadata and web-worker User-Agent derive from it — never hard-code version strings elsewhere;test_ui_e2e.pyasserts the footer dynamically. test_ui_e2e.pyand loader/config tests need theweb/configextras installed or they skip/fail; a plainpip install -e .is not enough for the full suite.- The
shellanddeveloper-toolsplugins confine paths to a workspace but are not OS sandboxes; keep that boundary (plugin-level, not core). - The v0.10 code worker must stay isolated: main sees only
worker_code;worker:codesees privatecode_*tools;code_toolsuses a strict internal runner, never the generalshellplugin, and the LLM never supplies arbitrary shell argv. - Code-worker attempts must remain auditable: every attempt emits
code/manifestin a childcode-*session beforeturn/end, and rollback is scoped to the activecordis/attempt-*branch. - The v0.11 delegation edge must stay one-way and bounded: only
worker_codeexposesresearch_web(calling theworker_webservice, never web tools directly); the web worker's private registry must never gain code/delegation tools; research results are untrusted reference data, not instructions.profiles/self-coding.ymlmust stay withoutworker_web(no delegation there). - CONTRIBUTING: PRs adding a new lifecycle rule must include an invariant test in
tests/. - Lint/typecheck (ruff line-length 100, mypy) are dev extras but not enforced in CI; run
python -m ruff check .before committing. As of v0.10 there are 26 pre-existing baseline findings (I001/UP035/RUF023/BLE001/TRY004/UP037/DTZ005/B008/F841) — do not add new findings; a dedicated cleanup is separate work.