From f850c58188637cbccf9913517b0349f41bc1bf3b Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Sat, 3 Oct 2026 15:54:21 +0200 Subject: [PATCH 1/2] docs: reference Swarm AI use of COMA APIs --- README.md | 2 ++ README_de.md | 2 ++ llms.txt | 1 + 3 files changed, 5 insertions(+) diff --git a/README.md b/README.md index aa3e1e6..282154d 100644 --- a/README.md +++ b/README.md @@ -490,6 +490,8 @@ COMA is part of the `ellmos-ai` orchestration architecture and the broader `open | **Agent Bootstrap** | [`dev-bricks/safe-start-for-codex`](https://github.com/dev-bricks/safe-start-for-codex) | Safe startup, environment checks & preflight diagnostics | | **Umbrella Ecosystem** | [`open-bricks`](https://github.com/open-bricks) | Open-source foundation for local-first developer tools | +- [Swarm AI](https://github.com/ellmos-ai/swarm_ai) optionally uses COMA's Spawner and adapter APIs for provider-backed workflows. + --- diff --git a/README_de.md b/README_de.md index 5bd98cf..7ed0a83 100644 --- a/README_de.md +++ b/README_de.md @@ -487,6 +487,8 @@ COMA ist integraler Bestandteil der `ellmos-ai`-Architektur und des `open-bricks | **Agenten-Bootstrap** | [`dev-bricks/safe-start-for-codex`](https://github.com/dev-bricks/safe-start-for-codex) | Sicherer Start, Umgebungsprüfungen & Preflight-Diagnostik | | **Dach-Ökosystem** | [`open-bricks`](https://github.com/open-bricks) | Open-Source-Fundament für lokale Entwicklerwerkzeuge | +- [Swarm AI](https://github.com/ellmos-ai/swarm_ai) nutzt COMAs Spawner- und Adapter-APIs optional für anbieterbasierte Workflows. + --- diff --git a/llms.txt b/llms.txt index a2a5dc5..1a31ec1 100644 --- a/llms.txt +++ b/llms.txt @@ -113,6 +113,7 @@ coma check .\coma-vendor.json - [`README.md`](README.md): Primary documentation and 18-point quick navigation. - [`README_de.md`](README_de.md): German documentation and architecture overview. +- [Swarm AI](https://github.com/ellmos-ai/swarm_ai): Optional consumer of COMA's Spawner and adapter APIs for provider-backed workflows. - [`NOTICE`](NOTICE): Formal copyright attribution (Lukas Geiger / ellmos-ai / open-bricks). - [`KONZEPT.md`](KONZEPT.md): Architectural rationale, session-decoupling motivation, and offline design. - [`SECURITY.md`](SECURITY.md): Process isolation, single-writer protocol boundaries, and 48h vulnerability reporting SLA. From bf4162fd3a1a6163fe769aa3130e808797862f09 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Sat, 3 Oct 2026 16:18:54 +0200 Subject: [PATCH 2/2] test: isolate provider launch contracts across platforms --- tests/test_session.py | 38 +++++++++++++++++- tests/test_skeleton_adapters.py | 68 +++++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/tests/test_session.py b/tests/test_session.py index f1965ce..d0fbbf8 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -126,10 +126,44 @@ def test_probe_accepts_token_from_bounded_fake_process(tmp_path): assert probe(command, 5, cwd=tmp_path) == (True, f"{PROBE_TOKEN} erkannt") -def test_cli_session_dry_run_never_starts_provider(prompt_file, tmp_path, capsys): +def test_cli_session_dry_run_never_starts_provider( + prompt_file, tmp_path, capsys, monkeypatch +): + monkeypatch.setattr( + "coma.session.shutil.which", + lambda provider: "codex-test" if provider == "codex" else None, + ) + + def fail_if_started(*args, **kwargs): + pytest.fail("dry-run darf keinen Provider-Prozess starten") + + monkeypatch.setattr("coma.cli.subprocess.run", fail_if_started) + monkeypatch.setattr("coma.cli.subprocess.Popen", fail_if_started) code = main([ "session", "--provider", "codex", "--prompt-file", str(prompt_file), "--request", "Nur planen.", "--cwd", str(tmp_path), "--dry-run", ]) assert code == 0 - assert "Nur planen." in capsys.readouterr().out + output = capsys.readouterr().out + assert output.startswith("codex-test ") + assert "Nur planen." in output + + +def test_cli_session_dry_run_reports_missing_cli_without_starting( + prompt_file, tmp_path, capsys, monkeypatch +): + monkeypatch.setattr("coma.session.shutil.which", lambda _provider: None) + + def fail_if_started(*args, **kwargs): + pytest.fail("fehlende Provider-CLI darf keinen Prozessstart auslösen") + + monkeypatch.setattr("coma.cli.subprocess.run", fail_if_started) + monkeypatch.setattr("coma.cli.subprocess.Popen", fail_if_started) + code = main([ + "session", "--provider", "codex", "--prompt-file", str(prompt_file), + "--request", "Nur planen.", "--cwd", str(tmp_path), "--dry-run", + ]) + captured = capsys.readouterr() + assert code == 2 + assert "Provider 'codex' wurde nicht gefunden" in captured.err + assert captured.out == "" diff --git a/tests/test_skeleton_adapters.py b/tests/test_skeleton_adapters.py index c2c9845..0aa7747 100644 --- a/tests/test_skeleton_adapters.py +++ b/tests/test_skeleton_adapters.py @@ -5,6 +5,8 @@ dass sie die dokumentierte Aufrufkonvention korrekt zusammenbauen — und dass die Sicherung greift, die sie von einem unbeaufsichtigten Lauf trennt. """ +import os + import pytest from coma import Spawner, UnverifiedAdapterError @@ -66,13 +68,43 @@ def test_claude_needs_no_opt_in(self, monkeypatch, fake_run): class TestCodex: - def test_uses_native_exec_via_windows_node_entrypoint(self): - cmd = CodexAdapter().build_cmd("Mach was") + def test_windows_node_entrypoint_contract(self): + entrypoint = ( + r"C:\fixture\node_modules\@openai\codex\bin\codex.js" + ) + cmd = CodexAdapter( + executable="node", entrypoint=entrypoint + ).build_cmd("Mach was") assert cmd[0] == "node" assert "@openai/codex/bin/codex.js" in cmd[1].replace("\\", "/") assert cmd[2] == "exec" assert cmd[-1] == "Mach was" + def test_native_executable_contract_has_no_node_entrypoint(self): + cmd = CodexAdapter(executable="codex").build_cmd( + "Mach was", entrypoint=None + ) + assert cmd == [ + "codex", "exec", "--sandbox", "read-only", + "--skip-git-repo-check", "--ephemeral", "Mach was", + ] + + def test_host_default_launcher_matches_platform(self): + adapter = CodexAdapter() + cmd = adapter.build_cmd("Mach was") + if os.name == "nt": + assert adapter.executable == "node" + assert adapter.entrypoint is not None + assert "@openai/codex/bin/codex.js" in str(adapter.entrypoint).replace( + "\\", "/" + ) + assert cmd[0] == "node" + assert cmd[2] == "exec" + else: + assert adapter.executable == "codex" + assert adapter.entrypoint is None + assert cmd[0:2] == ["codex", "exec"] + def test_read_only_by_default(self): cmd = CodexAdapter().build_cmd("Mach was") assert cmd[cmd.index("--sandbox") + 1] == "read-only" @@ -138,10 +170,38 @@ def test_prompt_directly_after_p(self): index = cmd.index("-p") assert cmd[index:index + 2] == ["-p", "mach was"] - def test_windows_uses_node_entrypoint(self): - cmd = KimiAdapter().build_cmd("mach was") + def test_windows_node_entrypoint_contract(self): + entrypoint = ( + r"C:\fixture\node_modules\@moonshot-ai\kimi-code\dist\main.mjs" + ) + cmd = KimiAdapter( + executable="node", entrypoint=entrypoint + ).build_cmd("mach was") assert cmd[0] == "node" assert "@moonshot-ai/kimi-code" in cmd[1].replace("\\", "/") + assert cmd[2:] == ["-p", "mach was"] + + def test_native_executable_contract_has_no_node_entrypoint(self): + cmd = KimiAdapter(executable="kimi").build_cmd( + "mach was", entrypoint=None + ) + assert cmd == ["kimi", "-p", "mach was"] + + def test_host_default_launcher_matches_platform(self): + adapter = KimiAdapter() + cmd = adapter.build_cmd("mach was") + if os.name == "nt": + assert adapter.executable == "node" + assert adapter.entrypoint is not None + assert "@moonshot-ai/kimi-code" in str(adapter.entrypoint).replace( + "\\", "/" + ) + assert cmd[0] == "node" + assert cmd[2:] == ["-p", "mach was"] + else: + assert adapter.executable == "kimi" + assert adapter.entrypoint is None + assert cmd == ["kimi", "-p", "mach was"] @pytest.mark.parametrize("flag", ["-y", "--yolo", "--auto"]) def test_incompatible_flags_are_refused(self, flag):