forge_workspace/lib.rs
1//! `forge-workspace` - multi-session orchestrator and view-facing
2//! facade.
3//!
4//! Pools [`forge_agent::Agent`] instances behind a single
5//! [`Workspace`] handle. One [`DomainSession`] per active session
6//! holds workspace-internal routing metadata (`AgentHandle` slot,
7//! claude-issued `session_id`, pending-interaction mailbox).
8//! Operational state a view renders (lifecycle, cwd, turn state,
9//! account info) stays on the view's side of the wire; workspace is a
10//! thin proxy that reacts to `Command`s in and emits `SessionUpdate`s
11//! out.
12//! A per-session actor pumps events from `AgentHandle::take_events()`,
13//! translates them into [`SessionUpdate`]s, and routes [`Command`]s
14//! back.
15//!
16//! ## Communication contract
17//!
18//! The view ↔ workspace contract is **one entry point in each
19//! direction**:
20//!
21//! - **view → workspace:** [`Workspace::dispatch`] takes a
22//! [`protocol::Command`]. One enum, one entry point.
23//! - **workspace → view:** [`Workspace::subscribe_mirror`] and
24//! [`Workspace::subscribe_answerer`] return receivers for
25//! [`protocol::SessionUpdate`]. Every caller gets a stream of its own, so
26//! a second view attaches beside the first, and a stream carries what is
27//! emitted after it subscribes. The socket's own fold takes the mirror;
28//! each client takes the answering or mirror role it declared, through
29//! `ViewSurface::subscribe_client`. The role decides one thing: whether
30//! the core may park a prompt on that subscriber's reply.
31//!
32//! No callback hooks, and no state shared between a view and the core. A view
33//! does not hold an
34//! `Arc<AgentHandle>`: every outbound call goes through
35//! `Workspace::dispatch(Command)`; query-style refreshes
36//! (`refresh_context_usage`, `refresh_mcp_snapshot`)
37//! and direct accessors (`settings_documents`,
38//! `project_memory_path`, `config_dir_for`) live as
39//! inherent methods on [`Workspace`]. The handle stays on the
40//! workspace's [`DomainSession`].
41//!
42//! ## Facade scope (intentionally thin)
43//!
44//! The MVVM boundary between a view and forge-agent is enforced at
45//! the **dependency graph** level: no view depends on `forge-agent`;
46//! everything routes through forge-workspace. The
47//! workspace exposes forge-agent's submodules verbatim via
48//! pass-through `pub use` (see the `env`, `tooling`, `translate`,
49//! `user_interaction` and `userdata` modules below). Types reached
50//! through them are *defined* in forge-agent - the workspace just
51//! exposes them under the workspace name so a view can keep its dep
52//! graph clean.
53
54mod account_cache;
55mod account_loader;
56pub mod bench;
57mod biometric;
58pub mod board;
59pub mod browser;
60pub mod catalogue;
61pub mod chase;
62mod config;
63pub mod conversation_turns;
64pub mod conversation_window;
65mod crons;
66mod dictate;
67pub mod documents;
68mod domain_session;
69mod error;
70pub mod file_index;
71mod gotify;
72mod handoff;
73mod healing;
74pub mod hub;
75pub mod idle_sweep;
76pub mod install;
77pub mod launch_settings;
78pub(crate) mod mcp;
79pub mod output_tail;
80mod parked;
81mod plugins;
82pub mod prompt;
83pub mod protocol;
84mod provider_probe;
85mod review;
86mod session_task;
87pub mod settings;
88mod settings_ask;
89mod settings_write;
90mod single_instance;
91mod slack;
92mod spawn;
93pub mod store;
94pub mod subagent_cards;
95mod systemone;
96mod target;
97mod tasks;
98pub mod terminal;
99pub mod tts;
100mod update_fanout;
101mod vault;
102mod vault_ask;
103mod views;
104pub mod voice;
105pub mod work;
106mod workspace;
107
108pub use dictate::{
109 DictateAxes, DictateBind, DictateFailure, DictateMode, DictateModel, DictateModelState,
110 DictateOverrideUpdate, DictateOverrides, DictateRole, DictateSettings, DictateSnapshot,
111};
112pub use forge_gateway::{LoadingState, Unusable, UsageFetchStatus};
113// The normalizer's prompt axes reach a view only through this
114// re-export: no view depends on a forge-dictate crate of its own.
115pub use domain_session::{ContextUsage, DomainSession, McpServers, TurnLiveness, liveness_of};
116pub use error::WorkspaceError;
117pub use forge_dictate::normalize::{Context, Structure, Styling};
118pub use handoff::{
119 ShippedSkill, SkillInstall, install_skill, shipped_skills, skill_dir, skill_path,
120};
121pub use protocol::{
122 Command, DictateOutcome, DispatchError, NoticeSeverity, PromptSource, SessionUpdate,
123 TurnErrorClass,
124};
125pub use target::{ProjectKey, SessionSlot, SessionTarget};
126pub use tts::{TtsSettings, TtsSnapshot, TtsView};
127
128pub use update_fanout::SeatUpdate;
129pub use vault::VaultState;
130pub use views::{
131 AccountGatewayView, AccountLoadingRow, AccountPoolView, AccountUsageView, ProjectView,
132};
133#[cfg(any(test, feature = "testing"))]
134pub use workspace::testing::set_seat_ring_cap;
135pub use workspace::{
136 BootWaveTarget, LEAD_RESTART_NOTE, PendingInteractionKind, PricingFetch, PricingFetcher,
137 SessionChipInfo, SessionChipState, SettingsSaveError, Workspace,
138};
139
140// MCP (agents / peers / workers) public surface. The `mcp` module
141// itself is crate-private now; these flat re-exports expose only the
142// types production consumers (views) need to read off
143// `SessionUpdate` payloads. The `testing`-feature block below adds the
144// extra surface the `forge-test-harness` integration tests need (MCP
145// server builders, mock facades, the caller-key resolver).
146pub use mcp::cron::schedule::next_fire_after;
147pub use mcp::gotify::types::GotifyNotification;
148pub use mcp::peers::types::{MessageId, WrappedKind, WrappedPrompt};
149pub use mcp::workers::types::{LiveWorkerState, WorkerEntry};
150
151// Re-export forge-agent types that public surface returns, so
152// callers can write `use forge_workspace::AgentHandle` if they
153// prefer.
154pub use forge_agent::AgentHandle;
155pub use forge_agent::client::SessionLaunchSettings;
156pub use forge_agent::has_a_transcript_row;
157pub use forge_agent::session_history;
158pub use forge_agent::session_history_main_thread;
159pub use forge_agent::transcript_rows;
160
161// Re-export forge-agent sub-surfaces the server reads, kept as a
162// pass-through so its callers reach them through the workspace rather
163// than a direct `forge-agent` dep. Most types here are themselves
164// re-exports from `forge_primitives`; the modules also surface a
165// handful of helper functions / network fetchers that genuinely live
166// in `forge-agent`.
167pub use config::{BrowserSettings, ConfigReload, PluginSettings};
168pub mod env {
169 pub mod cli_version {
170 pub use forge_agent::env::cli_version::*;
171 }
172 pub mod config_watch {
173 pub use forge_agent::env::config_watch::*;
174 }
175 pub mod documents {
176 pub use forge_agent::env::documents::*;
177 }
178 pub mod file_index {
179 pub use forge_agent::env::file_index::*;
180 }
181 pub mod git_diff {
182 pub use forge_agent::env::git_diff::*;
183 }
184 pub mod lsp_path {
185 pub use forge_agent::env::lsp_path::*;
186 }
187 pub mod processes {
188 pub use forge_agent::env::processes::*;
189 }
190 pub mod timezone {
191 pub use forge_agent::env::timezone::*;
192 }
193}
194pub mod tooling {
195 pub use forge_agent::tooling::*;
196}
197pub mod translate {
198 pub use forge_agent::translate::*;
199}
200/// Pass-through for the agent's typed tool-input parsers
201/// (`AskUserQuestion`, `Monitor`), which the server reads when folding
202/// a tool call into the block a view draws.
203pub mod user_interaction {
204 pub use forge_agent::user_interaction::*;
205}
206pub mod userdata {
207 pub use forge_agent::userdata::*;
208}
209pub use forge_primitives::background::BackgroundTask;
210pub use forge_primitives::permission::PermissionMode;
211pub use forge_primitives::runtime::RuntimeSessionState;
212
213// MCP test-harness re-exports. `forge-test-harness` integration tests
214// drive the workers MCP server directly against a mock facade; that
215// requires the server-builder and the facade trait / mock to be
216// visible cross-crate. Gating on `testing` keeps them out of
217// production builds.
218#[cfg(feature = "testing")]
219pub use mcp::agents::build_server as build_agents_server;
220#[cfg(feature = "testing")]
221pub use mcp::peers::facade::MockWorkspaceFacade;
222#[cfg(feature = "testing")]
223pub use mcp::peers::types::{PeerLiveness, PeerStatus};
224pub use mcp::workers::facade::LEAD_LABEL;
225#[cfg(feature = "testing")]
226pub use mcp::workers::facade::{CallerProject, MockWorkerFacade, WorkerFacade};