hermes-agent-rs is a Rust workspace for a model-agnostic agent runtime and developer-facing agent tooling.
Primary goals:
- Keep the core runtime model-agnostic.
- Support multiple model providers without leaking provider-specific behavior into core abstractions.
- Treat long-running agent execution as durable, observable, and resumable.
- Prefer explicit runtime events and state transitions over implicit control flow.
Primary crates:
crates/hermes-core: shared core types and contractscrates/hermes-provider: model/provider adapterscrates/hermes-tools: tool abstractions, registries, and implementationscrates/hermes-memory: memory/session persistence abstractions and implementationscrates/hermes-agent: agent loop, orchestration, and runtime-facing logiccrates/hermes-config: config loading, persistence, and runtime settingscrates/hermes-skills: skill loading and matching/injectioncrates/hermes-cli: local CLI entrypoint
- Prefer small, reviewable changes over broad rewrites.
- Preserve public API stability unless the task explicitly requires a breaking change.
- Keep behavior changes localized and easy to verify.
- Do not mix unrelated refactors into feature or bug-fix patches.
- When touching architecture-sensitive code, explain the tradeoffs in the final summary.
- Favor readability and explicitness over clever abstractions.
- Avoid introducing new dependencies unless clearly justified.
hermes-coremust remain provider-neutral.- Do not introduce Anthropic-, OpenAI-, or vendor-specific types into core contracts.
- Provider-specific request/response formats must stay in
hermes-provider. - Normalize external model behavior into shared internal types before crossing crate boundaries.
- Keep runtime/orchestration concerns separate from CLI presentation concerns.
- Do not let
hermes-clibecome the de facto runtime layer. - Prefer reusable execution primitives that can later serve CLI, gateway, cron, or server-style entrypoints.
- Prefer explicit runtime events, phases, and state transitions.
- Avoid hidden coupling through ad hoc flags or loosely documented control flow.
- Long-running behavior should be inspectable, resumable, and testable.
- Tool implementations should not smuggle provider-specific assumptions into generic tool interfaces.
- Tool side effects should be explicit.
- Dangerous operations must respect approval and safety controls.
- Treat session history as durable execution context, not just UI chat history.
- Prefer append-style recording of important execution facts.
- Do not silently discard information needed for replay, resume, or debugging.
- Use idiomatic Rust.
- Prefer explicit types at important API boundaries.
- Use
thiserrorfor domain errors where appropriate. - Use
anyhowmainly at application edges, not for core contracts. - Avoid panics in library code except for true invariants.
- Propagate errors with useful context.
- Be careful with cancellation, task lifetimes, and spawned work.
- Do not spawn detached tasks unless the lifecycle is clearly owned.
- Preserve backpressure and streaming semantics where relevant.
- Avoid blocking work in async code paths.
- Keep defaults safe and unsurprising.
- Do not silently enable dangerous capabilities.
- New config should have clear names and predictable precedence.
- Logs should help debug long-running agent behavior.
- Do not emit noisy logs for normal control flow unless there is strong value.
- User-facing CLI output should remain concise; detailed traces belong in debug/logging paths.
When changing Rust code, prefer these checks:
-
Format:
cargo fmt --all
-
Fast compile validation:
cargo check --workspace
-
Lints:
cargo clippy --workspace --all-targets --all-features -- -D warnings
-
Tests:
cargo test --workspace
If a full workspace run is too expensive for the task, run the narrowest meaningful crate-level checks and say so clearly.
- Be conservative.
- Assume downstream crates depend on these contracts.
- Highlight any schema, trait, or type changes clearly.
- Prefer additive changes over breaking ones.
- Keep vendor-specific logic isolated.
- Preserve normalized internal semantics.
- Call out differences in streaming, tool-calling, response shape, finish reasons, and error semantics.
- Verify behavior against at least one representative provider path.
- Preserve resumability and runtime invariants.
- Be explicit about loop termination, retries, compression, and approval handling.
- Watch for hidden coupling between orchestration and presentation.
- Verify argument validation and error handling.
- Be explicit about side effects, approvals, and failure modes.
- Avoid expanding tool scope accidentally.
- Preserve backward compatibility where feasible.
- Treat persistence format changes as high-risk.
- Call out migration or compatibility concerns.
- Keep UX predictable.
- Do not bury runtime decisions in CLI-only code if they belong in reusable layers.
- Preserve scriptability where possible.
When reviewing or generating code, prioritize:
-
Correctness
- Does the code do what the change claims?
- Are edge cases and error paths handled?
-
Architectural integrity
- Does the change preserve model-agnostic boundaries?
- Does it keep runtime logic reusable?
-
Long-running execution safety
- Are resume, retry, timeout, streaming, and cancellation semantics preserved?
-
API and config stability
- Does the patch unintentionally break callers, config compatibility, or persisted state?
-
Testability
- Are the changed behaviors verifiable with tests or at least narrow checks?
When working in this repository:
- First inspect the relevant crate and its nearby modules before editing.
- Prefer reading existing types and traits before introducing new ones.
- Before broad refactors, explain the reason and likely blast radius.
- For cross-crate changes, summarize dependency flow and compatibility implications.
- After changes, provide a concise summary:
- what changed
- why it changed
- what was verified
- any remaining risk or follow-up
- Large speculative refactors
- Renaming crates or major modules without strong need
- Provider-specific hacks in shared abstractions
- Silent behavioral changes to approvals, execution safety, or persistence
- Mixing style-only cleanup with semantic changes