pub(crate) struct BridgeInner {
client: Mutex<Option<Client>>,
event_tx: UnboundedSender<AgentEvent>,
events_rx: Mutex<Option<UnboundedReceiver<AgentEvent>>>,
pub(crate) pending: Arc<Mutex<HashMap<String, Sender<PermissionDecision>>>>,
pub(crate) pending_questions: Arc<Mutex<HashMap<String, Sender<QuestionOutcome>>>>,
pub(crate) session_id_slot: Arc<Mutex<String>>,
config_dir: PathBuf,
display_name: Option<String>,
extra_mcp_servers: Vec<(String, McpServer)>,
env: HashMap<String, String>,
context_probes_in_flight: Mutex<HashMap<String, Instant>>,
}Fields§
§client: Mutex<Option<Client>>Set after first new_session / resume_session; cleared on
session replace or shutdown.
event_tx: UnboundedSender<AgentEvent>Bridge → consumer event emission channel. Cloned freely into the
reader subtask + can_use_tool callback closures.
events_rx: Mutex<Option<UnboundedReceiver<AgentEvent>>>Single-take receiver handed out via ForgeSdkBridge::take_events.
pending: Arc<Mutex<HashMap<String, Sender<PermissionDecision>>>>Permission round-trip parking lot.
pending_questions: Arc<Mutex<HashMap<String, Sender<QuestionOutcome>>>>Question round-trip parking lot.
session_id_slot: Arc<Mutex<String>>Current session id, shared with the can_use_tool callback so
permission/question events carry the right session_id.
config_dir: PathBuf<config_dir> this bridge was bound to at construction time
(workspace-driven - typically the picked account’s
config_dir). Threaded into the spawned claude
subprocess as CLAUDE_CONFIG_DIR and consulted by every
in-process accessor (oauth, settings, catalog) so they
honour the bound account, not whatever $CLAUDE_CONFIG_DIR
the parent shell happened to have.
display_name: Option<String>Forge-internal account display name from forge.toml’s
[[accounts]], set by the workspace picker. None when
Agent::spawn is called directly (tests, smoke). When
present, read back through the handle’s display_name() so a
view can render which forge-account the bridge is bound to.
extra_mcp_servers: Vec<(String, McpServer)>Forge-workspace-supplied in-process MCP servers attached at
every spawn_session call. Today this is the per-session
forge MCP server, whose tool surface varies by session kind;
future modules (worktree, memory, …) slot in alongside under
the same forge server name. Cheap to clone - each entry
is just a name + a few Arc<dyn Tool>s.
env: HashMap<String, String>The session’s resolved forge.toml env - [env] merged with
[accounts.env] and the spawning project’s
env - stamped onto the spawned claude
subprocess by forge_sdk_worker::build_options_with_callback.
Empty when Agent::spawn is called directly without a
workspace (tests, smoke) or when no table declares anything.
context_probes_in_flight: Mutex<HashMap<String, Instant>>Session ids whose context-usage probe is out, each against the instant its mark was taken, added when the ask goes and removed when its answer, error or timeout lands.
A probe is answered inline over the CLI’s whole transcript, and the socket asks on every read of a seat that reports no usage, so without this one unanswered seat parks a probe per read. A probe that times out emits nothing, so a caller that coalesced only on its own side would repeat forever.
The instant bounds the mark the way the probe’s own timeout cannot:
every release lives inside the dispatched closure, so a closure that
never runs leaves a mark nothing will ever clear. Any asker past
ForgeSdkBridge::PROBE_MARK_DEADLINE reclaims it instead.