ethan-agent 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 (121) hide show
  1. ethan_agent-0.1.0/.gitignore +58 -0
  2. ethan_agent-0.1.0/LICENSE +21 -0
  3. ethan_agent-0.1.0/PKG-INFO +533 -0
  4. ethan_agent-0.1.0/README.md +492 -0
  5. ethan_agent-0.1.0/README_CN.md +584 -0
  6. ethan_agent-0.1.0/ethan/__init__.py +3 -0
  7. ethan_agent-0.1.0/ethan/acp/__init__.py +126 -0
  8. ethan_agent-0.1.0/ethan/core/__init__.py +0 -0
  9. ethan_agent-0.1.0/ethan/core/agent.py +394 -0
  10. ethan_agent-0.1.0/ethan/core/config.py +231 -0
  11. ethan_agent-0.1.0/ethan/core/heartbeat.py +178 -0
  12. ethan_agent-0.1.0/ethan/core/onboarding.py +24 -0
  13. ethan_agent-0.1.0/ethan/defaults/skills/channels/SKILL.md +43 -0
  14. ethan_agent-0.1.0/ethan/defaults/skills/deepwiki/SKILL.md +47 -0
  15. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/SKILL.md +232 -0
  16. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-chat-create.md +162 -0
  17. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-chat-identity.md +55 -0
  18. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-chat-list.md +166 -0
  19. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-chat-messages-list.md +157 -0
  20. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-chat-search.md +142 -0
  21. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-chat-update.md +84 -0
  22. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-feed-group-list-item.md +68 -0
  23. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-feed-group-list.md +65 -0
  24. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-feed-group-query-item.md +44 -0
  25. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-feed-groups.md +452 -0
  26. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-feed-shortcut-create.md +97 -0
  27. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-feed-shortcut-list.md +103 -0
  28. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-feed-shortcut-remove.md +48 -0
  29. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-flag-cancel.md +67 -0
  30. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-flag-create.md +67 -0
  31. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-flag-list.md +100 -0
  32. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-message-enrichment.md +54 -0
  33. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-messages-mget.md +99 -0
  34. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-messages-reply.md +247 -0
  35. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-messages-resources-download.md +94 -0
  36. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-messages-search.md +234 -0
  37. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-messages-send.md +248 -0
  38. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-reactions.md +299 -0
  39. ethan_agent-0.1.0/ethan/defaults/skills/lark-im/references/lark-im-threads-messages-list.md +115 -0
  40. ethan_agent-0.1.0/ethan/defaults/skills/lark-shared/SKILL.md +168 -0
  41. ethan_agent-0.1.0/ethan/defaults/skills/lark-shared/references/lark-wiki-token-routing.md +42 -0
  42. ethan_agent-0.1.0/ethan/defaults/skills/skills-manager/SKILL.md +70 -0
  43. ethan_agent-0.1.0/ethan/defaults/system/agent.md +29 -0
  44. ethan_agent-0.1.0/ethan/defaults/system/heartbeat.md +0 -0
  45. ethan_agent-0.1.0/ethan/defaults/system/identity.md +3 -0
  46. ethan_agent-0.1.0/ethan/defaults/system/soul.md +26 -0
  47. ethan_agent-0.1.0/ethan/defaults/system/tools.md +7 -0
  48. ethan_agent-0.1.0/ethan/interface/__init__.py +0 -0
  49. ethan_agent-0.1.0/ethan/interface/__main__.py +3 -0
  50. ethan_agent-0.1.0/ethan/interface/api.py +56 -0
  51. ethan_agent-0.1.0/ethan/interface/cli.py +128 -0
  52. ethan_agent-0.1.0/ethan/interface/commands/__init__.py +0 -0
  53. ethan_agent-0.1.0/ethan/interface/commands/code.py +75 -0
  54. ethan_agent-0.1.0/ethan/interface/commands/knowledge.py +93 -0
  55. ethan_agent-0.1.0/ethan/interface/commands/model.py +93 -0
  56. ethan_agent-0.1.0/ethan/interface/commands/provider.py +63 -0
  57. ethan_agent-0.1.0/ethan/interface/commands/schedule.py +100 -0
  58. ethan_agent-0.1.0/ethan/interface/commands/session.py +101 -0
  59. ethan_agent-0.1.0/ethan/interface/commands/skill.py +91 -0
  60. ethan_agent-0.1.0/ethan/interface/commands/update.py +267 -0
  61. ethan_agent-0.1.0/ethan/interface/lark.py +207 -0
  62. ethan_agent-0.1.0/ethan/interface/lark_events.py +388 -0
  63. ethan_agent-0.1.0/ethan/interface/repl.py +596 -0
  64. ethan_agent-0.1.0/ethan/interface/routers/__init__.py +0 -0
  65. ethan_agent-0.1.0/ethan/interface/routers/chat.py +287 -0
  66. ethan_agent-0.1.0/ethan/interface/routers/completions.py +185 -0
  67. ethan_agent-0.1.0/ethan/interface/routers/deps.py +55 -0
  68. ethan_agent-0.1.0/ethan/interface/routers/docs.py +46 -0
  69. ethan_agent-0.1.0/ethan/interface/routers/knowledge.py +76 -0
  70. ethan_agent-0.1.0/ethan/interface/routers/logs.py +22 -0
  71. ethan_agent-0.1.0/ethan/interface/routers/memory.py +80 -0
  72. ethan_agent-0.1.0/ethan/interface/routers/schedule.py +91 -0
  73. ethan_agent-0.1.0/ethan/interface/routers/sessions.py +115 -0
  74. ethan_agent-0.1.0/ethan/interface/routers/settings.py +256 -0
  75. ethan_agent-0.1.0/ethan/interface/routers/skills.py +57 -0
  76. ethan_agent-0.1.0/ethan/knowledge/__init__.py +0 -0
  77. ethan_agent-0.1.0/ethan/knowledge/base.py +177 -0
  78. ethan_agent-0.1.0/ethan/memory/__init__.py +0 -0
  79. ethan_agent-0.1.0/ethan/memory/api_keys.py +84 -0
  80. ethan_agent-0.1.0/ethan/memory/consolidator.py +139 -0
  81. ethan_agent-0.1.0/ethan/memory/embeddings.py +89 -0
  82. ethan_agent-0.1.0/ethan/memory/episodic.py +96 -0
  83. ethan_agent-0.1.0/ethan/memory/facts.py +161 -0
  84. ethan_agent-0.1.0/ethan/memory/persistent.py +22 -0
  85. ethan_agent-0.1.0/ethan/memory/procedures.py +72 -0
  86. ethan_agent-0.1.0/ethan/memory/session.py +274 -0
  87. ethan_agent-0.1.0/ethan/memory/vector_store.py +161 -0
  88. ethan_agent-0.1.0/ethan/memory/working.py +120 -0
  89. ethan_agent-0.1.0/ethan/providers/__init__.py +0 -0
  90. ethan_agent-0.1.0/ethan/providers/anthropic.py +196 -0
  91. ethan_agent-0.1.0/ethan/providers/base.py +71 -0
  92. ethan_agent-0.1.0/ethan/providers/manager.py +33 -0
  93. ethan_agent-0.1.0/ethan/providers/openai_compat.py +186 -0
  94. ethan_agent-0.1.0/ethan/scheduler/__init__.py +0 -0
  95. ethan_agent-0.1.0/ethan/scheduler/cron.py +111 -0
  96. ethan_agent-0.1.0/ethan/scheduler/heartbeat.py +41 -0
  97. ethan_agent-0.1.0/ethan/skills/__init__.py +0 -0
  98. ethan_agent-0.1.0/ethan/skills/generator.py +94 -0
  99. ethan_agent-0.1.0/ethan/skills/loader.py +100 -0
  100. ethan_agent-0.1.0/ethan/skills/registry.py +66 -0
  101. ethan_agent-0.1.0/ethan/skills/stats.py +46 -0
  102. ethan_agent-0.1.0/ethan/skills/updater.py +82 -0
  103. ethan_agent-0.1.0/ethan/tools/__init__.py +0 -0
  104. ethan_agent-0.1.0/ethan/tools/base.py +39 -0
  105. ethan_agent-0.1.0/ethan/tools/builtin/__init__.py +0 -0
  106. ethan_agent-0.1.0/ethan/tools/builtin/acp.py +37 -0
  107. ethan_agent-0.1.0/ethan/tools/builtin/file.py +121 -0
  108. ethan_agent-0.1.0/ethan/tools/builtin/knowledge.py +49 -0
  109. ethan_agent-0.1.0/ethan/tools/builtin/memory_write.py +36 -0
  110. ethan_agent-0.1.0/ethan/tools/builtin/procedure_write.py +32 -0
  111. ethan_agent-0.1.0/ethan/tools/builtin/profile_update.py +128 -0
  112. ethan_agent-0.1.0/ethan/tools/builtin/schedule.py +163 -0
  113. ethan_agent-0.1.0/ethan/tools/builtin/search.py +110 -0
  114. ethan_agent-0.1.0/ethan/tools/builtin/shell.py +40 -0
  115. ethan_agent-0.1.0/ethan/tools/builtin/skill_create.py +57 -0
  116. ethan_agent-0.1.0/ethan/tools/builtin/web.py +55 -0
  117. ethan_agent-0.1.0/ethan/tools/builtin/web_search.py +78 -0
  118. ethan_agent-0.1.0/ethan/tools/mcp_client.py +85 -0
  119. ethan_agent-0.1.0/ethan/tools/registry.py +71 -0
  120. ethan_agent-0.1.0/ethan/tools/result_compressor.py +41 -0
  121. ethan_agent-0.1.0/pyproject.toml +68 -0
@@ -0,0 +1,58 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ build/
7
+ dist/
8
+ wheels/
9
+ *.egg-info/
10
+ *.egg
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ env/
15
+
16
+ # IDE
17
+ .idea/
18
+ .vscode/
19
+ *.swp
20
+ *.swo
21
+ *~
22
+
23
+ # OS
24
+ .DS_Store
25
+ Thumbs.db
26
+
27
+ # Environment & secrets
28
+ .env
29
+ .env.local
30
+ .env.production
31
+ !.env.example
32
+
33
+ # Project data (user-specific)
34
+ *.db
35
+
36
+ # Testing
37
+ .coverage
38
+ htmlcov/
39
+ .pytest_cache/
40
+
41
+ # Distribution
42
+ *.tar.gz
43
+ *.whl
44
+
45
+ # Claude Code artifacts
46
+ .agents/
47
+ skills-lock.json
48
+ .claude/
49
+ .run/
50
+ plan/
51
+
52
+ # Local bin scripts
53
+ bin/
54
+
55
+ # Local personal notes
56
+ TODO.md
57
+
58
+ pypi/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ethan Agent Contributors
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,533 @@
1
+ Metadata-Version: 2.4
2
+ Name: ethan-agent
3
+ Version: 0.1.0
4
+ Summary: Ethan — Lightweight personal AI agent with memory, skills, and multi-model support
5
+ Project-URL: Homepage, https://github.com/llm011/ethan-agent
6
+ Project-URL: Repository, https://github.com/llm011/ethan-agent
7
+ Project-URL: Issues, https://github.com/llm011/ethan-agent/issues
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: agent,ai,assistant,claude,gemini,llm,memory,skills
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.12
21
+ Requires-Dist: aiosqlite>=0.22.1
22
+ Requires-Dist: anthropic>=0.109.1
23
+ Requires-Dist: apscheduler>=3.11.2
24
+ Requires-Dist: fastapi>=0.136.3
25
+ Requires-Dist: lark-oapi>=1.6.8
26
+ Requires-Dist: mcp>=1.27.2
27
+ Requires-Dist: openai>=2.41.1
28
+ Requires-Dist: pexpect>=4.9.0
29
+ Requires-Dist: prompt-toolkit>=3.0.52
30
+ Requires-Dist: pydantic-settings>=2.14.1
31
+ Requires-Dist: python-dotenv>=1.0.0
32
+ Requires-Dist: python-multipart>=0.0.32
33
+ Requires-Dist: pyyaml>=6.0.3
34
+ Requires-Dist: rich>=15.0.0
35
+ Requires-Dist: sqlalchemy>=2.0.50
36
+ Requires-Dist: sqlite-vec>=0.1.9
37
+ Requires-Dist: typer>=0.26.7
38
+ Requires-Dist: uvicorn>=0.49.0
39
+ Requires-Dist: uvloop>=0.22.1
40
+ Description-Content-Type: text/markdown
41
+
42
+ # Ethan Agent
43
+
44
+ [中文文档](./README_CN.md)
45
+
46
+ A lightweight, extensible personal AI agent built in Python. Designed to run persistently on your own hardware with memory that grows over time, scheduled tasks, and a pluggable tool/skill system.
47
+
48
+ Ethan combines ideas from [OpenClaw](https://github.com/openclaw/openclaw) (structured agent loop, layered memory), [Hermes Agent](https://github.com/NousResearch/hermes-agent) (self-improving skills, memory consolidation), and [nanobot](https://github.com/HKUDS/nanobot) (minimal core, readable codebase).
49
+
50
+ ---
51
+
52
+ ## Features
53
+
54
+ **Memory system (five layers)**
55
+ - Hot/warm/cold three-tier sliding window for long-conversation context; older content auto-compressed by a cheap model
56
+ - Structured Facts: confidence-scored entries with conflict detection and deduplication (`~/.ethan/memory/facts.json`)
57
+ - Behavioral Procedures: learned from user corrections, loaded every conversation (`procedures.json`)
58
+ - Session Episodes: auto-summarized on exit, supports keyword search (`episodes.json`)
59
+ - User Profile: narrative document storing personal phrases, goals, and agent agreements (`user_profile.md`)
60
+ - **Proactive memory write**: Agent calls tools mid-conversation to instantly persist anything worth remembering — no waiting for batch processing
61
+
62
+ **Skill system**
63
+ - Keyword trigger matching, auto-injected into system prompt
64
+ - `fast_path: true` routes matched input to the millisecond fast track
65
+ - `channels: [lark, web]` filters skills by channel so each surface gets only relevant skills
66
+ - Hit tracking and correction collection; Heartbeat auto-updates skill content with a cheap model when corrections accumulate
67
+ - Agent can create new skills mid-conversation via the `skill_create` tool
68
+
69
+ **Three-track routing**
70
+ - **fast**: short commands + keyword match → minimal prompt + fast_path tools only + 2 iterations
71
+ - **medium**: mid-length messages → full prompt + all tools + 4 iterations
72
+ - **full**: complex tasks → full prompt + all tools + 10 iterations
73
+
74
+ **Scheduler**
75
+ - Create cron or interval jobs in conversation; SQLite-persisted, survives restarts
76
+ - `heartbeat.md`: write natural-language tasks; the system runs them periodically
77
+
78
+ **Tool system**
79
+ - Shell execution, web search (DuckDuckGo), web fetch, file I/O, knowledge base
80
+ - Tool results over 4 000 chars are auto-summarized by a cheap model before going back to the main model
81
+ - Identical calls within the same turn hit an in-memory cache — no duplicate execution
82
+
83
+ **Prompt Caching**
84
+ - System prompt split into stable layer / dynamic layer; stable layer cached 5 min, token cost drops to 0.1×
85
+
86
+ **Multi-channel**
87
+ - CLI REPL, Web UI (Next.js), Lark/Feishu (WebSocket, no public IP required)
88
+
89
+ ---
90
+
91
+ ## Quick Start (Docker, recommended)
92
+
93
+ Docker is the easiest deployment path — backend and Web UI run as separate containers, data persisted to a local volume.
94
+
95
+ ### Prerequisites
96
+
97
+ - Docker 20.10+
98
+ - Docker Compose v2
99
+
100
+ ### 1. Clone
101
+
102
+ ```bash
103
+ git clone https://github.com/llm011/ethan-agent.git
104
+ cd ethan-agent
105
+ ```
106
+
107
+ ### 2. Configure
108
+
109
+ ```bash
110
+ cp deploy/.env.example deploy/.env
111
+ # Edit deploy/.env with your API keys
112
+ ```
113
+
114
+ At least one provider is required:
115
+
116
+ ```bash
117
+ # Anthropic (recommended — supports Prompt Caching)
118
+ ANTHROPIC_API_KEY=sk-ant-xxx
119
+
120
+ # Or any OpenAI-compatible API
121
+ OPENAI_API_KEY=sk-xxx
122
+ OPENAI_BASE_URL=https://api.example.com/v1
123
+
124
+ AGENT_DEFAULT_MODEL=claude-sonnet-4-6
125
+ ```
126
+
127
+ ### 3. Build and start
128
+
129
+ ```bash
130
+ cd deploy
131
+ docker compose up -d --build
132
+ ```
133
+
134
+ First build takes ~3–5 minutes (installing deps + building Next.js).
135
+
136
+ ### 4. Access
137
+
138
+ - **Web UI**: http://localhost:3000
139
+ - **API**: http://localhost:8900
140
+ - **Health check**: http://localhost:8900/health
141
+
142
+ ### 5. Common commands
143
+
144
+ ```bash
145
+ docker compose logs -f ethan # tail logs
146
+ docker compose restart ethan # restart backend
147
+ docker compose down # stop
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Local Development
153
+
154
+ ### Prerequisites
155
+
156
+ - Python 3.12+
157
+ - [uv](https://docs.astral.sh/uv/) package manager
158
+ - Node.js 20+ (Web UI)
159
+
160
+ ### Install
161
+
162
+ ```bash
163
+ git clone https://github.com/llm011/ethan-agent.git
164
+ cd ethan-agent
165
+ uv sync
166
+ ```
167
+
168
+ ### Configure
169
+
170
+ ```bash
171
+ cp .env.example .env
172
+ # Edit .env with your API keys
173
+ ```
174
+
175
+ Or via CLI:
176
+
177
+ ```bash
178
+ ethan provider set anthropic --api-key sk-ant-xxx
179
+ ethan model default claude-sonnet-4-6
180
+ ```
181
+
182
+ ### Run
183
+
184
+ ```bash
185
+ # Interactive REPL
186
+ uv run python -m ethan.interface.cli
187
+
188
+ # Single-turn query
189
+ uv run python -m ethan.interface.cli -p "What's the weather in Tokyo?"
190
+
191
+ # Specify model
192
+ uv run python -m ethan.interface.cli -m claude-sonnet-4-6
193
+
194
+ # Resume last session
195
+ uv run python -m ethan.interface.cli -r last
196
+
197
+ # Start HTTP API server
198
+ uv run python -m ethan.interface.cli serve
199
+ ```
200
+
201
+ ### Install globally (optional)
202
+
203
+ ```bash
204
+ chmod +x bin/ethan
205
+ ln -s $(pwd)/bin/ethan ~/bin/ethan
206
+ ```
207
+
208
+ ### Web UI (dev mode)
209
+
210
+ ```bash
211
+ cd web
212
+ npm install
213
+ npm run dev # http://localhost:3000
214
+ ```
215
+
216
+ ### macOS auto-start (launchd)
217
+
218
+ ```bash
219
+ ./deploy/install.sh
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Architecture
225
+
226
+ ```
227
+ ethan/
228
+ ├── core/
229
+ │ ├── agent.py # ReAct loop, three-track router (fast/medium/full)
230
+ │ ├── config.py # YAML config (~/.ethan/config.yaml)
231
+ │ └── heartbeat.py # Heartbeat system, periodic maintenance
232
+ ├── providers/
233
+ │ ├── base.py # Unified interface (Message, ToolCall, BaseProvider)
234
+ │ ├── anthropic.py # Claude native protocol + Prompt Caching
235
+ │ ├── openai_compat.py # OpenAI-compatible protocol
236
+ │ └── manager.py # Route model ID → provider
237
+ ├── memory/
238
+ │ ├── session.py # Session persistence (SQLite)
239
+ │ ├── working.py # Three-tier sliding window memory
240
+ │ ├── facts.py # Structured Facts (conflict detection + confidence)
241
+ │ ├── procedures.py # Behavioral rules (learned from corrections)
242
+ │ ├── episodic.py # Session episode archive
243
+ │ └── consolidator.py # Compress with cheap model
244
+ ├── skills/
245
+ │ ├── loader.py # Load skills (directory format + legacy .md)
246
+ │ ├── registry.py # Match (with channel filter) + hit stats
247
+ │ ├── stats.py # Hit count + correction collection
248
+ │ ├── updater.py # Auto-update skill content via cheap model
249
+ │ └── generator.py # Auto-generate skills from sessions
250
+ ├── tools/
251
+ │ ├── base.py # BaseTool abstract class
252
+ │ ├── registry.py # Registry + concurrent executor + turn cache
253
+ │ ├── result_compressor.py # Auto-summarize long tool output
254
+ │ └── builtin/
255
+ │ ├── shell.py # Execute shell commands
256
+ │ ├── web_search.py # DuckDuckGo search
257
+ │ ├── web.py # Fetch & extract web page text
258
+ │ ├── file.py # File read/write/list
259
+ │ ├── memory_write.py # Proactive fact write
260
+ │ ├── procedure_write.py # Proactive procedure write
261
+ │ ├── profile_update.py # Update user profile
262
+ │ └── skill_create.py # Create skill mid-conversation
263
+ ├── scheduler/
264
+ │ └── cron.py # APScheduler with SQLite persistence
265
+ └── interface/
266
+ ├── cli.py # Typer CLI entry point
267
+ ├── repl.py # Interactive REPL with prompt_toolkit
268
+ ├── api.py # FastAPI HTTP + SSE streaming
269
+ ├── lark_events.py # Lark WebSocket
270
+ └── commands/ # Subcommands (model, provider, session, skill, schedule)
271
+ ```
272
+
273
+ ---
274
+
275
+ ## Memory System
276
+
277
+ Ethan uses a five-layer memory architecture:
278
+
279
+ | Layer | Content | Storage |
280
+ |-------|---------|---------|
281
+ | Hot | Last N turns (full messages) | In-memory |
282
+ | Warm | Rolling summary of older turns | In-memory |
283
+ | Cold (Facts) | Key facts extracted across sessions | `~/.ethan/memory/facts.json` |
284
+ | Procedures | Behavioral rules learned from corrections | `~/.ethan/memory/procedures.json` |
285
+ | User Profile | Narrative personal context (goals, phrases, agreements) | `~/.ethan/memory/user_profile.md` |
286
+
287
+ Compression is **batched** (not per-turn) and uses an automatically inferred cheap model (e.g. Haiku for Claude users, Flash Lite for Gemini users).
288
+
289
+ Agent proactively writes to all layers mid-conversation via `memory_write`, `procedure_write`, and `profile_update` tools — no waiting for the next compression cycle.
290
+
291
+ ---
292
+
293
+ ## Skills
294
+
295
+ Skills are Markdown files loaded from two sources, in priority order:
296
+
297
+ 1. **Built-in skills** — `ethan/skills/<name>/SKILL.md` (shipped with the project)
298
+ 2. **User skills** — `~/.ethan/skills/<name>/SKILL.md` or `~/.ethan/skills/<name>.md`
299
+
300
+ Both support a directory format (`<name>/SKILL.md` + `references/`) and the legacy single-file `.md` format.
301
+
302
+ ```markdown
303
+ ---
304
+ name: deploy-checklist
305
+ trigger: deploy|ship|release
306
+ description: Pre-deployment checklist
307
+ fast_path: true # route to fast track when triggered
308
+ channels: # empty = all channels; list = restrict
309
+ - web
310
+ version: "1.0"
311
+ ---
312
+
313
+ Steps before deploying:
314
+ 1. Run tests
315
+ 2. Check for uncommitted changes
316
+ 3. ...
317
+ ```
318
+
319
+ When a user message matches a skill's `trigger`, the skill content is injected into the system prompt. Built-in skills include `channels`, `lark-im`, and `home-assistant`.
320
+
321
+ Skills accumulate hit stats and user corrections. When corrections reach a threshold (default: 2), the Heartbeat job merges them into the skill file using a cheap model.
322
+
323
+ ---
324
+
325
+ ## Tools
326
+
327
+ Tools are pluggable — add a new one without touching the agent loop:
328
+
329
+ ```python
330
+ from ethan.tools.base import BaseTool
331
+
332
+ class MyTool(BaseTool):
333
+ name = "my_tool"
334
+ description = "Does something useful"
335
+ fast_path = False # set True to make available in fast-track mode
336
+ cacheable = False # set True to cache identical calls within a turn
337
+ parameters = {"type": "object", "properties": {...}, "required": [...]}
338
+
339
+ async def run(self, **kwargs) -> str:
340
+ return "result"
341
+ ```
342
+
343
+ Register it in `cli.py` and the LLM will automatically use it when relevant.
344
+
345
+ ---
346
+
347
+ ## CLI Commands
348
+
349
+ ```
350
+ ethan Start interactive REPL
351
+ ethan -p "..." Single-turn query
352
+ ethan -m MODEL Use specific model
353
+ ethan -r last Resume last session
354
+ ethan serve Start HTTP API server
355
+
356
+ ethan model list|add|remove|default
357
+ ethan provider list|set
358
+ ethan session list|show|delete
359
+ ethan skill list|show|create
360
+ ethan schedule list|remove|pause|resume
361
+ ```
362
+
363
+ ---
364
+
365
+ ## HTTP API
366
+
367
+ ```bash
368
+ GET /health # Health check
369
+ GET /models # Available models
370
+ POST /chat # Chat (stream: true for SSE)
371
+ GET /sessions # Session list
372
+ GET /sessions/{id} # Session detail + messages
373
+ GET /memory/facts # Facts list
374
+ GET /memory/episodes # Episode summaries
375
+ GET /skills # Skill list
376
+ POST /skills # Create skill
377
+ POST /skills/evolve # Trigger skill auto-update
378
+ GET /schedule # Scheduled jobs
379
+ GET /system-prompt-preview # Current system prompt preview
380
+ GET /channels # Channel list
381
+ GET /knowledge/search # Semantic search
382
+ ```
383
+
384
+ ---
385
+
386
+ ## Configuration
387
+
388
+ All config lives in `~/.ethan/config.yaml`:
389
+
390
+ ```yaml
391
+ providers:
392
+ anthropic:
393
+ api_key: sk-ant-xxx
394
+ base_url: https://api.anthropic.com # optional
395
+ proxy: null # per-provider proxy
396
+ openai_compat:
397
+ api_key: sk-xxx
398
+ base_url: https://api.openai.com/v1
399
+
400
+ models:
401
+ - id: claude-sonnet-4-6
402
+ provider: anthropic
403
+ description: Claude Sonnet 4.6
404
+ alias: [sonnet]
405
+ - id: gpt-4o
406
+ provider: openai_compat
407
+ alias: [gpt]
408
+
409
+ network:
410
+ proxy: http://127.0.0.1:7890 # global proxy
411
+
412
+ defaults:
413
+ model: claude-sonnet-4-6
414
+ agent_name: Ethan
415
+ max_tokens: 4096
416
+ max_tool_iterations: 10
417
+ routing:
418
+ fast_max_length: 12
419
+ medium_max_length: 80
420
+ medium_max_iters: 4
421
+ fast_keywords:
422
+ - "turn off*light"
423
+ - "play music"
424
+ fast_skill_triggers:
425
+ - "home assistant"
426
+ ```
427
+
428
+ Environment variables in `.env` override config values (useful for secrets).
429
+
430
+ ### Config directory layout
431
+
432
+ ```
433
+ ~/.ethan/
434
+ ├── config.yaml # Main config (providers, models, routing)
435
+ ├── system/
436
+ │ ├── identity.md # Agent identity (name, role)
437
+ │ ├── soul.md # Behavioral principles
438
+ │ └── heartbeat.md # Heartbeat tasks (natural language)
439
+ ├── memory/
440
+ │ ├── facts.json # Structured facts
441
+ │ ├── procedures.json # Behavioral rules
442
+ │ ├── episodes.json # Session episode archive
443
+ │ └── user_profile.md # User profile (narrative)
444
+ ├── skills/ # User-defined skills
445
+ │ └── <name>/
446
+ │ └── SKILL.md
447
+ └── sessions.db # Session history (SQLite)
448
+ ```
449
+
450
+ ---
451
+
452
+ ## Roadmap
453
+
454
+ ### ✅ Completed
455
+
456
+ **Core Agent**
457
+ - [x] Multi-model provider (Anthropic + OpenAI-compatible: Gemini, GPT, Ollama, etc.)
458
+ - [x] ReAct agent loop with streaming output
459
+ - [x] Three-track router: fast / medium / full, tool result compression, per-turn dedup cache
460
+ - [x] Prompt Caching (Anthropic stable-prefix cache_control, ~0.1× input cost)
461
+
462
+ **Five-Layer Memory**
463
+ - [x] Hot/warm/cold sliding window + cheap-model batch compression
464
+ - [x] Structured Facts (confidence scoring + conflict detection)
465
+ - [x] Behavioral Procedures (learned from user corrections)
466
+ - [x] Session Episode archive (auto-summary, keyword search)
467
+ - [x] User Profile — narrative document with five named sections
468
+ - [x] Proactive memory write: `memory_write`, `procedure_write`, `profile_update`, `skill_create`
469
+ - [x] Memory context isolation (anti-pollution XML tags)
470
+
471
+ **Skill System**
472
+ - [x] Dual-source loading (built-in + user-defined) + channel filter (`channels` field)
473
+ - [x] `fast_path` opt-in, hit stats, correction collection, auto-update (Updater)
474
+ - [x] Session-end background Skill generation (Hermes-style)
475
+ - [x] Built-in skills: home-assistant, lark-im, channels, deepwiki
476
+
477
+ **Tools**
478
+ - [x] shell, web_search, web_fetch, file_read/write/list, rg, fd
479
+ - [x] Knowledge base (sqlite-vec semantic search), scheduler tools, ACP → Claude Code
480
+
481
+ **Scheduler**
482
+ - [x] Cron + interval, SQLite persistence, auto-restore on restart
483
+ - [x] `heartbeat.md`: natural-language periodic tasks executed automatically
484
+
485
+ **Channels & API**
486
+ - [x] Web UI (Next.js): chat timeline, memory, skills, schedule, knowledge, settings
487
+ - [x] Tool call timeline (collapsible, with icons + duration)
488
+ - [x] Feishu/Lark WebSocket (no public IP required)
489
+ - [x] OpenAI-compatible Completions API (`/v1/chat/completions`) + API key management
490
+ - [x] Docker deployment + macOS launchd auto-start
491
+
492
+ ---
493
+
494
+ ### 🚀 Planned
495
+
496
+ **UX Improvements**
497
+ - [ ] **Message quoting**: quote a previous message in the input box
498
+ - [ ] **User profile settings**: avatar upload, display name shown in chat bubbles
499
+ - [ ] **Scheduler suggestions**: Agent detects scheduling opportunities in conversation and prompts user with 1-2-3 options
500
+ - [ ] **Scheduler templates**: ready-to-use tasks (daily briefing, HA device check, knowledge digest)
501
+
502
+ **Channel Expansion**
503
+ - [ ] **WeCom (Enterprise WeChat)**: alongside Feishu as a second messaging channel
504
+ - [ ] **Mobile UI**: bottom tab nav, touch gestures, keyboard inset handling
505
+
506
+ **Coding Agent Integration**
507
+ - [ ] **ACP multi-turn optimization**: smoother Claude Code / OpenCode / Codex sessions with collapsible tool traces
508
+ - [ ] **MCP client**: connect external MCP servers, auto-register tools
509
+
510
+ **Long-term**
511
+ - [ ] **Space isolation**: separate memory/skills per context (life / work / project)
512
+ - [ ] **Async interrupt**: detect new messages during long tasks, respond between tool calls
513
+ - [ ] **Obsidian integration**: read/write Obsidian vault as knowledge base
514
+
515
+ ---
516
+
517
+ ## Documentation
518
+
519
+ Detailed design docs for each module are in [`docs/`](./docs/):
520
+
521
+ - [Architecture Overview](docs/architecture.md)
522
+ - [Agent Loop](docs/agent-loop.md)
523
+ - [Routing](docs/routing.md)
524
+ - [Provider Layer](docs/providers.md)
525
+ - [Tool System](docs/tools.md)
526
+ - [Memory System](docs/memory.md)
527
+ - [Skill System](docs/skills.md)
528
+ - [Scheduler](docs/scheduler.md)
529
+ - [Interface Layer](docs/interface.md)
530
+
531
+ ## License
532
+
533
+ MIT