tyrion-cli 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.
- tyrion_cli-0.1.0/.gitignore +17 -0
- tyrion_cli-0.1.0/LICENSE +21 -0
- tyrion_cli-0.1.0/PKG-INFO +247 -0
- tyrion_cli-0.1.0/README.md +217 -0
- tyrion_cli-0.1.0/pyproject.toml +74 -0
- tyrion_cli-0.1.0/src/tyrion_agent/__init__.py +0 -0
- tyrion_cli-0.1.0/src/tyrion_agent/events.py +89 -0
- tyrion_cli-0.1.0/src/tyrion_agent/harness.py +273 -0
- tyrion_cli-0.1.0/src/tyrion_agent/loop.py +273 -0
- tyrion_cli-0.1.0/src/tyrion_agent/messages.py +119 -0
- tyrion_cli-0.1.0/src/tyrion_agent/provider.py +32 -0
- tyrion_cli-0.1.0/src/tyrion_agent/provider_events.py +70 -0
- tyrion_cli-0.1.0/src/tyrion_agent/py.typed +0 -0
- tyrion_cli-0.1.0/src/tyrion_agent/sessions/__init__.py +28 -0
- tyrion_cli-0.1.0/src/tyrion_agent/sessions/entries.py +68 -0
- tyrion_cli-0.1.0/src/tyrion_agent/sessions/jsonl.py +67 -0
- tyrion_cli-0.1.0/src/tyrion_agent/sessions/tree.py +132 -0
- tyrion_cli-0.1.0/src/tyrion_agent/tool_history.py +59 -0
- tyrion_cli-0.1.0/src/tyrion_agent/tools.py +84 -0
- tyrion_cli-0.1.0/src/tyrion_agent/types.py +7 -0
- tyrion_cli-0.1.0/src/tyrion_ai/__init__.py +0 -0
- tyrion_cli-0.1.0/src/tyrion_ai/env.py +38 -0
- tyrion_cli-0.1.0/src/tyrion_ai/fake.py +50 -0
- tyrion_cli-0.1.0/src/tyrion_ai/http.py +21 -0
- tyrion_cli-0.1.0/src/tyrion_ai/model_limits.py +152 -0
- tyrion_cli-0.1.0/src/tyrion_ai/openai_compatible.py +432 -0
- tyrion_cli-0.1.0/src/tyrion_ai/py.typed +0 -0
- tyrion_cli-0.1.0/src/tyrion_coding/__init__.py +0 -0
- tyrion_cli-0.1.0/src/tyrion_coding/cli.py +166 -0
- tyrion_cli-0.1.0/src/tyrion_coding/commands.py +173 -0
- tyrion_cli-0.1.0/src/tyrion_coding/context_window.py +104 -0
- tyrion_cli-0.1.0/src/tyrion_coding/data/__init__.py +0 -0
- tyrion_cli-0.1.0/src/tyrion_coding/data/system_prompt.md +19 -0
- tyrion_cli-0.1.0/src/tyrion_coding/display.py +106 -0
- tyrion_cli-0.1.0/src/tyrion_coding/prompt_templates.py +41 -0
- tyrion_cli-0.1.0/src/tyrion_coding/provider_catalog.py +76 -0
- tyrion_cli-0.1.0/src/tyrion_coding/provider_config.py +161 -0
- tyrion_cli-0.1.0/src/tyrion_coding/py.typed +0 -0
- tyrion_cli-0.1.0/src/tyrion_coding/rendering.py +86 -0
- tyrion_cli-0.1.0/src/tyrion_coding/resources.py +36 -0
- tyrion_cli-0.1.0/src/tyrion_coding/session.py +258 -0
- tyrion_cli-0.1.0/src/tyrion_coding/session_coding.py +107 -0
- tyrion_cli-0.1.0/src/tyrion_coding/skills.py +71 -0
- tyrion_cli-0.1.0/src/tyrion_coding/system_prompt.py +42 -0
- tyrion_cli-0.1.0/src/tyrion_coding/theme.py +128 -0
- tyrion_cli-0.1.0/src/tyrion_coding/tools.py +529 -0
- tyrion_cli-0.1.0/src/tyrion_coding/tui/__init__.py +6 -0
- tyrion_cli-0.1.0/src/tyrion_coding/tui/app.py +411 -0
- tyrion_cli-0.1.0/src/tyrion_coding/tui/connect_modal.py +158 -0
- tyrion_cli-0.1.0/src/tyrion_coding/tui/picker.py +150 -0
- tyrion_cli-0.1.0/src/tyrion_coding/tui/styles.py +222 -0
- tyrion_cli-0.1.0/src/tyrion_coding/tui/welcome.py +89 -0
- tyrion_cli-0.1.0/src/tyrion_coding/tui/widgets.py +474 -0
- tyrion_cli-0.1.0/tests/conftest.py +42 -0
- tyrion_cli-0.1.0/tests/test_cli.py +31 -0
- tyrion_cli-0.1.0/tests/test_coding_session.py +83 -0
- tyrion_cli-0.1.0/tests/test_command_menu.py +372 -0
- tyrion_cli-0.1.0/tests/test_compaction.py +176 -0
- tyrion_cli-0.1.0/tests/test_context_window.py +133 -0
- tyrion_cli-0.1.0/tests/test_credentials.py +55 -0
- tyrion_cli-0.1.0/tests/test_display.py +136 -0
- tyrion_cli-0.1.0/tests/test_loop.py +99 -0
- tyrion_cli-0.1.0/tests/test_model_limits.py +149 -0
- tyrion_cli-0.1.0/tests/test_openai_provider.py +338 -0
- tyrion_cli-0.1.0/tests/test_packaging.py +353 -0
- tyrion_cli-0.1.0/tests/test_rendering.py +124 -0
- tyrion_cli-0.1.0/tests/test_session.py +65 -0
- tyrion_cli-0.1.0/tests/test_session_titles.py +207 -0
- tyrion_cli-0.1.0/tests/test_system_prompt.py +37 -0
- tyrion_cli-0.1.0/tests/test_theme.py +115 -0
- tyrion_cli-0.1.0/tests/test_tools.py +55 -0
- tyrion_cli-0.1.0/tests/test_tui_dialogs.py +292 -0
- tyrion_cli-0.1.0/tests/test_tui_widgets.py +398 -0
tyrion_cli-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tejasveer Singh Sodhi
|
|
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.
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tyrion-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A terminal-native AI coding agent: streaming TUI, tool use, resumable sessions, and any OpenAI-compatible model.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Xtejasveer/tyrion
|
|
6
|
+
Project-URL: Repository, https://github.com/Xtejasveer/tyrion
|
|
7
|
+
Project-URL: Issues, https://github.com/Xtejasveer/tyrion/issues
|
|
8
|
+
Author: Tejasveer Singh Sodhi
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai,cli,coding-agent,llm,terminal,textual,tui
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Requires-Dist: anyio>=4.0
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
24
|
+
Requires-Dist: pydantic>=2.11
|
|
25
|
+
Requires-Dist: pygments>=2.18
|
|
26
|
+
Requires-Dist: rich>=13.0
|
|
27
|
+
Requires-Dist: textual>=8.0
|
|
28
|
+
Requires-Dist: typer>=0.12
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# ⚔️ Tyrion — Terminal-Native AI Coding Agent
|
|
32
|
+
|
|
33
|
+
<div align="center">
|
|
34
|
+
|
|
35
|
+

|
|
36
|
+
|
|
37
|
+
*A powerful, transparent, and responsive AI coding companion built directly for your terminal.*
|
|
38
|
+
|
|
39
|
+
[](https://www.python.org/)
|
|
40
|
+
[](https://textual.textualize.io/)
|
|
41
|
+
[](#testing)
|
|
42
|
+
[](#architecture)
|
|
43
|
+
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 📦 Install
|
|
49
|
+
|
|
50
|
+
Works on **macOS and Linux**. On Windows, use [WSL](https://learn.microsoft.com/windows/wsl/install).
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
curl -LsSf https://raw.githubusercontent.com/Xtejasveer/tyrion/main/install.sh | sh && exec "$SHELL" -l
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Then just run `tyrion`. The `&& exec "$SHELL" -l` at the end restarts your shell so `tyrion` works right away in this same terminal window. (In scripts and CI, leave that part off: there is no shell to restart.) The script installs [`uv`](https://docs.astral.sh/uv/) if you don't have it, then installs Tyrion into its own isolated environment. You don't need Python installed (uv fetches Python 3.12 for you), and it never uses `sudo`. Want to read it first? [`install.sh`](install.sh) is short.
|
|
57
|
+
|
|
58
|
+
- **Update:** run the install command again.
|
|
59
|
+
- **Uninstall:** `uv tool uninstall tyrion-cli` (your chats and settings in `~/.tyrion` are left alone; delete that folder to remove them too).
|
|
60
|
+
- **`command not found: tyrion`?** The terminal window you installed from can't see the new command yet (this happens if the command was run without the `&& exec "$SHELL" -l` part). Run `exec "$SHELL" -l`, or open a new terminal window, or run `export PATH="$HOME/.local/bin:$PATH"` and try again. The installer prints these for you at the end.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 📖 Overview
|
|
65
|
+
|
|
66
|
+
**Tyrion** is an autonomous terminal-native coding agent designed to pair-program with you directly inside your command line. Whether you are exploring an unfamiliar codebase, refactoring complex modules, fixing bugs, or writing unit tests, Tyrion provides a fluid, distraction-free environment that operates on your local workspace.
|
|
67
|
+
|
|
68
|
+
Equipped with filesystem tools, terminal execution capabilities, automatic token compaction, and in-UI provider switching, Tyrion pairs deep reasoning with responsive terminal aesthetics.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 🏛️ Architecture
|
|
73
|
+
|
|
74
|
+
Tyrion's core engine architecture is derived from the **Pi coding agent architecture** developed by Mario Zechner (BadLogic Games). It adheres strictly to modular separation of concerns, separating pure stateless execution from stateful orchestration and persistence.
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
78
|
+
│ Presentation Layer │
|
|
79
|
+
│ Textual TUI (app.py) │ Print CLI (cli.py / rendering)│
|
|
80
|
+
├─────────────────────────────────────────────────────────────┤
|
|
81
|
+
│ Coding Domain Layer │
|
|
82
|
+
│ CodingSession │ Coding Tools (read, write, edit, bash) │
|
|
83
|
+
│ System Prompt Builder │ Compaction & Token Accounting │
|
|
84
|
+
├─────────────────────────────────────────────────────────────┤
|
|
85
|
+
│ Agent Harness Layer │
|
|
86
|
+
│ AgentHarness (Stateful brain, cancellation, events) │
|
|
87
|
+
│ Session Persistence (Append-only JSONL Tree) │
|
|
88
|
+
├─────────────────────────────────────────────────────────────┤
|
|
89
|
+
│ Pure Agent Loop Layer │
|
|
90
|
+
│ run_agent_loop (Stateless generator, turn manager) │
|
|
91
|
+
├─────────────────────────────────────────────────────────────┤
|
|
92
|
+
│ Model Provider Layer │
|
|
93
|
+
│ OpenAI-Compatible Streaming Provider (OpenRouter, etc.) │
|
|
94
|
+
└─────────────────────────────────────────────────────────────┘
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Key Architectural Layers
|
|
98
|
+
|
|
99
|
+
1. **Pure Agent Loop (`src/tyrion_agent/loop.py`)**:
|
|
100
|
+
- A completely **stateless generator** driving model-to-tool feedback cycles.
|
|
101
|
+
- Emits fine-grained events (`AgentStartEvent`, `TurnStartEvent`, `MessageUpdateEvent`, `ToolExecutionStartEvent`, `ToolExecutionEndEvent`, `TurnEndEvent`, `AgentEndEvent`).
|
|
102
|
+
- Knows nothing about disk persistence or frontends; simply consumes an immutable transcript and yields execution events.
|
|
103
|
+
|
|
104
|
+
2. **Agent Harness (`src/tyrion_agent/harness.py`)**:
|
|
105
|
+
- The stateful supervisor that wraps the pure loop.
|
|
106
|
+
- Manages message history, handles cooperative cancellation, dispatches events to listeners, and auto-repairs interrupted tool calls.
|
|
107
|
+
|
|
108
|
+
3. **Append-Only Tree Session Storage (`src/tyrion_agent/sessions/`)**:
|
|
109
|
+
- Conversations are persisted to append-only JSONL logs on disk.
|
|
110
|
+
- Sessions are structured as trees rather than linear lists, enabling branching, time-travel, and lossless restoration via `reconstruct_state()`.
|
|
111
|
+
|
|
112
|
+
4. **Model Provider Abstraction (`src/tyrion_ai/`)**:
|
|
113
|
+
- High-performance, streaming SSE client supporting OpenAI, OpenRouter, DeepSeek, and local LLMs (Ollama, vLLM).
|
|
114
|
+
- Captures real server-side token usage (`stream_options: {"include_usage": True}`) and features self-healing retries if a provider rejects usage flags.
|
|
115
|
+
- Configurable context window limits and local override support via `~/.tyrion/model_limits.json`.
|
|
116
|
+
|
|
117
|
+
5. **Coding Domain & Tools (`src/tyrion_coding/`)**:
|
|
118
|
+
- `CodingSession`: Coordinates the project workspace, loads project guidelines (`AGENTS.md` / instructions), and automatically assembles system prompts.
|
|
119
|
+
- Real-time token compaction: Summarizes older conversation history using the model when nearing the 80% context window ceiling.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 🎯 Use Cases
|
|
124
|
+
|
|
125
|
+
- **Codebase Exploration & Analysis**: Ask Tyrion to inspect directories, summarize module relationships, and trace function call graphs across unfamiliar repositories.
|
|
126
|
+
- **Hands-Off Multi-File Refactoring**: Prompt Tyrion to migrate legacy patterns or update dependencies; it reads targets, formulates plans, edits files, and verifies changes.
|
|
127
|
+
- **Bug Diagnosis & TDD**: Feed Tyrion a failing test suite or trace error; it will isolate the defect, apply precise edits, and re-run pytest until all tests are green.
|
|
128
|
+
- **Remote & SSH Pair Programming**: Full-featured interactive TUI that runs in headless servers, Docker containers, and SSH sessions without requiring a browser or Electron.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## ✨ Features
|
|
133
|
+
|
|
134
|
+
- 🎨 **Lannister Brand Theme**: Warm ink-black surfaces (`#0e0d10`, `#16151a`), rich Lannister gold (`#e0b04f`), crimson accents, and custom Pygments syntax highlighting designed for chat readability.
|
|
135
|
+
- ⌨️ **Interactive Slash Command Palette**: Type `/` anywhere in the prompt to open an instant auto-completing command menu with arrow-key navigation and keyboard shortcuts.
|
|
136
|
+
- ⚡ **Full Tool Arsenal**:
|
|
137
|
+
- `read`: Reads files with line number indexing and byte caps.
|
|
138
|
+
- `write`: Creates or replaces files safely.
|
|
139
|
+
- `edit`: Precise substring replacements with uniqueness validation.
|
|
140
|
+
- `bash`: Subprocess execution with process group termination (`os.killpg`) to eliminate orphan background processes.
|
|
141
|
+
- 🔌 **In-UI Connection (`/connect`)**: Connect your OpenRouter or OpenAI API keys directly within the app and have them saved in `~/.tyrion/credentials.json` (readable only by you).
|
|
142
|
+
- 📊 **Real-Time Token Usage Bar**: Dynamic status bar with visual block gauges (`▰▰▰▱▱▱`) displaying exact server-reported token usage against context limits.
|
|
143
|
+
- 🧹 **Automatic & Manual Compaction (`/compact`)**: Compresses long conversations into persistent summaries, preserving immediate context while keeping token usage lean.
|
|
144
|
+
- 🗂️ **Session Resuming (`/resume`)**: Visual session picker overlay allowing you to jump between past conversations and pick up right where you left off.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 🛡️ Safety
|
|
149
|
+
|
|
150
|
+
Tyrion is early software (v0.1). Read this before pointing it at a project you care about:
|
|
151
|
+
|
|
152
|
+
- **It runs commands and edits files without asking.** The `bash`, `write` and `edit` tools act immediately and can reach any path your user can. Use it inside a git repository so you can review and undo changes (`git diff`, `git restore`).
|
|
153
|
+
- **Your code leaves your machine.** Your prompts and the files Tyrion reads are sent to the model provider you connect.
|
|
154
|
+
- **Your API key is stored in plain text** in `~/.tyrion/credentials.json`, readable only by you (permissions `600`). Prefer a key with a spending limit.
|
|
155
|
+
- **Files can carry instructions.** In an untrusted repository, a malicious file could try to steer the model into running commands.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 🚀 Getting Started
|
|
160
|
+
|
|
161
|
+
> Just want to use Tyrion? See [Install](#-install) above. This section is for running it from source.
|
|
162
|
+
|
|
163
|
+
### Prerequisites
|
|
164
|
+
|
|
165
|
+
- Python 3.12+
|
|
166
|
+
- [`uv`](https://github.com/astral-sh/uv) (recommended) or `pip`
|
|
167
|
+
|
|
168
|
+
### Installation from source
|
|
169
|
+
|
|
170
|
+
Clone the repository and install dependencies:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
git clone https://github.com/Xtejasveer/tyrion.git
|
|
174
|
+
cd tyrion
|
|
175
|
+
uv sync
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Running Tyrion
|
|
179
|
+
|
|
180
|
+
Launch the interactive Terminal User Interface:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
uv run tyrion
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
*Tip: On first launch, run `/connect` to select your model provider (e.g. OpenRouter) and enter your API key.*
|
|
187
|
+
|
|
188
|
+
### Command-Line Usage
|
|
189
|
+
|
|
190
|
+
Tyrion also supports quick one-shot command-line runs:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Ask a direct question
|
|
194
|
+
uv run tyrion "Explain how the routing works in src/api.py"
|
|
195
|
+
|
|
196
|
+
# Specify a model
|
|
197
|
+
uv run tyrion --model google/gemini-2.5-flash "Write unit tests for tools.py"
|
|
198
|
+
|
|
199
|
+
# Resume an existing session by ID
|
|
200
|
+
uv run tyrion --resume <session_id>
|
|
201
|
+
|
|
202
|
+
# Include a file's contents in the prompt
|
|
203
|
+
uv run tyrion "What is causing this traceback? $(cat error.log)"
|
|
204
|
+
|
|
205
|
+
# Or pipe the whole prompt in
|
|
206
|
+
cat prompt.txt | uv run tyrion
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 🕹️ Slash Commands Reference
|
|
212
|
+
|
|
213
|
+
| Command | Description |
|
|
214
|
+
| :--- | :--- |
|
|
215
|
+
| `/connect` | Open provider dialog to enter & persist your API key |
|
|
216
|
+
| `/model <name>` | Hot-swap active LLM model on the fly |
|
|
217
|
+
| `/resume` | Open visual session picker modal to switch conversations |
|
|
218
|
+
| `/compact` | Manually compress and summarize older conversation history |
|
|
219
|
+
| `/clear` | Wipe current transcript and return to centered landing screen |
|
|
220
|
+
| `/help` | List all available slash commands and descriptions |
|
|
221
|
+
| `/quit` / `/exit` | Gracefully exit the application |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## 🧪 Testing
|
|
226
|
+
|
|
227
|
+
Tyrion comes with a comprehensive test suite covering the agent loop, bash tool process isolation, SSE streaming, model limits, context compaction, TUI dialogs, command menu navigation, and theme rendering:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
uv run pytest
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
============================= 200 passed in 17.28s =============================
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 🤝 Acknowledgements
|
|
240
|
+
|
|
241
|
+
Tyrion's core architecture and design principles are derived from the **Pi coding agent architecture** designed by [Mario Zechner](https://github.com/badlogic) ([BadLogic Games](https://badlogicgames.com/)). We extend our gratitude to Mario for the clean conceptual framework of separating the pure stateless agent loop from stateful harnesses and persistent session trees.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 📜 License
|
|
246
|
+
|
|
247
|
+
This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# ⚔️ Tyrion — Terminal-Native AI Coding Agent
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
*A powerful, transparent, and responsive AI coding companion built directly for your terminal.*
|
|
8
|
+
|
|
9
|
+
[](https://www.python.org/)
|
|
10
|
+
[](https://textual.textualize.io/)
|
|
11
|
+
[](#testing)
|
|
12
|
+
[](#architecture)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 📦 Install
|
|
19
|
+
|
|
20
|
+
Works on **macOS and Linux**. On Windows, use [WSL](https://learn.microsoft.com/windows/wsl/install).
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
curl -LsSf https://raw.githubusercontent.com/Xtejasveer/tyrion/main/install.sh | sh && exec "$SHELL" -l
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Then just run `tyrion`. The `&& exec "$SHELL" -l` at the end restarts your shell so `tyrion` works right away in this same terminal window. (In scripts and CI, leave that part off: there is no shell to restart.) The script installs [`uv`](https://docs.astral.sh/uv/) if you don't have it, then installs Tyrion into its own isolated environment. You don't need Python installed (uv fetches Python 3.12 for you), and it never uses `sudo`. Want to read it first? [`install.sh`](install.sh) is short.
|
|
27
|
+
|
|
28
|
+
- **Update:** run the install command again.
|
|
29
|
+
- **Uninstall:** `uv tool uninstall tyrion-cli` (your chats and settings in `~/.tyrion` are left alone; delete that folder to remove them too).
|
|
30
|
+
- **`command not found: tyrion`?** The terminal window you installed from can't see the new command yet (this happens if the command was run without the `&& exec "$SHELL" -l` part). Run `exec "$SHELL" -l`, or open a new terminal window, or run `export PATH="$HOME/.local/bin:$PATH"` and try again. The installer prints these for you at the end.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 📖 Overview
|
|
35
|
+
|
|
36
|
+
**Tyrion** is an autonomous terminal-native coding agent designed to pair-program with you directly inside your command line. Whether you are exploring an unfamiliar codebase, refactoring complex modules, fixing bugs, or writing unit tests, Tyrion provides a fluid, distraction-free environment that operates on your local workspace.
|
|
37
|
+
|
|
38
|
+
Equipped with filesystem tools, terminal execution capabilities, automatic token compaction, and in-UI provider switching, Tyrion pairs deep reasoning with responsive terminal aesthetics.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 🏛️ Architecture
|
|
43
|
+
|
|
44
|
+
Tyrion's core engine architecture is derived from the **Pi coding agent architecture** developed by Mario Zechner (BadLogic Games). It adheres strictly to modular separation of concerns, separating pure stateless execution from stateful orchestration and persistence.
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
48
|
+
│ Presentation Layer │
|
|
49
|
+
│ Textual TUI (app.py) │ Print CLI (cli.py / rendering)│
|
|
50
|
+
├─────────────────────────────────────────────────────────────┤
|
|
51
|
+
│ Coding Domain Layer │
|
|
52
|
+
│ CodingSession │ Coding Tools (read, write, edit, bash) │
|
|
53
|
+
│ System Prompt Builder │ Compaction & Token Accounting │
|
|
54
|
+
├─────────────────────────────────────────────────────────────┤
|
|
55
|
+
│ Agent Harness Layer │
|
|
56
|
+
│ AgentHarness (Stateful brain, cancellation, events) │
|
|
57
|
+
│ Session Persistence (Append-only JSONL Tree) │
|
|
58
|
+
├─────────────────────────────────────────────────────────────┤
|
|
59
|
+
│ Pure Agent Loop Layer │
|
|
60
|
+
│ run_agent_loop (Stateless generator, turn manager) │
|
|
61
|
+
├─────────────────────────────────────────────────────────────┤
|
|
62
|
+
│ Model Provider Layer │
|
|
63
|
+
│ OpenAI-Compatible Streaming Provider (OpenRouter, etc.) │
|
|
64
|
+
└─────────────────────────────────────────────────────────────┘
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Key Architectural Layers
|
|
68
|
+
|
|
69
|
+
1. **Pure Agent Loop (`src/tyrion_agent/loop.py`)**:
|
|
70
|
+
- A completely **stateless generator** driving model-to-tool feedback cycles.
|
|
71
|
+
- Emits fine-grained events (`AgentStartEvent`, `TurnStartEvent`, `MessageUpdateEvent`, `ToolExecutionStartEvent`, `ToolExecutionEndEvent`, `TurnEndEvent`, `AgentEndEvent`).
|
|
72
|
+
- Knows nothing about disk persistence or frontends; simply consumes an immutable transcript and yields execution events.
|
|
73
|
+
|
|
74
|
+
2. **Agent Harness (`src/tyrion_agent/harness.py`)**:
|
|
75
|
+
- The stateful supervisor that wraps the pure loop.
|
|
76
|
+
- Manages message history, handles cooperative cancellation, dispatches events to listeners, and auto-repairs interrupted tool calls.
|
|
77
|
+
|
|
78
|
+
3. **Append-Only Tree Session Storage (`src/tyrion_agent/sessions/`)**:
|
|
79
|
+
- Conversations are persisted to append-only JSONL logs on disk.
|
|
80
|
+
- Sessions are structured as trees rather than linear lists, enabling branching, time-travel, and lossless restoration via `reconstruct_state()`.
|
|
81
|
+
|
|
82
|
+
4. **Model Provider Abstraction (`src/tyrion_ai/`)**:
|
|
83
|
+
- High-performance, streaming SSE client supporting OpenAI, OpenRouter, DeepSeek, and local LLMs (Ollama, vLLM).
|
|
84
|
+
- Captures real server-side token usage (`stream_options: {"include_usage": True}`) and features self-healing retries if a provider rejects usage flags.
|
|
85
|
+
- Configurable context window limits and local override support via `~/.tyrion/model_limits.json`.
|
|
86
|
+
|
|
87
|
+
5. **Coding Domain & Tools (`src/tyrion_coding/`)**:
|
|
88
|
+
- `CodingSession`: Coordinates the project workspace, loads project guidelines (`AGENTS.md` / instructions), and automatically assembles system prompts.
|
|
89
|
+
- Real-time token compaction: Summarizes older conversation history using the model when nearing the 80% context window ceiling.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 🎯 Use Cases
|
|
94
|
+
|
|
95
|
+
- **Codebase Exploration & Analysis**: Ask Tyrion to inspect directories, summarize module relationships, and trace function call graphs across unfamiliar repositories.
|
|
96
|
+
- **Hands-Off Multi-File Refactoring**: Prompt Tyrion to migrate legacy patterns or update dependencies; it reads targets, formulates plans, edits files, and verifies changes.
|
|
97
|
+
- **Bug Diagnosis & TDD**: Feed Tyrion a failing test suite or trace error; it will isolate the defect, apply precise edits, and re-run pytest until all tests are green.
|
|
98
|
+
- **Remote & SSH Pair Programming**: Full-featured interactive TUI that runs in headless servers, Docker containers, and SSH sessions without requiring a browser or Electron.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## ✨ Features
|
|
103
|
+
|
|
104
|
+
- 🎨 **Lannister Brand Theme**: Warm ink-black surfaces (`#0e0d10`, `#16151a`), rich Lannister gold (`#e0b04f`), crimson accents, and custom Pygments syntax highlighting designed for chat readability.
|
|
105
|
+
- ⌨️ **Interactive Slash Command Palette**: Type `/` anywhere in the prompt to open an instant auto-completing command menu with arrow-key navigation and keyboard shortcuts.
|
|
106
|
+
- ⚡ **Full Tool Arsenal**:
|
|
107
|
+
- `read`: Reads files with line number indexing and byte caps.
|
|
108
|
+
- `write`: Creates or replaces files safely.
|
|
109
|
+
- `edit`: Precise substring replacements with uniqueness validation.
|
|
110
|
+
- `bash`: Subprocess execution with process group termination (`os.killpg`) to eliminate orphan background processes.
|
|
111
|
+
- 🔌 **In-UI Connection (`/connect`)**: Connect your OpenRouter or OpenAI API keys directly within the app and have them saved in `~/.tyrion/credentials.json` (readable only by you).
|
|
112
|
+
- 📊 **Real-Time Token Usage Bar**: Dynamic status bar with visual block gauges (`▰▰▰▱▱▱`) displaying exact server-reported token usage against context limits.
|
|
113
|
+
- 🧹 **Automatic & Manual Compaction (`/compact`)**: Compresses long conversations into persistent summaries, preserving immediate context while keeping token usage lean.
|
|
114
|
+
- 🗂️ **Session Resuming (`/resume`)**: Visual session picker overlay allowing you to jump between past conversations and pick up right where you left off.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 🛡️ Safety
|
|
119
|
+
|
|
120
|
+
Tyrion is early software (v0.1). Read this before pointing it at a project you care about:
|
|
121
|
+
|
|
122
|
+
- **It runs commands and edits files without asking.** The `bash`, `write` and `edit` tools act immediately and can reach any path your user can. Use it inside a git repository so you can review and undo changes (`git diff`, `git restore`).
|
|
123
|
+
- **Your code leaves your machine.** Your prompts and the files Tyrion reads are sent to the model provider you connect.
|
|
124
|
+
- **Your API key is stored in plain text** in `~/.tyrion/credentials.json`, readable only by you (permissions `600`). Prefer a key with a spending limit.
|
|
125
|
+
- **Files can carry instructions.** In an untrusted repository, a malicious file could try to steer the model into running commands.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 🚀 Getting Started
|
|
130
|
+
|
|
131
|
+
> Just want to use Tyrion? See [Install](#-install) above. This section is for running it from source.
|
|
132
|
+
|
|
133
|
+
### Prerequisites
|
|
134
|
+
|
|
135
|
+
- Python 3.12+
|
|
136
|
+
- [`uv`](https://github.com/astral-sh/uv) (recommended) or `pip`
|
|
137
|
+
|
|
138
|
+
### Installation from source
|
|
139
|
+
|
|
140
|
+
Clone the repository and install dependencies:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
git clone https://github.com/Xtejasveer/tyrion.git
|
|
144
|
+
cd tyrion
|
|
145
|
+
uv sync
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Running Tyrion
|
|
149
|
+
|
|
150
|
+
Launch the interactive Terminal User Interface:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
uv run tyrion
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
*Tip: On first launch, run `/connect` to select your model provider (e.g. OpenRouter) and enter your API key.*
|
|
157
|
+
|
|
158
|
+
### Command-Line Usage
|
|
159
|
+
|
|
160
|
+
Tyrion also supports quick one-shot command-line runs:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Ask a direct question
|
|
164
|
+
uv run tyrion "Explain how the routing works in src/api.py"
|
|
165
|
+
|
|
166
|
+
# Specify a model
|
|
167
|
+
uv run tyrion --model google/gemini-2.5-flash "Write unit tests for tools.py"
|
|
168
|
+
|
|
169
|
+
# Resume an existing session by ID
|
|
170
|
+
uv run tyrion --resume <session_id>
|
|
171
|
+
|
|
172
|
+
# Include a file's contents in the prompt
|
|
173
|
+
uv run tyrion "What is causing this traceback? $(cat error.log)"
|
|
174
|
+
|
|
175
|
+
# Or pipe the whole prompt in
|
|
176
|
+
cat prompt.txt | uv run tyrion
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 🕹️ Slash Commands Reference
|
|
182
|
+
|
|
183
|
+
| Command | Description |
|
|
184
|
+
| :--- | :--- |
|
|
185
|
+
| `/connect` | Open provider dialog to enter & persist your API key |
|
|
186
|
+
| `/model <name>` | Hot-swap active LLM model on the fly |
|
|
187
|
+
| `/resume` | Open visual session picker modal to switch conversations |
|
|
188
|
+
| `/compact` | Manually compress and summarize older conversation history |
|
|
189
|
+
| `/clear` | Wipe current transcript and return to centered landing screen |
|
|
190
|
+
| `/help` | List all available slash commands and descriptions |
|
|
191
|
+
| `/quit` / `/exit` | Gracefully exit the application |
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 🧪 Testing
|
|
196
|
+
|
|
197
|
+
Tyrion comes with a comprehensive test suite covering the agent loop, bash tool process isolation, SSE streaming, model limits, context compaction, TUI dialogs, command menu navigation, and theme rendering:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
uv run pytest
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
============================= 200 passed in 17.28s =============================
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 🤝 Acknowledgements
|
|
210
|
+
|
|
211
|
+
Tyrion's core architecture and design principles are derived from the **Pi coding agent architecture** designed by [Mario Zechner](https://github.com/badlogic) ([BadLogic Games](https://badlogicgames.com/)). We extend our gratitude to Mario for the clean conceptual framework of separating the pure stateless agent loop from stateful harnesses and persistent session trees.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 📜 License
|
|
216
|
+
|
|
217
|
+
This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
# "tyrion" is already taken on PyPI, so the package is published as "tyrion-cli".
|
|
7
|
+
# The command you type is still `tyrion` (see [project.scripts]).
|
|
8
|
+
name = "tyrion-cli"
|
|
9
|
+
version = "0.1.0"
|
|
10
|
+
description = "A terminal-native AI coding agent: streaming TUI, tool use, resumable sessions, and any OpenAI-compatible model."
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.12"
|
|
13
|
+
license = "MIT"
|
|
14
|
+
license-files = ["LICENSE"]
|
|
15
|
+
authors = [{ name = "Tejasveer Singh Sodhi" }]
|
|
16
|
+
keywords = ["ai", "coding-agent", "llm", "cli", "tui", "terminal", "textual"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Environment :: Console",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Operating System :: MacOS",
|
|
22
|
+
"Operating System :: POSIX :: Linux",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Software Development",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"anyio>=4.0",
|
|
30
|
+
"httpx>=0.27",
|
|
31
|
+
"pydantic>=2.11",
|
|
32
|
+
"pygments>=2.18",
|
|
33
|
+
"rich>=13.0",
|
|
34
|
+
"textual>=8.0",
|
|
35
|
+
"typer>=0.12",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/Xtejasveer/tyrion"
|
|
40
|
+
Repository = "https://github.com/Xtejasveer/tyrion"
|
|
41
|
+
Issues = "https://github.com/Xtejasveer/tyrion/issues"
|
|
42
|
+
|
|
43
|
+
[project.scripts]
|
|
44
|
+
tyrion = "tyrion_coding.cli:app"
|
|
45
|
+
|
|
46
|
+
# Development tools live here, not in the runtime dependencies, so a normal
|
|
47
|
+
# install does not pull in pytest and ruff. `uv sync` installs this group.
|
|
48
|
+
[dependency-groups]
|
|
49
|
+
dev = [
|
|
50
|
+
"pytest>=8.0",
|
|
51
|
+
"pytest-asyncio>=1.4.0",
|
|
52
|
+
"ruff>=0.5",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[tool.hatch.build.targets.wheel]
|
|
56
|
+
packages = ["src/tyrion_ai", "src/tyrion_agent", "src/tyrion_coding"]
|
|
57
|
+
|
|
58
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
59
|
+
"src/tyrion_coding/data/system_prompt.md" = "tyrion_coding/data/system_prompt.md"
|
|
60
|
+
|
|
61
|
+
# Ship only what belongs in a source release (no scratch scripts, screenshots, or caches).
|
|
62
|
+
[tool.hatch.build.targets.sdist]
|
|
63
|
+
include = ["/src", "/tests", "/README.md", "/LICENSE", "/pyproject.toml"]
|
|
64
|
+
|
|
65
|
+
[tool.pytest.ini_options]
|
|
66
|
+
asyncio_mode = "auto"
|
|
67
|
+
|
|
68
|
+
# Pin the lint rules to real-bug checks (pyflakes + syntax) so CI does not change
|
|
69
|
+
# behaviour whenever ruff changes its defaults.
|
|
70
|
+
[tool.ruff]
|
|
71
|
+
target-version = "py312"
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint]
|
|
74
|
+
select = ["E4", "E7", "E9", "F"]
|
|
File without changes
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Events emitted by the portable agent layer."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from typing import Annotated, Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
8
|
+
from tyrion_agent.messages import AgentMessage, ToolResultMessage, WireModel
|
|
9
|
+
from tyrion_agent.provider_events import AssistantMessageEvent
|
|
10
|
+
from tyrion_agent.tools import AgentToolResult
|
|
11
|
+
from tyrion_agent.types import JSONValue
|
|
12
|
+
|
|
13
|
+
class AgentStartEvent(WireModel):
|
|
14
|
+
"""The agent has started processing."""
|
|
15
|
+
type: Literal["agent_start"] = "agent_start"
|
|
16
|
+
|
|
17
|
+
class AgentEndEvent(WireModel):
|
|
18
|
+
"""The agent has finished processing."""
|
|
19
|
+
|
|
20
|
+
type: Literal["agent_end"] = "agent_end"
|
|
21
|
+
messages: list[AgentMessage] = Field(default_factory=list)
|
|
22
|
+
|
|
23
|
+
class TurnStartEvent(WireModel):
|
|
24
|
+
"""A new model turn has started."""
|
|
25
|
+
|
|
26
|
+
type: Literal["turn_start"] = "turn_start"
|
|
27
|
+
|
|
28
|
+
class TurnEndEvent(WireModel):
|
|
29
|
+
"""A model turn has completed."""
|
|
30
|
+
|
|
31
|
+
type: Literal["turn_end"] = "turn_end"
|
|
32
|
+
message: AgentMessage
|
|
33
|
+
tool_results: list[ToolResultMessage] = Field(default_factory=list)
|
|
34
|
+
|
|
35
|
+
class MessageStartEvent(WireModel):
|
|
36
|
+
"""A message has started (user, assistant or tool result)."""
|
|
37
|
+
type: Literal["message_start"] = "message_start"
|
|
38
|
+
message: AgentMessage
|
|
39
|
+
|
|
40
|
+
class MessageUpdateEvent(WireModel):
|
|
41
|
+
"""A streaming delta has arrived for the current message."""
|
|
42
|
+
|
|
43
|
+
type: Literal["message_update"] = "message_update"
|
|
44
|
+
message: AgentMessage
|
|
45
|
+
assistant_message_event: AssistantMessageEvent
|
|
46
|
+
|
|
47
|
+
class MessageEndEvent(WireModel):
|
|
48
|
+
"""A message has been finalized."""
|
|
49
|
+
type: Literal["message_end"] = "message_end"
|
|
50
|
+
message: AgentMessage
|
|
51
|
+
|
|
52
|
+
class ToolExecutionStartEvent(WireModel):
|
|
53
|
+
"""A tool has started executing."""
|
|
54
|
+
|
|
55
|
+
type: Literal["tool_execution_start"] = "tool_execution_start"
|
|
56
|
+
tool_call_id: str
|
|
57
|
+
tool_name : str
|
|
58
|
+
args: dict[str, JSONValue] = Field(default_factory=dict)
|
|
59
|
+
|
|
60
|
+
class ToolExecutionUpdateEvent(WireModel):
|
|
61
|
+
"""A tool has started executing."""
|
|
62
|
+
|
|
63
|
+
type: Literal["tool_execution_update"] = "tool_execution_update"
|
|
64
|
+
tool_call_id: str
|
|
65
|
+
tool_name : str
|
|
66
|
+
args: dict[str, JSONValue] = Field(default_factory=dict)
|
|
67
|
+
partial_result: AgentToolResult
|
|
68
|
+
|
|
69
|
+
class ToolExecutionEndEvent(WireModel):
|
|
70
|
+
"""A tool has finished executing."""
|
|
71
|
+
type: Literal["tool_execution_end"] = "tool_execution_end"
|
|
72
|
+
tool_call_id: str
|
|
73
|
+
tool_name: str
|
|
74
|
+
result: AgentToolResult
|
|
75
|
+
is_error: bool
|
|
76
|
+
# Union of all agent events
|
|
77
|
+
type AgentEvent = Annotated[
|
|
78
|
+
AgentStartEvent
|
|
79
|
+
| AgentEndEvent
|
|
80
|
+
| TurnStartEvent
|
|
81
|
+
| TurnEndEvent
|
|
82
|
+
| MessageStartEvent
|
|
83
|
+
| MessageUpdateEvent
|
|
84
|
+
| MessageEndEvent
|
|
85
|
+
| ToolExecutionStartEvent
|
|
86
|
+
| ToolExecutionUpdateEvent
|
|
87
|
+
| ToolExecutionEndEvent,
|
|
88
|
+
Field(discriminator="type"),
|
|
89
|
+
]
|