agentmux_srv/backend/agent_session/global_store.rs
1// Copyright 2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Cross-channel transcript singleton + block-meta zone resolution.
5
6use std::sync::Arc;
7
8use crate::backend::storage::filestore::FileStore;
9
10use super::zone_naming::{agent_current_zone, is_valid_definition_id};
11
12/// Process-global handle to the GLOBAL transcript FileStore (the one rooted at
13/// `<shared>/agents/transcripts`, opened once in `main.rs`). Backs the
14/// `agent:<defId>:current` zone so a conversation loads when the agent is
15/// opened from *any* build/channel — finishing the cross-channel arc
16/// (#1387–#1396). `None` until `set_global_transcript_store` runs (or never, in
17/// unit tests / when the shared root can't be resolved), in which case the
18/// hot-path mirror is a no-op and reads fall back to the per-channel store.
19///
20/// It's a process-global rather than a threaded parameter because the store is
21/// genuinely process-wide (one per srv instance) and the alternative would be
22/// plumbing an `Option<Arc<FileStore>>` through `resync_controller` and every
23/// block-controller constructor purely to reach the stdout-reader hot path.
24static GLOBAL_TRANSCRIPT_STORE: std::sync::OnceLock<Arc<FileStore>> = std::sync::OnceLock::new();
25
26/// Install the global transcript store. Called once at startup from
27/// `bootstrap/stores.rs`.
28/// Idempotent — a second call is ignored (the first store wins).
29pub fn set_global_transcript_store(store: Arc<FileStore>) {
30 let _ = GLOBAL_TRANSCRIPT_STORE.set(store);
31}
32
33/// Borrow the global transcript store, if installed.
34pub fn global_transcript_store() -> Option<&'static Arc<FileStore>> {
35 GLOBAL_TRANSCRIPT_STORE.get()
36}
37
38/// Resolve the agent's GLOBAL `agent:<defId>:current` zone from a block's meta.
39///
40/// The block's `agentId` meta IS the agent `definition_id` (the same value the
41/// snapshot RPCs and `blockfile:read_range` fallback key on — see
42/// `global_output_source` in `app_api/mod.rs`), so the zone the hot-path
43/// mirror *writes* and the zone the read fallback *reads* are identical by
44/// construction. Returns `None` when the block isn't agent-anchored or carries
45/// an invalid id (no mirror/fallback).
46pub fn agent_zone_for_block_meta(meta: &crate::backend::obj::MetaMapType) -> Option<String> {
47 let def_id = crate::backend::obj::meta_get_string(meta, "agentId", "");
48 if is_valid_definition_id(&def_id) {
49 Some(agent_current_zone(&def_id))
50 } else {
51 None
52 }
53}