jac-coder 0.1.0__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.
Files changed (97) hide show
  1. jac_coder-0.1.0/PKG-INFO +9 -0
  2. jac_coder-0.1.0/README.md +141 -0
  3. jac_coder-0.1.0/jac_coder/__init__.jac +0 -0
  4. jac_coder-0.1.0/jac_coder/api.jac +82 -0
  5. jac_coder-0.1.0/jac_coder/cli_entry.py +25 -0
  6. jac_coder-0.1.0/jac_coder/config.jac +36 -0
  7. jac_coder-0.1.0/jac_coder/context.jac +17 -0
  8. jac_coder-0.1.0/jac_coder/data/examples/ai_agent.md +90 -0
  9. jac_coder-0.1.0/jac_coder/data/examples/blog_app.md +386 -0
  10. jac_coder-0.1.0/jac_coder/data/examples/core_patterns.md +321 -0
  11. jac_coder-0.1.0/jac_coder/data/examples/todo_app.md +321 -0
  12. jac_coder-0.1.0/jac_coder/data/reference/ai.md +131 -0
  13. jac_coder-0.1.0/jac_coder/data/reference/backend.md +215 -0
  14. jac_coder-0.1.0/jac_coder/data/reference/frontend.md +271 -0
  15. jac_coder-0.1.0/jac_coder/data/reference/osp.md +229 -0
  16. jac_coder-0.1.0/jac_coder/data/reference/pitfalls.md +141 -0
  17. jac_coder-0.1.0/jac_coder/data/reference/syntax.md +159 -0
  18. jac_coder-0.1.0/jac_coder/data/rules/core_jac.md +559 -0
  19. jac_coder-0.1.0/jac_coder/data/rules/fullstack.md +362 -0
  20. jac_coder-0.1.0/jac_coder/data/rules/workflow.md +88 -0
  21. jac_coder-0.1.0/jac_coder/events.jac +110 -0
  22. jac_coder-0.1.0/jac_coder/impl/api.impl.jac +399 -0
  23. jac_coder-0.1.0/jac_coder/impl/config.impl.jac +163 -0
  24. jac_coder-0.1.0/jac_coder/impl/context.impl.jac +117 -0
  25. jac_coder-0.1.0/jac_coder/impl/mcp_manager.impl.jac +380 -0
  26. jac_coder-0.1.0/jac_coder/impl/memory.impl.jac +247 -0
  27. jac_coder-0.1.0/jac_coder/impl/nodes.impl.jac +259 -0
  28. jac_coder-0.1.0/jac_coder/impl/permission.impl.jac +62 -0
  29. jac_coder-0.1.0/jac_coder/impl/walkers.impl.jac +298 -0
  30. jac_coder-0.1.0/jac_coder/mcp_manager.jac +35 -0
  31. jac_coder-0.1.0/jac_coder/memory.jac +15 -0
  32. jac_coder-0.1.0/jac_coder/nodes.jac +306 -0
  33. jac_coder-0.1.0/jac_coder/permission.jac +19 -0
  34. jac_coder-0.1.0/jac_coder/serve_entry.jac +30 -0
  35. jac_coder-0.1.0/jac_coder/server.jac +324 -0
  36. jac_coder-0.1.0/jac_coder/tool/__init__.jac +17 -0
  37. jac_coder-0.1.0/jac_coder/tool/checked.jac +10 -0
  38. jac_coder-0.1.0/jac_coder/tool/delegation.jac +23 -0
  39. jac_coder-0.1.0/jac_coder/tool/filesystem.jac +25 -0
  40. jac_coder-0.1.0/jac_coder/tool/git.jac +18 -0
  41. jac_coder-0.1.0/jac_coder/tool/guarded.jac +23 -0
  42. jac_coder-0.1.0/jac_coder/tool/impl/checked.impl.jac +38 -0
  43. jac_coder-0.1.0/jac_coder/tool/impl/delegation.impl.jac +157 -0
  44. jac_coder-0.1.0/jac_coder/tool/impl/filesystem.impl.jac +781 -0
  45. jac_coder-0.1.0/jac_coder/tool/impl/git.impl.jac +115 -0
  46. jac_coder-0.1.0/jac_coder/tool/impl/guarded.impl.jac +72 -0
  47. jac_coder-0.1.0/jac_coder/tool/impl/jac_analyzer.impl.jac +593 -0
  48. jac_coder-0.1.0/jac_coder/tool/impl/jac_docs.impl.jac +136 -0
  49. jac_coder-0.1.0/jac_coder/tool/impl/jac_tools.impl.jac +79 -0
  50. jac_coder-0.1.0/jac_coder/tool/impl/mcp.impl.jac +32 -0
  51. jac_coder-0.1.0/jac_coder/tool/impl/preview.impl.jac +233 -0
  52. jac_coder-0.1.0/jac_coder/tool/impl/question.impl.jac +29 -0
  53. jac_coder-0.1.0/jac_coder/tool/impl/scaffold.impl.jac +231 -0
  54. jac_coder-0.1.0/jac_coder/tool/impl/search.impl.jac +85 -0
  55. jac_coder-0.1.0/jac_coder/tool/impl/shell.impl.jac +89 -0
  56. jac_coder-0.1.0/jac_coder/tool/impl/task.impl.jac +12 -0
  57. jac_coder-0.1.0/jac_coder/tool/impl/think.impl.jac +4 -0
  58. jac_coder-0.1.0/jac_coder/tool/impl/todo.impl.jac +58 -0
  59. jac_coder-0.1.0/jac_coder/tool/impl/validate.impl.jac +236 -0
  60. jac_coder-0.1.0/jac_coder/tool/impl/web.impl.jac +91 -0
  61. jac_coder-0.1.0/jac_coder/tool/jac_analyzer.jac +21 -0
  62. jac_coder-0.1.0/jac_coder/tool/jac_docs.jac +9 -0
  63. jac_coder-0.1.0/jac_coder/tool/jac_tools.jac +11 -0
  64. jac_coder-0.1.0/jac_coder/tool/mcp.jac +17 -0
  65. jac_coder-0.1.0/jac_coder/tool/preview.jac +31 -0
  66. jac_coder-0.1.0/jac_coder/tool/question.jac +7 -0
  67. jac_coder-0.1.0/jac_coder/tool/scaffold.jac +10 -0
  68. jac_coder-0.1.0/jac_coder/tool/search.jac +14 -0
  69. jac_coder-0.1.0/jac_coder/tool/shell.jac +12 -0
  70. jac_coder-0.1.0/jac_coder/tool/task.jac +9 -0
  71. jac_coder-0.1.0/jac_coder/tool/think.jac +5 -0
  72. jac_coder-0.1.0/jac_coder/tool/todo.jac +12 -0
  73. jac_coder-0.1.0/jac_coder/tool/validate.jac +11 -0
  74. jac_coder-0.1.0/jac_coder/tool/vision.jac +17 -0
  75. jac_coder-0.1.0/jac_coder/tool/web.jac +10 -0
  76. jac_coder-0.1.0/jac_coder/util/__init__.jac +18 -0
  77. jac_coder-0.1.0/jac_coder/util/colors.jac +20 -0
  78. jac_coder-0.1.0/jac_coder/util/impl/sandbox.impl.jac +38 -0
  79. jac_coder-0.1.0/jac_coder/util/impl/tool_output.impl.jac +208 -0
  80. jac_coder-0.1.0/jac_coder/util/sandbox.jac +8 -0
  81. jac_coder-0.1.0/jac_coder/util/tool_output.jac +29 -0
  82. jac_coder-0.1.0/jac_coder/walkers.jac +67 -0
  83. jac_coder-0.1.0/jac_coder.egg-info/PKG-INFO +9 -0
  84. jac_coder-0.1.0/jac_coder.egg-info/SOURCES.txt +95 -0
  85. jac_coder-0.1.0/jac_coder.egg-info/dependency_links.txt +1 -0
  86. jac_coder-0.1.0/jac_coder.egg-info/entry_points.txt +3 -0
  87. jac_coder-0.1.0/jac_coder.egg-info/requires.txt +4 -0
  88. jac_coder-0.1.0/jac_coder.egg-info/top_level.txt +1 -0
  89. jac_coder-0.1.0/pyproject.toml +29 -0
  90. jac_coder-0.1.0/setup.cfg +4 -0
  91. jac_coder-0.1.0/tests/test_context.py +53 -0
  92. jac_coder-0.1.0/tests/test_events.py +40 -0
  93. jac_coder-0.1.0/tests/test_graph.py +33 -0
  94. jac_coder-0.1.0/tests/test_interact.py +40 -0
  95. jac_coder-0.1.0/tests/test_memory.py +53 -0
  96. jac_coder-0.1.0/tests/test_selfcorrect.py +72 -0
  97. jac_coder-0.1.0/tests/test_tools.py +45 -0
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: jac-coder
3
+ Version: 0.1.0
4
+ Summary: AI coding agent backend for Jac, powered by jac-byllm
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: python-dotenv>=1.0.0
7
+ Requires-Dist: jaclang
8
+ Requires-Dist: byllm
9
+ Requires-Dist: mcp>=1.0.0
@@ -0,0 +1,141 @@
1
+ # JacCoder
2
+
3
+ ![JacCoder CLI](assets/cli.png)
4
+
5
+ AI coding agent for the Jaseci stack, built entirely in [Jac](https://github.com/jaseci-labs/jaseci) using Object-Spatial Programming. Features an orchestrator-worker architecture with compiler-level Jac Intelligence, self-correcting code writes, and in-process SubAgent delegation.
6
+
7
+ ## Architecture
8
+
9
+ ![JacCoder Graph Architecture](assets/graph.png)
10
+
11
+ ## Features
12
+
13
+ - **Orchestrator-Worker Architecture** - MainAgent handles tasks directly or delegates to focused SubAgents (WorkerRunner / ExplorerRunner)
14
+ - **Jac Intelligence** - compiler-level AST analysis via jaclang (`analyze_project`, `find_symbol`)
15
+ - **22 Built-in Tools** - filesystem, search, git, shell, web, Jac compiler, scaffolding, delegation
16
+ - **Self-Correcting Writes** - automatic `jac check` on `.jac` file writes with error feedback
17
+ - **In-Process SubAgents** - no subprocess overhead, shared event system, capability-scoped tools
18
+ - **Multi-Provider LLM** - 100+ models via [byllm](https://github.com/jaseci-labs/byllm)
19
+ - **Public API** - clean interface for external apps (JacBuilder, IDE extensions)
20
+ - **Context Management** - smart compaction keeps conversations under token budget
21
+
22
+ ## Prerequisites
23
+
24
+ - Python 3.12+
25
+ - jaclang, byllm, jac-super
26
+
27
+ ```bash
28
+ pip install jaclang byllm jac-super python-dotenv
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ```bash
34
+ # Set API key
35
+ export OPENAI_API_KEY="sk-..."
36
+
37
+ # Interactive REPL
38
+ jac cli.jac
39
+
40
+ # Single prompt (non-interactive)
41
+ jac cli.jac run "build a hello world jac app at /tmp/myapp"
42
+
43
+ # Resume a session
44
+ jac cli.jac session <id-prefix>
45
+ ```
46
+
47
+ ## Architecture
48
+
49
+ ```
50
+ Root → Session → MainAgent
51
+ ├── handles simple tasks directly (read, search, edit, git)
52
+ └── spawn_agent() → WorkerRunner (read+write) or ExplorerRunner (read-only)
53
+ └── runs in-process, returns result to MainAgent
54
+ ```
55
+
56
+ - **MainAgent (node)** - orchestrator with 25 tools. Handles simple tasks directly, delegates complex work.
57
+ - **WorkerRunner (obj)** - in-process SubAgent with 18 tools (can write/edit/run). Full Jac syntax rules.
58
+ - **ExplorerRunner (obj)** - in-process SubAgent with 10 tools (read-only). For investigation tasks.
59
+ - **Session (node)** - persistent chat state, history, active files, errors.
60
+ - **ProjectMemory (node)** - AST-derived codebase knowledge (nodes, walkers, edges, imports).
61
+
62
+ ## Tools
63
+
64
+ | Tool | Description |
65
+ |------|-------------|
66
+ | `read_file` | Read files with line numbers and pagination |
67
+ | `write_code` | Write file with auto `jac_check` validation |
68
+ | `edit_code` | Find-and-replace with auto `jac_check` validation |
69
+ | `find_files` | Find files by glob pattern |
70
+ | `grep_search` | Regex search across files |
71
+ | `list_files` | List directory contents |
72
+ | `run_command` | Execute shell commands (permission-guarded) |
73
+ | `jac_check` | Type-check `.jac` files |
74
+ | `jac_run` | Run `.jac` files |
75
+ | `jac_docs` | Search Jac language reference and examples |
76
+ | `analyze_project` | Full AST analysis of a Jac project |
77
+ | `find_symbol` | Find symbol definitions, usages, and imports |
78
+ | `git_status` | Show working tree status |
79
+ | `git_diff` | Show uncommitted changes |
80
+ | `git_log` | Show recent commit history |
81
+ | `git_commit` | Stage and commit changes |
82
+ | `think` | Explicit reasoning step before acting |
83
+ | `spawn_agent` | Delegate task to a SubAgent (worker/explorer) |
84
+ | `web_fetch` | Fetch URL content |
85
+ | `web_search` | Web search via DuckDuckGo |
86
+ | `scaffold_project` | Generate project templates |
87
+ | `ask_question` | Prompt user for input |
88
+ | `update_todos` | Track multi-step task progress |
89
+
90
+ ## Public API
91
+
92
+ External apps import only from `jac_coder.api`:
93
+
94
+ ```python
95
+ from jac_coder.api import initialize, create_session, chat, close_session
96
+
97
+ initialize("web")
98
+ session = create_session("/path/to/project", title="My App")
99
+ result = chat(session["session_id"], "build a calculator")
100
+ print(result["response"])
101
+ ```
102
+
103
+ ## Configuration
104
+
105
+ Config sources (highest priority first):
106
+
107
+ 1. Environment: `MODEL`, `TEMPERATURE`, `MAX_TOKENS`, `MAX_REACT_ITERATIONS`
108
+ 2. Project: `./jaccoder.json`
109
+ 3. Global: `~/.jaccoder/config.json`
110
+
111
+ ## Testing
112
+
113
+ ```bash
114
+ python -m pytest tests/ -v # 58 tests
115
+ ```
116
+
117
+ ## Project Structure
118
+
119
+ ```
120
+ jac-code/
121
+ ├── cli.jac # CLI entry point (REPL + subcommands)
122
+ ├── jac_coder/ # Core package
123
+ │ ├── api.jac # Public API (the only module external apps import)
124
+ │ ├── nodes.jac # MainAgent, WorkerRunner, ExplorerRunner, Session, ProjectMemory
125
+ │ ├── walkers.jac # Walker definitions (interact, new_session, etc.)
126
+ │ ├── config.jac # Multi-source config + LLM init
127
+ │ ├── context.jac # Smart context compaction
128
+ │ ├── events.jac # Tool event system + turn summaries
129
+ │ ├── memory.jac # AST-based project memory
130
+ │ ├── permission.jac # Permission rule engine
131
+ │ ├── tool/ # 22 tools organized by domain
132
+ │ │ ├── delegation.jac # spawn_agent (in-process SubAgent execution)
133
+ │ │ ├── think.jac # Chain-of-thought reasoning
134
+ │ │ ├── git.jac # Git operations
135
+ │ │ ├── jac_analyzer.jac # AST-based Jac Intelligence
136
+ │ │ └── ...
137
+ │ ├── data/ # Jac syntax rules (build_rules, client_rules, server_rules)
138
+ │ └── impl/ # Implementation files
139
+ ├── tests/ # 58 tests (pytest + jac)
140
+ └── docs/ # Architecture, roadmap, progress
141
+ ```
File without changes
@@ -0,0 +1,82 @@
1
+ """JacCoder public API."""
2
+
3
+ import os;
4
+ import sys;
5
+
6
+ import from datetime { datetime }
7
+ import from jac_coder.config {
8
+ get_config,
9
+ set_model as _set_model,
10
+ get_model as _get_model
11
+ }
12
+ import from jac_coder.util.tool_output { tool_end }
13
+ import from jac_coder.permission { permission_engine }
14
+ import from jac_coder.util.sandbox { set_sandbox_root }
15
+ import from jac_coder.context { build_context, ContextConfig }
16
+ import from jac_coder.walkers { new_session, ensure_main_agent, _consume_llm_stream }
17
+ import from jac_coder.mcp_manager {
18
+ mcp_add_server,
19
+ mcp_remove_server,
20
+ mcp_list_servers,
21
+ mcp_get_tools,
22
+ mcp_register_builtin
23
+ }
24
+ import from jac_coder.memory {
25
+ find_or_create_memory,
26
+ _init_memory,
27
+ update_memory_from_session
28
+ }
29
+ import from jac_coder.nodes { Session, MainAgent }
30
+ import from jac_coder.events {
31
+ register_event_callback,
32
+ clear_event_callbacks,
33
+ reset_steps,
34
+ start_turn,
35
+ emit_turn_summary,
36
+ emit_event
37
+ }
38
+
39
+
40
+ # Thread-safe session registry — graph root() is per-request in jac-cloud,
41
+ # so background threads can't find sessions via [root()-->]. This dict
42
+ # is module-level and accessible from any thread.
43
+ glob _session_registry: dict = {};
44
+
45
+ """Initialize jac-coder."""
46
+ def initialize(mode: str = "web") -> None;
47
+
48
+ """Create a new session."""
49
+ def create_session(directory: str, title: str = "", agent: str = "main") -> dict;
50
+
51
+ """Send a message and get a response."""
52
+ def chat(
53
+ session_id: str,
54
+ message: str,
55
+ directory: str = "",
56
+ on_event: Any = None,
57
+ mode: str = "full",
58
+ agent_context: str = ""
59
+ ) -> dict;
60
+
61
+ def list_sessions() -> list;
62
+
63
+ def get_session(session_id: str) -> dict;
64
+
65
+ def close_session(session_id: str) -> dict;
66
+
67
+ # --- Model management API ---
68
+ """Switch the active LLM model."""
69
+ def api_set_model(model: str) -> dict;
70
+
71
+ """Get current model info."""
72
+ def api_get_model() -> dict;
73
+
74
+ # --- MCP management API ---
75
+ """Add or update an MCP server."""
76
+ def api_mcp_add(name: str, config: dict) -> dict;
77
+
78
+ """Remove an MCP server."""
79
+ def api_mcp_remove(name: str) -> dict;
80
+
81
+ """List all configured MCP servers."""
82
+ def api_mcp_list() -> list;
@@ -0,0 +1,25 @@
1
+ """CLI entry point for `jac-coder` command."""
2
+
3
+ import subprocess
4
+ import sys
5
+ import os
6
+
7
+
8
+ def main():
9
+ # Find the project root (where cli.jac lives)
10
+ pkg_dir = os.path.dirname(os.path.abspath(__file__))
11
+ project_root = os.path.dirname(pkg_dir)
12
+ cli_jac = os.path.join(project_root, "cli.jac")
13
+
14
+ if not os.path.exists(cli_jac):
15
+ print("Error: cli.jac not found. Run from the jac-coder project directory.")
16
+ sys.exit(1)
17
+
18
+ # Set JACCODER_ROOT so tools can find data/ directory regardless of CWD
19
+ os.environ["JACCODER_ROOT"] = project_root
20
+
21
+ sys.exit(subprocess.call(["jac", cli_jac] + sys.argv[1:]))
22
+
23
+
24
+ if __name__ == "__main__":
25
+ main()
@@ -0,0 +1,36 @@
1
+ import os;
2
+ import json;
3
+
4
+ import from pathlib { Path }
5
+ import from dotenv { load_dotenv }
6
+ import from byllm.lib { Model }
7
+
8
+
9
+ obj JacCoderConfig {
10
+ has default_model: str = "gpt-5.2-2025-12-11",
11
+ temperature: float = 0.2,
12
+ max_tokens: int = 8192,
13
+ max_react_iterations: int = 30,
14
+ project_dir: str = "";
15
+ }
16
+
17
+ glob _config: JacCoderConfig = JacCoderConfig();
18
+
19
+ # --- Config ---
20
+ def get_home_dir() -> str;
21
+ def _merge_from_file(config: JacCoderConfig, path: str) -> None;
22
+ def load_config(project_dir: str = "") -> JacCoderConfig;
23
+ def get_config() -> JacCoderConfig;
24
+ def get_data_dir() -> str;
25
+
26
+ # --- Model management ---
27
+ def set_model(model: str) -> dict;
28
+ def get_model() -> dict;
29
+
30
+ with entry {
31
+ load_dotenv(override=True);
32
+ load_config();
33
+ }
34
+
35
+ glob model_name: str = _config.default_model;
36
+ glob llm = Model(model_name=model_name);
@@ -0,0 +1,17 @@
1
+ obj ContextConfig {
2
+ has token_budget: int = 20000;
3
+ has recent_window: int = 8;
4
+ }
5
+
6
+
7
+ def estimate_tokens(messages: list[dict]) -> int;
8
+
9
+ def needs_compaction(messages: list[dict], budget: int = 20000) -> bool;
10
+
11
+ def build_context(
12
+ chat_history: list[dict],
13
+ active_files: list[str] = [],
14
+ pending_errors: list[str] = [],
15
+ config: ContextConfig | None = None,
16
+ project_summary: str = ""
17
+ ) -> list[dict];
@@ -0,0 +1,90 @@
1
+ # Example: AI Agent with LLM Routing
2
+
3
+ Uses `by llm()` for intelligent routing, `sem` annotations for prompts, walker graph traversal.
4
+
5
+ ## Backend: agent.jac — Declarations
6
+
7
+ ```jac
8
+ """AI agent with LLM-driven routing between handler nodes."""
9
+
10
+ import from byllm.lib { Model }
11
+
12
+ glob llm: Model = Model(model_name="gpt-4.1-mini");
13
+
14
+ node Router {}
15
+
16
+ node QAHandler {
17
+ def respond(message: str, context: list[dict]) -> str by llm(
18
+ method="Reason",
19
+ messages=context,
20
+ temperature=0.3
21
+ );
22
+ can handle with process_request entry;
23
+ }
24
+
25
+ node CodeHandler {
26
+ def respond(message: str, context: list[dict]) -> str by llm(
27
+ messages=context,
28
+ tools=[search_docs]
29
+ );
30
+ can handle with process_request entry;
31
+ }
32
+
33
+ walker :pub process_request {
34
+ has message: str = "";
35
+ has context: list[dict] = [];
36
+
37
+ can route with Router entry {
38
+ visit [-->] by llm(
39
+ incl_info={"message": self.message, "context": self.context[-5:]}
40
+ );
41
+ }
42
+
43
+ can init_graph with Root entry {
44
+ visit [-->][?:Router] else {
45
+ router = (here ++> Router())[0];
46
+ router ++> QAHandler();
47
+ router ++> CodeHandler();
48
+ visit router;
49
+ };
50
+ }
51
+ }
52
+ ```
53
+
54
+ ## Implementation: impl/agent.impl.jac
55
+
56
+ ```jac
57
+ sem Router.classify = """Return exactly one label: QA, CODE, or DONE.
58
+ QA = general questions, CODE = programming tasks, DONE = task complete.""";
59
+
60
+ sem QAHandler.respond = """You are a helpful assistant. Answer clearly and concisely.""";
61
+
62
+ sem CodeHandler.respond = """You are an expert programmer.
63
+ Use search_docs to find documentation before answering.""";
64
+
65
+ impl QAHandler.handle with process_request entry {
66
+ response = here.respond(message=self.message, context=self.context);
67
+ report {"response": response, "handler": "qa"};
68
+ visit [<--][?:Router];
69
+ }
70
+
71
+ impl CodeHandler.handle with process_request entry {
72
+ response = here.respond(message=self.message, context=self.context);
73
+ report {"response": response, "handler": "code"};
74
+ visit [<--][?:Router];
75
+ }
76
+
77
+ impl process_request.route with Router entry {
78
+ label = here.classify(message=self.message, context=self.context);
79
+ if "DONE" in label { disengage; }
80
+ }
81
+ ```
82
+
83
+ ## Key Patterns Demonstrated
84
+
85
+ - **Lazy graph creation**: `visit [-->][?:Router] else { ... }` creates graph on first use
86
+ - **LLM-driven routing**: `visit [-->] by llm(...)` lets LLM choose which handler
87
+ - **Interface/impl split**: Declarations in `.jac`, semantics + logic in `.impl.jac`
88
+ - **Multi-node traversal**: Walker visits Router → Handler → back to Router
89
+ - **`sem` annotations**: System prompts for each LLM-powered method
90
+ - **`disengage`**: Stops walker when task is complete