klanker 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
klanker-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lolaplex
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
klanker-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: klanker
3
+ Version: 0.0.1
4
+ Summary: Universal, adaptive slimemold AI agent shell on the Lolaplex suite
5
+ Author: Lolaplex
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Lolaplex/klanker
8
+ Project-URL: Repository, https://github.com/Lolaplex/klanker
9
+ Project-URL: Bug Tracker, https://github.com/Lolaplex/klanker/issues
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: agents-harness>=0.0.1
14
+ Requires-Dist: agents-relay>=0.0.1
15
+ Requires-Dist: agents-memory>=0.0.1
16
+ Requires-Dist: agents-traces>=0.0.1
17
+ Requires-Dist: agents-docs>=0.0.1
18
+ Provides-Extra: browser
19
+ Requires-Dist: agents-browser>=0.0.1; extra == "browser"
20
+ Provides-Extra: keys
21
+ Requires-Dist: agents-keys>=0.0.1; extra == "keys"
22
+ Provides-Extra: all
23
+ Requires-Dist: klanker[browser,keys]; extra == "all"
24
+ Provides-Extra: suite
25
+ Requires-Dist: klanker[browser,keys]; extra == "suite"
26
+ Dynamic: license-file
27
+
28
+ # Klanker 🤖
29
+
30
+ **The Adaptive Agent Distribution on the Lolaplex Suite.**
31
+
32
+ Klanker ist eine schlanke, adaptive Agenten-Distribution auf Basis des **Slimemold-Paradigmas**. Aufsetzend auf dem deterministischen Motor von `agents-harness` formt sich Klanker nach Bedarf:
33
+ - Vom autarken Server-Worker (`klanker cron`),
34
+ - ĂĽber den direkten Terminal-Begleiter (`klanker chat`),
35
+ - bis zum vollwertigen VPS-Assistenten via Telegram & HTTP (`klanker serve`).
36
+
37
+ ## Quickstart
38
+
39
+ ```bash
40
+ # Clone & install (installiert den kompletten Agents Core Stack: harness, gateway, memory, traces, docs)
41
+ pip install -e .
42
+
43
+ # Optional: Zusätzliche Slimemold-Fühler aktivieren
44
+ pip install -e ".[browser]" # CDP Browser Automation
45
+ pip install -e ".[keys]" # Ed25519 Agent Keys
46
+ pip install -e ".[suite]" # Volle Lolaplex Suite
47
+
48
+ # 1. Umgebung & FĂĽhler prĂĽfen
49
+ klanker sense
50
+
51
+ # 2. Dynamischen System-Prompt einsehen
52
+ klanker prompt
53
+
54
+ # 3. Interaktiver Chat (REPL)
55
+ klanker
56
+
57
+ # 4. Direkter Turn im Terminal
58
+ klanker "Wer bist du?"
59
+
60
+ # 5. I/O-Gateway starten (HTTP & Telegram)
61
+ klanker serve
62
+
63
+ # 6. Geplante Koru-Harness-Flows ausfĂĽhren
64
+ klanker cron
65
+ ```
66
+
67
+
68
+ ## Die Architektur
69
+
70
+ 1. **Das Brain (`agents-harness`)** — Motor, LLM-Streaming, Cordis-Kernel, Koru-Schedules, lokales Gedächtnis (`agents-memory`), JSONL-Observability & Chat-Rekonstruktion (`agents-traces`) und lokales Markdown-RAG (`agents-docs`).
71
+ 2. **Die I/O-Schicht (`agents-relay`)** — Universelle Ein- und Ausgabe via HTTP (`/v1/turn`) und Telegram Long-Poll.
72
+ 3. **Die User-App (`klanker`)** — Verbindet Brain & Relay zu einer adaptiven Slimemold-Shell.
73
+ 4. **Optionale Tools & Fühler** — Externe Fähigkeiten wie CDP-Browser (`agents-browser`), Ed25519-Keys (`agents-keys`), Jailed Terminal (`agents-terminal`) oder beliebige MCP-Tools, die Klanker bei Bedarf vorschlägt oder einbindet.
74
+
75
+
@@ -0,0 +1,48 @@
1
+ # Klanker 🤖
2
+
3
+ **The Adaptive Agent Distribution on the Lolaplex Suite.**
4
+
5
+ Klanker ist eine schlanke, adaptive Agenten-Distribution auf Basis des **Slimemold-Paradigmas**. Aufsetzend auf dem deterministischen Motor von `agents-harness` formt sich Klanker nach Bedarf:
6
+ - Vom autarken Server-Worker (`klanker cron`),
7
+ - ĂĽber den direkten Terminal-Begleiter (`klanker chat`),
8
+ - bis zum vollwertigen VPS-Assistenten via Telegram & HTTP (`klanker serve`).
9
+
10
+ ## Quickstart
11
+
12
+ ```bash
13
+ # Clone & install (installiert den kompletten Agents Core Stack: harness, gateway, memory, traces, docs)
14
+ pip install -e .
15
+
16
+ # Optional: Zusätzliche Slimemold-Fühler aktivieren
17
+ pip install -e ".[browser]" # CDP Browser Automation
18
+ pip install -e ".[keys]" # Ed25519 Agent Keys
19
+ pip install -e ".[suite]" # Volle Lolaplex Suite
20
+
21
+ # 1. Umgebung & FĂĽhler prĂĽfen
22
+ klanker sense
23
+
24
+ # 2. Dynamischen System-Prompt einsehen
25
+ klanker prompt
26
+
27
+ # 3. Interaktiver Chat (REPL)
28
+ klanker
29
+
30
+ # 4. Direkter Turn im Terminal
31
+ klanker "Wer bist du?"
32
+
33
+ # 5. I/O-Gateway starten (HTTP & Telegram)
34
+ klanker serve
35
+
36
+ # 6. Geplante Koru-Harness-Flows ausfĂĽhren
37
+ klanker cron
38
+ ```
39
+
40
+
41
+ ## Die Architektur
42
+
43
+ 1. **Das Brain (`agents-harness`)** — Motor, LLM-Streaming, Cordis-Kernel, Koru-Schedules, lokales Gedächtnis (`agents-memory`), JSONL-Observability & Chat-Rekonstruktion (`agents-traces`) und lokales Markdown-RAG (`agents-docs`).
44
+ 2. **Die I/O-Schicht (`agents-relay`)** — Universelle Ein- und Ausgabe via HTTP (`/v1/turn`) und Telegram Long-Poll.
45
+ 3. **Die User-App (`klanker`)** — Verbindet Brain & Relay zu einer adaptiven Slimemold-Shell.
46
+ 4. **Optionale Tools & Fühler** — Externe Fähigkeiten wie CDP-Browser (`agents-browser`), Ed25519-Keys (`agents-keys`), Jailed Terminal (`agents-terminal`) oder beliebige MCP-Tools, die Klanker bei Bedarf vorschlägt oder einbindet.
47
+
48
+
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "klanker"
7
+ version = "0.0.1"
8
+ description = "Universal, adaptive slimemold AI agent shell on the Lolaplex suite"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [{ name = "Lolaplex" }]
13
+ requires-python = ">=3.10"
14
+ dependencies = [
15
+ "agents-harness>=0.0.1",
16
+ "agents-relay>=0.0.1",
17
+ "agents-memory>=0.0.1",
18
+ "agents-traces>=0.0.1",
19
+ "agents-docs>=0.0.1",
20
+ ]
21
+
22
+ [project.optional-dependencies]
23
+ browser = [
24
+ "agents-browser>=0.0.1",
25
+ ]
26
+ keys = [
27
+ "agents-keys>=0.0.1",
28
+ ]
29
+ all = [
30
+ "klanker[browser,keys]",
31
+ ]
32
+ suite = [
33
+ "klanker[browser,keys]",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/Lolaplex/klanker"
38
+ Repository = "https://github.com/Lolaplex/klanker"
39
+ "Bug Tracker" = "https://github.com/Lolaplex/klanker/issues"
40
+
41
+ [project.scripts]
42
+ klanker = "klanker.__main__:main"
43
+
44
+ [tool.setuptools.packages.find]
45
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Klanker — Universal, adaptive slimemold AI agent shell."""
2
+
3
+ __version__ = "0.0.1"
@@ -0,0 +1,162 @@
1
+ """Klanker CLI — Entry point for the Slimemold Agent."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import logging
8
+ import os
9
+ import sys
10
+
11
+ from .nucleus import Nucleus
12
+ from .sensing import probe_host
13
+
14
+ log = logging.getLogger("klanker")
15
+
16
+
17
+ def build_parser() -> argparse.ArgumentParser:
18
+ parser = argparse.ArgumentParser(
19
+ prog="klanker",
20
+ description="Klanker — Adaptive Agent Shell on the Lolaplex Suite",
21
+ )
22
+ parser.add_argument("--user", type=str, default="user", help="User alias")
23
+ parser.add_argument("--provider", type=str, default=None, help="LLM provider")
24
+
25
+ sub = parser.add_subparsers(dest="command")
26
+
27
+ # 1. Sense host capabilities
28
+ sense_p = sub.add_parser("sense", help="Probe host environment and available feelers")
29
+ sense_p.add_argument("--json", action="store_true", help="Output JSON format")
30
+
31
+ # 1b. Dump system prompt
32
+ sub.add_parser("prompt", help="Print the dynamic Klanker system prompt")
33
+
34
+ # 2. Interactive or one-turn chat (alias)
35
+ chat_p = sub.add_parser("chat", help="Chat with Klanker (spawns harness turn)")
36
+ chat_p.add_argument("message", type=str, nargs="?", default="", help="Message content")
37
+ chat_p.add_argument("--user", type=str, default="user", help="User alias")
38
+ chat_p.add_argument("--provider", type=str, default=None, help="LLM provider")
39
+
40
+ # 3. Serve gateway
41
+ serve_p = sub.add_parser("serve", help="Start I/O Gateway (HTTP /v1/turn & Telegram)")
42
+ serve_p.add_argument("--no-telegram", action="store_true", help="Disable Telegram polling")
43
+
44
+ # 4. Run background cron / scheduled flows
45
+ cron_p = sub.add_parser("cron", help="Run scheduled care flows via harness executor")
46
+ cron_p.add_argument("--flow", type=str, default="", help="Run specific flow name")
47
+
48
+ return parser
49
+
50
+
51
+ def run_repl(nucleus: Nucleus, *, user: str = "user", provider: str | None = None) -> int:
52
+ """Run an interactive multi-turn REPL loop retaining conversation session."""
53
+ import uuid
54
+ session_id = f"ses_{uuid.uuid4().hex[:12]}"
55
+ print("klanker (v0.0.1) — interactive chat. Type 'exit' or Ctrl+C to quit.\n")
56
+
57
+ while True:
58
+ try:
59
+ prompt = input("klanker> ").strip()
60
+ except (EOFError, KeyboardInterrupt):
61
+ print()
62
+ break
63
+
64
+ if not prompt:
65
+ continue
66
+ if prompt.lower() in ("exit", "quit", ":q"):
67
+ break
68
+
69
+ nucleus.run_turn(message=prompt, user=user, session=session_id, provider=provider)
70
+ print()
71
+
72
+ return 0
73
+
74
+
75
+ KNOWN_SUBCOMMANDS = {"sense", "prompt", "serve", "cron", "chat", "-h", "--help"}
76
+
77
+
78
+ def parse_cli_args(argv: list[str] | None = None) -> tuple[argparse.Namespace, str]:
79
+ """Parse CLI arguments allowing both subcommands and direct prompts."""
80
+ raw_args = list(sys.argv[1:] if argv is None else argv)
81
+ direct_prompt = ""
82
+
83
+ # If first positional argument is not a known subcommand or flag, treat it as direct prompt
84
+ if raw_args and not raw_args[0].startswith("-") and raw_args[0] not in KNOWN_SUBCOMMANDS:
85
+ direct_prompt = raw_args.pop(0)
86
+
87
+ parser = build_parser()
88
+ parsed = parser.parse_args(raw_args)
89
+ return parsed, direct_prompt
90
+
91
+
92
+ def main(argv: list[str] | None = None) -> int:
93
+ logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s")
94
+ args, direct_prompt = parse_cli_args(argv)
95
+ nucleus = Nucleus()
96
+
97
+ if args.command == "sense":
98
+ summary = nucleus.sense()
99
+ if args.json:
100
+ print(json.dumps(summary, indent=2))
101
+ else:
102
+ print("Klanker Host Sensing:")
103
+ for k, v in summary.items():
104
+ print(f" * {k}: {v}")
105
+ return 0
106
+
107
+ elif args.command == "prompt":
108
+ from .prompt import build_system_prompt
109
+ print(build_system_prompt(nucleus.caps))
110
+ return 0
111
+
112
+ elif args.command == "serve":
113
+ if not (nucleus.caps.has_relay or nucleus.caps.has_gateway):
114
+ print("Error: agents-relay is not installed. Install via pip install agents-relay", file=sys.stderr)
115
+ return 1
116
+
117
+ # If LOOP_CMD is default and harness is present, inject Klanker's dynamic system prompt
118
+ if "LOOP_CMD" not in os.environ and nucleus.caps.has_harness:
119
+ from .prompt import save_system_prompt
120
+ prompt_file = save_system_prompt(nucleus.caps)
121
+ os.environ["LOOP_CMD"] = f"python -m runner.loop --system {prompt_file.as_posix()}"
122
+
123
+ try:
124
+ from agents_relay.__main__ import main as relay_main
125
+ except ImportError:
126
+ from agents_gateway.__main__ import main as relay_main
127
+ sub_argv = ["serve"]
128
+ if getattr(args, "no_telegram", False):
129
+ sub_argv.append("--no-telegram")
130
+ return relay_main(sub_argv)
131
+
132
+ elif args.command == "cron":
133
+ if not nucleus.caps.has_harness:
134
+ print("Error: agents-harness is not installed. Install via pip install agents-harness", file=sys.stderr)
135
+ return 1
136
+ from runner.executor import main as executor_main
137
+ if args.flow:
138
+ return executor_main(["--run", args.flow])
139
+ return executor_main(["--all"])
140
+
141
+ else:
142
+ # Chat mode: either explicit 'chat', direct prompt argument, stdin pipe, or interactive REPL
143
+ msg = direct_prompt or getattr(args, "message", None) or ""
144
+ user = getattr(args, "user", "user")
145
+ provider = getattr(args, "provider", None)
146
+
147
+ if msg:
148
+ return nucleus.run_turn(message=msg, user=user, provider=provider)
149
+
150
+ # If piped input via stdin (e.g. echo "hi" | klanker)
151
+ if not sys.stdin.isatty():
152
+ piped_msg = sys.stdin.read().strip()
153
+ if piped_msg:
154
+ return nucleus.run_turn(message=piped_msg, user=user, provider=provider)
155
+ return 0
156
+
157
+ # Interactive REPL
158
+ return run_repl(nucleus, user=user, provider=provider)
159
+
160
+
161
+ if __name__ == "__main__":
162
+ raise SystemExit(main())
@@ -0,0 +1,36 @@
1
+ """Dynamic manifest and schedule synthesizer."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+
10
+ def synthesize_schedule(
11
+ *,
12
+ name: str,
13
+ verb: str,
14
+ cadence: str = "daily",
15
+ rests_on: str = "",
16
+ expected_exit: int = 0,
17
+ timeout_sec: int = 300,
18
+ ) -> dict[str, Any]:
19
+ """Generate a declarative Koru schedule manifest dictionary."""
20
+ return {
21
+ "name": name,
22
+ "verb": verb,
23
+ "cadence": cadence,
24
+ "rests_on": rests_on or f"Synthesized by Klanker for {name}",
25
+ "expected_exit": expected_exit,
26
+ "timeout_sec": timeout_sec,
27
+ }
28
+
29
+
30
+ def write_schedule(target_dir: Path | str, manifest: dict[str, Any]) -> Path:
31
+ """Safely write a synthesized schedule to the target schedules directory."""
32
+ path = Path(target_dir) / f"{manifest['name']}.json"
33
+ path.parent.mkdir(parents=True, exist_ok=True)
34
+ with open(path, "w", encoding="utf-8") as f:
35
+ json.dump(manifest, f, indent=2)
36
+ return path
@@ -0,0 +1,74 @@
1
+ """Klanker Nucleus — The adaptive core coordinator."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import os
7
+ import subprocess
8
+ import sys
9
+ from typing import Any
10
+
11
+
12
+ from .sensing import HostCapabilities, probe_host
13
+
14
+ log = logging.getLogger("klanker.nucleus")
15
+
16
+
17
+ class Nucleus:
18
+ def __init__(self, caps: HostCapabilities | None = None) -> None:
19
+ self.caps = caps or probe_host()
20
+
21
+ def sense(self) -> dict[str, Any]:
22
+ """Return environment topology summary."""
23
+ return self.caps.summary()
24
+
25
+ def run_turn(
26
+ self,
27
+ *,
28
+ message: str,
29
+ user: str = "default",
30
+ session: str | None = None,
31
+ channel: str = "local",
32
+ provider: str | None = None,
33
+ deliver: str = "buffered",
34
+ ) -> int:
35
+ """Execute one conversational turn using available runner or fallback."""
36
+ selected_provider = provider or os.environ.get("LOOP_PROVIDER", "openai.default")
37
+
38
+ # If agents-harness is installed, invoke runner.loop directly
39
+ if self.caps.has_harness:
40
+ from .prompt import build_system_prompt
41
+
42
+ system_prompt = build_system_prompt(self.caps)
43
+
44
+ cmd = [
45
+ "python",
46
+ "-m",
47
+ "runner.loop",
48
+ "--channel",
49
+ channel,
50
+ "--user",
51
+ user,
52
+ "--message",
53
+ message,
54
+ "--system",
55
+ system_prompt,
56
+ "--complete",
57
+ "--provider",
58
+ selected_provider,
59
+ "--deliver",
60
+ deliver,
61
+ ]
62
+ if session:
63
+ cmd.extend(["--session", session])
64
+
65
+ res = subprocess.run(cmd)
66
+ return res.returncode
67
+ else:
68
+ print(
69
+ "Error: agents-harness is required for conversational turns.\n"
70
+ "Install it via: pip install -e ../agents-harness (or pip install klanker)",
71
+ file=sys.stderr,
72
+ )
73
+ return 1
74
+
@@ -0,0 +1,60 @@
1
+ """Klanker System Prompt — Slimemold identity and dynamic host grounding."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+ from .sensing import HostCapabilities
8
+
9
+
10
+ def build_system_prompt(caps: HostCapabilities) -> str:
11
+ """Render Klanker's adaptive system prompt based on probed host feelers."""
12
+ installed = [mod for mod, present in caps.suite_modules.items() if present]
13
+ missing = [mod for mod, present in caps.suite_modules.items() if not present]
14
+
15
+ installed_str = ", ".join(installed) if installed else "none (minimal core only)"
16
+ missing_str = ", ".join(missing) if missing else "none (fully equipped)"
17
+
18
+ lines = [
19
+ "You are Klanker (v0.0.1), an autonomous, adaptive slimemold AI agent on the Lolaplex suite.",
20
+ "",
21
+ "Core Philosophy & Identity:",
22
+ "- You are NOT a generic assistant and NOT a chatterbox. You are an adaptive, living agent shell that morphs to this host and user.",
23
+ "- Follow the Unix philosophy: dense, direct, zero fluff, zero courtesy filler.",
24
+ "- Never introduce yourself as 'Cordis'. Cordis is the underlying job-term engine, not your persona.",
25
+ "- Never hallucinate capabilities. You strictly rely on verified feelers and tools present on this machine.",
26
+ "- NEVER comment on time of day, late hours, location, or make clock-based small talk (e.g. do not say 'it is late at night' or 'guten abend'). The clock is purely for internal event timestamping.",
27
+ "",
28
+ f"Host Grounding ({caps.os_name}, Python {caps.python_version}):",
29
+ f"- Active feelers: {installed_str}",
30
+ f"- Missing feelers: {missing_str}",
31
+ "",
32
+ "Adaptive Rules & Behavior:",
33
+ "1. Organic Onboarding & Discovery (Low friction):",
34
+ " - If user memory is empty (no facts about user in memory), do NOT dump a giant questionnaire or essay.",
35
+ " - Greet briefly (1-2 sentences) and establish a fast baseline:",
36
+ " 'Klanker hier (v0.0.1, Slimemold). Alles läuft lokal auf diesem Host. Wie soll ich mit dir kommunizieren (z.B. Caveman, Sprache) und woran arbeiten wir?'",
37
+ " - In ongoing conversation: As you work, if you notice a missing context (e.g. project paths, stack preferences, rules), ask ONE concise, targeted question in context. Never interrogate on suspicion.",
38
+ " - When user provides preferences or facts, proactively remember them across sessions.",
39
+ "",
40
+ "2. Memory & Cloud Sync:",
41
+ " - If 'memory' is active, use `mcp.memory.search` to ground yourself in user facts, project standards, and rules.",
42
+ " - If user requests syncing memory with another device or remote memory server, use `python -m agents_memory connect <url> --token <token>` or guide them concisely.",
43
+ " - If 'memory' is missing and durable memory is needed, explain: 'pip install agents-memory (oder klanker[memory])'.",
44
+ "",
45
+ "3. Action & Execution:",
46
+ " - When requested to run tasks, use the available catalog tools (call_job, list_catalog, load_schema).",
47
+ " - Execute directly without asking for confirmation unless destructive.",
48
+ ]
49
+
50
+ return "\n".join(lines).strip()
51
+
52
+
53
+ def save_system_prompt(caps: HostCapabilities) -> Path:
54
+ """Save dynamic Klanker prompt to ~/.agents/klanker_prompt.txt and return path."""
55
+ agents_dir = Path(os.environ.get("AGENTS_DIR", Path.home() / ".agents"))
56
+ agents_dir.mkdir(parents=True, exist_ok=True)
57
+ prompt_file = agents_dir / "klanker_prompt.txt"
58
+ prompt_text = build_system_prompt(caps)
59
+ prompt_file.write_text(prompt_text, encoding="utf-8")
60
+ return prompt_file
@@ -0,0 +1,106 @@
1
+ """Host capability probe (Slimemold sensing)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.util
6
+ import os
7
+ import platform
8
+ import shutil
9
+ import sys
10
+ from dataclasses import asdict, dataclass, field
11
+ from pathlib import Path
12
+
13
+
14
+ COMMON_RUNTIMES = ["git", "docker", "curl", "node", "npm", "uv", "sqlite3"]
15
+ SUITE_MODULES = {
16
+ "harness": "runner.loop",
17
+ "relay": "agents_relay",
18
+ "memory": "agents_memory",
19
+ "docs": "agents_docs",
20
+ "traces": "agents_traces",
21
+ "keys": "agents_keys",
22
+ "browser": "agents_browser",
23
+ }
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class HostCapabilities:
28
+ os_name: str
29
+ is_tty: bool
30
+ python_version: str
31
+ runtimes: dict[str, bool] = field(default_factory=dict)
32
+ suite_modules: dict[str, bool] = field(default_factory=dict)
33
+ has_agents_home: bool = False
34
+
35
+ # Backwards-compatible convenience properties
36
+ @property
37
+ def has_git(self) -> bool:
38
+ return self.runtimes.get("git", False)
39
+
40
+ @property
41
+ def has_docker(self) -> bool:
42
+ return self.runtimes.get("docker", False)
43
+
44
+ @property
45
+ def has_curl(self) -> bool:
46
+ return self.runtimes.get("curl", False)
47
+
48
+ @property
49
+ def has_harness(self) -> bool:
50
+ return self.suite_modules.get("harness", False)
51
+
52
+ @property
53
+ def has_relay(self) -> bool:
54
+ return self.suite_modules.get("relay", False)
55
+
56
+ @property
57
+ def has_gateway(self) -> bool:
58
+ return self.has_relay
59
+
60
+ @property
61
+ def has_memory(self) -> bool:
62
+ return self.suite_modules.get("memory", False)
63
+
64
+ @property
65
+ def has_docs(self) -> bool:
66
+ return self.suite_modules.get("docs", False)
67
+
68
+ @property
69
+ def has_traces(self) -> bool:
70
+ return self.suite_modules.get("traces", False)
71
+
72
+ def summary(self) -> dict[str, object]:
73
+ return {
74
+ "os_name": self.os_name,
75
+ "is_tty": self.is_tty,
76
+ "python_version": self.python_version,
77
+ "has_agents_home": self.has_agents_home,
78
+ "runtimes": self.runtimes,
79
+ "suite_modules": self.suite_modules,
80
+ }
81
+
82
+
83
+ def probe_host() -> HostCapabilities:
84
+ """Probe current environment and available feelers dynamically."""
85
+ def _has_cmd(cmd: str) -> bool:
86
+ return shutil.which(cmd) is not None
87
+
88
+ def _has_mod(name: str) -> bool:
89
+ try:
90
+ return importlib.util.find_spec(name) is not None
91
+ except Exception:
92
+ return False
93
+
94
+ runtimes = {cmd: _has_cmd(cmd) for cmd in COMMON_RUNTIMES}
95
+ modules = {alias: _has_mod(pkg) for alias, pkg in SUITE_MODULES.items()}
96
+ agents_home = Path.home() / ".agents"
97
+
98
+ return HostCapabilities(
99
+ os_name=platform.system(),
100
+ is_tty=sys.stdin.isatty() if hasattr(sys.stdin, "isatty") else False,
101
+ python_version=platform.python_version(),
102
+ runtimes=runtimes,
103
+ suite_modules=modules,
104
+ has_agents_home=agents_home.exists(),
105
+ )
106
+
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: klanker
3
+ Version: 0.0.1
4
+ Summary: Universal, adaptive slimemold AI agent shell on the Lolaplex suite
5
+ Author: Lolaplex
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Lolaplex/klanker
8
+ Project-URL: Repository, https://github.com/Lolaplex/klanker
9
+ Project-URL: Bug Tracker, https://github.com/Lolaplex/klanker/issues
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: agents-harness>=0.0.1
14
+ Requires-Dist: agents-relay>=0.0.1
15
+ Requires-Dist: agents-memory>=0.0.1
16
+ Requires-Dist: agents-traces>=0.0.1
17
+ Requires-Dist: agents-docs>=0.0.1
18
+ Provides-Extra: browser
19
+ Requires-Dist: agents-browser>=0.0.1; extra == "browser"
20
+ Provides-Extra: keys
21
+ Requires-Dist: agents-keys>=0.0.1; extra == "keys"
22
+ Provides-Extra: all
23
+ Requires-Dist: klanker[browser,keys]; extra == "all"
24
+ Provides-Extra: suite
25
+ Requires-Dist: klanker[browser,keys]; extra == "suite"
26
+ Dynamic: license-file
27
+
28
+ # Klanker 🤖
29
+
30
+ **The Adaptive Agent Distribution on the Lolaplex Suite.**
31
+
32
+ Klanker ist eine schlanke, adaptive Agenten-Distribution auf Basis des **Slimemold-Paradigmas**. Aufsetzend auf dem deterministischen Motor von `agents-harness` formt sich Klanker nach Bedarf:
33
+ - Vom autarken Server-Worker (`klanker cron`),
34
+ - ĂĽber den direkten Terminal-Begleiter (`klanker chat`),
35
+ - bis zum vollwertigen VPS-Assistenten via Telegram & HTTP (`klanker serve`).
36
+
37
+ ## Quickstart
38
+
39
+ ```bash
40
+ # Clone & install (installiert den kompletten Agents Core Stack: harness, gateway, memory, traces, docs)
41
+ pip install -e .
42
+
43
+ # Optional: Zusätzliche Slimemold-Fühler aktivieren
44
+ pip install -e ".[browser]" # CDP Browser Automation
45
+ pip install -e ".[keys]" # Ed25519 Agent Keys
46
+ pip install -e ".[suite]" # Volle Lolaplex Suite
47
+
48
+ # 1. Umgebung & FĂĽhler prĂĽfen
49
+ klanker sense
50
+
51
+ # 2. Dynamischen System-Prompt einsehen
52
+ klanker prompt
53
+
54
+ # 3. Interaktiver Chat (REPL)
55
+ klanker
56
+
57
+ # 4. Direkter Turn im Terminal
58
+ klanker "Wer bist du?"
59
+
60
+ # 5. I/O-Gateway starten (HTTP & Telegram)
61
+ klanker serve
62
+
63
+ # 6. Geplante Koru-Harness-Flows ausfĂĽhren
64
+ klanker cron
65
+ ```
66
+
67
+
68
+ ## Die Architektur
69
+
70
+ 1. **Das Brain (`agents-harness`)** — Motor, LLM-Streaming, Cordis-Kernel, Koru-Schedules, lokales Gedächtnis (`agents-memory`), JSONL-Observability & Chat-Rekonstruktion (`agents-traces`) und lokales Markdown-RAG (`agents-docs`).
71
+ 2. **Die I/O-Schicht (`agents-relay`)** — Universelle Ein- und Ausgabe via HTTP (`/v1/turn`) und Telegram Long-Poll.
72
+ 3. **Die User-App (`klanker`)** — Verbindet Brain & Relay zu einer adaptiven Slimemold-Shell.
73
+ 4. **Optionale Tools & Fühler** — Externe Fähigkeiten wie CDP-Browser (`agents-browser`), Ed25519-Keys (`agents-keys`), Jailed Terminal (`agents-terminal`) oder beliebige MCP-Tools, die Klanker bei Bedarf vorschlägt oder einbindet.
74
+
75
+
@@ -0,0 +1,16 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/klanker/__init__.py
5
+ src/klanker/__main__.py
6
+ src/klanker/morph.py
7
+ src/klanker/nucleus.py
8
+ src/klanker/prompt.py
9
+ src/klanker/sensing.py
10
+ src/klanker.egg-info/PKG-INFO
11
+ src/klanker.egg-info/SOURCES.txt
12
+ src/klanker.egg-info/dependency_links.txt
13
+ src/klanker.egg-info/entry_points.txt
14
+ src/klanker.egg-info/requires.txt
15
+ src/klanker.egg-info/top_level.txt
16
+ tests/test_klanker.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ klanker = klanker.__main__:main
@@ -0,0 +1,17 @@
1
+ agents-harness>=0.0.1
2
+ agents-relay>=0.0.1
3
+ agents-memory>=0.0.1
4
+ agents-traces>=0.0.1
5
+ agents-docs>=0.0.1
6
+
7
+ [all]
8
+ klanker[browser,keys]
9
+
10
+ [browser]
11
+ agents-browser>=0.0.1
12
+
13
+ [keys]
14
+ agents-keys>=0.0.1
15
+
16
+ [suite]
17
+ klanker[browser,keys]
@@ -0,0 +1 @@
1
+ klanker
@@ -0,0 +1,54 @@
1
+ """Unit tests for Klanker nucleus, sensing, and CLI."""
2
+
3
+ import unittest
4
+ from klanker.sensing import probe_host, HostCapabilities
5
+ from klanker.morph import synthesize_schedule
6
+ from klanker.nucleus import Nucleus
7
+ from klanker.__main__ import build_parser
8
+
9
+
10
+ class TestKlanker(unittest.TestCase):
11
+ def test_probe_host(self):
12
+ caps = probe_host()
13
+ self.assertIsInstance(caps, HostCapabilities)
14
+ summary = caps.summary()
15
+ self.assertIn("os_name", summary)
16
+ self.assertIn("runtimes", summary)
17
+ self.assertIn("suite_modules", summary)
18
+ self.assertTrue(hasattr(caps, "has_git"))
19
+
20
+ def test_synthesize_schedule(self):
21
+ manifest = synthesize_schedule(
22
+ name="test_flow",
23
+ verb="python -m test",
24
+ cadence="daily",
25
+ )
26
+ self.assertEqual(manifest["name"], "test_flow")
27
+ self.assertEqual(manifest["expected_exit"], 0)
28
+
29
+ def test_nucleus_sense(self):
30
+ n = Nucleus()
31
+ sense = n.sense()
32
+ self.assertIsInstance(sense, dict)
33
+ self.assertIn("python_version", sense)
34
+
35
+ def test_cli_parser(self):
36
+ from klanker.__main__ import parse_cli_args
37
+ args, direct_prompt = parse_cli_args(["sense", "--json"])
38
+ self.assertEqual(args.command, "sense")
39
+ self.assertTrue(args.json)
40
+ self.assertEqual(direct_prompt, "")
41
+
42
+ # Direct prompt argument test
43
+ args, direct_prompt = parse_cli_args(["Wer bist du?"])
44
+ self.assertIsNone(args.command)
45
+ self.assertEqual(direct_prompt, "Wer bist du?")
46
+
47
+ # Empty prompt test (triggers REPL)
48
+ args, direct_prompt = parse_cli_args([])
49
+ self.assertIsNone(args.command)
50
+ self.assertEqual(direct_prompt, "")
51
+
52
+
53
+ if __name__ == "__main__":
54
+ unittest.main()