Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/plannotator-tui-hosts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ pub fn detect_host(env: impl Fn(&str) -> Option<String>) -> Result<Host, HostErr
return Ok(Host::Copilot);
}
let ai_agent = env("AI_AGENT").map(|v| v.trim().to_ascii_lowercase());
// Oh My Pi is a pi harness: it reuses pi's `PI_CODING_AGENT` flag, so its own name must
// be checked before pi's flag. `OMPCODE` is exported into every shell OMP spawns, which is
// why Plannotator checks it last of all the markers.
// Oh My Pi exports `OMPCODE=1` (and `CLAUDECODE=1`) into the shells it spawns and no
// pi marker (verified in oh-my-pi `packages/utils/src/procmgr.ts`), so it is found by
// `OMPCODE` below; `AI_AGENT=omp` is accepted in case a future version adds it.
if ai_agent.as_deref() == Some("omp") {
return Ok(Host::Omp);
}
Expand Down
19 changes: 16 additions & 3 deletions crates/plannotator-tui/src/last/locate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,8 @@ pub(crate) fn locate(options: &LastOptions) -> Result<Located> {
let Some(id) = options.session_id.as_deref().filter(|s| !s.trim().is_empty()) else {
bail!("hermes needs a session id (Herdr provides it; or pass --session-id)");
};
let db = std::env::var_os("HERMES_HOME")
.map_or_else(|| home().join(".hermes"), PathBuf::from)
.join(hermes::DB_FILE);
let db =
std::env::var_os("HERMES_HOME").map_or_else(hermes_home, PathBuf::from).join(hermes::DB_FILE);
let messages = hermes::messages_for_session(&db, id, pick)?;
(db, messages)
}
Expand Down Expand Up @@ -150,6 +149,20 @@ pub(crate) fn screen_fallback(env: &crate::herdr::context::HerdrEnv) -> Option<D
))
}

/// Hermes' platform default (`hermes_constants.py`): `%LOCALAPPDATA%\hermes` on Windows,
/// `~/.hermes` elsewhere.
fn hermes_home() -> PathBuf {
#[cfg(windows)]
{
let base = std::env::var_os("LOCALAPPDATA")
.filter(|v| !v.is_empty())
.map_or_else(|| home().join("AppData").join("Local"), PathBuf::from);
return base.join("hermes");
}
#[cfg(not(windows))]
home().join(".hermes")
}

fn home() -> PathBuf {
std::env::home_dir().unwrap_or_else(|| PathBuf::from("/"))
}
Expand Down
9 changes: 9 additions & 0 deletions docs/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,12 @@ beats touching a running agent's store), and issues one query over
(`kind: path | id`) rather than host+pid discovery; a transcript path handed over without a
host name is recognised by its first lines (`sniff`), so any Herdr-integrated agent that
writes one of the known formats works without a host table entry.

Source-verified 2026-08-29 against `NousResearch/hermes-agent` (`hermes_state_common.py`,
`hermes_state.py`, `hermes_state_search.py`, `hermes_constants.py`) and `oh-my-pi`
(`packages/utils/src/procmgr.ts`): Hermes writes `messages.timestamp` with `time.time()`
(Unix seconds); `active=0, compacted=0` rows are rewinds the user took back and
`active=0, compacted=1` rows are compaction archives, so `active = 1` is the right filter;
the session id Herdr reports is `sessions.id`; `HERMES_HOME` else `~/.hermes`
(`%LOCALAPPDATA%\hermes` on Windows). omp exports only `OMPCODE=1` and `CLAUDECODE=1` to
child shells, no pi marker, so the marker chain reaches `OMPCODE` correctly.
Loading