pub struct AppServerController {
block_id: String,
inner: Arc<Mutex<AppServerInner>>,
broker: Option<Arc<Broker>>,
event_bus: Option<Arc<EventBus>>,
mstore: Option<Arc<Store>>,
filestore: Option<Arc<FileStore>>,
health_monitor: Arc<TurnActivityTracker>,
self_ref: Mutex<Option<Weak<Self>>>,
}Expand description
Controller for one isolated codex app-server --listen stdio:// child.
Fields§
§block_id: String§inner: Arc<Mutex<AppServerInner>>§broker: Option<Arc<Broker>>§event_bus: Option<Arc<EventBus>>§mstore: Option<Arc<Store>>§filestore: Option<Arc<FileStore>>§health_monitor: Arc<TurnActivityTracker>§self_ref: Mutex<Option<Weak<Self>>>Implementations§
Source§impl AppServerController
impl AppServerController
pub fn new( block_id: String, broker: Option<Arc<Broker>>, event_bus: Option<Arc<EventBus>>, mstore: Option<Arc<Store>>, filestore: Option<Arc<FileStore>>, ) -> Self
pub fn set_self_ref(self: &Arc<Self>)
fn set_status(&self, status: &str)
fn publish_status(&self)
fn command_from_meta( &self, block_meta: &HashMap<String, Value>, ) -> Result<Command, String>
Sourcefn publish_protocol_frame(&self, frame: Value, global_zone: Option<&str>)
fn publish_protocol_frame(&self, frame: Value, global_zone: Option<&str>)
global_zone is resolved once per process by spawn_event_loop, not
per frame: resolving reads the block from the store, and frames arrive
many times a second while a turn streams.
fn spawn_event_loop( &self, process: Arc<AppServerProcess>, session: Arc<CodexAppServerSession>, )
fn spawn_turn(&self, session: Arc<CodexAppServerSession>, message: String)
pub fn send_message(&self, message: String) -> Result<(), String>
Sourcefn clear_process_and_session(&self)
fn clear_process_and_session(&self)
Clear a dead process/session so a later start() (resync) doesn’t see
its own stale process.is_some() guard and wrongly no-op instead of
respawning. ReAgent P1 on PR #3215’s third review: start()’s own
initialize/thread-setup failure branches set STATUS_DONE and shut the
process down without this, unlike the analogous EOF path in
spawn_event_loop — a handshake failure permanently bricked the block
with no recovery short of replacing the controller entirely.
fn stop_process(&self)
Trait Implementations§
Source§impl Controller for AppServerController
impl Controller for AppServerController
Source§fn start(
&self,
block_meta: HashMap<String, Value>,
_rt_opts: Option<Value>,
_force: bool,
) -> Result<(), String>
fn start( &self, block_meta: HashMap<String, Value>, _rt_opts: Option<Value>, _force: bool, ) -> Result<(), String>
Start the controller. May spawn background tasks.
force restarts even if already running.Source§fn stop(&self, _graceful: bool, new_status: &str) -> Result<(), String>
fn stop(&self, _graceful: bool, new_status: &str) -> Result<(), String>
Stop the controller.
graceful waits for process to exit; new_status is the target state.Source§fn get_runtime_status(&self) -> BlockControllerRuntimeStatus
fn get_runtime_status(&self) -> BlockControllerRuntimeStatus
Get the current runtime status.
Source§fn send_input(
&self,
input: BlockInputUnion,
_seq: Option<u64>,
) -> Result<(), String>
fn send_input( &self, input: BlockInputUnion, _seq: Option<u64>, ) -> Result<(), String>
Send input (terminal data, signal, or resize) to the controller.
seq is the per-TermViewModel monotonic counter; None means fire-and-forget (no ordering).Source§fn turn_tracker(&self) -> Option<Arc<TurnActivityTracker>>
fn turn_tracker(&self) -> Option<Arc<TurnActivityTracker>>
This controller’s turn tracker, for the turn ledger’s bookkeeping from
outside the controller (a held message’s join hint).
None for
controllers that track no turns.Source§fn controller_type(&self) -> &str
fn controller_type(&self) -> &str
Get the controller type (e.g., “shell”, “cmd”).
Source§fn stop_for_replace(&self, new_status: &str) -> Result<(), String>
fn stop_for_replace(&self, new_status: &str) -> Result<(), String>
Stop this controller because it’s being REPLACED by a new one for
the same block (session restart / resync’s
needs_replace path),
NOT because the block itself is being closed. Must terminate this
controller’s own CLI process so it doesn’t linger, but — unlike
stop() — must NOT reach any declared-background descendant the
process may have spawned (e.g. a task dev instance launched via
run_in_background: true); those must survive the replace. See
docs/specs/SPEC_BACKGROUND_TASK_TEARDOWN_SURVIVAL_2026_08_20.md. Read moreSource§fn shutdown(
&self,
_deadline: Instant,
) -> Pin<Box<dyn Future<Output = StopOutcome> + Send>>
fn shutdown( &self, _deadline: Instant, ) -> Pin<Box<dyn Future<Output = StopOutcome> + Send>>
Stop gracefully because the block is being CLOSED: end any turn in
progress, ask the process to exit, force-kill at
deadline. The
returned future resolves once the process has actually exited (or
there was none). Does NOT drop the block’s process tracker — the
caller does that after this resolves, so nothing the agent started
outlives it. See
docs/specs/SPEC_AGENT_PANE_CLOSE_GRACEFUL_SHUTDOWN_2026_09_18.md §9.3. Read moreSource§fn agent_id(&self) -> Option<String>
fn agent_id(&self) -> Option<String>
This block’s own live, spawn-time-captured jekt/muxbus identity, if
it has one — an independent source of truth for the recipient-
identity check in
ReactiveHandler::inject_message_inner, deliberately
NOT derived from ReactiveHandler’s own name_to_blocks/agent_info
maps (checking a registry against itself would be a tautology and
catch nothing). Default None for controller types that aren’t
jekt-addressable at all (e.g. plain terminals) — only ShellController
and PersistentSubprocessController currently override this, mirroring
the two paths that already resolve a jekt-registration identity at
spawn time (resolve_agent_id_for_jekt, muxbus_agent_id_from_env).Source§fn turn_provenance(&self) -> Option<TurnProvenance>
fn turn_provenance(&self) -> Option<TurnProvenance>
What started this agent’s current turn (SPEC_AGENT_SELF_QUIT §6.3), if
the controller tracks it.
None = unknown, which never counts as the
user — the safe answer for controllers that can’t tell (ACP, App
Server, subprocess: both origins reach them through one method).Source§fn set_agent_id(&self, _id: Option<String>)
fn set_agent_id(&self, _id: Option<String>)
Refresh this block’s own captured jekt/muxbus identity (see
agent_id’s doc comment). Called whenever
ReactiveHandler::register_agent/register_agent_with_nonce
(re-)registers THIS block’s block_id under a (possibly different)
agent_id, so the two independently-written copies never drift apart
(#2697: agent_id() was captured once at spawn and never
refreshed, while the registry’s name bindings get re-keyed on every
register_agent call — e.g. handle_reactive_register’s
frontend-initiated HTTP path — causing a legitimately renamed or
reconfigured agent’s own messages to be falsely rejected as an
identity mismatch). Default no-op for controller types that don’t
override agent_id either.Source§fn stable_agent_id(&self) -> Option<String>
fn stable_agent_id(&self) -> Option<String>
This block’s STABLE jekt identity —
AGENTMUX_AGENT_ID as captured
once at spawn, never refreshed. Distinct from agent_id,
which tracks the LIVE, renameable display name and is deliberately
refreshed every turn (see that method’s doc comment on #2697).
AGENTMUX_AGENT_ID is embedded verbatim in PR-body tags specifically
because it does NOT change if the agent is later renamed — but
ReactiveHandler’s registry only tracks one key per block, and
every turn’s agent_id() refresh evicts whatever this block was
previously registered under. ReactiveHandler::inject_message_inner’s
recipient-identity check (#2695) uses this alongside agent_id() so
a jekt addressed to the stable ID is validated correctly even after
the primary registry key has moved on to the live display name. See
INCIDENT_2026_09_09_JEKT_STABLE_ID_ALIAS.md. Default None,
mirroring agent_id’s default — only PersistentSubprocessController
currently overrides this.Source§fn stable_agent_uid(&self) -> Option<String>
fn stable_agent_uid(&self) -> Option<String>
Identity M2: this block’s UID (
db_agents.id) as carried in its
spawn env (AGENTMUX_AGENT_UID, set by build_persistent_spawn_env
since M1a), captured once at spawn like [stable_agent_id]. Backs the
registry’s UID confirmer: consulted only for targets that resolved by
UID, and None — a registered-but-not-yet-spawned controller, or a
block with no row — is “unverifiable”, not a mismatch. Default
None; only PersistentSubprocessController overrides this.Auto Trait Implementations§
impl !Freeze for AppServerController
impl !RefUnwindSafe for AppServerController
impl !UnwindSafe for AppServerController
impl Send for AppServerController
impl Sync for AppServerController
impl Unpin for AppServerController
impl UnsafeUnpin for AppServerController
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn 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>
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)
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)
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
impl<T> DowncastSync for T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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