Store

Struct Store 

Source
pub struct Store {
    pub(super) conn: Mutex<Connection>,
    registry: Mutex<Option<Arc<Registry>>>,
    def_registry: Mutex<Option<Arc<DefinitionStore>>>,
    registry_agents_base: Mutex<Option<PathBuf>>,
}
Expand description

SQLite-backed object store for StoreObj types.

Fields§

§conn: Mutex<Connection>

pub(super) so sibling subsystem modules (e.g. memory_bundles) can take the lock. Each per-subsystem file adds methods to Store via impl Store {} and needs the connection.

§registry: Mutex<Option<Arc<Registry>>>

Cross-version named-agent registry. None for in-memory test stores; Some for production srv. Mutations to db_agent_instances parallel-write to this registry when set. See docs/specs/SPEC_SHARED_AGENT_REGISTRY_2026_05_12.md.

§def_registry: Mutex<Option<Arc<DefinitionStore>>>

GLOBAL (cross-channel) agent-definition store. None for in-memory test stores and when the shared dir can’t be resolved; Some for production srv. Definition mutations mirror to it so an agent created in one channel is visible in every channel (cross-channel agent persistence, docs/specs/SPEC_CROSS_CHANNEL_AGENT_PERSISTENCE_2026-06-13.md).

§registry_agents_base: Mutex<Option<PathBuf>>

Base directory that named-instance working_directory values are expressed relative to in the instance registry (write side: registry_mirror; read side: the listnamedagents handler).

This is the current channel’s agents dir (channels/<ch>/agents, i.e. AGENTMUX_AGENTS_DIR). It must be tracked separately from the registry’s own file location because P0.3 re-roots the registry to the global ~/.agentmux/shared/agents/registry/ — once the registry no longer sits under channels/<ch>/agents/, its parent (agents_root()) stops coinciding with the channel agents dir, and using it to strip / re-join working_directory would drop every live instance.

In production it is wired from AGENTMUX_AGENTS_DIR in main.rs (P0.3b), atomically with the re-root to the global shared registry — which is why the wiring waited for the re-root: setting it earlier would diverge in dev mode, where AGENTMUX_AGENTS_DIR ≠ the (then channel-local) registry parent. None only for in-memory test stores and odd envs where the var is unset; the accessor then falls back to the registry’s parent (which equals the channel agents dir in the pre-re-root layout), so existing mirror tests are unchanged. When set, it is passed in explicitly — never read from ambient env inside the Store — so tests running inside an AgentMux pane don’t pick up the host’s AGENTMUX_AGENTS_DIR. See docs/specs/SPEC_CROSS_CHANNEL_AGENT_PERSISTENCE_2026-06-13.md (P0.3).

Implementations§

Source§

impl Store

Source

pub fn user_clone_defs_for_template( &self, template_id: &str, ) -> Result<Vec<AgentDefinition>, StoreError>

List all agent definitions, most-recently-used first.

Reads from the consolidated db_agents table, ordered by updated_at DESC then created_at ASC. Dual-write keeps db_agents.updated_at fresh on every definition mutation AND every instance lifecycle touch, so recency on a row tracks the last time the agent was either edited or launched.

Result-set shape: every db_agents row is returned — templates (is_template = 1) and user-clone projections (is_template = 0) each appear once. parent_id is sourced from db_agents.parent_template_id.

Find user-clone definitions for a given seeded template (rows in db_agent_definitions with is_seeded = 0 and parent_id = <template.id>). Returns the most-recent-first.

Reads db_agent_definitions directly — NOT the db_agents consolidated view — because the latter surfaces template- instance projection rows under the same is_template = 0 AND parent_template_id = <tpl> shape as user-clone defs, which would conflate two distinct things.

Sole production caller today is the template_promote migration’s “did the user delete the deterministic-id clone?” diagnostic logging and its tests. Kept public so follow-up callers (e.g. a cleanup pass that GCs orphaned pre-deterministic-id clones from earlier migration code) can use it without re-deriving the schema.

Source

pub fn agent_def_get( &self, id: &str, ) -> Result<Option<AgentDefinition>, StoreError>

Fetch a single agent definition by primary key. Reads db_agent_definitions directly (not the db_agents consolidated view that agent_def_list reads), so it returns user-clone definitions and seeded templates, never template-instance projection rows.

Used by the template_promote migration’s deterministic-id idempotency check (see migrate_promote_template_sessions_v1): every retry asks “does the promote-target clone for this template already exist?” and either reuses it or inserts it.

Source

pub fn agent_def_list(&self) -> Result<Vec<AgentDefinition>, StoreError>

Source

pub fn agent_def_count(&self) -> Result<i64, StoreError>

Count agent rows (used by seed engine to check if seeding is needed). Reads from the consolidated db_agents table. The seed engine only cares about == 0 to decide “fresh database, seed templates”.

Source

pub(super) fn agent_def_exists_local( &self, id: &str, ) -> Result<bool, StoreError>

Whether a definition with id exists in the LOCAL channel’s SQLite (db_agents). Gates the cross-channel content/skills fallback: a locally-known agent with genuinely empty content/skills must NOT resurrect them from the global record. (reagent P1 on #1385.)

Source

pub fn agent_def_delete_seeded(&self) -> Result<usize, StoreError>

Delete all seeded agents (is_seeded=1). Used by reseed to clear built-in agents.

Source

pub fn agent_def_insert( &self, agent: &mut AgentDefinition, ) -> Result<(), StoreError>

Insert a new agent definition. Auto-derives slug from name if empty, resolves collisions by appending -2, -3, etc., and mutates agent.slug so the caller sees the resolved value (important for handlers that serialize the struct back to the frontend after insert).

The collision check + insert run under a single mutex lock, so this is race-safe against concurrent inserts on the same connection.

Source

pub fn agent_def_find_or_insert( &self, agent: &mut AgentDefinition, ) -> Result<Option<AgentDefinition>, StoreError>

Atomic check-then-insert for agent.define.

Looks up an existing user-owned definition by name (case-insensitive) or derived slug under the SAME mutex guard that protects the INSERT — preventing TOCTOU when two concurrent agent.define calls arrive for the same name.

Returns:

  • Ok(Some(def)) — an existing row matched; agent was NOT inserted.
  • Ok(None) — no match; agent was inserted and agent.slug now holds the collision-resolved slug.
Source

pub fn agent_def_set_hidden( &self, id: &str, hidden: bool, ) -> Result<bool, StoreError>

Set the user_hidden flag on a single agent definition. Phase 2 of the two-tier picker (Q2 Decision Y). Returns: Ok(true) — row updated. Ok(false) — no row with that id exists. Err(...) — the row exists but is NOT a seeded template (is_seeded != 1). User-owned definitions go through agent_def_delete, not hide.

Does NOT bump updated_at: hide is a per-user view-state flag, not a definition-content edit. Keeps updated_at faithful to the agent’s payload (the manifest re-sync compares description etc. against the canonical row).

Source

pub fn agent_def_update( &self, agent: &mut AgentDefinition, ) -> Result<bool, StoreError>

Update an existing agent definition (all fields except id, created_at, is_seeded). parent_id and branch_label are NOT updatable post-insert — they describe the agent’s provenance; renaming or re-branching is done by creating a new fork, not mutating the original.

Self-stamps updated_at with the current time and writes it back into agent.updated_at, so the caller’s struct (e.g. an RPC response body) reflects exactly what landed in the database.

Source

pub fn agent_def_delete(&self, id: &str) -> Result<bool, StoreError>

Delete a agent definition by id. Returns true if a row was deleted.

Source§

impl Store

Source

pub fn instance_list( &self, definition_id: Option<&str>, status: Option<&str>, ) -> Result<Vec<AgentInstance>, StoreError>

List instances. Both filters are optional — pass None to scan all instances. Ordered by updated_at descending, with created_at as a tiebreaker (most recent activity first; the dual-write bumps updated_at on every launch / continuation, so a continued older agent ranks ahead of a brand-new untouched one).

Reads from the consolidated db_agents table (is_template = 0, user_hidden = 0) for the no-status case. Continuation chains pre-collapse — one row per logical agent. The definition_id filter, when supplied, matches the agent’s own id only (templates aren’t agents and user-clones derived from a template are SEPARATE agents — see the implementation note below for why parent_template_id traversal was dropped).

Field mapping for fields with no consolidated-row analog:

  • block_id, session_id, status, ended_at, parent_instance_id → type defaults ("" / 0). Truly transient per-launch state; not modelled on db_agents.
  • started_atdb_agents.created_at. Same proxy used by instance_get_by_name (3b.2); the consolidated row’s creation IS the agent’s launch moment in the new model.
  • display_hidden → always false. The WHERE clause filters user_hidden = 0, so hidden rows never surface here.

Callers passing a status filter need transient runtime state that db_agents doesn’t model. Route those to the legacy db_agent_instances path so existing semantics are preserved until the updateagentinstance handler’s “fetch + merge transient fields” pattern is refactored. (Currently no production caller passes statuslistagentinstances RPC frontends call with empty filters — so the legacy path is exercised only by tests.) Spec: docs/specs/SPEC_AGENT_ARCHITECTURE_2026_05_27.md §3b.3.

Source

fn instance_list_legacy( &self, definition_id: Option<&str>, status: Option<&str>, ) -> Result<Vec<AgentInstance>, StoreError>

Legacy db_agent_instances read — preserved for the status-filter case (transient state). Will retire when the updateagentinstance handler’s fetch-and-merge pattern is refactored. Do NOT add new callers; use instance_list instead.

Source

pub fn instance_get( &self, id: &str, ) -> Result<Option<AgentInstance>, StoreError>

Source

pub fn instance_create(&self, inst: &AgentInstance) -> Result<(), StoreError>

Insert a new instance row. Caller is responsible for the id (UUID).

Source

pub fn instance_set_hidden( &self, id: &str, hidden: bool, ) -> Result<bool, StoreError>

Set the display_hidden flag on an existing instance row. Used by the “Forget agent” affordance — soft-delete only; the row + working directory remain on disk for audit + recovery.

Cross-version case: an agent migrated into the registry from another version’s SQLite won’t have a row in the current version’s SQLite. The UPDATE returns 0 rows, but the registry still needs to flip — otherwise “Forget agent” silently no-ops on cross-version entries. Returns true if either side acted.

Source

pub fn instance_list_named( &self, limit: usize, definition_id: Option<&str>, identity_id: Option<&str>, include_continuations: bool, ) -> Result<Vec<AgentInstance>, StoreError>

List named instances for the launch-modal “Continue agent” dropdown (include_continuations = false) or the picker “My Agents” surface (include_continuations = true). Filters to non-hidden + named rows, sorted by started_at DESC, capped by limit.

definition_id, when provided, restricts the result to instances of that definition. Server-side filtering is necessary because the launch modal opens per-definition: a user with 200+ named agents across many definitions could have the current definition’s older instances cut off by a purely global limit otherwise.

include_continuations controls whether rows with parent_instance_id != '' (continuation chains) are returned:

  • false (legacy “head-of-chain only”). Pre-Option-E semantics: hides continuation rows so the launch-modal dropdown shows one entry per chain root. listnamedagents ALSO uses this mode for its same-version SQLite enrichment of registry-sourced rows — the registry mirror filter at registry_upsert_if_named excludes continuations symmetrically, and breaking that symmetry under a limit truncation would let continuation rows displace registry-head rows in the top-N and miss the merge-by-id enrichment. (Codex P1 on PR #1016 first cut: regresses running-state badges and focus-existing-pane hints for any user whose latest instance is a continuation.)

  • true (Option-E “include continuations”). For the picker’s listrecentsessions flow and the template_promote migration’s instance-name lookup. Under Option E the session zone is anchored on definition_id, so a continuation row is simply the most-recent named instance of an agent the user actively used — exactly what those callers want visible. Excluding them hides real agents (the original 2026-05-24 “Maks doesn’t appear under My Agents” report) and makes template_promote’s name lookup miss the real instance_name, falling back to the template name.

Source

pub fn instance_get_by_name( &self, instance_name: &str, ) -> Result<Option<AgentInstance>, StoreError>

Look up the canonical named-agent row matching instance_name. Used by the launch modal to detect name collisions (“did you mean to continue?”) and by ContinueNamedAgentCommand to resolve the consolidated row when the caller only knows the name. Hidden rows are excluded.

Reads from the consolidated db_agents table — is_template = 0 (named user agent), instance_name matches, user_hidden = 0. Continuation chains are pre-collapsed in db_agents (one row per logical agent), so this returns the canonical agent regardless of how many launches its chain has. MRU tiebreak is by updated_at (the dual-write touches it on every continuation), then created_at for stable order.

The legacy db_agent_instances carried per-launch runtime state (block_id, session_id, status, started_at, ended_at, parent_instance_id) that has no analog in db_agents — those fields are returned as their AgentInstance defaults (empty strings, 0). Callers wanting transient state should consult runtime sources (the controller, the block row); none of the documented use cases need it (collision detection only cares about identity / cwd; ContinueNamed only cares about id + bindings). Spec: docs/specs/SPEC_AGENT_ARCHITECTURE_2026_05_27.md §3b.

Source

pub fn instance_update_partial( &self, id: &str, upd: &InstanceUpdate, ) -> Result<Option<AgentInstance>, StoreError>

Partial update of an instance’s mutable runtime fields. Only Some fields are written; None leaves that column untouched.

Replaces the updateagentinstance handler’s fetch-and-merge (read the full row → fill the unspecified fields → write the whole struct back). That read was the only production caller that needed instance_get’s transient per-launch fields, which pinned instance_get to the legacy db_agent_instances table. With a partial write the handler no longer reads the row at all. See docs/specs/SPEC_UPDATEAGENTINSTANCE_PARTIAL_UPDATE_2026_05_29.md.

Returns the post-update row so callers that need definition_id for an event scope — or want to echo the row back — get it from the same authoritative reload this method already runs to refresh the registry mirror + dual-write. Those consumers read only non-transient fields, so the reload survives a future instance_get → db_agents flip.

None is reserved for not-found (the id doesn’t exist). An all-None update on an existing id is a no-op that returns the unchanged row — so callers can distinguish “nothing to change” from “no such instance”.

Source

pub fn instance_update(&self, inst: &AgentInstance) -> Result<bool, StoreError>

Update mutable instance fields. id, definition_id, parent_instance_id, started_at, created_at are immutable after insert (they describe provenance, not state).

Retained as a full-struct convenience for store tests + internal callers; the updateagentinstance handler now uses Self::instance_update_partial so it no longer reads the row to merge.

Source

pub fn instance_repoint_definition( &self, old_def_id: &str, new_def_id: &str, ) -> Result<usize, StoreError>

Repoint every instance currently referencing old_def_id to new_def_id. Used by the Phase 1 two-tier-picker migration (SPEC_AGENT_PICKER_TWO_TIER_2026_05_24.md): when a seeded template has been used directly (carries an agent:<id>:current zone), the migration clones the template into a user agent and repoints any instances so the existing reattach flow (continueOfInstanceId) keeps working against the new definition_id. Returns the number of rows updated.

definition_id is declared immutable post-insert on the normal instance_update path. This is the migration escape hatch.

Source

pub fn instance_delete(&self, id: &str) -> Result<bool, StoreError>

Source

pub fn instance_backfill_identity_id( &self, new_identity_id: &str, ) -> Result<usize, StoreError>

Back-fill db_agent_instances.identity_id for legacy rows that have either the empty string (post-v7 default before the launch modal required Identity) or the literal "blank" sentinel (pre-v8 placeholder for “use ambient creds”). Both shapes map to “no Identity bundle assigned” and the OAuth-bundles startup migration (PR E, spec §5) routes them to the newly-seeded Default bundle so the resolver can inject env vars from the captured ambient credentials at the next spawn.

Returns the number of rows touched. Caller must verify that new_identity_id is a real db_identity_bundles.id — this method does NOT enforce FK validity (the column has no FK constraint per the v7 migration). Mis-use would orphan the rows to a non-existent bundle; the OAuth-bundles migration guards against this by only calling here when it just upserted the bundle row.

Source

pub fn instance_get_active_for_block( &self, block_id: &str, ) -> Result<Option<AgentInstance>, StoreError>

Resolve the agent bindings tied to a block.

Resolve through block.meta.agentId (or legacy agent:id) against db_agents for the user-clone case; fall back to the legacy db_agent_instances lookup by block_id for seeded-template launches and any other block the consolidated path can’t satisfy.

We deliberately do NOT consult block.meta.agentInstanceId: codex P1 on PR #1114 round 3 surfaced that pane reuse (backToPicker clears agentId but not agentInstanceId) and quick-launch (no instance-id stamp) leave the key stale. Trusting it would silently bleed the prior agent’s identity across reopens — exactly the regression the legacy active- instance query avoided. The agentId-then-legacy path covers every launch shape without needing instance-id stamping:

  • User-clone: db_agents.id == def.id, hits is_template=0.
  • Template direct-launch: agentId points at a template (is_template=1, filtered out). Legacy fallback finds the active instance row keyed on block_id.
  • Pane reuse: stale agentInstanceId is ignored; current agentId wins.

Replaces the legacy “find most recent active instance for this block” as the PRIMARY path for user-clones; the legacy query remains as fallback for templates + edge cases. Retires fully when Phase 3c drops the legacy table. Spec: docs/specs/SPEC_AGENT_ARCHITECTURE_2026_05_27.md §3b.

user_hidden is NOT filtered — hiding a named agent (“forget”) is a picker-visibility concept; the pane bound to that agent must keep resolving credentials. Codex P2 on PR #1114 round 2.

Used by the identity resolver to pull identity_id / memory_id for environment injection on every command dispatch. Caller only reads identity_id from the returned AgentInstance — transient per-launch fields (status, session_id, started_at, ended_at, parent_instance_id) come back as type defaults. block_id echoes back the caller’s argument.

Source§

impl Store

Source

pub fn agent_content_get( &self, agent_id: &str, content_type: &str, ) -> Result<Option<AgentContent>, StoreError>

Source

pub fn agent_content_set( &self, content: &AgentContent, ) -> Result<(), StoreError>

Upsert a content blob for an agent.

Source

pub(super) fn agent_content_get_all_local( &self, agent_id: &str, ) -> Result<Vec<AgentContent>, StoreError>

Get all content blobs for an agent. LOCAL channel’s content blobs only — NO cross-channel fallback. Used by the def-registry mirror (which always operates on a local agent, so must never read the global record) and by agent_content_get_all.

Source

pub fn agent_content_get_all( &self, agent_id: &str, ) -> Result<Vec<AgentContent>, StoreError>

Source

pub fn agent_content_delete( &self, agent_id: &str, content_type: &str, ) -> Result<bool, StoreError>

Delete a specific content blob. Returns true if a row was deleted.

Source§

impl Store

Source

pub(super) fn registry_def_upsert(&self, def_id: &str)

Mirror a definition (by id) into the global store, reading its full row + content + skills from SQLite. Best-effort: a missing global store or any failure is logged, never propagated (SQLite stays authoritative). No-op when the definition no longer exists.

upsert itself refuses to resurrect a tombstoned id, so a stale mirror call for a deleted agent won’t un-delete it.

Source

pub(super) fn registry_def_retire(&self, def_id: &str) -> bool

Mirror a user-agent deletion as a global tombstone (retired/) so another channel’s stale SQLite can’t resurrect it via upsert. Best-effort.

Source

pub(super) fn registry_def_update_definition_fields( &self, agent: &AgentDefinition, ) -> bool

Apply a definition edit directly to the global record’s definition fields, PRESERVING its existing content + skills. Used when editing a cross-channel agent that has no local SQLite row to update. Returns whether a global record was found + updated. Best-effort. Tombstoned (retired) agents are not resurrected — get reads only the active tree.

Source§

impl Store

Source

pub(crate) fn agents_dual_write_definition_upsert( &self, def: &AgentDefinition, ) -> Result<(), StoreError>

Mirror a db_agent_definitions row into db_agents as the canonical row for that definition.

  • is_seeded = 1 rows become is_template = 1 (canonical template; bindings stay empty).
  • is_seeded = 0 rows become is_template = 0 with parent_template_id = parent_id (user-cloned from a template; bindings come from the matching instance, if any — handled by agents_dual_write_instance_create updating in place).

Idempotent: uses INSERT … ON CONFLICT(id) DO UPDATE. Existing bindings on the row (written previously by an instance dual-write) are preserved — only definition-side fields are overwritten.

Phase 3b: returns Err on failure (previously logged + continued). Phase 3b readers see db_agents, so a silent dual-write failure would leak stale data into the picker.

Source

pub(crate) fn agents_dual_write_definition_delete( &self, def_id: &str, ) -> Result<(), StoreError>

Mirror a db_agent_definitions DELETE into db_agents. The definition row itself is removed; any user-cloned children (rows with parent_template_id = old_id) are left intact because the FK cascade on the OLD schema only deletes instances, not other definitions.

Source

pub(crate) fn agents_dual_write_seeded_delete( &self, cascaded_inst_ids: &[String], ) -> Result<(), StoreError>

Bulk dual-write: mirror agent_def_delete_seeded. Deletes:

  1. every is_template = 1 row (the template projections), AND
  2. every db_agents row whose id is in the cascaded_inst_ids set (template-instance projections that were just removed by the FK cascade on db_agent_instances).

User-clone DEFINITION projections (is_template = 0, id is a def_id in db_agent_definitions) are NOT touched — those rows represent persistent user agents and live or die with their db_agent_definitions row, not with the seeded-template bulk delete. Reagent P1 round 4 on #1013: the previous version scoped by parent_template_id and over-deleted user-clone DEF projections too. Idempotent.

Source

pub(crate) fn agents_dual_write_instance_create( &self, inst: &AgentInstance, ) -> Result<(), StoreError>

Mirror a db_agent_instances INSERT into db_agents. Always creates a NEW row in db_agents whose id == instance id.

The row’s identity comes from the instance (id, name, bindings). Its template-config fields are copied from the parent definition; parent_template_id points at that definition.

Continuations (parent_instance_id non-empty) mirror their new bindings into the chain’s existing db_agents row rather than creating a separate one. The chain root is resolved via agents_projection_key_for_inst. Codex P2 on PR #1110 — without this, a named-agent rebind (continue with different identity, memory, cwd, or github context) never reaches the consolidated row and Phase 3b readers see stale data.

Source

pub(crate) fn agents_dual_write_instance_update( &self, inst: &AgentInstance, ) -> Result<(), StoreError>

Mirror a db_agent_instances UPDATE into db_agents. Touches only the fields that instance_update writes (block + session + status + github_context + ended_at) — name/bindings come from the original create.

Continuations flow through here too — agents_projection_key_for_inst walks up the chain to the head’s id, so a continuation’s github_context refresh lands on the canonical row (codex P2 on PR #1110, paired with the create-path fix in agents_dual_write_instance_create).

Source

pub(crate) fn agents_dual_write_instance_set_hidden( &self, id: &str, hidden: bool, ) -> Result<(), StoreError>

Mirror instance_set_hidden into db_agents.user_hidden.

Source

pub(crate) fn agents_dual_write_instance_repoint( &self, old_def_id: &str, new_def_id: &str, ) -> Result<(), StoreError>

Mirror instance_repoint_definition into db_agents. The parent_template_id of every user-clone row that pointed at old_def_id is updated to new_def_id.

Source

pub(crate) fn agents_dual_write_instance_delete( &self, id: &str, ) -> Result<(), StoreError>

Mirror instance_delete into db_agents.

Source

pub(crate) fn agents_dual_write_backfill_identity( &self, new_identity_id: &str, ) -> Result<(), StoreError>

Mirror instance_backfill_identity_id into db_agents. Same filter (empty or "blank" identity_id) restricted to user-clone rows.

Source

fn agents_projection_key_for_inst( conn: &Connection, inst_id: &str, ) -> Option<(String, bool)>

Reagent P1 round 4 on #1013 — fold-aware projection key lookup. Resolves “for an instance with this id, which db_agents row represents it post-create?”. Returns Some((key, is_folded)):

  • is_folded = true → key is the parent definition id (the user-clone-def projection absorbs the instance’s bindings).
  • is_folded = false → key is the instance id (template-instance projection is its own row). Returns None if the instance no longer exists in db_agent_instances (e.g. already deleted by FK cascade).
Source

fn load_definition_for_dual_write( conn: &Connection, id: &str, ) -> Result<Option<AgentDefinition>>

Helper: re-read a definition row from inside an active connection lock (used by agents_dual_write_instance_create to avoid re-locking the mutex recursively).

Source§

impl Store

Source

pub fn agent_history_append( &self, agent_id: &str, entry: &str, ) -> Result<AgentHistory, StoreError>

Append a history entry for an agent. Auto-sets session_date (today) and timestamp.

Source

pub fn agent_history_list( &self, agent_id: &str, session_date: Option<&str>, limit: i64, offset: i64, ) -> Result<Vec<AgentHistory>, StoreError>

List history entries for an agent, with optional date filter and pagination.

Search history entries for an agent using LIKE-based matching.

Source§

impl Store

Source

pub fn identity_list( &self, provider: Option<&str>, ) -> Result<Vec<IdentityAccount>, StoreError>

List identity accounts. If provider is Some, filter to that provider; otherwise return every account, ordered by most recent update first (so the identity panel shows live accounts on top).

Source

pub fn identity_get( &self, id: &str, ) -> Result<Option<IdentityAccount>, StoreError>

Source

pub fn identity_upsert( &self, account: &IdentityAccount, ) -> Result<(), StoreError>

Upsert an identity account. If account.id is empty the caller must generate one first (we don’t silently mint ids here — callers should know whether they’re creating vs updating).

Source

pub fn identity_delete(&self, id: &str) -> Result<bool, StoreError>

Link an agent to an identity for a given provider. Overwrites any existing link for the same (agent_id, provider) — each agent has at most one account per provider.

Remove the identity link for a given (agent_id, provider). Returns true iff a link existed.

Source

pub fn agent_identity_list_for_agent( &self, agent_id: &str, ) -> Result<Vec<AgentIdentityLink>, StoreError>

List all (agent_id, account_id, provider) triples for an agent.

Source

pub fn bundle_identity_list(&self) -> Result<Vec<Identity>, StoreError>

List all Identity bundles, blank singleton last so the picker shows user-defined bundles first.

Source

pub fn bundle_identity_get( &self, id: &str, ) -> Result<Option<Identity>, StoreError>

Source

pub fn bundle_identity_upsert( &self, identity: &Identity, ) -> Result<(), StoreError>

Upsert an Identity bundle. Caller mints the id (no silent generation). The is_blank flag is reserved for the seeded singleton — callers should pass false for user-created identities.

Source

pub fn bundle_identity_delete(&self, id: &str) -> Result<bool, StoreError>

Delete an Identity bundle. Refuses to delete the blank singleton — the launch UI depends on it as the always-present default option.

Source

pub fn bundle_identity_bind( &self, identity_id: &str, provider: &str, account_id: &str, ) -> Result<(), StoreError>

Set the account for (identity_id, provider). Overwrites any existing binding for the same (identity, provider).

Source

pub fn bundle_identity_unbind( &self, identity_id: &str, provider: &str, ) -> Result<bool, StoreError>

Remove the binding for (identity_id, provider). Returns whether a row was deleted.

Source

pub fn bundle_identity_bindings( &self, identity_id: &str, ) -> Result<Vec<IdentityBinding>, StoreError>

List bindings for an Identity bundle.

Source§

impl Store

Source

pub fn bundle_memory_list(&self) -> Result<Vec<Memory>, StoreError>

Source

pub fn bundle_memory_list_global(&self) -> Result<Vec<Memory>, StoreError>

Returns only the global bundles (is_global = 1), in explicit sort_order (then name as a stable tiebreak). Called at agent launch to inject workspace-wide rules into every agent in the order the user arranged them in the Trust Center Brain tab.

Source

pub fn bundle_memory_get(&self, id: &str) -> Result<Option<Memory>, StoreError>

Source

pub fn bundle_memory_upsert(&self, memory: &Memory) -> Result<(), StoreError>

Source

pub fn bundle_memory_delete(&self, id: &str) -> Result<bool, StoreError>

Delete a Memory bundle. Refuses to delete the blank singleton.

Source

pub fn bundle_memory_reorder( &self, ordered_ids: &[String], ) -> Result<usize, StoreError>

Assign sort_order to the given bundle ids in the order supplied (position 0, 1, 2, …). Drives the Trust Center global brain ordering, which in turn controls CLAUDE.md injection order. Ids not present in the table are skipped silently (a concurrently-deleted section is not an error). Runs in a single transaction so a partial reorder never lands. Returns the number of rows updated.

Source§

impl Store

Source§

impl Store

Source

pub(super) fn registry_upsert_if_named(&self, inst: &AgentInstance)

Mirror a db_agent_instances mutation into the cross-version registry. Only fires for named rows. Routes by display_hidden so the registry file ends up in the tree matching SQLite’s dropdown filter:

  • hidden = true → upsert (atomic write to active/) then retire (atomic rename to retired/). Net: file lives in retired/<id>.json with the freshest content. Prevents instance_update on a previously-hidden row from resurrecting an active registry file, AND keeps the retired tombstone’s content current.
  • hidden = false → unretire (no-op if not retired) then upsert. Net: file in active/<id>.json, no orphan retired.

Failures are logged, never propagated: SQLite remains authoritative.

Source§

impl Store

Source

pub(super) fn agent_skill_list_local( &self, agent_id: &str, ) -> Result<Vec<AgentSkill>, StoreError>

List all skills for an agent, ordered by created_at ascending. LOCAL channel’s skills only — NO cross-channel fallback. Used by the def-registry mirror (which always operates on a local agent) and by agent_skill_list.

Source

pub fn agent_skill_list( &self, agent_id: &str, ) -> Result<Vec<AgentSkill>, StoreError>

Source

pub fn agent_skill_get( &self, id: &str, ) -> Result<Option<AgentSkill>, StoreError>

Get a single skill by id.

Source

pub fn agent_skill_insert(&self, skill: &AgentSkill) -> Result<(), StoreError>

Insert a new skill.

Source

pub fn agent_skill_update(&self, skill: &AgentSkill) -> Result<bool, StoreError>

Update an existing skill (all fields except id, agent_id, created_at).

Source

pub fn agent_skill_delete(&self, id: &str) -> Result<bool, StoreError>

Delete a skill by id. Returns true if a row was deleted.

Source§

impl Store

Source

pub fn open(path: &Path) -> Result<Self, StoreError>

Open a Store backed by a file on disk. Configures WAL mode and 5s busy timeout (matching Go).

Source

pub fn open_in_memory() -> Result<Self, StoreError>

Open an in-memory Store for testing.

Source

pub(crate) fn conn(&self) -> &Mutex<Connection>

Crate-internal accessor for sibling modules that maintain their own per-table CRUD via the DroneStore extension trait pattern (see agentmux-srv/src/drone/storage.rs). Outside callers must use the typed methods on this impl.

Source

pub fn run_agents_consolidate( &self, data_dir: Option<&Path>, ) -> Result<ConsolidateStats, StoreError>

Run the db_agents consolidation backfill under the wstore’s exclusive connection lock. Idempotent — gated by a marker file in data_dir (skip with None for tests).

Source

pub fn repair_agent_def_gaps(&self) -> Result<usize, StoreError>

Backfill any db_agent_definitions rows that are missing from db_agents. Not marker-gated — runs cheaply on every startup. See agents_consolidate::repair_def_gaps for details.

Source

fn configure_and_migrate(conn: Connection) -> Result<Self, StoreError>

Source

pub fn set_registry(&self, registry: Arc<Registry>)

Attach a shared cross-version agent registry. Called once on srv startup after Store::open and before the store is wrapped in Arc. Mutations to db_agent_instances will then parallel-write to the registry; the SQLite table remains the authoritative read path for PR A.

Source

pub(super) fn registry(&self) -> Option<Arc<Registry>>

Source

pub fn set_registry_agents_base(&self, base: PathBuf)

Set the channel agents dir that instance working_directory values are stored relative to (see the registry_agents_base field). Wired from AGENTMUX_AGENTS_DIR in main.rs in P0.3b (atomically with the registry re-root); until then it is exercised only by tests.

Source

pub fn registry_agents_base(&self) -> Option<PathBuf>

The base dir for instance working_directory relative paths.

Returns the explicitly-set channel agents dir ([set_registry_agents_base]) when present; otherwise falls back to the registry’s parent (agents_root()), which equals the channel agents dir in the pre-re-root layout. Used symmetrically by the write mirror and the read handler so the two never disagree on the anchor.

Source

pub fn shared_agent_registry(&self) -> Option<Arc<Registry>>

Public accessor for the cross-version named-agent registry. Returns None when the registry couldn’t be resolved at startup (CI / unusual envs); callers must handle the absent case by falling back to SQLite.

Source

pub fn set_def_registry(&self, def_registry: Arc<DefinitionStore>)

Attach the GLOBAL (cross-channel) agent-definition store. Called once on srv startup after Store::open, before the store is wrapped in Arc. Definition mutations then mirror to it.

Source

pub fn shared_def_registry(&self) -> Option<Arc<DefinitionStore>>

Public accessor for the global agent-definition store. None when it couldn’t be resolved at startup (CI / unusual envs / in-memory tests); callers fall back to SQLite.

Source

fn table_name<T: StoreObj>() -> String

Table name for a StoreObj type: db_<otype>.

Source

pub fn get<T: StoreObj>(&self, oid: &str) -> Result<Option<T>, StoreError>

Get a single object by OID. Returns None if not found.

Source

pub fn must_get<T: StoreObj>(&self, oid: &str) -> Result<T, StoreError>

Get a single object, returning StoreError::NotFound if missing.

Source

pub fn get_raw( &self, otype: &str, oid: &str, ) -> Result<Option<Value>, StoreError>

Get a single object as raw JSON Value by otype and OID. Used by GetObject/GetObjects to return data without strict struct deserialization.

Source

pub fn exists_raw(&self, otype: &str, oid: &str) -> Result<bool, StoreError>

Check if an object exists (by otype and OID).

Source

pub fn insert<T: StoreObj>(&self, obj: &mut T) -> Result<(), StoreError>

Insert a new object. Sets version to 1.

Source

pub fn update<T: StoreObj>(&self, obj: &mut T) -> Result<i64, StoreError>

Update an existing object. Increments version atomically. Returns the new version number.

Source

pub fn update_raw( &self, otype: &str, oid: &str, value: &Value, ) -> Result<i64, StoreError>

Update an object using raw JSON (bypasses struct deserialization). Used by UpdateObject where the frontend sends the full replacement object. This matches Go’s generic map-based UpdateObject behavior.

Source

pub fn delete<T: StoreObj>(&self, oid: &str) -> Result<(), StoreError>

Delete an object by OID.

Source

pub fn delete_by_otype(&self, otype: &str, oid: &str) -> Result<(), StoreError>

Delete by otype string and OID (for dynamic dispatch). Validates otype against VALID_OTYPES to prevent SQL injection.

Source

pub fn get_all<T: StoreObj>(&self) -> Result<Vec<T>, StoreError>

Get all objects of a given type.

Source

pub fn count<T: StoreObj>(&self) -> Result<i64, StoreError>

Count objects of a given type.

Source

pub fn with_tx<F, R>(&self, f: F) -> Result<R, StoreError>
where F: FnOnce(&StoreTx<'_>) -> Result<R, StoreError>,

Execute multiple operations in a single SQLite transaction. Acquires the Mutex once, wraps all operations in BEGIN/COMMIT. On error, rolls back and returns the error.

This is the key performance primitive — reduces N lock acquisitions and N fsyncs to 1 each.

Trait Implementations§

Source§

impl DroneStore for Store

Source§

fn drone_list(&self) -> Result<Vec<DroneDefinition>, StoreError>

Source§

fn drone_get(&self, id: &str) -> Result<Option<DroneDefinition>, StoreError>

Source§

fn drone_upsert(&self, wf: &DroneDefinition) -> Result<(), StoreError>

Source§

fn drone_delete(&self, id: &str) -> Result<bool, StoreError>

Source§

fn drone_run_insert(&self, run: &DroneRun) -> Result<(), StoreError>

Source§

fn drone_run_update(&self, run: &DroneRun) -> Result<usize, StoreError>

Update an existing run row in place. Returns rows affected (0 if no row matched run.id). Used to flip a placeholder running row into its terminal state once the drain task completes, so the row exists from the moment the RPC returns.
Source§

fn drone_runs_for( &self, drone_id: &str, limit: i64, ) -> Result<Vec<DroneRun>, StoreError>

Auto Trait Implementations§

§

impl !Freeze for Store

§

impl RefUnwindSafe for Store

§

impl Send for Store

§

impl Sync for Store

§

impl Unpin for Store

§

impl UnwindSafe for Store

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert 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>

Convert 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)

Convert &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)

Convert &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
where T: Any + Send + Sync,

§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,