hermes - 💡(How to fix) Fix [i18n] Thai Translation: Developer Guide Part c - environments, extending-the-cli, gateway-internals, memory-provider-plugin, prompt-assembly [1 participants]
ON THIS PAGE
Recommended Tools
×6Utilities matched from this issue’s tags and category — try them while you read without losing context.
GitHub issue graph ai analysis
Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.
The report is written in English Markdown for sharing and archival.
Helpful · Quick feedback
Error Message
def sync_turn(self, user_content, assistant_content): def _sync(): try: self._api.ingest(user_content, assistant_content) except Exception as e: logger.warning("Sync failed: %s", e)
if self._sync_thread and self._sync_thread.is_alive():
self._sync_thread.join(timeout=5.0)
self._sync_thread = threading.Thread(target=_sync, daemon=True)
self._sync_thread.start()Fix Action
Fix / Workaround
เป็น layer ของ hermes-agent (environments/hermes_base_env.py) เพิ่มเติม:
- Terminal backend configuration - กำหนด
TERMINAL_ENVสำหรับการรันแบบ sandboxed (local, Docker, Modal, Daytona, SSH, Singularity) - Tool resolution -
_resolve_tools_for_group()เรียกใช้get_tool_definitions()ของ hermes-agent เพื่อรับ tool schemas ที่ถูกต้องตาม toolsets ที่เปิด/ปิดใช้งาน - Agent loop integration -
collect_trajectory()รันHermesAgentLoopและให้คะแนนผลลัพธ์ - Two-phase operation - Phase 1 (OpenAI server) สำหรับ eval/SFT, Phase 2 (VLLM ManagedServer) สำหรับ full RL พร้อม logprobs
- Async safety patches - monkey-patches Modal backend เพื่อให้ทำงานภายใน event loop ของ Atropos
- ส่ง messages + tool schemas ไปยัง API ผ่าน
server.chat_completion() - หาก response มี
tool_callsให้ dispatch แต่ละตัวผ่านhandle_function_call() - แนบ tool results เข้าไปใน conversation, กลับไปที่ขั้นตอน 1
- หากไม่มี
tool_calls, agent จะเสร็จสิ้น
@dataclass
class AgentResult:
messages: List[Dict[str, Any]] # Full conversation history
turns_used: int # Number of LLM calls made
finished_naturally: bool # True if model stopped on its own
reasoning_per_turn: List[Optional[str]] # Extracted reasoning content
tool_errors: List[ToolError] # Errors encountered during tool dispatch
managed_state: Optional[Dict] # VLLM ManagedServer state (Phase 2)Code Example
classDiagram
class BaseEnv {
Server management
Worker scheduling
Wandb logging
CLI: serve / process / evaluate
}
class HermesAgentBaseEnv {
Terminal backend configuration
Tool resolution
Agent loop engine
ToolContext access
}
class TerminalTestEnv {
Stack testing
}
class HermesSweEnv {
SWE training
}
class TerminalBench2EvalEnv {
Benchmark evaluation
}
class TBLiteEvalEnv {
Fast benchmark
}
class YCBenchEvalEnv {
Long-horizon benchmark
}
BaseEnv <|-- HermesAgentBaseEnv
HermesAgentBaseEnv <|-- TerminalTestEnv
HermesAgentBaseEnv <|-- HermesSweEnv
HermesAgentBaseEnv <|-- TerminalBench2EvalEnv
TerminalBench2EvalEnv <|-- TBLiteEvalEnv
TerminalBench2EvalEnv <|-- YCBenchEvalEnv
---
@dataclass
class AgentResult:
messages: List[Dict[str, Any]] # Full conversation history
turns_used: int # Number of LLM calls made
finished_naturally: bool # True if model stopped on its own
reasoning_per_turn: List[Optional[str]] # Extracted reasoning content
tool_errors: List[ToolError] # Errors encountered during tool dispatch
managed_state: Optional[Dict] # VLLM ManagedServer state (Phase 2)
---
async def compute_reward(self, item, result, ctx: ToolContext):
# Run tests in the model's terminal sandbox
test = ctx.terminal("pytest -v")
if test["exit_code"] == 0:
return 1.0
# Check if a file was created
content = ctx.read_file("/workspace/solution.py")
if content.get("content"):
return 0.5
# Download files for local verification
ctx.download_file("/remote/output.bin", "/local/output.bin")
return 0.0
---
from environments.tool_call_parsers import get_parser
parser = get_parser("hermes") # or "mistral", "llama3_json", "qwen", "deepseek_v3", etc.
content, tool_calls = parser.parse(raw_model_output)
---
python environments/benchmarks/terminalbench_2/terminalbench2_env.py evaluate \
--config environments/benchmarks/terminalbench_2/default.yaml
# Run specific tasks
python environments/benchmarks/terminalbench_2/terminalbench2_env.py evaluate \
--config environments/benchmarks/terminalbench_2/default.yaml \
--env.task_filter fix-git,git-multibranch
---
python environments/benchmarks/tblite/tblite_env.py evaluate \
--config environments/benchmarks/tblite/default.yaml
---
# ติดตั้ง yc-bench (optional dependency)
pip install "hermes-agent[yc-bench]"
# Run evaluation
bash environments/benchmarks/yc_bench/run_eval.sh
# หรือโดยตรง
python environments/benchmarks/yc_bench/yc_bench_env.py evaluate \
--config environments/benchmarks/yc_bench/default.yaml
# Quick single-preset test
python environments/benchmarks/yc_bench/yc_bench_env.py evaluate \
--config environments/benchmarks/yc_bench/default.yaml \
--env.presets '["fast_test"]' --env.seeds '[1]'
---
# Process mode (บันทึก rollouts เป็น JSONL, ไม่ต้องใช้ training server)
python environments/terminal_test_env/terminal_test_env.py process \
--env.data_path_to_save_groups terminal_test_output.jsonl
# Serve mode (เชื่อมต่อกับ Atropos API สำหรับ RL training)
python environments/terminal_test_env/terminal_test_env.py serve
---
python environments/hermes_swe_env/hermes_swe_env.py serve \
--openai.model_name YourModel \
--env.dataset_name bigcode/humanevalpack \
--env.terminal_backend modal
---
python environments/benchmarks/tblite/tblite_env.py evaluate \
--config environments/benchmarks/tblite/default.yaml \
--openai.model_name anthropic/claude-sonnet-4.6
---
python environments/terminal_test_env/terminal_test_env.py process \
--env.data_path_to_save_groups output.jsonl \
--openai.model_name anthropic/claude-sonnet-4.6
---
# Terminal 1: Start the Atropos API
run-api
# Terminal 2: Start the environment
python environments/hermes_swe_env/hermes_swe_env.py serve \
--openai.model_name YourModel
---
from environments.hermes_base_env import HermesAgentBaseEnv, HermesAgentEnvConfig
from atroposlib.envs.server_handling.server_manager import APIServerConfig
class MyEnvConfig(HermesAgentEnvConfig):
my_custom_field: str = "default_value"
class MyEnv(HermesAgentBaseEnv):
name = "my-env"
env_config_cls = MyEnvConfig
@classmethod
def config_init(cls):
env_config = MyEnvConfig(
enabled_toolsets=["terminal", "file"],
terminal_backend="modal",
max_agent_turns=30,
)
server_configs = [APIServerConfig(
base_url="https://openrouter.ai/api/v1",
model_name="anthropic/claude-sonnet-4.6",
server_type="openai",
)]
return env_config, server_configs
async def setup(self):
from datasets import load_dataset
self.dataset = list(load_dataset("my-dataset", split="train"))
self.iter = 0
async def get_next_item(self):
item = self.dataset[self.iter % len(self.dataset)]
self.iter += 1
return item
def format_prompt(self, item):
return item["instruction"]
async def compute_reward(self, item, result, ctx):
# ctx gives full tool access to the rollout's sandbox
test = ctx.terminal("pytest -v")
return 1.0 if test["exit_code"] == 0 else 0.0
async def evaluate(self, *args, **kwargs):
# Periodic evaluation during training
pass
if __name__ == "__main__":
MyEnv.cli()
---
env:
enabled_toolsets: ["terminal", "file"]
max_agent_turns: 60
max_token_length: 32000
agent_temperature: 0.8
terminal_backend: "modal"
terminal_timeout: 300
dataset_name: "NousResearch/terminal-bench-2"
tokenizer_name: "NousResearch/Hermes-3-Llama-3.1-8B"
use_wandb: true
wandb_name: "my-benchmark"
openai:
base_url: "https://openrouter.ai/api/v1"
model_name: "anthropic/claude-sonnet-4.6"
server_type: "openai"
health_check: false
---
python my_env.py evaluate \
--config my_config.yaml \
--openai.model_name anthropic/claude-opus-4.6 # overrides YAML
---
environments/
├── hermes_base_env.py # Abstract base class (HermesAgentBaseEnv)
├── agent_loop.py # Multi-turn agent engine (HermesAgentLoop)
├── tool_context.py # Per-rollout tool access for reward functions
├── patches.py # Async-safety patches for Modal backend
│
├── tool_call_parsers/ # Phase 2 client-side parsers
│ ├── hermes_parser.py # Hermes/ChatML <tool_call> format
│ ├── mistral_parser.py # Mistral [TOOL_CALLS] format
│ ├── llama_parser.py # Llama 3 JSON tool calling
│ ├── qwen_parser.py # Qwen format
│ ├── deepseek_v3_parser.py # DeepSeek V3 format
│ └── ... # + kimi_k2, longcat, glm45/47, etc.
│
├── terminal_test_env/ # Stack validation (inline tasks)
├── hermes_swe_env/ # SWE-bench training environment
│
└── benchmarks/ # Evaluation benchmarks
├── terminalbench_2/ # 89 terminal tasks, Modal sandboxes
├── tblite/ # 100 calibrated tasks (fast TB2 proxy)
└── yc_bench/ # Long-horizon strategic benchmark
---
#!/usr/bin/env python3
"""my_cli.py - ตัวอย่าง wrapper CLI ที่ขยาย Hermes."""
from cli import HermesCLI
from prompt_toolkit.layout import FormattedTextControl, Window
from prompt_toolkit.filters import Condition
class MyCLI(HermesCLI):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._panel_visible = False
def _get_extra_tui_widgets(self):
"""เพิ่ม info panel แบบสลับได้เหนือ status bar."""
cli_ref = self
return [
Window(
FormattedTextControl(lambda: "📊 My custom panel content"),
height=1,
filter=Condition(lambda: cli_ref._panel_visible),
),
]
def _register_extra_tui_keybindings(self, kb, *, input_area):
"""F2 สลับ panel ที่กำหนดเอง."""
cli_ref = self
@kb.add("f2")
def _toggle_panel(event):
cli_ref._panel_visible = not cli_ref._panel_visible
def process_command(self, cmd: str) -> bool:
"""เพิ่ม slash command /panel."""
if cmd.strip().lower() == "/panel":
self._panel_visible = not self._panel_visible
state = "visible" if self._panel_visible else "hidden"
print(f"Panel is now {state}")
return True
return super().process_command(cmd)
if __name__ == "__main__":
cli = MyCLI()
cli.run()
---
cd ~/.hermes/hermes-agent
source .venv/bin/activate
python my_cli.py
---
def _get_extra_tui_widgets(self) -> list:
return [] # default: no extra widgets
---
from prompt_toolkit.layout import ConditionalContainer, Window, FormattedTextControl
from prompt_toolkit.filters import Condition
def _get_extra_tui_widgets(self):
return [
ConditionalContainer(
Window(FormattedTextControl("Status: connected"), height=1),
filter=Condition(lambda: self._show_status),
),
]
---
def _register_extra_tui_keybindings(self, kb, *, input_area):
pass # default: no extra keybindings
---
def _register_extra_tui_keybindings(self, kb, *, input_area):
cli_ref = self
@kb.add("f3")
def _clear_input(event):
input_area.text = ""
@kb.add("f4")
def _insert_template(event):
input_area.text = "/search "
---
def _build_tui_layout_children(self, *, sudo_widget, secret_widget,
approval_widget, clarify_widget, spinner_widget, spacer,
status_bar, input_rule_top, image_bar, input_area,
input_rule_bot, voice_status_bar, completions_menu) -> list:
---
[
Window(height=0), # anchor
sudo_widget, # sudo password prompt (conditional)
secret_widget, # secret input prompt (conditional)
approval_widget, # dangerous command approval (conditional)
clarify_widget, # clarify question UI (conditional)
spinner_widget, # thinking spinner (conditional)
spacer, # fills remaining vertical space
*self._get_extra_tui_widgets(), # YOUR WIDGETS GO HERE
status_bar, # model/token/context status line
input_rule_top, # ─── border above input
image_bar, # attached images indicator
input_area, # user text input
input_rule_bot, # ─── border below input
voice_status_bar, # voice mode status (conditional)
completions_menu, # autocomplete dropdown
]
---
┌─────────────────────────────────────────────────┐
│ GatewayRunner │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Telegram │ │ Discord │ │ Slack │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ ▼ │
│ _handle_message() │
│ │ │
│ ┌───────────┼───────────┐ │
│ ▼ ▼ ▼ │
│ Slash command AIAgent Queue/BG │
│ dispatch creation sessions │
│ │ │
│ ▼ │
│ SessionStore │
│ (SQLite persistence) │
└─────────────────────────────────────────────────┘
---
agent:main:{platform}:{chat_type}:{chat_id}
---
Admin: /pair
Gateway: "Pairing code: ABC123. Share with the user."
New user: ABC123
Gateway: "Paired! You're now authorized."
---
if _quick_key in self._running_agents:
if canonical == "model":
return "⏳ Agent is running - wait for it to finish or /stop first."
---
gateway/platforms/
├── base.py # BaseAdapter - shared logic for all platforms
├── telegram.py # Telegram Bot API (long polling or webhook)
├── discord.py # Discord bot via discord.py
├── slack.py # Slack Socket Mode
├── whatsapp.py # WhatsApp Business Cloud API
├── signal.py # Signal via signal-cli REST API
├── matrix.py # Matrix via mautrix (optional E2EE)
├── mattermost.py # Mattermost WebSocket API
├── email.py # Email via IMAP/SMTP
├── sms.py # SMS via Twilio
├── dingtalk.py # DingTalk WebSocket
├── feishu.py # Feishu/Lark WebSocket or webhook
├── wecom.py # WeCom (WeChat Work) callback
├── weixin.py # Weixin (personal WeChat) via iLink Bot API
├── bluebubbles.py # Apple iMessage via BlueBubbles macOS server
├── qqbot.py # QQ Bot (Tencent QQ) via Official API v2
├── webhook.py # Inbound/outbound webhook adapter
├── api_server.py # REST API server adapter
└── homeassistant.py # Home Assistant conversation integration
---
AIAgent._invoke_tool()
→ self._memory_manager.handle_tool_call(name, args)
→ provider.handle_tool_call(name, args)
---
plugins/memory/my-provider/
├── __init__.py # MemoryProvider implementation + register() entry point
├── plugin.yaml # Metadata (name, description, hooks)
└── README.md # Setup instructions, config reference, tools
---
from agent.memory_provider import MemoryProvider
class MyMemoryProvider(MemoryProvider):
@property
def name(self) -> str:
return "my-provider"
def is_available(self) -> bool:
"""Check if this provider can activate. NO network calls."""
return bool(os.environ.get("MY_API_KEY"))
def initialize(self, session_id: str, **kwargs) -> None:
"""Called once at agent startup.
kwargs always includes:
hermes_home (str): Active HERMES_HOME path. Use for storage.
"""
self._api_key = os.environ.get("MY_API_KEY", "")
self._session_id = session_id
# ... implement remaining methods
---
def get_config_schema(self):
return [
{
"key": "api_key",
"description": "My Provider API key",
"secret": True, # → written to .env
"required": True,
"env_var": "MY_API_KEY", # explicit env var name
"url": "https://my-provider.com/keys", # where to get it
},
{
"key": "region",
"description": "Server region",
"default": "us-east",
"choices": ["us-east", "eu-west", "ap-south"],
},
{
"key": "project",
"description": "Project identifier",
"default": "hermes",
},
]
---
def save_config(self, values: dict, hermes_home: str) -> None:
"""Write non-secret config to your native location."""
import json
from pathlib import Path
config_path = Path(hermes_home) / "my-provider.json"
config_path.write_text(json.dumps(values, indent=2))
---
def register(ctx) -> None:
"""Called by the memory plugin discovery system."""
ctx.register_memory_provider(MyMemoryProvider())
---
name: my-provider
version: 1.0.0
description: "Short description of what this provider does."
hooks:
- on_session_end # list hooks you implement
---
def sync_turn(self, user_content, assistant_content):
def _sync():
try:
self._api.ingest(user_content, assistant_content)
except Exception as e:
logger.warning("Sync failed: %s", e)
if self._sync_thread and self._sync_thread.is_alive():
self._sync_thread.join(timeout=5.0)
self._sync_thread = threading.Thread(target=_sync, daemon=True)
self._sync_thread.start()
---
# CORRECT - profile-scoped
from hermes_constants import get_hermes_home
data_dir = get_hermes_home() / "my-provider"
# WRONG - shared across all profiles
data_dir = Path("~/.hermes/my-provider").expanduser()
---
from agent.memory_manager import MemoryManager
mgr = MemoryManager()
mgr.add_provider(my_provider)
mgr.initialize_all(session_id="test-1", platform="cli")
# Test tool routing
result = mgr.handle_tool_call("my_tool", {"action": "add", "content": "test"})
# Test lifecycle
mgr.sync_all("user msg", "assistant msg")
mgr.on_session_end([])
mgr.shutdown_all()
---
# plugins/memory/my-provider/cli.py
def my_command(args):
"""Handler dispatched by argparse."""
sub = getattr(args, "my_command", None)
if sub == "status":
print("Provider is active and connected.")
elif sub == "config":
print("Showing config...")
else:
print("Usage: hermes my-provider <status|config>")
def register_cli(subparser) -> None:
"""Build the hermes my-provider argparse tree.
Called by discover_plugin_cli_commands() at argparse setup time.
"""
subs = subparser.add_subparsers(dest="my_command")
subs.add_parser("status", help="Show provider status")
subs.add_parser("config", help="Show provider config")
subparser.set_defaults(func=my_command)
---
plugins/memory/my-provider/
├── __init__.py # MemoryProvider implementation + register()
├── plugin.yaml # Metadata
├── cli.py # register_cli(subparser) - CLI commands
└── README.md # Setup instructions
---
# Layer 1: Agent Identity (from ~/.hermes/SOUL.md)
You are Hermes, an AI assistant created by Nous Research.
You are an expert software engineer and researcher.
You value correctness, clarity, and efficiency.
...
# Layer 2: Tool-aware behavior guidance
You have persistent memory across sessions. Save durable facts using
the memory tool: user preferences, environment details, tool quirks,
and stable conventions. Memory is injected into every turn, so keep
it compact and focused on facts that will still matter later.
...
When the user references something from a past conversation or you
suspect relevant cross-session context exists, use session_search
to recall it before asking them to repeat themselves.
# Tool-use enforcement (for GPT/Codex models only)
You MUST use your tools to take action - do not describe what you
would do or plan to do without actually doing it.
...
# Layer 3: Honcho static block (when active)
[Honcho personality/context data]
# Layer 4: Optional system message (from config or API)
[User-configured system message override]
# Layer 5: Frozen MEMORY snapshot
## Persistent Memory
- User prefers Python 3.12, uses pyproject.toml
- Default editor is nvim
- Working on project "atlas" in ~/code/atlas
- Timezone: US/Pacific
# Layer 6: Frozen USER profile snapshot
## User Profile
- Name: Alice
- GitHub: alice-dev
# Layer 7: Skills index
## Skills (mandatory)
Before replying, scan the skills below. If one clearly matches
your task, load it with skill_view(name) and follow its instructions.
...
<available_skills>
software-development:
- code-review: Structured code review workflow
- test-driven-development: TDD methodology
research:
- arxiv: Search and summarize arXiv papers
</available_skills>
# Layer 8: Context files (from project directory)
# Project Context
The following project context files have been loaded and should be followed:
## AGENTS.md
This is the atlas project. Use pytest for testing. The main
entry point is src/atlas/main.py. Always run `make lint` before
committing.
# Layer 9: Timestamp + session
Current time: 2026-03-30T14:30:00-07:00
Session: abc123
# Layer 10: Platform hint
You are a CLI AI Agent. Try not to use markdown but simple text
renderable inside a terminal.
---
# From agent/prompt_builder.py (simplified)
def load_soul_md() -> Optional[str]:
soul_path = get_hermes_home() / "SOUL.md"
if not soul_path.exists():
return None
content = soul_path.read_text(encoding="utf-8").strip()
content = _scan_context_content(content, "SOUL.md") # Security scan
content = _truncate_content(content, "SOUL.md") # Cap at 20k chars
return content
---
You are Hermes Agent, an intelligent AI assistant created by Nous Research.
You are helpful, knowledgeable, and direct. You assist users with a wide
range of tasks including answering questions, writing and editing code,
analyzing information, creative work, and executing actions via your tools.
You communicate clearly, admit uncertainty when appropriate, and prioritize
being genuinely useful over being verbose unless otherwise directed below.
Be targeted and efficient in your exploration and investigations.
---
# From agent/prompt_builder.py (simplified)
def build_context_files_prompt(cwd=None, skip_soul=False):
cwd_path = Path(cwd).resolve()
# Priority: first match wins - only ONE project context loaded
project_context = (
_load_hermes_md(cwd_path) # 1. .hermes.md / HERMES.md (walks to git root)
or _load_agents_md(cwd_path) # 2. AGENTS.md (cwd only)
or _load_claude_md(cwd_path) # 3. CLAUDE.md (cwd only)
or _load_cursorrules(cwd_path) # 4. .cursorrules / .cursor/rules/*.mdc
)
sections = []
if project_context:
sections.append(project_context)
# SOUL.md from HERMES_HOME (independent of project context)
if not skip_soul:
soul_content = load_soul_md()
if soul_content:
sections.append(soul_content)
if not sections:
return ""
return (
"# Project Context\n\n"
"The following project context files have been loaded "
"and should be followed:\n\n"
+ "\n".join(sections)
)RAW_BUFFERClick to expand / collapse
📄 developer-guide/environments.md
sidebar_position: 5 title: "Environments, Benchmarks & Data Generation" description: "การสร้างสภาพแวดล้อมสำหรับการฝึก RL, การรัน evaluation benchmarks, และการสร้าง SFT data ด้วยการรวมระบบ Hermes-Agent Atropos"
Environments, Benchmarks & Data Generation
Hermes Agent มี full environment framework ที่เชื่อมโยง tool-calling capabilities เข้ากับ Atropos RL training framework สิ่งนี้ช่วยให้สามารถทำงานได้สามรูปแบบหลัก:
- RL Training - ฝึก language models บน multi-turn agentic tasks ด้วย GRPO
- Benchmarks - ประเมิน models บน standardized agentic benchmarks
- Data Generation - สร้าง SFT training data จาก agent rollouts
ทั้งสามส่วนนี้ใช้ core เดียวกันคือ environment class ที่ทำหน้าที่กำหนด tasks, รัน agent loop, และให้คะแนน output
:::info Repo environments vs RL training tools
Python environment framework ที่ระบุไว้ในเอกสารนี้อยู่ใน directory environments/ ของ repo และเป็น API ระดับ implementation สำหรับการรวมระบบ Hermes/Atropos ซึ่งแยกต่างหากจาก rl_* tools ที่ผู้ใช้ใช้งาน ซึ่งทำหน้าที่เป็น orchestration surface สำหรับ workflow การฝึก RL ระยะไกล
:::
:::tip Quick Links
- ต้องการรัน benchmarks? ข้ามไปที่ Available Benchmarks
- ต้องการฝึกด้วย RL? ดู RL Training Tools สำหรับ agent-driven interface หรือ Running Environments สำหรับการรันด้วยตนเอง
- ต้องการสร้าง environment ใหม่? ดู Creating Environments :::
Architecture
ระบบ environment ถูกสร้างขึ้นบน three-layer inheritance chain:
classDiagram
class BaseEnv {
Server management
Worker scheduling
Wandb logging
CLI: serve / process / evaluate
}
class HermesAgentBaseEnv {
Terminal backend configuration
Tool resolution
Agent loop engine
ToolContext access
}
class TerminalTestEnv {
Stack testing
}
class HermesSweEnv {
SWE training
}
class TerminalBench2EvalEnv {
Benchmark evaluation
}
class TBLiteEvalEnv {
Fast benchmark
}
class YCBenchEvalEnv {
Long-horizon benchmark
}
BaseEnv <|-- HermesAgentBaseEnv
HermesAgentBaseEnv <|-- TerminalTestEnv
HermesAgentBaseEnv <|-- HermesSweEnv
HermesAgentBaseEnv <|-- TerminalBench2EvalEnv
TerminalBench2EvalEnv <|-- TBLiteEvalEnv
TerminalBench2EvalEnv <|-- YCBenchEvalEnvBaseEnv (Atropos)
เป็น foundation จาก atroposlib ให้บริการ:
- Server management - เชื่อมต่อกับ OpenAI-compatible APIs (VLLM, SGLang, OpenRouter)
- Worker scheduling - การประสานงาน rollout แบบ parallel
- Wandb integration - การบันทึก metrics และการแสดงผล rollout
- CLI interface - มี subcommands สามตัว:
serve,process,evaluate - Eval logging -
evaluate_log()บันทึกผลลัพธ์เป็น JSON + JSONL
HermesAgentBaseEnv
เป็น layer ของ hermes-agent (environments/hermes_base_env.py) เพิ่มเติม:
- Terminal backend configuration - กำหนด
TERMINAL_ENVสำหรับการรันแบบ sandboxed (local, Docker, Modal, Daytona, SSH, Singularity) - Tool resolution -
_resolve_tools_for_group()เรียกใช้get_tool_definitions()ของ hermes-agent เพื่อรับ tool schemas ที่ถูกต้องตาม toolsets ที่เปิด/ปิดใช้งาน - Agent loop integration -
collect_trajectory()รันHermesAgentLoopและให้คะแนนผลลัพธ์ - Two-phase operation - Phase 1 (OpenAI server) สำหรับ eval/SFT, Phase 2 (VLLM ManagedServer) สำหรับ full RL พร้อม logprobs
- Async safety patches - monkey-patches Modal backend เพื่อให้ทำงานภายใน event loop ของ Atropos
Concrete Environments
Environment ของคุณจะสืบทอดมาจาก HermesAgentBaseEnv และต้อง implement ห้า methods:
| Method | Purpose |
|---|---|
setup() | โหลด dataset, initialise state |
get_next_item() | คืนค่า item ถัดไปสำหรับ rollout |
format_prompt(item) | แปลง item ให้เป็น user message |
compute_reward(item, result, ctx) | ให้คะแนน rollout (0.0–1.0) |
evaluate() | logic การ evaluation เป็นระยะ |
Core Components
Agent Loop
HermesAgentLoop (environments/agent_loop.py) คือ multi-turn agent engine ที่นำกลับมาใช้ใหม่ได้ มันรัน tool-calling pattern เดียวกับ main loop ของ hermes-agent:
- ส่ง messages + tool schemas ไปยัง API ผ่าน
server.chat_completion() - หาก response มี
tool_callsให้ dispatch แต่ละตัวผ่านhandle_function_call() - แนบ tool results เข้าไปใน conversation, กลับไปที่ขั้นตอน 1
- หากไม่มี
tool_calls, agent จะเสร็จสิ้น
Tool calls จะถูก execute ใน thread pool (ThreadPoolExecutor(128)) เพื่อป้องกันไม่ให้ async backends (Modal, Docker) deadlock ภายใน event loop ของ Atropos
ส่งคืนค่า AgentResult:
@dataclass
class AgentResult:
messages: List[Dict[str, Any]] # Full conversation history
turns_used: int # Number of LLM calls made
finished_naturally: bool # True if model stopped on its own
reasoning_per_turn: List[Optional[str]] # Extracted reasoning content
tool_errors: List[ToolError] # Errors encountered during tool dispatch
managed_state: Optional[Dict] # VLLM ManagedServer state (Phase 2)Tool Context
ToolContext (environments/tool_context.py) ให้ reward functions เข้าถึง sandbox เดียวกัน ที่ model ใช้ระหว่างการ rollout การกำหนดขอบเขตด้วย task_id หมายความว่า state ทั้งหมด (files, processes, browser tabs) จะถูกเก็บรักษาไว้
async def compute_reward(self, item, result, ctx: ToolContext):
# Run tests in the model's terminal sandbox
test = ctx.terminal("pytest -v")
if test["exit_code"] == 0:
return 1.0
# Check if a file was created
content = ctx.read_file("/workspace/solution.py")
if content.get("content"):
return 0.5
# Download files for local verification
ctx.download_file("/remote/output.bin", "/local/output.bin")
return 0.0Available methods:
| Category | Methods |
|---|---|
| Terminal | terminal(command, timeout) |
| Files | read_file(path), write_file(path, content), search(query, path) |
| Transfers | upload_file(), upload_dir(), download_file(), download_dir() |
| Web | web_search(query), web_extract(urls) |
| Browser | browser_navigate(url), browser_snapshot() |
| Generic | call_tool(name, args) - escape hatch สำหรับ tool ใดๆ ของ hermes-agent |
| Cleanup | cleanup() - ปล่อยทรัพยากรทั้งหมด |
Tool Call Parsers
สำหรับ Phase 2 (VLLM ManagedServer), server จะส่งคืน raw text โดยไม่มี structured tool calls client-side parsers ใน environments/tool_call_parsers/ จะดึง tool_calls จาก raw output:
from environments.tool_call_parsers import get_parser
parser = get_parser("hermes") # or "mistral", "llama3_json", "qwen", "deepseek_v3", etc.
content, tool_calls = parser.parse(raw_model_output)Available parsers: hermes, mistral, llama3_json, qwen, qwen3_coder, deepseek_v3, deepseek_v3_1, kimi_k2, longcat, glm45, glm47.
ใน Phase 1 (ประเภท OpenAI server), ไม่จำเป็นต้องใช้ parsers - server จะจัดการ tool call parsing โดยธรรมชาติ
Available Benchmarks
TerminalBench2
89 terminal tasks พร้อมสภาพแวดล้อม Docker sandbox ต่อ task
| What it tests | ความสามารถในการเขียนโค้ด/sysadmin แบบ single-task |
| Scoring | ผ่าน/ไม่ผ่าน แบบ binary (การตรวจสอบ test suite) |
| Sandbox | Modal cloud sandboxes (Docker images ต่อ task) |
| Tools | terminal + file |
| Tasks | 89 tasks ในหลาย categories |
| Cost | ~$50–200 สำหรับ full eval (parallel execution) |
| Time | ~2–4 ชั่วโมง |
python environments/benchmarks/terminalbench_2/terminalbench2_env.py evaluate \
--config environments/benchmarks/terminalbench_2/default.yaml
# Run specific tasks
python environments/benchmarks/terminalbench_2/terminalbench2_env.py evaluate \
--config environments/benchmarks/terminalbench_2/default.yaml \
--env.task_filter fix-git,git-multibranchDataset: NousResearch/terminal-bench-2 บน HuggingFace.
TBLite (OpenThoughts Terminal Bench Lite)
100 tasks ที่มีการปรับระดับความยาก - เป็น proxy ที่เร็วกว่า TerminalBench2
| What it tests | เหมือน TB2 (coding/sysadmin), มีการปรับระดับความยาก |
| Scoring | ผ่าน/ไม่ผ่าน แบบ binary |
| Sandbox | Modal cloud sandboxes |
| Tools | terminal + file |
| Tasks | 100 tasks: Easy (40), Medium (26), Hard (26), Extreme (8) |
| Correlation | r=0.911 กับ full TB2 |
| Speed | เร็วกว่า TB2 2.6–8× |
python environments/benchmarks/tblite/tblite_env.py evaluate \
--config environments/benchmarks/tblite/default.yamlTBLite เป็น thin subclass ของ TerminalBench2 - มีเพียง dataset และ timeouts ที่แตกต่างกัน สร้างโดยทีม OpenThoughts Agent (Snorkel AI + Bespoke Labs). Dataset: NousResearch/openthoughts-tblite.
YC-Bench
Long-horizon strategic benchmark - agent จะรับบทเป็น CEO ของ AI startup
| What it tests | ความสอดคล้องเชิงกลยุทธ์ multi-turn ตลอดหลายร้อย turns |
| Scoring | Composite: 0.5 × survival + 0.5 × normalised_funds |
| Sandbox | Local terminal (ไม่จำเป็นต้องใช้ Modal) |
| Tools | terminal เท่านั้น |
| Runs | 9 default (3 presets × 3 seeds), sequential |
| Cost | ~$50–200 สำหรับ full eval |
| Time | ~3–6 ชั่วโมง |
# ติดตั้ง yc-bench (optional dependency)
pip install "hermes-agent[yc-bench]"
# Run evaluation
bash environments/benchmarks/yc_bench/run_eval.sh
# หรือโดยตรง
python environments/benchmarks/yc_bench/yc_bench_env.py evaluate \
--config environments/benchmarks/yc_bench/default.yaml
# Quick single-preset test
python environments/benchmarks/yc_bench/yc_bench_env.py evaluate \
--config environments/benchmarks/yc_bench/default.yaml \
--env.presets '["fast_test"]' --env.seeds '[1]'YC-Bench ใช้ collinear-ai/yc-bench - ซึ่งเป็นการจำลองแบบ deterministic ที่มี 4 skill domains (research, inference, data_environment, training), prestige system, employee management, และ financial pressure ต่างจาก YC-Bench ที่ใช้ binary scoring ต่อ task ของ TB2, YC-Bench จะวัดว่า agent สามารถรักษา coherent strategy ตลอดหลายร้อยการตัดสินใจที่สะสมกันได้หรือไม่
Training Environments
TerminalTestEnv
environment แบบ minimal self-contained ที่มี inline tasks (ไม่มี external dataset) ใช้สำหรับ validating the full stack end-to-end แต่ละ task จะขอให้ model สร้างไฟล์ที่ path ที่ทราบล่วงหน้า และ verifier จะตรวจสอบ content
# Process mode (บันทึก rollouts เป็น JSONL, ไม่ต้องใช้ training server)
python environments/terminal_test_env/terminal_test_env.py process \
--env.data_path_to_save_groups terminal_test_output.jsonl
# Serve mode (เชื่อมต่อกับ Atropos API สำหรับ RL training)
python environments/terminal_test_env/terminal_test_env.py serveHermesSweEnv
environment สำหรับการฝึกสไตล์ SWE-bench model จะได้รับ coding task, ใช้ terminal + file + web tools ในการแก้ไขปัญหา และ reward function จะรัน tests ใน Modal sandbox เดียวกัน
python environments/hermes_swe_env/hermes_swe_env.py serve \
--openai.model_name YourModel \
--env.dataset_name bigcode/humanevalpack \
--env.terminal_backend modalRunning Environments
ทุก environment เป็น standalone Python script ที่มีสาม CLI subcommands:
evaluate - รัน benchmark
สำหรับ environments ที่ใช้ eval เท่านั้น (benchmarks) รันทุก items, คำนวณ metrics, และบันทึกไปยัง wandb
python environments/benchmarks/tblite/tblite_env.py evaluate \
--config environments/benchmarks/tblite/default.yaml \
--openai.model_name anthropic/claude-sonnet-4.6ไม่จำเป็นต้องมี training server หรือ run-api environment จะจัดการทุกอย่าง
process - สร้าง SFT data
รัน rollouts และบันทึก scored trajectories เป็น JSONL มีประโยชน์สำหรับการสร้าง training data โดยไม่ต้องมี full RL loop
python environments/terminal_test_env/terminal_test_env.py process \
--env.data_path_to_save_groups output.jsonl \
--openai.model_name anthropic/claude-sonnet-4.6รูปแบบ output: แต่ละบรรทัดคือ scored trajectory ที่มี full conversation history, reward, และ metadata
serve - เชื่อมต่อกับ Atropos สำหรับ RL training
เชื่อมต่อ environment กับ Atropos API server ที่กำลังรันอยู่ (run-api) ใช้ระหว่าง live RL training
# Terminal 1: Start the Atropos API
run-api
# Terminal 2: Start the environment
python environments/hermes_swe_env/hermes_swe_env.py serve \
--openai.model_name YourModelenvironment จะรับ items จาก Atropos, รัน agent rollouts, คำนวณ rewards, และส่ง scored trajectories กลับไปสำหรับการฝึก
Two-Phase Operation
Phase 1: OpenAI Server (Eval / SFT)
ใช้ server.chat_completion() ด้วยพารามิเตอร์ tools= server (VLLM, SGLang, OpenRouter, OpenAI) จะจัดการ tool call parsing โดยธรรมชาติ ส่งคืน ChatCompletion objects ที่มี tool_calls แบบ structured
- ใช้สำหรับ: evaluation, การสร้าง SFT data, benchmarks, testing
- Placeholder tokens ถูกสร้างขึ้นสำหรับ Atropos pipeline (เนื่องจากไม่มี real token IDs จาก OpenAI API)
Phase 2: VLLM ManagedServer (Full RL)
ใช้ ManagedServer สำหรับ exact token IDs + logprobs ผ่าน /generate client-side tool call parser จะ reconstruct structured tool_calls จาก raw output
- ใช้สำหรับ: full RL training ด้วย GRPO/PPO
- Real tokens, masks, และ logprobs จะไหลผ่าน pipeline
- กำหนด
tool_call_parserใน config ให้ตรงกับ format ของ model ของคุณ (เช่น"hermes","qwen","mistral")
Creating Environments
Training Environment
from environments.hermes_base_env import HermesAgentBaseEnv, HermesAgentEnvConfig
from atroposlib.envs.server_handling.server_manager import APIServerConfig
class MyEnvConfig(HermesAgentEnvConfig):
my_custom_field: str = "default_value"
class MyEnv(HermesAgentBaseEnv):
name = "my-env"
env_config_cls = MyEnvConfig
@classmethod
def config_init(cls):
env_config = MyEnvConfig(
enabled_toolsets=["terminal", "file"],
terminal_backend="modal",
max_agent_turns=30,
)
server_configs = [APIServerConfig(
base_url="https://openrouter.ai/api/v1",
model_name="anthropic/claude-sonnet-4.6",
server_type="openai",
)]
return env_config, server_configs
async def setup(self):
from datasets import load_dataset
self.dataset = list(load_dataset("my-dataset", split="train"))
self.iter = 0
async def get_next_item(self):
item = self.dataset[self.iter % len(self.dataset)]
self.iter += 1
return item
def format_prompt(self, item):
return item["instruction"]
async def compute_reward(self, item, result, ctx):
# ctx gives full tool access to the rollout's sandbox
test = ctx.terminal("pytest -v")
return 1.0 if test["exit_code"] == 0 else 0.0
async def evaluate(self, *args, **kwargs):
# Periodic evaluation during training
pass
if __name__ == "__main__":
MyEnv.cli()Eval-Only Benchmark
สำหรับ benchmarks ให้ทำตาม pattern ที่ใช้โดย TerminalBench2, TBLite, และ YC-Bench:
- สร้างภายใต้
environments/benchmarks/your-benchmark/ - ตั้งค่า config สำหรับ eval เท่านั้น:
eval_handling=STOP_TRAIN,steps_per_eval=1,total_steps=1 - Stub training methods:
collect_trajectories()คืนค่า(None, []),score()คืนค่าNone - Implement
rollout_and_score_eval(eval_item)- agent loop ต่อ item + scoring - Implement
evaluate()- จัดการการรันทั้งหมด, คำนวณ metrics รวม - เพิ่ม streaming JSONL สำหรับการบันทึกผลลัพธ์ที่ปลอดภัยจากการ crash
- เพิ่ม cleanup: การจัดการ
KeyboardInterrupt,cleanup_all_environments(),_tool_executor.shutdown() - รันด้วย subcommand
evaluate
ดูที่ environments/benchmarks/yc_bench/yc_bench_env.py สำหรับ reference implementation ที่สะอาดและมีเอกสารประกอบที่ดี
Configuration Reference
HermesAgentEnvConfig Fields
| Field | Type | Default | Description |
|---|---|---|---|
enabled_toolsets | List[str] | None (all) | hermes toolsets ที่ต้องการเปิดใช้งาน |
disabled_toolsets | List[str] | None | Toolsets ที่ต้องการกรองออก |
distribution | str | None | ชื่อการกระจาย toolset แบบ probabilistic |
max_agent_turns | int | 30 | จำนวน LLM calls สูงสุดต่อ rollout |
agent_temperature | float | 1.0 | Sampling temperature |
system_prompt | str | None | System message สำหรับ agent |
terminal_backend | str | "local" | local, docker, modal, daytona, ssh, singularity |
terminal_timeout | int | 120 | วินาทีต่อ terminal command |
terminal_lifetime | int | 3600 | อายุ sandbox สูงสุด |
dataset_name | str | None | HuggingFace dataset identifier |
tool_pool_size | int | 128 | ขนาด thread pool สำหรับ tool execution |
tool_call_parser | str | "hermes" | Parser สำหรับ raw output ของ Phase 2 |
extra_body | Dict | None | extra params สำหรับ OpenAI API (เช่น OpenRouter provider prefs) |
eval_handling | Enum | STOP_TRAIN | STOP_TRAIN, LIMIT_TRAIN, NONE |
YAML Configuration
Environments สามารถกำหนดค่าผ่านไฟล์ YAML ที่ส่งด้วย --config:
env:
enabled_toolsets: ["terminal", "file"]
max_agent_turns: 60
max_token_length: 32000
agent_temperature: 0.8
terminal_backend: "modal"
terminal_timeout: 300
dataset_name: "NousResearch/terminal-bench-2"
tokenizer_name: "NousResearch/Hermes-3-Llama-3.1-8B"
use_wandb: true
wandb_name: "my-benchmark"
openai:
base_url: "https://openrouter.ai/api/v1"
model_name: "anthropic/claude-sonnet-4.6"
server_type: "openai"
health_check: falseค่าใน YAML จะ override ค่า default ของ config_init() อาร์กิวเมนต์ CLI จะ override ค่า YAML:
python my_env.py evaluate \
--config my_config.yaml \
--openai.model_name anthropic/claude-opus-4.6 # overrides YAMLPrerequisites
สำหรับทุก environments
- Python >= 3.11
atroposlib:pip install git+https://github.com/NousResearch/atropos.git- API key ของ LLM (OpenRouter, OpenAI, หรือ self-hosted VLLM/SGLang)
สำหรับ Modal-sandboxed benchmarks (TB2, TBLite)
- บัญชีและ CLI ของ Modal:
pip install "hermes-agent[modal]" - environment variables
MODAL_TOKEN_IDและMODAL_TOKEN_SECRET
สำหรับ YC-Bench
pip install "hermes-agent[yc-bench]"(ติดตั้ง yc-bench CLI + SQLAlchemy)- ไม่จำเป็นต้องใช้ Modal - รันด้วย local terminal backend
สำหรับ RL training
TINKER_API_KEY- API key สำหรับบริการฝึก TinkerWANDB_API_KEY- สำหรับการติดตาม metrics ของ Weights & Biases- submodule
tinker-atropos(ที่tinker-atropos/ใน repo)
ดูที่ RL Training สำหรับ workflow RL แบบ agent-driven
Directory Structure
environments/
├── hermes_base_env.py # Abstract base class (HermesAgentBaseEnv)
├── agent_loop.py # Multi-turn agent engine (HermesAgentLoop)
├── tool_context.py # Per-rollout tool access for reward functions
├── patches.py # Async-safety patches for Modal backend
│
├── tool_call_parsers/ # Phase 2 client-side parsers
│ ├── hermes_parser.py # Hermes/ChatML <tool_call> format
│ ├── mistral_parser.py # Mistral [TOOL_CALLS] format
│ ├── llama_parser.py # Llama 3 JSON tool calling
│ ├── qwen_parser.py # Qwen format
│ ├── deepseek_v3_parser.py # DeepSeek V3 format
│ └── ... # + kimi_k2, longcat, glm45/47, etc.
│
├── terminal_test_env/ # Stack validation (inline tasks)
├── hermes_swe_env/ # SWE-bench training environment
│
└── benchmarks/ # Evaluation benchmarks
├── terminalbench_2/ # 89 terminal tasks, Modal sandboxes
├── tblite/ # 100 calibrated tasks (fast TB2 proxy)
└── yc_bench/ # Long-horizon strategic benchmark📄 developer-guide/extending-the-cli.md
sidebar_position: 8 title: "การขยาย CLI" description: "สร้าง wrapper CLIs ที่ขยาย Hermes TUI ด้วย widgets, keybindings, และการเปลี่ยนแปลง layout ที่กำหนดเอง"
การขยาย CLI
Hermes เปิดเผย protected extension hooks บน HermesCLI เพื่อให้ wrapper CLIs สามารถเพิ่ม widgets, keybindings, และการปรับแต่ง layout ได้ โดยไม่ต้อง override method run() ที่มีความยาวกว่า 1000 บรรทัด วิธีนี้ช่วยให้ extension ของคุณแยกออกจาก internal changes ได้
จุดขยาย (Extension points)
มีจุดขยาย (extension seams) ห้าจุดให้ใช้งาน:
| Hook | วัตถุประสงค์ | ต้อง override เมื่อ... |
|---|---|---|
_get_extra_tui_widgets() | แทรก widgets เข้าไปใน layout | คุณต้องการองค์ประกอบ UI ที่คงอยู่ (panel, status line, mini-player) |
_register_extra_tui_keybindings(kb, *, input_area) | เพิ่ม hotkeys (ปุ่มลัด) | คุณต้องการ hotkeys (สลับ panel, ควบคุม transport, hotkeys แบบ modal) |
_build_tui_layout_children(**widgets) | ควบคุมลำดับ widgets ทั้งหมด | คุณต้องการจัดเรียงใหม่หรือห่อ widgets ที่มีอยู่ (กรณีหายาก) |
process_command() | เพิ่ม slash commands ที่กำหนดเอง | คุณต้องการการจัดการ /mycommand (hook ที่มีอยู่ก่อนแล้ว) |
_build_tui_style_dict() | custom prompt_toolkit styles | คุณต้องการสีหรือการจัดรูปแบบที่กำหนดเอง (hook ที่มีอยู่ก่อนแล้ว) |
สามตัวแรกเป็น protected hooks ที่เป็นใหม่ สองตัวหลังมีอยู่แล้ว
เริ่มต้นอย่างรวดเร็ว: wrapper CLI
#!/usr/bin/env python3
"""my_cli.py - ตัวอย่าง wrapper CLI ที่ขยาย Hermes."""
from cli import HermesCLI
from prompt_toolkit.layout import FormattedTextControl, Window
from prompt_toolkit.filters import Condition
class MyCLI(HermesCLI):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._panel_visible = False
def _get_extra_tui_widgets(self):
"""เพิ่ม info panel แบบสลับได้เหนือ status bar."""
cli_ref = self
return [
Window(
FormattedTextControl(lambda: "📊 My custom panel content"),
height=1,
filter=Condition(lambda: cli_ref._panel_visible),
),
]
def _register_extra_tui_keybindings(self, kb, *, input_area):
"""F2 สลับ panel ที่กำหนดเอง."""
cli_ref = self
@kb.add("f2")
def _toggle_panel(event):
cli_ref._panel_visible = not cli_ref._panel_visible
def process_command(self, cmd: str) -> bool:
"""เพิ่ม slash command /panel."""
if cmd.strip().lower() == "/panel":
self._panel_visible = not self._panel_visible
state = "visible" if self._panel_visible else "hidden"
print(f"Panel is now {state}")
return True
return super().process_command(cmd)
if __name__ == "__main__":
cli = MyCLI()
cli.run()วิธีรัน:
cd ~/.hermes/hermes-agent
source .venv/bin/activate
python my_cli.pyการอ้างอิง Hook
_get_extra_tui_widgets()
ส่งคืนรายการของ prompt_toolkit widgets เพื่อแทรกเข้าไปใน TUI layout Widgets จะปรากฏ ระหว่าง spacer และ status bar - อยู่เหนือ input area แต่ใต้ main output
def _get_extra_tui_widgets(self) -> list:
return [] # default: no extra widgetswidgets แต่ละตัวควรเป็น prompt_toolkit container (เช่น Window, ConditionalContainer, HSplit) ใช้ ConditionalContainer หรือ filter=Condition(...) เพื่อให้ widgets สามารถสลับได้
from prompt_toolkit.layout import ConditionalContainer, Window, FormattedTextControl
from prompt_toolkit.filters import Condition
def _get_extra_tui_widgets(self):
return [
ConditionalContainer(
Window(FormattedTextControl("Status: connected"), height=1),
filter=Condition(lambda: self._show_status),
),
]_register_extra_tui_keybindings(kb, *, input_area)
ถูกเรียกใช้หลังจากที่ Hermes ลงทะเบียน keybindings ของตัวเอง และก่อนที่ layout จะถูกสร้างขึ้น เพิ่ม keybindings ของคุณเข้าไปใน kb
def _register_extra_tui_keybindings(self, kb, *, input_area):
pass # default: no extra keybindingsParameters:
kb- instance ของKeyBindingsสำหรับแอปพลิเคชัน prompt_toolkitinput_area- widgetTextAreaหลัก หากคุณต้องการอ่านหรือจัดการ input ของผู้ใช้
def _register_extra_tui_keybindings(self, kb, *, input_area):
cli_ref = self
@kb.add("f3")
def _clear_input(event):
input_area.text = ""
@kb.add("f4")
def _insert_template(event):
input_area.text = "/search "หลีกเลี่ยงความขัดแย้ง กับ keybindings ที่มีอยู่: Enter (submit), Escape Enter (ขึ้นบรรทัดใหม่), Ctrl-C (interrupt), Ctrl-D (exit), Tab (auto-suggest accept). ปุ่มฟังก์ชัน F2+ และ Ctrl-combinations โดยทั่วไปปลอดภัย
_build_tui_layout_children(**widgets)
ให้ override เฉพาะเมื่อคุณต้องการควบคุมลำดับ widgets อย่างสมบูรณ์ ส่วนใหญ่แล้ว extension ควรใช้ _get_extra_tui_widgets() แทน
def _build_tui_layout_children(self, *, sudo_widget, secret_widget,
approval_widget, clarify_widget, spinner_widget, spacer,
status_bar, input_rule_top, image_bar, input_area,
input_rule_bot, voice_status_bar, completions_menu) -> list:การ implement ค่าเริ่มต้นจะส่งคืน:
[
Window(height=0), # anchor
sudo_widget, # sudo password prompt (conditional)
secret_widget, # secret input prompt (conditional)
approval_widget, # dangerous command approval (conditional)
clarify_widget, # clarify question UI (conditional)
spinner_widget, # thinking spinner (conditional)
spacer, # fills remaining vertical space
*self._get_extra_tui_widgets(), # YOUR WIDGETS GO HERE
status_bar, # model/token/context status line
input_rule_top, # ─── border above input
image_bar, # attached images indicator
input_area, # user text input
input_rule_bot, # ─── border below input
voice_status_bar, # voice mode status (conditional)
completions_menu, # autocomplete dropdown
]แผนภาพ Layout
Layout ค่าเริ่มต้นจากบนลงล่าง:
- Output area - ประวัติการสนทนาที่เลื่อนได้
- Spacer
- Extra widgets - จาก
_get_extra_tui_widgets() - Status bar - model, context %, elapsed time
- Image bar - จำนวน image ที่แนบมา
- Input area - prompt ของผู้ใช้
- Voice status - ตัวบ่งชี้การบันทึก
- Completions menu - คำแนะนำ autocomplete
เคล็ดลับ
- ทำให้ display ไม่ถูกต้อง (Invalidate the display) หลังจากการเปลี่ยนแปลง state: เรียกใช้
self._invalidate()เพื่อกระตุ้นการ redraw ของ prompt_toolkit - เข้าถึง agent state:
self.agent,self.model,self.conversation_historyสามารถใช้งานได้ทั้งหมด - Custom styles: Override
_build_tui_style_dict()และเพิ่ม entries สำหรับ custom style classes ของคุณ - Slash commands: Override
process_command(), จัดการคำสั่งของคุณ, และเรียกใช้super().process_command(cmd)สำหรับทุกอย่างที่เหลือ - อย่า override
run()เว้นแต่จำเป็นจริงๆ - extension hooks มีอยู่โดยเฉพาะเพื่อหลีกเลี่ยงการผูกติดกันแบบนั้น
📄 developer-guide/gateway-internals.md
sidebar_position: 7 title: "Gateway Internals" description: "วิธีการบูตของ messaging gateway, การอนุญาตผู้ใช้, การกำหนดเส้นทางเซสชัน, และการส่งข้อความ"
Gateway Internals
messaging gateway คือ process ที่ทำงานต่อเนื่องซึ่งเชื่อมต่อ Hermes เข้ากับแพลตฟอร์ม messaging ภายนอกกว่า 14 แพลตฟอร์มผ่านสถาปัตยกรรมแบบรวมศูนย์
Key Files
| File | Purpose |
|---|---|
gateway/run.py | GatewayRunner - main loop, slash commands, message dispatch (~9,000 lines) |
gateway/session.py | SessionStore - conversation persistence and session key construction |
gateway/delivery.py | Outbound message delivery to target platforms/channels |
gateway/pairing.py | DM pairing flow for user authorization |
gateway/channel_directory.py | Maps chat IDs to human-readable names for cron delivery |
gateway/hooks.py | Hook discovery, loading, and lifecycle event dispatch |
gateway/mirror.py | Cross-session message mirroring for send_message |
gateway/status.py | Token lock management for profile-scoped gateway instances |
gateway/builtin_hooks/ | Always-registered hooks (e.g., BOOT.md system prompt hook) |
gateway/platforms/ | Platform adapters (one per messaging platform) |
Architecture Overview
┌─────────────────────────────────────────────────┐
│ GatewayRunner │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Telegram │ │ Discord │ │ Slack │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ ▼ │
│ _handle_message() │
│ │ │
│ ┌───────────┼───────────┐ │
│ ▼ ▼ ▼ │
│ Slash command AIAgent Queue/BG │
│ dispatch creation sessions │
│ │ │
│ ▼ │
│ SessionStore │
│ (SQLite persistence) │
└─────────────────────────────────────────────────┘Message Flow
เมื่อมีข้อความเข้ามาจากแพลตฟอร์มใดๆ:
- Platform adapter จะรับ raw event และทำให้เป็นรูปแบบ
MessageEvent - Base adapter จะตรวจสอบ active session guard:
- หาก agent กำลังทำงานสำหรับ session นี้ -> จะ queue message และตั้งค่า interrupt event
- หากเป็น
/approve,/deny,/stop-> จะข้าม guard (dispatched inline)
- GatewayRunner._handle_message() จะรับ event:
- Resolve session key ผ่าน
_session_key_for_source()(format:agent:main:{platform}:{chat_type}:{chat_id}) - ตรวจสอบการอนุญาต (ดู Authorization ด้านล่าง)
- ตรวจสอบว่าเป็น slash command หรือไม่ -> dispatch ไปยัง command handler
- ตรวจสอบว่า agent กำลังทำงานอยู่หรือไม่ -> intercept commands เช่น
/stop,/status - หากไม่เป็นเช่นนั้น -> สร้าง instance ของ
AIAgentและรัน conversation
- Resolve session key ผ่าน
- Response จะถูกส่งกลับผ่าน platform adapter
Session Key Format
Session keys จะเข้ารหัส context การกำหนดเส้นทางทั้งหมด:
agent:main:{platform}:{chat_type}:{chat_id}ตัวอย่างเช่น: agent:main:telegram:private:123456789
แพลตฟอร์มที่รองรับ thread (เช่น Telegram forum topics, Discord threads, Slack threads) อาจรวม thread IDs ไว้ในส่วน chat_id ห้ามสร้าง session keys ด้วยตนเองเด็ดขาด - ให้ใช้ build_session_key() จาก gateway/session.py เสมอ
Two-Level Message Guard
เมื่อ agent กำลังทำงานอยู่ ข้อความขาเข้าจะผ่าน guard สองระดับตามลำดับ:
-
Level 1 - Base adapter (
gateway/platforms/base.py): ตรวจสอบ_active_sessionsหาก session นั้น active จะ queue ข้อความใน_pending_messagesและตั้งค่า interrupt event สิ่งนี้จะดักจับข้อความ ก่อน ที่จะถึง gateway runner -
Level 2 - Gateway runner (
gateway/run.py): ตรวจสอบ_running_agentsจะ intercept commands เฉพาะ (/stop,/new,/queue,/status,/approve,/deny) และกำหนดเส้นทางอย่างเหมาะสม ส่วนอื่นๆ ทั้งหมดจะเรียกใช้running_agent.interrupt()
Commands ที่ต้องถึง runner แม้ว่า agent จะถูกบล็อก (เช่น /approve) จะถูก dispatch inline ผ่าน await self._message_handler(event) - ซึ่งจะข้ามระบบ background task เพื่อหลีกเลี่ยง race conditions
Authorization
gateway ใช้การตรวจสอบการอนุญาตแบบหลายชั้น (multi-layer authorization check) ซึ่งจะถูกประเมินตามลำดับ:
- Per-platform allow-all flag (เช่น
TELEGRAM_ALLOW_ALL_USERS) - หากตั้งค่าไว้ ผู้ใช้ทุกคนบนแพลตฟอร์มนั้นจะได้รับอนุญาต - Platform allowlist (เช่น
TELEGRAM_ALLOWED_USERS) - user IDs ที่คั่นด้วยเครื่องหมายจุลภาค - DM pairing - ผู้ใช้ที่ได้รับการยืนยันตัวตนสามารถจับคู่ผู้ใช้ใหม่ผ่าน pairing code
- Global allow-all (
GATEWAY_ALLOW_ALL_USERS) - หากตั้งค่าไว้ ผู้ใช้ทุกคนบนทุกแพลตฟอร์มจะได้รับอนุญาต - Default: deny - ผู้ใช้ที่ไม่ได้รับอนุญาตจะถูกปฏิเสธ
DM Pairing Flow
Admin: /pair
Gateway: "Pairing code: ABC123. Share with the user."
New user: ABC123
Gateway: "Paired! You're now authorized."สถานะการจับคู่ (Pairing state) จะถูกบันทึกใน gateway/pairing.py และคงอยู่แม้จะมีการรีสตาร์ท
Slash Command Dispatch
slash commands ทั้งหมดใน gateway จะไหลผ่าน pipeline การ resolve เดียวกัน:
resolve_command()จากhermes_cli/commands.pyจะแมป input ไปยัง canonical name (จัดการ aliases, prefix matching)- canonical name จะถูกตรวจสอบกับ
GATEWAY_KNOWN_COMMANDS - Handler ใน
_handle_message()จะ dispatch ตาม canonical name - คำสั่งบางอย่างถูกจำกัดด้วย config (
gateway_config_gateบนCommandDef)
Running-Agent Guard
Commands ที่ต้องไม่ถูก execute ขณะที่ agent กำลังประมวลผลจะถูกปฏิเสธตั้งแต่เนิ่นๆ:
if _quick_key in self._running_agents:
if canonical == "model":
return "⏳ Agent is running - wait for it to finish or /stop first."Commands ที่ข้าม (Bypass commands) (เช่น /stop, /new, /approve, /deny, /queue, /status) มีการจัดการพิเศษ
Config Sources
gateway อ่าน configuration จากหลายแหล่ง:
| Source | What it provides |
|---|---|
~/.hermes/.env | API keys, bot tokens, platform credentials |
~/.hermes/config.yaml | Model settings, tool configuration, display options |
| Environment variables | Override any of the above |
ต่างจาก CLI (ซึ่งใช้ load_cli_config() พร้อมค่า default ที่ hardcoded) gateway จะอ่าน config.yaml โดยตรงผ่าน YAML loader ซึ่งหมายความว่า config keys ที่มีอยู่ใน defaults dict ของ CLI แต่ไม่มีในไฟล์ config ของผู้ใช้อาจมีพฤติกรรมที่แตกต่างกันระหว่าง CLI และ gateway
Platform Adapters
แต่ละแพลตฟอร์ม messaging มี adapter อยู่ใน gateway/platforms/:
gateway/platforms/
├── base.py # BaseAdapter - shared logic for all platforms
├── telegram.py # Telegram Bot API (long polling or webhook)
├── discord.py # Discord bot via discord.py
├── slack.py # Slack Socket Mode
├── whatsapp.py # WhatsApp Business Cloud API
├── signal.py # Signal via signal-cli REST API
├── matrix.py # Matrix via mautrix (optional E2EE)
├── mattermost.py # Mattermost WebSocket API
├── email.py # Email via IMAP/SMTP
├── sms.py # SMS via Twilio
├── dingtalk.py # DingTalk WebSocket
├── feishu.py # Feishu/Lark WebSocket or webhook
├── wecom.py # WeCom (WeChat Work) callback
├── weixin.py # Weixin (personal WeChat) via iLink Bot API
├── bluebubbles.py # Apple iMessage via BlueBubbles macOS server
├── qqbot.py # QQ Bot (Tencent QQ) via Official API v2
├── webhook.py # Inbound/outbound webhook adapter
├── api_server.py # REST API server adapter
└── homeassistant.py # Home Assistant conversation integrationAdapters จะ implement interface ทั่วไป:
connect()/disconnect()- lifecycle managementsend_message()- outbound message deliveryon_message()- inbound message normalization ->MessageEvent
Token Locks
Adapters ที่เชื่อมต่อด้วย credentials เฉพาะ จะเรียกใช้ acquire_scoped_lock() ใน connect() และ release_scoped_lock() ใน disconnect() สิ่งนี้ป้องกันไม่ให้โปรไฟล์สองโปรไฟล์ใช้ bot token เดียวกันพร้อมกัน
Delivery Path
การส่งออก (Outgoing deliveries) (gateway/delivery.py) จัดการ:
- Direct reply - ส่ง response กลับไปยัง chat ต้นทาง
- Home channel delivery - กำหนดเส้นทางผลลัพธ์ของ cron job และ background results ไปยัง home channel ที่กำหนดค่าไว้
- Explicit target delivery -
send_messagetool ที่ระบุtelegram:-1001234567890 - Cross-platform delivery - ส่งไปยังแพลตฟอร์มที่แตกต่างจากข้อความต้นทาง
การส่งมอบจาก cron job จะไม่ถูก mirror เข้าไปใน history ของ session ของ gateway - มันจะอยู่ใน cron session ของตัวเองเท่านั้น นี่คือการออกแบบโดยเจตนาเพื่อหลีกเลี่ยงการละเมิดข้อจำกัดเรื่อง message alternation
Hooks
Gateway hooks คือ Python modules ที่ตอบสนองต่อ lifecycle events:
Gateway Hook Events
| Event | When fired |
|---|---|
gateway:startup | Gateway process starts |
session:start | New conversation session begins |
session:end | Session completes or times out |
session:reset | User resets session with /new |
agent:start | Agent begins processing a message |
agent:step | Agent completes one tool-calling iteration |
agent:end | Agent finishes and returns response |
command:* | Any slash command is executed |
Hooks ถูกค้นพบจาก gateway/builtin_hooks/ (active เสมอ) และ ~/.hermes/hooks/ (ติดตั้งโดยผู้ใช้) แต่ละ hook คือ directory ที่มี manifest ชื่อ HOOK.yaml และ handler.py
Memory Provider Integration
เมื่อเปิดใช้งาน memory provider plugin (เช่น Honcho):
- Gateway จะสร้าง
AIAgentต่อข้อความหนึ่งๆ พร้อมด้วย session ID MemoryManagerจะ initialize provider ด้วย session context- Provider tools (เช่น
honcho_profile,viking_search) จะถูกส่งผ่าน:
AIAgent._invoke_tool()
→ self._memory_manager.handle_tool_call(name, args)
→ provider.handle_tool_call(name, args)- เมื่อ session end/reset จะมีการเรียกใช้
on_session_end()สำหรับการ cleanup และการ flush data ครั้งสุดท้าย
Memory Flush Lifecycle
เมื่อ session ถูก reset, resume, หรือหมดอายุ:
- Built-in memories จะถูก flush ไปยัง disk
- hook
on_session_end()ของ memory provider จะถูกเรียกใช้ AIAgentชั่วคราวจะรัน turn conversation ที่เกี่ยวกับ memory เท่านั้น- Context จะถูก discard หรือ archive
Background Maintenance
gateway จะรันการบำรุงรักษาแบบ periodic ควบคู่ไปกับการจัดการข้อความ:
- Cron ticking - ตรวจสอบตารางงานและเรียกใช้งานที่ถึงกำหนด
- Session expiry - ทำความสะอาด session ที่ถูกทิ้งไว้หลังจากหมดเวลา
- Memory flush - flush memory ล่วงหน้าก่อนที่ session จะหมดอายุ
- Cache refresh - refresh รายชื่อ model และสถานะของ provider
Process Management
gateway ทำงานเป็น process ที่มีอายุการใช้งานยาวนาน (long-lived process) โดยจัดการผ่าน:
hermes gateway start/hermes gateway stop- การควบคุมด้วยตนเองsystemctl(Linux) หรือlaunchctl(macOS) - การจัดการ service- PID file ที่
~/.hermes/gateway.pid- การติดตาม process ที่จำกัดขอบเขตโปรไฟล์ (profile-scoped)
Profile-scoped vs global: start_gateway() ใช้ PID files ที่จำกัดขอบเขตโปรไฟล์ hermes gateway stop จะหยุดเฉพาะ gateway ของ profile ปัจจุบันเท่านั้น hermes gateway stop --all ใช้การสแกน ps aux แบบ global เพื่อ kill gateway processes ทั้งหมด (ใช้ระหว่างการอัปเดต)
Related Docs
📄 developer-guide/memory-provider-plugin.md
sidebar_position: 8 title: "ปลั๊กอิน Memory Provider" description: "วิธีการสร้างปลั๊กอิน memory provider สำหรับ Hermes Agent"
การสร้าง Memory Provider Plugin
Memory provider plugins ช่วยให้ Hermes Agent มีความรู้ที่คงอยู่ข้ามเซสชัน (persistent, cross-session knowledge) นอกเหนือจาก MEMORY.md และ USER.md ที่มีมาให้แล้ว คู่มือนี้จะครอบคลุมวิธีการสร้างปลั๊กอินดังกล่าว
:::tip
Memory providers เป็นหนึ่งในสองประเภทของ provider plugin อีกประเภทหนึ่งคือ Context Engine Plugins ซึ่งทำหน้าที่แทนที่ context compressor ที่มีมาให้ ทั้งสองประเภทนี้ใช้รูปแบบเดียวกัน: เลือกใช้ได้เพียงรายการเดียว (single-select) และขับเคลื่อนด้วยการตั้งค่า (config-driven) โดยจัดการผ่าน hermes plugins
:::
โครงสร้างไดเรกทอรี
Memory provider แต่ละตัวจะอยู่ใน plugins/memory/<name>/:
plugins/memory/my-provider/
├── __init__.py # MemoryProvider implementation + register() entry point
├── plugin.yaml # Metadata (name, description, hooks)
└── README.md # Setup instructions, config reference, toolsMemoryProvider ABC
ปลั๊กอินของคุณต้องใช้งาน MemoryProvider abstract base class จาก agent/memory_provider.py:
from agent.memory_provider import MemoryProvider
class MyMemoryProvider(MemoryProvider):
@property
def name(self) -> str:
return "my-provider"
def is_available(self) -> bool:
"""Check if this provider can activate. NO network calls."""
return bool(os.environ.get("MY_API_KEY"))
def initialize(self, session_id: str, **kwargs) -> None:
"""Called once at agent startup.
kwargs always includes:
hermes_home (str): Active HERMES_HOME path. Use for storage.
"""
self._api_key = os.environ.get("MY_API_KEY", "")
self._session_id = session_id
# ... implement remaining methodsเมธอดที่จำเป็น
Core Lifecycle
| Method | When Called | Must Implement? |
|---|---|---|
name (property) | Always | Yes |
is_available() | Agent init, before activation | Yes - no network calls |
initialize(session_id, **kwargs) | Agent startup | Yes |
get_tool_schemas() | After init, for tool injection | Yes |
handle_tool_call(name, args) | When agent uses your tools | Yes (if you have tools) |
Config
| Method | Purpose | Must Implement? |
|---|---|---|
get_config_schema() | Declare config fields for hermes memory setup | Yes |
save_config(values, hermes_home) | Write non-secret config to native location | Yes (unless env-var-only) |
Optional Hooks
| Method | When Called | Use Case |
|---|---|---|
system_prompt_block() | System prompt assembly | Static provider info |
prefetch(query) | Before each API call | Return recalled context |
queue_prefetch(query) | After each turn | Pre-warm for next turn |
sync_turn(user, assistant) | After each completed turn | Persist conversation |
on_session_end(messages) | Conversation ends | Final extraction/flush |
on_pre_compress(messages) | Before context compression | Save insights before discard |
on_memory_write(action, target, content) | Built-in memory writes | Mirror to your backend |
shutdown() | Process exit | Clean up connections |
Schema การตั้งค่า
get_config_schema() คืนค่ารายการของ field descriptors ที่ใช้โดย hermes memory setup:
def get_config_schema(self):
return [
{
"key": "api_key",
"description": "My Provider API key",
"secret": True, # → written to .env
"required": True,
"env_var": "MY_API_KEY", # explicit env var name
"url": "https://my-provider.com/keys", # where to get it
},
{
"key": "region",
"description": "Server region",
"default": "us-east",
"choices": ["us-east", "eu-west", "ap-south"],
},
{
"key": "project",
"description": "Project identifier",
"default": "hermes",
},
]Field ที่มี secret: True และ env_var จะถูกบันทึกไปยัง .env ส่วน Field ที่ไม่เป็นความลับจะถูกส่งไปยัง save_config().
:::tip Minimal vs Full Schema
ทุก field ใน get_config_schema() จะถูกถามระหว่างการเรียกใช้ hermes memory setup สำหรับ Provider ที่มีตัวเลือกจำนวนมาก ควรทำให้ schema มีความเรียบง่ายที่สุด - ให้รวมเฉพาะ field ที่ผู้ใช้ ต้อง กำหนดค่าเท่านั้น (เช่น API key, credentials ที่จำเป็น) ให้จัดทำเอกสารเกี่ยวกับการตั้งค่าทางเลือกในไฟล์อ้างอิง config (เช่น $HERMES_HOME/myprovider.json) แทนการถามทั้งหมดในระหว่างการตั้งค่า วิธีนี้จะทำให้ setup wizard ทำงานได้รวดเร็ว ในขณะที่ยังคงรองรับการตั้งค่าขั้นสูง ดูตัวอย่างจาก Supermemory provider - ตัวนี้จะถามเฉพาะ API key เท่านั้น ส่วนตัวเลือกอื่น ๆ จะอยู่ใน supermemory.json.
:::
การบันทึก Config
def save_config(self, values: dict, hermes_home: str) -> None:
"""Write non-secret config to your native location."""
import json
from pathlib import Path
config_path = Path(hermes_home) / "my-provider.json"
config_path.write_text(json.dumps(values, indent=2))สำหรับ Provider ที่ใช้ env-var เท่านั้น ให้ปล่อยค่า default no-op ไว้
จุดเข้าใช้งาน Plugin
def register(ctx) -> None:
"""Called by the memory plugin discovery system."""
ctx.register_memory_provider(MyMemoryProvider())plugin.yaml
name: my-provider
version: 1.0.0
description: "Short description of what this provider does."
hooks:
- on_session_end # list hooks you implementข้อกำหนดเรื่อง Threading
sync_turn() ต้องไม่บล็อก (non-blocking) อย่างเด็ดขาด หาก backend ของคุณมีการหน่วงเวลา (latency) (เช่น API calls, LLM processing) ให้รันงานนั้นใน daemon thread:
def sync_turn(self, user_content, assistant_content):
def _sync():
try:
self._api.ingest(user_content, assistant_content)
except Exception as e:
logger.warning("Sync failed: %s", e)
if self._sync_thread and self._sync_thread.is_alive():
self._sync_thread.join(timeout=5.0)
self._sync_thread = threading.Thread(target=_sync, daemon=True)
self._sync_thread.start()การแยก Profile
เส้นทางจัดเก็บข้อมูลทั้งหมด ต้อง ใช้ hermes_home kwarg จาก initialize(), ห้ามใช้ ~/.hermes แบบ hardcoded:
# CORRECT - profile-scoped
from hermes_constants import get_hermes_home
data_dir = get_hermes_home() / "my-provider"
# WRONG - shared across all profiles
data_dir = Path("~/.hermes/my-provider").expanduser()การทดสอบ
ดูที่ tests/agent/test_memory_plugin_e2e.py สำหรับรูปแบบการทดสอบ E2E ที่สมบูรณ์โดยใช้ SQLite provider จริง
from agent.memory_manager import MemoryManager
mgr = MemoryManager()
mgr.add_provider(my_provider)
mgr.initialize_all(session_id="test-1", platform="cli")
# Test tool routing
result = mgr.handle_tool_call("my_tool", {"action": "add", "content": "test"})
# Test lifecycle
mgr.sync_all("user msg", "assistant msg")
mgr.on_session_end([])
mgr.shutdown_all()การเพิ่มคำสั่ง CLI
Memory provider plugins สามารถลงทะเบียน subcommand tree ของ CLI ของตัวเองได้ (เช่น hermes my-provider status, hermes my-provider config) วิธีนี้ใช้ระบบการค้นพบแบบ convention-based - ไม่จำเป็นต้องแก้ไขไฟล์หลักใด ๆ
วิธีการทำงาน
- เพิ่มไฟล์
cli.pyในไดเรกทอรีปลั๊กอินของคุณ - กำหนดฟังก์ชัน
register_cli(subparser)ที่สร้างโครงสร้าง argparse tree - ระบบ memory plugin จะค้นพบมันเมื่อเริ่มต้นผ่าน
discover_plugin_cli_commands() - คำสั่งของคุณจะปรากฏภายใต้
hermes <provider-name> <subcommand>
Active-provider gating: คำสั่ง CLI ของคุณจะปรากฏก็ต่อเมื่อ Provider ของคุณเป็น memory.provider ที่ใช้งานอยู่ใน config เท่านั้น หากผู้ใช้ยังไม่ได้ตั้งค่า Provider ของคุณ คำสั่งของคุณจะไม่แสดงใน hermes --help.
ตัวอย่าง
# plugins/memory/my-provider/cli.py
def my_command(args):
"""Handler dispatched by argparse."""
sub = getattr(args, "my_command", None)
if sub == "status":
print("Provider is active and connected.")
elif sub == "config":
print("Showing config...")
else:
print("Usage: hermes my-provider <status|config>")
def register_cli(subparser) -> None:
"""Build the hermes my-provider argparse tree.
Called by discover_plugin_cli_commands() at argparse setup time.
"""
subs = subparser.add_subparsers(dest="my_command")
subs.add_parser("status", help="Show provider status")
subs.add_parser("config", help="Show provider config")
subparser.set_defaults(func=my_command)Reference implementation
ดูที่ plugins/memory/honcho/cli.py สำหรับตัวอย่างเต็มรูปแบบที่มี 13 subcommands, การจัดการข้าม profile (--target-profile), และการอ่าน/เขียน config
โครงสร้างไดเรกทอรีพร้อม CLI
plugins/memory/my-provider/
├── __init__.py # MemoryProvider implementation + register()
├── plugin.yaml # Metadata
├── cli.py # register_cli(subparser) - CLI commands
└── README.md # Setup instructionsกฎของ Single Provider
สามารถมี Memory provider ภายนอกที่ใช้งานได้เพียง หนึ่งเดียว เท่านั้น หากผู้ใช้พยายามลงทะเบียนตัวที่สอง MemoryManager จะปฏิเสธด้วยคำเตือน สิ่งนี้ช่วยป้องกันปัญหา tool schema bloat และ backend ที่ขัดแย้งกัน
📄 developer-guide/prompt-assembly.md
sidebar_position: 5 title: "Prompt Assembly" description: "How Hermes builds the system prompt, preserves cache stability, and injects ephemeral layers"
Prompt Assembly
Hermes แยกส่วนอย่างจงใจระหว่าง:
- cached system prompt state
- ephemeral API-call-time additions
นี่คือหนึ่งในการตัดสินใจด้านการออกแบบที่สำคัญที่สุดในโปรเจกต์นี้ เพราะมันส่งผลกระทบต่อ:
- token usage
- prompt caching effectiveness
- session continuity
- memory correctness
ไฟล์หลัก:
run_agent.pyagent/prompt_builder.pytools/memory_tool.py
Cached system prompt layers
ระบบ prompt ของ agent ที่ถูกแคชจะถูกประกอบขึ้นตามลำดับโดยประมาณดังนี้:
- agent identity -
SOUL.mdจากHERMES_HOMEเมื่อมีอยู่ มิฉะนั้นจะใช้DEFAULT_AGENT_IDENTITYในprompt_builder.pyเป็นค่าสำรอง - tool-aware behavior guidance
- Honcho static block (เมื่อเปิดใช้งาน)
- optional system message
- frozen MEMORY snapshot
- frozen USER profile snapshot
- skills index
- context files (
AGENTS.md,.cursorrules,.cursor/rules/*.mdc) - SOUL.md จะ ไม่ ถูกรวมอยู่ในส่วนนี้เมื่อถูกโหลดเป็น identity ในขั้นตอนที่ 1 แล้ว - timestamp / optional session ID
- platform hint
เมื่อตั้งค่า skip_context_files (เช่น การมอบหมายงานให้ subagent) SOUL.md จะไม่ถูกโหลด และจะใช้ DEFAULT_AGENT_IDENTITY ที่กำหนดไว้แบบ hardcode แทน
ตัวอย่างที่เป็นรูปธรรม: assembled system prompt
นี่คือมุมมองแบบง่ายๆ ว่าระบบ prompt สุดท้ายมีหน้าตาอย่างไรเมื่อมีทุก layer (ความคิดเห็นแสดงแหล่งที่มาของแต่ละส่วน):
# Layer 1: Agent Identity (from ~/.hermes/SOUL.md)
You are Hermes, an AI assistant created by Nous Research.
You are an expert software engineer and researcher.
You value correctness, clarity, and efficiency.
...
# Layer 2: Tool-aware behavior guidance
You have persistent memory across sessions. Save durable facts using
the memory tool: user preferences, environment details, tool quirks,
and stable conventions. Memory is injected into every turn, so keep
it compact and focused on facts that will still matter later.
...
When the user references something from a past conversation or you
suspect relevant cross-session context exists, use session_search
to recall it before asking them to repeat themselves.
# Tool-use enforcement (for GPT/Codex models only)
You MUST use your tools to take action - do not describe what you
would do or plan to do without actually doing it.
...
# Layer 3: Honcho static block (when active)
[Honcho personality/context data]
# Layer 4: Optional system message (from config or API)
[User-configured system message override]
# Layer 5: Frozen MEMORY snapshot
## Persistent Memory
- User prefers Python 3.12, uses pyproject.toml
- Default editor is nvim
- Working on project "atlas" in ~/code/atlas
- Timezone: US/Pacific
# Layer 6: Frozen USER profile snapshot
## User Profile
- Name: Alice
- GitHub: alice-dev
# Layer 7: Skills index
## Skills (mandatory)
Before replying, scan the skills below. If one clearly matches
your task, load it with skill_view(name) and follow its instructions.
...
<available_skills>
software-development:
- code-review: Structured code review workflow
- test-driven-development: TDD methodology
research:
- arxiv: Search and summarize arXiv papers
</available_skills>
# Layer 8: Context files (from project directory)
# Project Context
The following project context files have been loaded and should be followed:
## AGENTS.md
This is the atlas project. Use pytest for testing. The main
entry point is src/atlas/main.py. Always run `make lint` before
committing.
# Layer 9: Timestamp + session
Current time: 2026-03-30T14:30:00-07:00
Session: abc123
# Layer 10: Platform hint
You are a CLI AI Agent. Try not to use markdown but simple text
renderable inside a terminal.How SOUL.md appears in the prompt
SOUL.md อยู่ที่ ~/.hermes/SOUL.md และทำหน้าที่เป็น identity ของ agent - ซึ่งเป็นส่วนแรกสุดของระบบ prompt ลอจิกการโหลดใน prompt_builder.py ทำงานดังนี้:
# From agent/prompt_builder.py (simplified)
def load_soul_md() -> Optional[str]:
soul_path = get_hermes_home() / "SOUL.md"
if not soul_path.exists():
return None
content = soul_path.read_text(encoding="utf-8").strip()
content = _scan_context_content(content, "SOUL.md") # Security scan
content = _truncate_content(content, "SOUL.md") # Cap at 20k chars
return contentเมื่อ load_soul_md() ส่งคืน content มันจะแทนที่ DEFAULT_AGENT_IDENTITY ที่กำหนดไว้แบบ hardcode จากนั้นจะเรียกใช้ฟังก์ชัน build_context_files_prompt() ด้วย skip_soul=True เพื่อป้องกันไม่ให้ SOUL.md ปรากฏซ้ำสองครั้ง (ครั้งหนึ่งเป็น identity และอีกครั้งเป็น context file)
หาก SOUL.md ไม่มีอยู่ ระบบจะใช้ค่าสำรองดังนี้:
You are Hermes Agent, an intelligent AI assistant created by Nous Research.
You are helpful, knowledgeable, and direct. You assist users with a wide
range of tasks including answering questions, writing and editing code,
analyzing information, creative work, and executing actions via your tools.
You communicate clearly, admit uncertainty when appropriate, and prioritize
being genuinely useful over being verbose unless otherwise directed below.
Be targeted and efficient in your exploration and investigations.How context files are injected
build_context_files_prompt() ใช้ priority system - โดยจะโหลด project context ได้เพียงประเภทเดียว (first match wins):
# From agent/prompt_builder.py (simplified)
def build_context_files_prompt(cwd=None, skip_soul=False):
cwd_path = Path(cwd).resolve()
# Priority: first match wins - only ONE project context loaded
project_context = (
_load_hermes_md(cwd_path) # 1. .hermes.md / HERMES.md (walks to git root)
or _load_agents_md(cwd_path) # 2. AGENTS.md (cwd only)
or _load_claude_md(cwd_path) # 3. CLAUDE.md (cwd only)
or _load_cursorrules(cwd_path) # 4. .cursorrules / .cursor/rules/*.mdc
)
sections = []
if project_context:
sections.append(project_context)
# SOUL.md from HERMES_HOME (independent of project context)
if not skip_soul:
soul_content = load_soul_md()
if soul_content:
sections.append(soul_content)
if not sections:
return ""
return (
"# Project Context\n\n"
"The following project context files have been loaded "
"and should be followed:\n\n"
+ "\n".join(sections)
)Context file discovery details
| Priority | Files | Search scope | Notes |
|---|---|---|---|
| 1 | .hermes.md, HERMES.md | CWD up to git root | Hermes-native project config |
| 2 | AGENTS.md | CWD only | Common agent instruction file |
| 3 | CLAUDE.md | CWD only | Claude Code compatibility |
| 4 | .cursorrules, .cursor/rules/*.mdc | CWD only | Cursor compatibility |
Context files ทั้งหมดจะผ่านกระบวนการ:
- Security scanned - ตรวจสอบรูปแบบ prompt injection (invisible unicode, "ignore previous instructions", credential exfiltration attempts)
- Truncated - ถูกจำกัดความยาวสูงสุดที่ 20,000 characters โดยใช้อัตราส่วน head/tail 70/20 พร้อมเครื่องหมาย truncation
- YAML frontmatter stripped - ส่วน frontmatter ของ
.hermes.mdจะถูกลบออก (สงวนไว้สำหรับการ override config ในอนาคต)
API-call-time-only layers
ส่วนเหล่านี้ถูกตั้งใจให้ ไม่ ถูกบันทึกเป็นส่วนหนึ่งของ cached system prompt:
ephemeral_system_prompt- prefill messages
- gateway-derived session context overlays
- later-turn Honcho recall injected into the current-turn user message
การแยกส่วนนี้ช่วยให้ stable prefix ยังคงเสถียรสำหรับการแคช
Memory snapshots
ข้อมูล memory ท้องถิ่นและ user profile จะถูกฉีดเป็น frozen snapshots เมื่อเริ่ม session การเขียนข้อมูลระหว่าง session จะอัปเดตสถานะบน disk แต่จะไม่เปลี่ยนแปลง system prompt ที่สร้างขึ้นแล้ว จนกว่าจะเกิด session ใหม่หรือมีการบังคับ rebuild
Context files
agent/prompt_builder.py จะสแกนและ sanitize context files ของโปรเจกต์โดยใช้ priority system - โดยจะโหลดได้เพียงประเภทเดียว (first match wins):
.hermes.md/HERMES.md(walks to git root)AGENTS.md(CWD เมื่อเริ่มต้น; subdirectories จะถูกค้นพบอย่างต่อเนื่องระหว่าง session ผ่านagent/subdirectory_hints.py)CLAUDE.md(CWD เท่านั้น).cursorrules/.cursor/rules/*.mdc(CWD เท่านั้น)
SOUL.md ถูกโหลดแยกต่างหากผ่าน load_soul_md() สำหรับ slot ของ identity เมื่อโหลดสำเร็จ build_context_files_prompt(skip_soul=True) จะป้องกันไม่ให้มันปรากฏซ้ำ
ไฟล์ที่มีความยาวมากจะถูก truncate ก่อนการ injection
Skills index
ระบบ skills จะช่วยเพิ่ม skills index ที่กระชับเข้าไปใน prompt เมื่อมีเครื่องมือ skills tooling ให้ใช้งาน
Why prompt assembly is split this way
สถาปัตยกรรมนี้ถูกออกแบบมาโดยเจตนาเพื่อ:
- preserve provider-side prompt caching
- avoid mutating history unnecessarily
- keep memory semantics understandable
- let gateway/ACP/CLI add context without poisoning persistent prompt state
Related docs
extent analysis
TL;DR
To resolve the issue, identify the root cause by analyzing the error message and the code, then apply the necessary fix, which may involve modifying the code, updating dependencies, or adjusting configuration settings.
Guidance
- Analyze the error message: Carefully read the error message to understand the nature of the issue.
- Review the code: Examine the code to identify potential causes of the error, such as syntax errors, incorrect function calls, or missing dependencies.
- Check dependencies and versions: Verify that all dependencies are up-to-date and compatible with the current version of the code.
- Apply the fix: Based on the analysis, apply the necessary fix, which may involve modifying the code, updating dependencies, or adjusting configuration settings.
- Test the fix: Thoroughly test the fix to ensure that it resolves the issue without introducing new problems.
Example
Since the issue body does not provide a specific error message or code snippet, it is not possible to provide a concrete example. However, in general, when encountering an error, it is essential to analyze the error message, review the code, and apply the necessary fix.
Notes
- The issue body appears to be a collection of documentation pages for the Hermes Agent project, which provides a framework for building conversational AI models.
- The pages cover various topics, including environments, benchmarks, data generation, and memory provider plugins.
- Without a specific error message or code snippet, it is challenging to provide a precise solution.
Recommendation
Based on the information provided, it is recommended to review the documentation pages carefully and identify the specific section or code snippet that is causing the issue. Then, apply the necessary fix, which may involve modifying the code, updating dependencies, or adjusting configuration settings. If the issue persists, consider seeking additional support from the Hermes Agent community or a qualified developer.
Vote matrix · Quick signals
Still need to ship something?
×6Another batch ranked right after the header list — different links, same matching logic.
TRENDING
- Feature Request: Configurable per-minute rate limiting (RPM) for models to prevent 429 errors
- Android: Hermes App + Termux install share ~/.hermes and cause silent permission loops
- hermes update emits unicode-animations ANSI demo in non-interactive logs
- hermes update downgrades aiohttp from 3.13.4 to 3.13.3
- npm install warns about deprecated @babel/plugin-proposal-private-methods
- DingTalk inbound media URLs are skipped as unreadable native image paths
- fix(dashboard): ChatPage clears header action buttons on ALL pages, not just Sessions
- [Bug]: check_web_api_key() hardcodes built-in backends — third-party web search plugins silently disabled
- Hermes Web UI 修复经验:GatewayManager 补丁、进程 D 状态、数据库升级问题
- Telegram gateway can silently drop turn after /stop with response=0 chars while internal work continues
- Bug Report: v0.14.0 上下文污染 — 历史回复碎片回注到新请求
- Bug: hermes skills search table truncates Identifier column — install fails with copied value
- [skills-index-watchdog] Skills index is stale or degraded (degraded)
- Discord approval embed not rendering on web/mobile — embed data present in API but invisible
- Idea: Discord voice-channel participation / opt-in auto-join mode
- [Feature]: Claude Code--ultrawork
- build-arm64 job deterministically fails on cold cache (Azure SAS token expires mid-build)
- [Enhancement] computer_use: action=type should fall back to key events for terminal emulators (Ghostty/Terminal.app/iTerm2)
- Feature Request: Session Recovery on Temporary Provider Outage
- [Bug]: Hermes dashboard not working on NixOS (container)
- [Feature]: Add option to ignore @all/@everyone mentions in Feishu group chats
- QQ Bot WebSocket 频繁断开:长时间工具执行阻塞 asyncio 事件循环导致心跳超时
- patch tool: new_string escape sequences (\t) get written literally
- Feature Request: i18n / 多语言支持(国际化)
- Bug: web_crawl schema lets models auto-guess "instructions" instead of asking the user via clarify
- feat: `!command` prefix for direct shell execution (like Claude Code)
- Expose currently-running cron jobs via /api/jobs (or new endpoint)
- [Bug]: Kanban parent-child handoff: scratch workspace GC destroys artifacts before child can read them
- [Bug, Windows] hermes gateway restart loses session context — planned_stop_marker not written before SIGTERM
- [Bug]: Codex→DeepSeek fallback sends assistant turns without reasoning_content → HTTP 400 (require-side cross-provider failover)
- [Bug]: Update got stuck half way, reboot it, then ModuleNotFoundError: No module named 'hermes_cli'
- Kanban dispatcher corrupt-board handling and multi-profile gateway ownership ambiguity
- Gateway can resend a short fallback message when the real final Telegram response was already delivered
- [BUG] Bedrock: Fix 'Invalid API Key format' for presigned URL tokens
- Secret redaction corrupts code syntax in tool output (write_file, execute_code, terminal)
- Unable to connect Ollama Cloud with Pro Subscription to Hermes
- feat: fuzzy substring matching for /skill autocomplete
- PRD: Autonomous market-impact prediction briefing system
- Kanban dashboard should support task/card deep links
- [Feature] Native Feishu CardKit Streaming: consolidate best-in-class implementations
- [Feature]: Inject mental model into context when using Hindsight
- Interactive CLI hides tool output despite display.tool_progress=all, and hermes chat -v does not restore it
- fix(api_server): _handle_responses drops text.format JSON schema — structured output constraints silently ignored
- state.db FTS corruption goes undetected — no integrity check, no repair path
- bug: fallback routing can select text-only models for image requests and hide the primary failure
- feat(kanban): persist worker session_id per run and pass --resume on respawn after unblock
- feat(kanban): support GitHub/OMO lifecycle bridge for Xiyou-style automation
- Expose update-safe TUI/composer hooks for voice transcript and composer events
- Hide or configure voice transcript status rows in editable dictation mode
- [Feature]: Per-Tool / Per-Toolset Approval Policies
- Context compression creates orphan sessions missing from state.db
- messaging platform
- feat: Add read-only / silent monitoring mode for WhatsApp adapter
- double-.hermes path mismatch, the HOME env var leak, and the fallback-notification UX problem
- Bug: Plattform-Bundle name `hermes-yuanbao` in `agent.disabled_toolsets` silently kills ALL tools in gateway path (Telegram + cron), CLI unaffected
- CLI /yolo (in-chat) does not bypass dangerous command approvals — env var freeze + missing enable_session_yolo call
- OpenAI Codex provider crashes with "'NoneType' object is not iterable" (HTTP None)
- DEEPSEEK_API_KEY blocked by env blocklist in gateway process — cron jobs fail with deepseek provider
- fix(feishu): Card action callback routing issues - invalid message_id and unrecognized /card command
- Discord plugin: profiles without explicit `discord:` block silently get `require_mention=true` + `auto_thread=true` (regression in cc8e5ec2a)
- [Bug]: DISCORD_ALLOWED_ROLES ignored by gateway _is_user_authorized — role-authorized users get 'Unauthorized user' rejection
- [Bug]: /new, /clear, and /reset commands freeze the terminal session
- openai-codex subscription backend returns HTTP 200 with response.output=None, causing Slack/cron failures
- RFC: Centralized Model/Provider Registry
- bug: openai-codex provider — TypeError: 'NoneType' object is not iterable on every request (gpt-5.5)
- [Feature]: Source-aware instruction gate — architectural mitigation for indirect prompt injection
- Named custom provider stale_timeout_seconds ignored because runtime provider is normalized to `custom`
- guard test (ignore)
- [Feature]: per-platform LLM request_overrides (extra_body / reasoning_effort / service_tier)
- One-shot smoke: add Flue-backed orchestration fixture
- Gateway should not treat stale Codex app-server progress as final response after post-tool silence
- `docker_run_as_host_user: true` breaks bundled skills: Hermes home is mounted into `/root/.hermes` but the container runs as a non-root user (`HOME=/home/pn`)
- [Bug]: gateway api_server streaming bypasses server-side tool-call loop when chat_template_kwargs.enable_thinking=false (model emits tool name as plain text)
- [Feature]: Pre-install python-telegram-bot in Umbrel Hermes Docker image
- YouTube Shorts filter not working in youtube-content skill
- v0.15.0 PyPI release breaks ALL platforms — plugin.yaml manifests missing from package
- RFC: On-demand tool/skill/MCP discovery — decouple schema registration from process lifecycle
- Pixshelf: local-first stock photo workflow command center
- [Bug]: baoyu infographic skill should not silently bypass image_generate
- Pixshelf v1.5: manual submission tracking for stock agencies
- `hermes config set` silently accepts unknown keys, writing them where the runtime never reads
- Honcho memory prefetch hang on fresh CLI subprocess in v0.15.0 (regression from #27190)
- [Bug] v0.15.0 Docker image: stage2-hook.sh, main-wrapper.sh missing; container_boot module removed
- Feature: Reduce cache-read token overhead for DeepSeek providers — configurable cache_ttl, skills snapshot trimming, memory compaction
- Windows: three bugs from daily use (plugin discovery, gateway exit code, Unicode decode
- holographic memory: HRR silently degrades to FTS5 when numpy is missing
- Make max_tokens configurable for aux vision calls
- Conversation compression desynchronizes session ID between agent context and gateway routing, causing silent message loss
- [Bug]: v0.15.0 Docker image:The TUI cannot be used in the dashboard.
- cron: skip_memory=True blocks fact_store/memory tools from all cron jobs
- TUI: Node.js OOM crash when agent uses browser tools repeatedly
- feat: model_profiles — per-model toolset and memory config
- Automatic background skill patching disrupts active sessions (severe impact on local models)
- ensure_hermes_home() creates root-owned dirs in profile subdirectories when kanban workers are dispatched
- Feature: opt-in webhook bypass for DISCORD_ALLOW_BOTS — allow operator-initiated probes without weakening bot-loop guard
- v0.15.0: Codex requests fail HTTP 400 when participant display_name contains non-ASCII (emoji breaks input[].name pattern)
- Architecture: State Persistence Precedence (Memory vs Skills vs Hooks)
- [Bug]: cronjob tool: create action always fails with "schedule is required for create" even when parameters are provided
- codex-oauth: 'NoneType' object is not iterable in _run_codex_stream (gpt-5.5) — every turn fails non-retryably
- Docs/Config: Plugin local scope enablement ambiguity
- [Bug]: CLI freezes after using /new command (WSL)
- Profile Codex auth can ignore global credential pool when local state is stale
- [workflow-engine] CRITICAL: variable substitution crashes on regex metachars in user input
- [workflow-engine] HIGH: loop and bash nodes leak subprocesses on timeout
- [workflow-engine] HIGH: README documents config env vars the engine never reads
- [workflow-engine] MEDIUM: workflow_run rate limit bypassable via concurrent calls (TOCTOU)
- [workflow-engine] chore: manifest gaps, side-effectful register(), dead code, unauth kanban dispatch
- [mcp_lazy] HIGH: synthetic mcp_server_<name> stub collides with a real MCP server named 'server'
- [mcp_lazy] HIGH: promote_server eager flag documented but never persisted
- [mcp_lazy] MEDIUM: _prev_mode dict leaks and goes stale; not cleared on session evict
- [mcp_lazy] MEDIUM: get_pool has unlocked check-then-set race on pool creation
- [mcp_lazy] MEDIUM: pre_tool_call gives no guidance for unpromoted server-stub calls
- [mcp_lazy] chore: undeclared pre_tool_call hook, nonexistent 'mcp_load_tools' name in docs, missing tests
- [a2a_fleet] CRITICAL: server never auto-starts — register() runs outside an event loop
- [a2a_fleet] CRITICAL: auth_required defaults to false on a cross-machine surface
- [a2a_fleet] HIGH: remove invented disable() hook — loader never calls it, port leaks on reload
- [a2a_fleet] HIGH: plugin.yaml missing kind / provides_tools / requires_env (token env undeclared)
- [a2a_fleet] MEDIUM: tighten wide-open CORS, anonymous /health peer leak, and peer-URL SSRF
- [a2a_fleet] MEDIUM: relocate tests to tests/plugins/ and cover sync-register + auth-default paths
- xai-oauth auxiliary client incorrectly uses Responses API (CodexAuxiliaryClient), causing 403 on compression/vision/web_extract
- [Bug]: Direct Copilot gpt-5.5 large resumes are killed by 12s Codex TTFB watchdog
- [Bug]: `hermes uninstall` does not work on Windows
- TUI: Thinking block leaks raw JSON and Σ character
- Hostinger VPS: migration Hermes Agent → Hermes WebUI impossible (tini + UID mismatch + sessions)
- /goal judge over-continues exploratory goals unless the assistant explicitly says the goal is complete
- /goal auto-continuation can be amplified by preflight compression/session split and resurrect stale task state
- Dashboard infinite reload loop in loopback mode — GET /api/auth/me returns 401 on every page load
- [Bug]: Provider/LLM switch leaves stale encrypted_content causing 400 errors on Telegram sessions
- [Bug]: Infinite reload loop / React state loop on Sessions tab (Firefox + Chrome) — repeated 401 on /api/auth/me (v0.15.0)
- show_reasoning should work independently of streaming in CLI mode
- Feature Request: Strip reasoning/<think> blocks from TTS preprocessing
- mcp add / mcp test raise NameError when mcp package not installed
- v0.14.0 dashboard breaks behind reverse proxies — two regressions
- Skills hub creates empty category directories when no skills installed
- [Bug]: Custom endpoint: ChatCompletions returns content, but Hermes treats response as empty (v0.14.0)
- fix: atomic_replace() fails with EXDEV when HERMES_HOME is a cross-filesystem symlink
- fix(gateway): Feishu session cancellation orphans session guard, permanently blocking messages
- Custom endpoint pricing can overestimate Crof qwen3.5-9b cost by 1,000,000x
- MCP OAuth callback: module-level port global causes port collisions and structural weaknesses vs upstream
- Bug: send_message tool bypasses validate_media_delivery_path security check
- Proposal: Add Mnemosyne to official memory provider documentation
- feat(swarm): support custom verifier/synthesizer body + skills
- Template conversion failed
- Error occurred in the operation of the agent node in the workflow.
- PubSub client overrides Sentinel client when REDIS_USE_SENTINEL is enabled
- Frontend description of the Retrieval node output does not match the actual output
- JSON type input var raise Intenal server error
- cannot extract elements from a scalar
- 负载均衡 为模型配置多组凭据,并自动调用,此功能无法选择
- add models is error
- panic: could not create filter
- Persist partially generated messages when /chat-messages/:task_id/stop is called
- MCP server connection fails with 403 — request never leaves Dify (SSRF proxy suspected)
- Support durable async execution backends for long-running workflow steps
- [Xiaomi MiMo] Credentials validation fails with 400 "Not supported model mimo-v2-flash" when using Token Plan endpoint (v0.0.7)
- After clicking preview on a parent-child segmented knowledge base, it shows 0 chunks
- Retrieval score differs between UI upload (.docx) and API upload (.txt) despite identical chunk content and embedding model
- gemini cli crash again
- Xbox gift card code damage
- Damage caused by the gemini cli crash
- ioctl(2) failed, EBADF (Bad File Descriptor)
- Feat: Support Bun as an alternative runtime/package manager for updates and extensions
- fatal error again!!!!
- ioctl error
- Critical Crash: ioctl(2) failed, EBADF in ShellExecutionService.resizePty
- ioctl(2) failed, EBADF
- v0.44.0 Regression: Critical crash with ioctl(2) failed, EBADF during PTY resize
- Crash on startup: ioctl(2) failed, EBADF in UnixTerminal.resize
- Crash: `ioctl(2) failed, EBADF` in `node-pty` during PTY resize on macOS
- Gemini CLI crashes with `ioctl(2) failed, EBADF` in `node-pty` during `resizePty`
- Remote Role
- ERROR ioctl(2) failed, EBADF /home/mich
- RangeError: Maximum call stack size exceeded
- EBADF Error during folder creationg broke session and terminal glitches
- MAIP / Gargoub Project - Mediterania - North Coast
- Gemini cli crash again in this morning
- ERROR ioctl(2) failed, EBADF
- Verified node install fails — Checksum verification failed (Cloud)
- The extended debugging key did not arrive during registration.
- CollaborationPane unmounts collaboration store on single-user instances, causing permanent "No network connection" state
- Workflow cannot be saved when the name contains "->" (Potentially malicious string)
- automation does not work and does not show an error
- Raj Ai Automation
- Default Data Loader: DOMMatrix is not defined error
- Feature: Per-node execution timestamp overlay on canvas during workflow run
- AI Agent + Vertex `gemini-3.5-flash`: 400 "missing thought_signature" on sequential multi-turn tool calls (post-#24982)
- PDF Loader in Pinecone Vector Store fails due to pdf-parse version conflict (v2 not supported)
- emailReadImap: add UID deduplication, batch size cap, and numeric uid enforcement
- Manual node execution fails with "Could not find a node" when autosave is disabled (N8N_WORKFLOWS_AUTOSAVE_DISABLED)
- Schedule Trigger stopped firing — workflow Published & active, manual executions succeed, no automated fires for 2+ hours
- [MCP SDK] create_workflow_from_code intermittently returns HTTP 500, often as a false negative (workflow persists anyway, causing duplicates on retry)
- Credential-load wedge: workflows using googleApi/jwtAuth credentials silently fail to execute after key rotation
- Google Sheets Trigger every minute is not working manual Execute is working sent email
- [BUG] Plugin marketplace MCP connector remains stuck "still connecting" when mcp-remote requires OAuth
- [redacted at user request]
- Opus 4.7 behavioral regression: loaded instruction-following discipline degraded in recent Claude Code/Cowork updates
- [BUG] Tailscale via Homebrew CLI + Mac App Store GUI, both Macs on macOS, Cowork blocked by VPN detector despite Tailscale being a mesh VPN with no traffic interception
- stopShellPty on tab switch kills active sessions (exit 143) — regression in May 27 build
- [BUG] Long URLs are broken into multiple lines and become unclickable in terminal output
- [BUG] claude rm/stop/reap SIGKILLs background session tree without SIGTERM grace, orphaning git index.lock and similar
- [BUG] Default git workflow in the system prompt was pushed without context or consent
- [MODEL] Inconsistent output quality / Ignoring instructions (overfitting and inappropriate repetition of Korean vocabulary)
- You've hit your weekly limit · resets May 31 at 5pm (Asia/Shanghai)
- Paid yearly subscription silently downgraded to Free with no user action
- [Regression v2.1.153] Plugin bash hooks fail with "echo: write error: Permission denied" on Windows (claude-mem, shell: "bash")
- [BUG] Connector toggles in conversation are not clickable — must click text label instead
- [remote-control] Input from mobile app/browser not reaching host session — output works fine
- Model fails to read/reference CLAUDE.md contents despite being loaded in context
- [BUG] Claude Desktop reinstall destroys Code chat history (transcripts + Recents) while regular Chat history, project files, and memory all survive
- Bypass mode clamps to Accept Edits even with the toggle ON (Claude Code Desktop 1.9255.2 / CC 2.1.149)
- [BUG] TUI input freezes randomly mid-typing — entire prompt becomes unresponsive for minutes
- [BUG] Cowork downloads Linux ELF binary instead of macOS binary on macOS Sonoma 14.8.7 — exit code 132 (SIGILL) on every session
- [Feature Request] Persistent project memory — sessions forget everything on close, forcing users to keep many sessions open
- [Bug] Thread context stale after sleep/resume, returns outdated date and calendar data
- [FEATURE] Add context window usage indicator and warning before auto-compaction
- [BUG] Dictation error: Invalid character in header content ["x-config-keyterms"] on Windows
- [Bug] Anthropic API Error: Server rate limiting despite normal usage
- Does delegating work to `claude -p` subprocesses reduce context accumulation in the parent session?
- [BUG] Claude Code hangs on M1 Mac when terminal says "opening browser to sign in" and browser opens
- [BUG] Claude_Preview MCP preview_start spawns dev server with main-repo cwd instead of session's worktree cwd
- [Bug] Anthropic API Error: Server rate limiting during request execution
- [Bug] Anthropic API Error: Server rate limiting on concurrent requests
- [Bug] Ultraplan ready notification fires before cloud agent completes execution
- [BUG] API 500 ERROR ALL THROUGHOUT THE DAY
- [BUG] Cowork: Live Artifacts folder path changed in 1.9255.2, no automatic migration from Documents\Claude\Artifacts
- [Bug] Auto-compact never triggers despite statusline reporting "100% context used" (v2.1.153, Max sub, 200K mode)
- [BUG] [Desktop / macOS] 'Open in → New Window' detached session: font renders smaller than main, no per-window controls, Cmd+/Cmd- keystrokes routed to main window instead
- Feature request: option to switch between classic and new minimal UI
- [Feature Request] Show timestamps for each message
- [BUG] Terminal corruption when permission prompt appears while navigating Agent Teams agent selection menu
- [FEATURE] Allow users to customize the background color of the Claude desktop app beyond the current light/dark theme presets.
- [BUG] Statusline not displaying on Windows [fixed]
- Background agent UI Stop button is a no-op for stuck agents — process keeps consuming tokens
- Background agents silently die on session pause/resume — no completion notification, no work recovery
- Add option to hide email address from welcome banner
- [BUG] SSH Remote: `projects` field in remote ~/.claude.json becomes null after desktop restart — jsonl files intact, UI shows 'No messages yet' for every session
- [Bug] Claude Code not applying fixes despite claiming to complete tasks
- billing is unfair and poorly documented
- [BUG] Claude Code on the web: declared plugins inactive on first session, require restart to fully load
- [BUG] Restore from archive deleted sessions instead of restoring them
- [BUG] M365 connector fails with AADSTS50011 in Cowork — localhost vs 127.0.0.1 redirect URI mismatch
- claude agents: workflow slash-commands missing from dispatch-input completion (regression-adjacent to #61424)
- Claude Desktop's Info.plist missing TCC usage strings, blocks all EventKit-based MCP servers
- False-positive safety blocks on self-administered governance amendments — request for owner-authority mode for verified professional users
- [BUG] Stop pushing "AUTO"-mode
- [DOCS] Plugin marketplace guide omits `skipLfs` option for git-based sources
- [DOCS] MCP docs omit combined startup notification for MCP server and connector authentication
- [DOCS] Agent view docs omit macOS Privacy & Security identity for background agents
- [DOCS] Npm update docs do not explain release-channel behavior for `claude update`
- [DOCS] Agent SDK docs omit `subagent_type: "claude"` worktree and output persistence behavior
- [DOCS] Background session docs omit `$CLAUDE_JOB_DIR` temp-file behavior
- [FR] mask env-var values in 'claude mcp get <server>' output
- [FR] subagent worktrees should not inherit stale local 'user.email' from prior dispatches
- [BUG] Windows: Grep tool leaks rg.exe + conhost.exe processes (~2000 zombies / 14 GB RAM in long sessions)
- [BUG] Stats dashboard "Peak hour" appears off by one hour
- [BUG] Diff highlight (teal SGR background) bleeds past changed text in 2.1.150–2.1.153
- [FEATURE] confirm before deleting session
- Plugin PostToolUse hooks still silently skip in Claude Desktop / Cowork (re-filing closed #51904)
- /code-review skill: silent fallback to main...HEAD reviews other people's commits, and JSON-only output is hard to read
- Monitor tool doesn't source the shell snapshot like Bash does; PATH-dependent tools (jq, sleep, etc.) fail in Monitor commands on macOS/Nix
- [Bug] Long input lines truncated with ellipsis while typing instead of wrapping in terminal UI
- [FEATURE] VS Code extension: Render submitted user messages as Markdown in chat
- OSC 52 copy from Claude TUI doesn't reach clipboard inside tmux (regression in 2.1.146–2.1.153)
- [BUG] RemoteTrigger create/update returns HTTP 400 with circular error: "event_type is required" / "unknown field event_type"
- [BUG] Option to hide or minimize the built-in "status footer" (multi-line debug/cost panel) [re-raise of #31475]
- [Bug] Feedback submissions being closed without review or action
- [FEATURE] Word-jump cursor navigation in Chat input (option+arrow / bindable actions)
- [FEATURE] ! shell mode: filesystem tab completion
- [BUG] API Error: Usage credits required for 1M context
- claude agents: OSC 52 clipboard emission broken in tmux (regression in 2.1.146–2.1.153)
- CLI crashes on macOS 15 M3 - exit code 1
- [FEATURE] Support Cmd+V image paste from clipboard
- [FEATURE] Enhance claude.ai M365 connector to support MS Planner
- [BUG] Slash command autocomplete hijacks pasted absolute file paths starting with /
- PreToolUse hook `if` filter false-positives on complex Bash commands
- [BUG] Diff panel hangs/whites out
- Feature Request: Support drag-and-drop for binary documents (.wps, .doc, .docx, .xlsx, .pdf) in VS Code extension
- [BUG] activation of 1M context in VSCode
- [FEATURE] Support i18n / language localization for built-in slash command outputs
- Ctrl+V para colar imagens deixou de funcionar no CLI (Windows, PowerShell)
- [FEATURE] Please add Norwegian (Bokmål/Nynorsk) language support to the Claude Code interface
- [BUG] OTel log events (claude_code.user_prompt, api_request_body, tool_decision, hook_execution_complete) emitted with empty trace_id/span_id while sibling spans correlate correctly
- [BUG] Cowork crashes on every message, no VM logs generated, missing AppData\Roaming\Claude
- [FEATURE] first-class session handoff + per-session token budgets for unattended runs
- [FEATURE] Smart paste: convert clipboard code to file reference chips (like Cursor)
- [Feature Request] Restore chat pin functionality to title chat submenu
- [BUG] SIGILL issues with version 2.1.153
- [BUG] Cowork plugin upload fails with generic "Plugin validation failed" when a `description` field in any SKILL.md frontmatter contains angle brackets (`<…>`)
- [BUG] Desktop App 2.1.144+: startup scanner deletes cliSessionId from claude-code-sessions local files on every launch — session not found on disk
- [Feature Request] Add keyboard shortcut to copy last message with proper formatting
- [MODEL] Opus 4.7 not 1M
- Allow naming/renaming background agents in `claude agents` view
- Stale worktrees in .claude/worktrees/ are never cleaned up, consuming massive disk space
- Agent worktrees are never cleaned up, silently consuming disk space
- Subagent worktrees not auto-cleaned when reviewer writes scratch files
- [Bug] Skill initialization hangs for extended duration in Plan Mode
- Claude Desktop writes malformed registry Run entry (nested escaped quotes) - crashes Windows Task Manager and other Run-key parsers
- IME candidate window shows at bottom-right corner instead of caret position (Windows CMD)
- [BUG] Pressing 'Escape' doesn't close the /BTW conversation when the main conversation is asking for approval
- [BUG] Opus 4.7 (1M) intermittently emits empty-string values for tool_use.input fields, killing the session
- FleetView agent UI shows "running" with incrementing elapsed time after agent has returned
- /doctor flags context-scoped cmd+c binding as macOS conflict (false positive)
- [BUG] Text Rendering in Elvish
- Desktop app: Bypass Permissions mode flips to Accept Edits on first prompt (M5 / macOS 26.5)
- [Workaround] Date-Weekday Verification Hook — Prevents Claude from writing wrong weekdays
- [BUG] Claude Code create c:/memfs directory without asking me.
- [BUG] Claude Code's Bash execution waits forever with no processes running
- [BUG] usage stays stuck waiting for 5 hr limit after upgrading to premium seat in team plan
- [Workflow tool] resume cache is unreachable for nontrivial workflows because LLM dispatchers can't transcribe args byte-exactly
- Code review (Preview): "Add a repository" shows no results for private GitHub org repos
- [BUG] /context commands blows up context
- [Feature Request] Add precache expiry hook to enable proactive compaction before token eviction
- [BUG] Context indicator shows 0% at session start despite ~20K+ tokens already loaded
- [Feature Request] Add semantic search for --resume session history
- [Feature Request] Add session search, tagging, and filtering capabilities
- [BUG] Cowork Dispatch reports "desktop not available" on Windows 11 while standard Cowork works normally
- [Bug] Claude Code provides incorrect suggestions with high confidence despite errors
- defaultMode: acceptEdits silently overrides per-path permissions.ask rules for Write/Edit
- [FEATUR configurable tip interval (e.g. tipIntervalSeconds: 30 in settings)E]
- Plugin marketplace fails to load: schema rejects 'displayName' key (v2.1.153)
- claude agents: in-session copy uses broken OSC 52 path while overview correctly uses tmux buffer
- [BUG] Plugin agent descriptions (and custom agents) load unconditionally into context — no parity with disable-model-invocation for skills
- Crashed ultrareview consumed a free credit despite producing zero findings
- [Bug] Character rendering issue - invisible or missing text display
- [BUG] Cowork: processo Claude Code encerra com código 3 — .claude.json não contém token de autenticação (Windows 11 25H2)
- [BUG] 2.1.153 silently discards tools/list response from rmcp 0.12.0 HTTP MCP server (works in 2.1.152, wire-identical handshake)
- VS Code extension: option to auto-resume last session when reopening a workspace folder
- [Bug] Conversation continuation failure
- [BUG] Cowork crashes every time I start a new chat or attempt to continue an existing one in any project. The error displayed is: "Claude Code è andato in crash
- [Bug] Unannounced quota changes
- Native update/install fails with 'socket connection was closed unexpectedly' behind proxy — undici TLS incompatibility
- [BUG] Session name reverting after manual change
- [BUG] 非正常思考,上下文过长时,一直显示思考,点击interrupt按钮失效
- Honor `tools:` frontmatter when an agent is invoked via `@mention` — strip `Task` only when the agent did not declare it
- macOS TCC popup still recurring on v2.1.153 — "2.1.153" would like to access data from other apps
- Claude Code leaks pty handles — exhausts pseudo-terminals on macOS after long session
- [Bug] Agent fails to execute or respond to user input
- [BUG] Persistent "Expecting value: line 1 column 1 (char 0)" JSON parse error after tool execution
- [Feature Request] Implement proactive unit test coverage recommendations for recurring bugs
- VS Code panel lacks status line + terminal lacks image paste in Codespaces, forcing a tradeoff
- `/powerup` only shows ~10 lessons — allow viewing the full catalog
- [Bug] Context contamination after auto-compact with unrelated email draft of Tejo/Sado Basin
- [Bug] VSCode terminal output displays corrupted text with garbled symbols
- [Feature Request] Add LaTeX/KaTeX math rendering to TUI
- [Bug] Sub-agent PR review results not validated by orchestrating agent
- Subagents on Pro 1M tier: trivial probes pass, real workloads fail at first tool call (probe-vs-workload divergence)
- Path-scoped rules and subdirectory CLAUDE.md not loaded when creating new files matching the pattern
- AskUserQuestion: cancelling during extended thinking poisons the whole session with 400 'thinking blocks cannot be modified' (2.1.153); concurrent prompts overwrite each other
- Ideas Missing from Claude Cowork Menu (Windows)
- [BUG_BOUNTY_SAFE_POC_2026] Prompt Injection RCE Test - Command Execution Proof
- [BUG] Cowork scheduled task: execution history row not showing after successful run
- Resuming an extended-thinking session fails permanently with 400 "thinking blocks cannot be modified" (transcript stores thinking text as empty but keeps signature)
- [Bug] Plugin-registered CwdChanged and FileChanged hooks don't fire (settings.json works) — v2.1.153
- Auto-archive on PR merge / branch delete — clarify autoArchiveSessions semantics or add dedicated opt-out
- `claude mcp add` echoes Authorization header value verbatim to stdout, leaks bearer tokens to terminal and session transcripts
- [BUG] Bug report — /insights skill, Claude Code The /insights skill outputs a malformed file path.
- Plugin slash commands render with '*'-inline format instead of two-column, despite matching official plugin shape
- [Bug] Unexpected long text generation without user input or goal
- [Bug] Thinking blocks causing task progression blocked without user modification
- [BUG] (Critical!) contamination by an unknown session simirlar to the report => [Bug] Context contamination after auto-compact with unrelated email draft of Tejo/Sado Basin #63137
- [Critical] Opus 4.7 Korean output degeneration — Korean grammar itself collapses in long contexts
- [BUG] Title: Autocompact buffer persists across /clear — wastes tokens for irrelevant old context
- [Bug] Auto-Compact loses user input before processing in conversation history
- Feature: per-invocation effort parameter + runtime session-config introspection for skills
- Auto-mode classifier mislabels Azure DevOps vote -5 as "Reject" when denying PR vote actions
- [BUG] Claude Desktop and Claude Code CLI never re-register MCP tools after OAuth 2.1 handshake on a remote HTTP server
- [BUG] Workspace file tags leak across sessions
- [BUG] Ink renderer crashes on Windows 11 build 26200 (Canary) duplicate banners, terminal mode leaks, mid-operation aborts
- [BUG] Claude Code Desktop issue
- PTY master fd leak in Claude desktop app exhausts macOS kern.tty.ptmx_max after ~2-3 days
- [BUG] Claude Code — Session Management after Unexpected Interruption
- [Windows] Cowork OpenTelemetry exporter does not initialize - zero events emitted to any destination, including loopback
- [Bug] Opus 4.7: 400 `thinking blocks ... cannot be modified` on long extended-thinking sessions, triggered by history-altering events (scheduled prompts / parallel tool-call cancellation)
- [BUG] API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited
- Multi-plugin custom marketplace: only first plugin registered in installed_plugins.json, skills don't load
- [BUG] Git push through the SDK's git proxy fan-outs into ~500 GitHub REST API calls, exhausting the 5,000/hour budget after a handful of pushes
- [BUG] Claude took liberties it really shouldn't with my global config
- [BUG] Agent window focus lost after navigating with arrow keys, causing scroll deadlock
- [BUG] `--model` flag silently ignored in interactive sessions (works in `--print` only)
- [BUG] Dispatch permanently shows "desktop appears offline" on Windows 11 - never worked on first use
- feat: support per-command enableWeakerNetworkIsolation as safer alternative to dangerouslyDisableSandbox
- /code-review outputs a raw JSON array instead of readable findings
- [BUG] Cowork — Additional allowed domains ignored on Team plan; same domain works on Pro plan
- Haiku
- [Bug] False positive blocking beneficial outcomes in tool execution
- 3P Bedrock SSO: credentials silently expire without triggering re-auth on day 2+
- CLAUDE_AUTOCOMPACT_PCT_OVERRIDE in settings.json env block silently ignored by autocompact logic
- Auto-compaction deletes main session JSONL before verifying summary completion, causing data loss
- [Bug] Claude Code not executing stated actions or producing expected results
- [FEATURE] Deferred Messages — Queue Input for End of Turn
- [BUG] Up/Down arrows in input box navigate history instead of moving cursor — regression in 2.1.149+
- Cancelling a parallel tool-call batch corrupts thinking blocks -> 400 "thinking blocks cannot be modified" permanently wedges the session
- Claude Code caused data loss, then contradicted itself about recovery (two incidents, one session)
- [Bug] Unclear error messages from Claude Code CLI
- [Bug] Agent tool rejecting due to context size limit exceeded
- claude agents: daemon and bg-spare processes spin at ~100% CPU when idle
- [BUG] Compaction fails with "context window limit" error even when context usage is low (e.g., 20%) — regression in v2.1.153
- Remote Control entitlement lost after May 27-28 incident — `Error: Remote Control is not yet enabled for your account` on active Max subscription
- PreToolUse hook exit code 2 does not block Write tool
- [Bug] Thinking blocks in latest assistant message are immutable
- GUI: dispatch file:// and custom-scheme clicks to OS shell handler
- Show current model in statusLine by default
- [Bug] Agent console becomes unresponsive to keyboard input after multiple agents initialized
- [FEATURE] PreToolUse hooks should have a way of updating the environment
- [Bug] Unable to start or use Claude Code CLI
- [BUG] Repository not visible in Claude Code web repo picker
- Session permanently wedged on 400 "thinking blocks cannot be modified" after parallel tool_results
- [Bug] @ autocomplete loses sibling repos after a file edit in multi-repo workspace
- Unclear error message when creating sub-agent without authentication
- [Bug] Anthropic API errors causing frequent failures and high token usage
- [BUG] @ mention file picker only shows packages, not individual files (desktop app - Code tab)
- [Bug] TUI panel footer remains sticky and consumes excessive terminal space
- PR-status polling exhausts GitHub GraphQL rate limit on repos with many open PRs
- [BUG] Windows: welcome panel not shown in some project folders (2.1.153)
- [Bug] Anthropic API Error: thinking blocks corrupted during context compaction with extended thinking enabled
- API 400 "thinking blocks cannot be modified" permanently bricks session during agent activation (interleaved thinking + tool use)
- Right-click Copy copies the whole message instead of the selection; pasted text retains dark background
- Mid-session model switch corrupts conversation when extended thinking is enabled (API 400: 'thinking blocks cannot be modified')
- [BUG] Markdown file links in chat output do not open files when clicked (VS Code extension)
- Stuck retry loop: `400 thinking blocks cannot be modified` on large interleaved-thinking turns using AskUserQuestion
- [FEATURE] Prompt user for approval before auto-compaction proceeds
- Custom MCP connectors not attachable to scheduled routines — no UUID discovery path
- [BUG] Claude in Chrome — Navigation blocked for teams.cloud.microsoft and outlook.cloud.microsoft after Microsoft domain migration**
- [BUG] Claude Desktop — Personal plugins panel renders list but is entirely non-interactive (macOS, v1.9255.2)
- [Bug] error when using Workflows
- [BUG] Persistent "update available" notification despite being on latest version
- [BUG] Sweep Agent from /code-review never completes
- [Bug] Tool calls not executing or returning results
- [FEATURE] Cloud-synced memory and settings across machines
- [Bug] Terminal UI freezes when Ctrl+O view exits during interactive prompt in plan mode
- Continuous api errors when using claude code with Opus 4.7 with thinking on low
- [Feature Request] Add support for installing and using previous Claude Code versions
- [Bug] Extended Thinking: Summarized thinking blocks fail signature validation when resent to API
- [Bug] Anthropic API Error: 'thinking' blocks cannot be modified
- [Bug] Anthropic API Error: Thinking blocks cannot be modified with extended thinking mode
- Feature request: Lazy/on-demand MCP server connections
- [Bug] Tool Arguments Parsed as String Instead of Object
- [Bug] Anthropic API Error: Insufficient context provided
- [Bug] Claude Opus occasionally uses moskovian(russian) orthography instead of Ukrainian in system-prompted responses
- Opus 4.8: backgrounded task completions (subagents AND Bash) crash with 400 "thinking blocks cannot be modified"
- [Bug] Opus 4.7 fabricates stable preferences ("my default") to rationalize arbitrary choices when challenged
- [Bug] Unable to update Claude Code CLI
- [BUG] Desktop app: /remote-control mints link + connects bridge (main.log) but in-chat link/QR panel never renders
- Feature: sessionColor and sessionName in .claude/settings.json
- [BUG] Anthropic API error: thinking blocks
- [FEATURE] Support Remote MCPs in Cowork as in Claude Code
- [Bug] Anthropic API Error: 400 Bad Request with Redacted Thinking - 0 4.7 & 4.8
- [Bug] Anthropic API Error: Cannot modify thinking blocks from different model versions
- Interleaved thinking + multi-tool turn corrupts thinking block (text blanked, signature kept) → permanent 400 'blocks must remain as they were'
- [BUG] Mode/permission changes mid-tool-loop (effortLevel: xhigh) poisons entire session
- Session failure log: Opus 4.6 ignores its own rules for an entire session
- [BUG] "400 Guardrail was enabled" error when using Claude Opus 4.8 with AWS Bedrock
- [Feature Request] Add subagent approach selection option to avoid accidental feedback
- Persistent 400 'thinking blocks in the latest assistant message cannot be modified' — interleaved thinking persisted with empty text + signature bricks sessions
- [BUG] DesktopvsApp
- [BUG] Opus 4.7 cache hit rate collapse after May 27 incident — Messages 1.1k→88.9k in 9 minutes, $630/session
- [Bug] Anthropic API Error: Invalid thinking block format
- [BUG] FUCK CLAUDE
- Opus 4.8 extended thinking: Stop hook block re-entry corrupts thinking blocks → 400
- [Bug] 4.8 Fails when accessing previous model history
- [Bug] Unintended File Modifications During Execution
- [DOCS] Model configuration docs omit lean system prompt default scope and model exceptions
- Add "Always allow globally" option to permission prompts
- Server-side model upgrade (Opus 4.7→4.8) wedges in-flight sessions with `thinking blocks cannot be modified` 400
- [DOCS] AskUserQuestion docs missing multiple-choice prompt decision threshold
- [DOCS] Agent view docs omit shell-command background session launch syntax
- [DOCS] Agent view dispatch input docs incorrectly imply `/logout` dispatches as a prompt
- [DOCS] Claude in Chrome docs omit connected-browser selection behavior
- [DOCS] Plugin docs omit `defaultEnabled: false` for opt-in plugins
- Feature Request: Customizable chat text colors for user and assistant messages
- [DOCS] `/plugin` Discover tab docs omit directory-based suggested plugin pins
- VSCode Chrome integration silently fails: 3 distinct bugs
- [DOCS] MCP stdio docs omit session environment variables
- [Bug] Anthropic API error on second request within session with Claude Opus 4.8
- Cowork emits a blank session "index" handoff on focus when a CLI session is paused awaiting input
- [DOCS] MCP docs omit `claude mcp list/get` pending-approval output for unapproved project servers
- [BUG] /compact fails with 400 error when last assistant turn contains thinking blocks
- [DOCS] `/claude-api` docs omit Opus 4.8 migration guidance
- [DOCS] Fast mode docs still recommend deprecated Opus 4.6 override variable
- [DOCS] Bash tool docs omit `$TMPDIR` consistency across sandboxed and unsandboxed commands
- [Bug] Anthropic API Error: 400 Bad Request on Extended Thinking
- [DOCS] Background session docs omit worktree-isolation behavior for spawned subagents
- Built-in mechanistic self-verification of verifiable claims (symmetric to the auto permission gate)
- [DOCS] Worktree docs do not clarify `worktree.baseRef: "head"` inside linked worktrees
- [BUG] Excessive RAM usage with multiple parallel chats (~10 sessions → 30 GB memory pressure, macOS OOM)
- [DOCS] Managed MCP policy docs omit invalid `allowedMcpServers`/`deniedMcpServers` entry behavior
- [DOCS] Effort docs omit `CLAUDE_CODE_ALWAYS_ENABLE_EFFORT` unsupported-model behavior
- Regression (2.1.147–2.1.150?): resuming an extended-thinking session after a CC update/model-switch → unrecoverable 400, session bricked
- [DOCS] Windows updater docs omit `claude.exe` in-use recovery guidance
- [DOCS] VS Code auto mode docs still tie mode-picker visibility to bypass-permissions setting
- [DOCS] MCP docs omit `/mcp` tool list and detail rendering behavior
- [DOCS] Fine-grained tool streaming docs still describe provider opt-in behavior
- bypassPermissions: session startup reads flat pref, GUI toggle writes per-account pref — they never sync
- [BUG] Claude Desktop Code tab causes disk write limit violation — 8.5GB in 11 min, macOS kills app (M5, v1.9659.1)
- Ultrareview v2.1.96: docs describe /tasks command + claude ultrareview --json subcommand that don't exist; findings hard to read after completion
- I'd be happy to help create a GitHub issue title, but I don't see the error message in your message. Could you please share the specific error you're encountering? That way I can generate an accurate and descriptive issue title for you.
- [BUG] Claude in Chrome `file_upload` rejects all scheduled-task sessions with misleading error (real cause: INVALID_SESSION)
- Extended thinking: signed thinking block 'cannot be modified' (400) permanently wedges session
- RTL text support for Hebrew (and Arabic) in Claude Code
- [Bug] Random errors occurring across multiple operations