pub struct Registry {
root: PathBuf,
write_lock: Mutex<()>,
}Fields§
§root: PathBuf§write_lock: Mutex<()>Implementations§
Source§impl Registry
impl Registry
Sourcepub fn open(root: PathBuf) -> Result<Self, RegistryError>
pub fn open(root: PathBuf) -> Result<Self, RegistryError>
Open or create the registry rooted at root. Ensures the
active dir and retired/ subdir both exist.
pub fn root(&self) -> &Path
Sourcepub fn agents_root(&self) -> Option<&Path>
pub fn agents_root(&self) -> Option<&Path>
Resolved <shared_home>/agents/ — one level above root.
Used by callers that need to express working-directory paths
as relative subpaths under the shared agents tree. Returns
None if the registry root has no parent (only happens in
pathological filesystem-root setups; production always nests
under ~/.agentmux/agents/registry).
fn active_path(&self, instance_id: &str) -> PathBuf
fn retired_path(&self, instance_id: &str) -> PathBuf
Sourcepub fn upsert(&self, rec: &NamedAgentRecord) -> Result<(), RegistryError>
pub fn upsert(&self, rec: &NamedAgentRecord) -> Result<(), RegistryError>
Insert or update a record. If the file already exists, unknown
top-level + data fields are preserved (forward-compat with
future schemas that add columns this binary doesn’t know).
Forward-compat invariant (spec §6): never write a higher-schema
row into a lower schema, never overwrite a corrupt/unparseable
file. Both cases skip the mirror with a warning — the on-disk
file stays intact for the binary that authored it (or for ops
triage). Skipping is Ok(()): SQLite remains authoritative.
Sourcepub fn upsert_keeping_session(
&self,
rec: &NamedAgentRecord,
) -> Result<(), RegistryError>
pub fn upsert_keeping_session( &self, rec: &NamedAgentRecord, ) -> Result<(), RegistryError>
Self::upsert, except a session_id the record already holds
wins over rec’s. For a mirror that didn’t capture a session — a
launch — whose local row may carry an older session id than the one
another channel has since written here (#3586). A record without a
session id still takes rec’s.
Sourcefn upsert_unlocked(
&self,
rec: &NamedAgentRecord,
session: SessionWrite,
) -> Result<(), RegistryError>
fn upsert_unlocked( &self, rec: &NamedAgentRecord, session: SessionWrite, ) -> Result<(), RegistryError>
Self::upsert without taking write_lock, for callers already
holding it. write_lock is a plain Mutex — re-entering it from a
locked section would deadlock, not nest.
Sourcepub fn set_instance_name_for_agent(
&self,
agent_id: &str,
scope: RecordScope,
instance_name: &str,
) -> Result<usize, RegistryError>
pub fn set_instance_name_for_agent( &self, agent_id: &str, scope: RecordScope, instance_name: &str, ) -> Result<usize, RegistryError>
Set instance_name on every ACTIVE record for agent_id. Returns
how many files changed.
Every record, not just the canonical one, because
listrecentsessions dedupes rows by (definition_id, instance_name)
— renaming one of an agent’s records and not the others splits it
into two picker rows, one under each name. (An agent can hold more
than one record for the same reason
Self::hard_delete_for_agent exists.)
Active tree only: upsert writes to the active path, so touching a
retired record here would resurrect a deliberately-forgotten agent.
A retired record keeps its old name until unretire_for_agent
brings it back.
Sourcepub fn retire(&self, instance_id: &str) -> Result<(), RegistryError>
pub fn retire(&self, instance_id: &str) -> Result<(), RegistryError>
Move record into retired/ (soft delete — keeps the working
dir intact, drops it from the launch-modal dropdown). Idempotent.
Sourcepub fn unretire(&self, instance_id: &str) -> Result<(), RegistryError>
pub fn unretire(&self, instance_id: &str) -> Result<(), RegistryError>
Move record back from retired/ to active. Idempotent.
Sourcepub fn hard_delete(&self, instance_id: &str) -> Result<usize, RegistryError>
pub fn hard_delete(&self, instance_id: &str) -> Result<usize, RegistryError>
Hard-delete by FILE KEY only (drops both active and retired files), returning how many files were removed.
The narrow counterpart of Self::hard_delete_for_agent. Deleting a
TEMPLATE needs this one: a template’s id can legitimately appear as
the definition_id of a legacy, never-re-keyed launch record
belonging to a real agent launched from it, and matching on
definition_id would take that agent’s record along with the
template (ReAgent P1 round 3 on PR #3262). A template’s own record,
if it has one, is file-keyed, so this is the complete sweep for one.
Sourcepub fn hard_delete_for_agent(
&self,
agent_id: &str,
scope: RecordScope,
) -> Result<usize, RegistryError>
pub fn hard_delete_for_agent( &self, agent_id: &str, scope: RecordScope, ) -> Result<usize, RegistryError>
Drop every record — active AND retired — that belongs to the agent
agent_id, matching on either the record’s file key (its instance
id) or its definition_id. Returns how many files were removed.
Why this exists alongside Self::hard_delete. That one keys
only on the file name. Since the agent-concept consolidation an
agent’s instance id and definition id are the same value, so the two
agree — but only for records this tree has been re-keyed to.
m0026_registry_agent_id_rekey does that re-keying by reading
db_agent_instances, and returns a no-op the moment that legacy
table is gone (v32 dropped it). An install that consolidated before
m0026 could run therefore still holds one launch-keyed file per
agent, in addition to the correctly-keyed one.
Deleting such an agent removed only the correctly-keyed file,
leaving the launch-keyed one active with no definition behind it —
which listrecentsessions still renders as a row (no provider, so
no icon, and "(missing definition)"). The delete looked like it
half-worked: the icon vanished, the card stayed.
Both keys are checked rather than just definition_id because
callers legitimately hold either one: a consolidated agent id, or
the launch id of a registry-only row this channel’s SQLite has never
seen. Matching both is a strict superset of Self::hard_delete,
and a cross-key collision would require the same uuid to name two
different agents.
Sourcepub fn retire_for_agent(
&self,
agent_id: &str,
scope: RecordScope,
) -> Result<usize, RegistryError>
pub fn retire_for_agent( &self, agent_id: &str, scope: RecordScope, ) -> Result<usize, RegistryError>
Retire every active record for agent_id, matched the same way
Self::hard_delete_for_agent matches. A launch-keyed record left
active by the file-keyed Self::retire is exactly the “‘Forget
agent’ retires the NEW key while the stale file stays active, so the
forgotten agent reappears” failure m0026_registry_agent_id_rekey’s
own doc comment predicted.
Sourcepub fn unretire_for_agent(
&self,
agent_id: &str,
scope: RecordScope,
) -> Result<usize, RegistryError>
pub fn unretire_for_agent( &self, agent_id: &str, scope: RecordScope, ) -> Result<usize, RegistryError>
Inverse of Self::retire_for_agent.
Sourcefn move_for_agent(
&self,
agent_id: &str,
scope: RecordScope,
from_dir: PathBuf,
to_dir: PathBuf,
) -> Result<usize, RegistryError>
fn move_for_agent( &self, agent_id: &str, scope: RecordScope, from_dir: PathBuf, to_dir: PathBuf, ) -> Result<usize, RegistryError>
Move an agent’s records between the active and retired trees,
keeping each file’s own name (the instance id) so the record stays
addressable by Self::get. Caller holds write_lock.
Sourcepub fn exists(&self, instance_id: &str) -> bool
pub fn exists(&self, instance_id: &str) -> bool
Whether an active record exists for instance_id. Doesn’t
validate — useful for migration idempotency checks. Use
Self::exists_anywhere when retired records should also
count (e.g. so migration doesn’t resurrect a hidden agent).
Sourcepub fn get(
&self,
instance_id: &str,
) -> Result<Option<NamedAgentRecord>, RegistryError>
pub fn get( &self, instance_id: &str, ) -> Result<Option<NamedAgentRecord>, RegistryError>
Read a single active record by id, without scanning the whole
tree. Ok(None) when there’s no active file for instance_id
(never checks retired/). Callers that need to patch a single
field (e.g. propagating a fresh session_id) must read-modify-
upsert via this rather than constructing a partial record —
upsert’s merge overwrites every field present in the struct,
so a partially-populated record would clobber the rest with
defaults.
Sourcepub fn exists_anywhere(&self, instance_id: &str) -> bool
pub fn exists_anywhere(&self, instance_id: &str) -> bool
Whether a record exists in either active or retired. Used by
migration to skip already-tombstoned records (avoid
resurrecting a user’s deliberate “Forget agent” via a
per-version SQLite row that still has display_hidden = 0).
Sourcepub fn list_active(&self) -> Result<Vec<NamedAgentRecord>, RegistryError>
pub fn list_active(&self) -> Result<Vec<NamedAgentRecord>, RegistryError>
Read every valid active record. Invalid files are skipped + logged. PR A doesn’t wire this into the RPC path; included so PR B can swap reads over without further restructuring.
Auto Trait Implementations§
impl !Freeze for Registry
impl RefUnwindSafe for Registry
impl Send for Registry
impl Sync for Registry
impl Unpin for Registry
impl UnsafeUnpin for Registry
impl UnwindSafe for Registry
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more