Skip to main content

ForgeSdkBridge

Struct ForgeSdkBridge 

Source
pub struct ForgeSdkBridge {
    inner: Arc<BridgeInner>,
}
Expand description

In-process bridge wrapping a single forge_sdk::Client.

Single instance per connection. The bridge owns the spawned forge_sdk::Client, the can_use_tool parking lots, and the outbound AgentEvent channel.

Fields§

§inner: Arc<BridgeInner>

Implementations§

Source§

impl ForgeSdkBridge

Source

pub(crate) fn new( config_dir: PathBuf, display_name: Option<String>, extra_mcp_servers: Vec<(String, McpServer)>, env: HashMap<String, String>, ) -> Self

Construct a fresh bridge bound to config_dir with an optional forge-account display_name. Both are stored as typed fields. config_dir is consulted by every in-process accessor (oauth, settings, catalog scans) and exported to the spawned claude subprocess as CLAUDE_CONFIG_DIR. display_name, when present, is read back through the handle’s display_name(). The internal event channel is created here; consumers grab the receiver once via ForgeSdkBridge::take_events.

Source

pub(crate) fn env(&self) -> HashMap<String, String>

The resolved forge.toml env to stamp onto every spawned claude subprocess. Cloned per spawn_session call, but the values were read from disk once at forge BOOT - a new bridge does not re-read them, so a forge.toml edit needs a forge restart, not a new session.

Source

pub(crate) fn extra_mcp_servers(&self) -> Vec<(String, McpServer)>

Forge-workspace-supplied in-process MCP servers to attach to every spawned claude subprocess (e.g. the forge server carrying the peer-coordination tools - #114 v1). Cheap-clone via the McpServer’s Arc<dyn Tool> internals; called once per spawn_session invocation.

Source

pub(crate) fn event_tx(&self) -> &UnboundedSender<AgentEvent>

Source

pub(crate) fn inner_pending( &self, ) -> &Arc<Mutex<HashMap<String, Sender<PermissionDecision>>>> ⓘ

Source

pub(crate) fn inner_pending_questions( &self, ) -> &Arc<Mutex<HashMap<String, Sender<QuestionOutcome>>>> ⓘ

Source

pub(crate) fn session_id_slot_arc(&self) -> &Arc<Mutex<String>> ⓘ

Source

fn client(&self) -> Option<Client>

Source

pub(crate) fn set_client(&self, client: Client)

Source

pub(crate) fn clear_client(&self) -> Option<Client>

Source

fn emit_mcp_error_or_log( event_tx: &UnboundedSender<AgentEvent>, session_id: String, operation: &'static str, server_name: Option<String>, error_msg: String, )

Send an AgentEvent::McpOperationError to the consumer; log a BRIDGE_LIFECYCLE warn if the channel is closed (terminal event for a user-visible MCP failure - silent-drop on teardown race would lose the only path to surface). On send failure we destructure the unsent event back out of the SendError so the warn log carries session_id, server_name, and the underlying error text - the most actionable triage signals.

Source

fn dispatch<F, Fut>(&self, label: &'static str, f: F) -> Result<()>
where F: FnOnce(Client) -> Fut + Send + 'static, Fut: Future<Output = Result<()>> + Send + 'static,

Spawn a fire-and-forget client call. Logs and drops on failure.

Source

fn dispatch_with_failure<F, Fut, T>( &self, label: &'static str, f: F, on_failure: T, ) -> Result<()>
where F: FnOnce(Client) -> Fut + Send + 'static, Fut: Future<Output = Result<()>> + Send + 'static, T: FnOnce(&Error) -> Option<AgentEvent> + Send + 'static,

Spawn a fire-and-forget client call. A failure inside the spawned future - or the client-less early Err - is emitted as the caller’s typed event (a None maps to log-only) - a view rolls back nothing otherwise: the spinner, the optimistic chip, the waiting awaiter all unwind only if this event arrives.

Source§

impl ForgeSdkBridge

Source

const CONTROL_RESPONSE_TIMEOUT: Duration

send_control parks until the CLI’s control_response; the wait is bounded so a silent CLI surfaces as a typed failure instead of a chip/spinner stuck on an optimistic change that never took.

Source

const PROBE_MARK_DEADLINE: Duration = Self::CONTROL_RESPONSE_TIMEOUT

How long a seat’s context-usage mark may stand before any asker may reclaim it. The probe gives up at Self::CONTROL_RESPONSE_TIMEOUT, counted from its closure’s first poll, so a mark past that bound is either already dead or a probe the timeout is about to drop - reclaiming costs at worst one overlapping probe, while leaving a dead mark costs the seat its reading for the life of the process.

Source

pub(crate) fn take_events(&self) -> Option<UnboundedReceiver<AgentEvent>>

Source

pub(crate) fn prompt_text( &self, session_id: String, text: String, uuid: String, ) -> Result<()>

Source

pub(crate) fn prompt_with_images( &self, session_id: String, text: String, images: Vec<ImageAttachment>, uuid: String, ) -> Result<()>

Source

pub(crate) fn cancel_queued( &self, session_id: String, uuid: String, ) -> Result<()>

Drop one prompt still in the CLI’s queue. The answer rides AgentEvent::PromptCancelResolved rather than the call’s return: the dispatcher channel is fire-and-forget, and a caller that reads false has to tell the reader the prompt was already taken.

Source

pub(crate) fn cancel(&self, session_id: String) -> Result<()>

Source

fn set_mode_rejection_text(err: &Error) -> String

The CLI’s own refusal text for a failed set_permission_mode. The SDK wraps it twice (MessageParse Display + a control failed: prefix); strip both so the chat line carries the reason, not the wrapper.

Source

pub(crate) fn set_mode( &self, session_id: String, mode: PermissionMode, ) -> Result<()>

Source

pub(crate) fn set_model(&self, session_id: String, model: String) -> Result<()>

Source

fn check_session_id(&self, session_id: &str, label: &'static str) -> bool

Verify the requested session_id matches the bridge’s current session before dispatching a user-action method. In a session-swap race a cancel/set_mode/set_model for session A could otherwise hit session B’s Client. Logs the mismatch and returns false; the caller is expected to drop the dispatch with a no-op Ok(()). A request carrying no id at all is refused the same way and logged at error, since there is nothing to match it to.

Other user-action methods (prompt_with_images) intentionally opt out - see the inline rationale at each call site.

Source

fn trace_session_id_bypass(&self, session_id: &str, label: &'static str)

Sibling of Self::check_session_id for methods that intentionally pass through on mismatch (no silent drop). Logs a breadcrumb at trace so postmortems can correlate “my prompt vanished” with a session-swap race, without raising the noise floor under normal operation.

Source

pub(crate) fn get_context_usage(&self, session_id: String) -> Result<()>

Source

pub(crate) fn get_mcp_snapshot(&self, session_id: String) -> Result<()>

Source

fn decode_or_report( &self, session_id: Option<&str>, launch_settings: Value, ) -> Result<SessionLaunchSettings>

Decode the launch settings, or report the refusal as the spawn failure it is (Ved refused the spawn, 2026-10-15).

The event is the point of doing it here. ConnectionFailed is what the seat’s own task records a spawn failure by and rolls the row on, so a payload that does not decode leaves a seat that says why and can be retried, not one stuck at Spawning with nothing to read.

Source

pub(crate) fn new_session( &self, session_id: Option<String>, cwd: String, launch_settings: Value, ) -> Result<()>

Start a session under session_id, or under an id the CLI picks when that is None.

Source

pub(crate) fn resume_session( &self, session_id: String, cwd: String, launch_settings: Value, ) -> Result<()>

Source

pub(crate) fn resume_or_new_session( &self, session_id: String, cwd: String, launch_settings: Value, ) -> Result<()>

Re-enter session_id, keeping the identity through a refusal. Three attempts, in order: resume it; when the CLI refuses - the transcript is gone - start a new session under the SAME id, which the CLI adopts and which keeps the identity stable; when that is refused too, start under a fresh id rather than failing. The third attempt exists because the first two can both be refused for a reason that is neither of them being absent (an unreadable transcript), and that is a state the pre-id fallback handled silently by starting a new session. Only all three failing surfaces ConnectionFailed. Used by project-rooted spawns (Default / Named) where the catalog’s recorded lead may be stale (e.g. cross-account scan, deleted file, schema drift). See forge_primitives::AgentCommand::ResumeOrNewSession for the motivation.

cwd is also passed to the resume attempt, not just the fallback. claude --resume <id> indexes sessions by the project key derived from its own working directory, so inheriting forge’s $PWD (the default when cwd is empty) makes every cross-directory resume fail with “No conversation found with session ID …” even when the .jsonl exists.

Source

pub(crate) fn permission_response( &self, _session_id: String, tool_call_id: String, outcome: PermissionOutcome, ) -> Result<()>

Source

pub(crate) fn question_response( &self, _session_id: String, tool_call_id: String, outcome: QuestionOutcome, ) -> Result<()>

Source

pub(crate) fn config_dir(&self) -> PathBuf

Cheap clone of the bridge’s bound config_dir. Used by the session worker to thread CLAUDE_CONFIG_DIR into the spawned claude subprocess and by direct-return accessors.

Source

pub(crate) fn display_name(&self) -> Option<String>

Cheap clone of the bridge’s bound forge-account display_name, when forge-workspace picked one. Used by the session worker to attach forge_account to the initial ForgeAccountIdentity emit alongside the CLI-side account.

Source

pub(crate) fn claude_pid(&self) -> Option<u32>

OS PID of the spawned claude child, when a client is bound and the transport reported one. Used by crate::env::processes to anchor an OS-level walk of the descendant process tree for the Inspector pane’s PROCESSES section. Returns None before the first new_session / resume_session lands a client, or after clear_client.

Source

pub(crate) fn project_memory_path(&self, cwd: &Path) -> PathBuf

Source

pub(crate) fn settings_documents(&self, cwd: Option<&Path>) -> SettingsDocuments

Trait Implementations§

Source§

impl Clone for ForgeSdkBridge

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Default for ForgeSdkBridge

Available on crate features testing only.
Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

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

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
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.

§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

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

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + 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: Sized + 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

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

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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