SubagentManager class final

Constructors

SubagentManager({required String parentSessionId, ChildSessionFactory? createChildSession, SubagentRegistrySink? sink, SubagentRegistrySource? source, MessagingRepository? messaging, MailboxWakeLauncher? wakeProcess, String? homeDir, String selfId = 'main', int maxPendingMessages = 16, int maxReplyChars = 8000, DateTime clock() = _systemUtcClock})

Properties

a2aGateway ↔ A2aMailGateway?
The A2A boundary gateway (issue #27 phase 3): when set, a name@machine address naming ANOTHER machine delivers through the a2a: config's server for that machine. Null = foreign machines stay unresolved (the phase-2 honest error).
getter/setter pair
clock → DateTime Function()
The manager's clock, stamped into createdAt/lastActivity. Injectable so tests drive spawn ages and stall windows deterministically (issue #383 heartbeat).
final
createChildSession → ChildSessionFactory?
Injected: creates a new child session, returns its id.
final
events → Stream<SubagentEvent>
Broadcast stream of subagent events.
no setter
handles → List<SubagentHandle>
All handles in registration order.
no setter
hashCode → int
The hash code for this object.
no setterinherited
homeDir → String?
The user's home directory, used to shorten cwd tags in directory views (/home/u/git/x → ~/git/x). Null leaves cwd tags as-is.
final
machineName ↔ String?
This host's machine name for name@machine addressing (issue #27 phase 2): a @machine suffix matching it (case-insensitive) is stripped before local resolution; any other machine is phase-3 A2A territory and stays unresolved. Null = the host did not report one — machine-suffixed addresses then never resolve locally.
getter/setter pair
mailboxPrefix ↔ String
Namespace prefix for every mailbox this manager touches (e.g. the parent session id): two Fa instances sharing one messaging root never drain each other's inboxes. Mutable — the host sets it once the session (and thus its id) exists. Empty = single-instance mode.
getter/setter pair
maxPendingMessages → int
Size guard: messages queued to one child before new ones are rejected (Phase 3b pending-queue guard).
final
maxReplyChars → int
Size guard: reply/message body cap in characters.
final
messaging → MessagingRepository?
Injected: the messaging fabric. When present, inter-agent messages go through per-agent inboxes (visible across processes sharing the messaging root) instead of the in-process pending queue.
final
parentSessionId ↔ String
The parent session id (used to derive child session paths and to write child session headers' metadata.parent at flush time).
getter/setter pair
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
selfId → String
This host's own agent id in the messaging fabric (main for the orchestrator). Inbox drains for the loop use it.
final
sink → SubagentRegistrySink?
Injected: persists the registry into the parent session.
final
source → SubagentRegistrySource?
Injected: reads the persisted registry from the parent session.
final
wakeChild ↔ Future<void> Function(String id)?
Wakes a retained child with pending inbox mail by resuming it in its own session (gh-970). Hosts WITH the child-resume capability wire their executor's resume here: wakeChild = (id) => executor.resumeChild(id, childInboxWakePrompt) — the resumed run's warm wake drains the inbox as its first action, so the reminder (or sibling mail) that fired into a finished monitor is consumed and the recurrence continues. Null (the app host — no child-resume capability) keeps the sweep a no-op; the mail waits for the next task_send/steer.
getter/setter pair
wakeProcess → MailboxWakeLauncher?
Detached headless launcher for waking asleep mailboxes (see MailboxWakeLauncher). Null when the host cannot spawn processes.
final

Methods

attachSession(String id, String sessionPath) → Future<void>
Attaches the child's real session file to its handle (called by the executor at child completion when a session factory is wired). The handle's placeholder id becomes the real JSONL path used by /agents open <id> and task_observe.
close() → Future<void>
Closes the event stream.
dispose(String id) → Future<void>
Removes a handle (dispose).
drainMessages(String id) → Future<List<SubagentMessage>>
Drains id's pending queue (delivered messages leave the registry). With a messaging fabric this consumes the agent's file inbox.
enqueueMessage(String id, SubagentMessage message, {bool capText = true}) → Future<void>
Queues message for id (Phase 3b agent_message / parent steering). Throws StateError for an unknown id or a full pending queue; caps the body at maxReplyChars unless capText is false — the warm-wake resume path passes the parent's steering verbatim (the mailbox cap would silently truncate it AND shrink it under the over-window guard, letting an oversized request slip through to the provider). Aborted children refuse new messages.
hasPendingMessages(String id) → Future<bool>
Non-draining pending check (issue #647): the child-loop steering probe. Covers the fabric inbox AND the in-memory pending queue — the mail:N panel count above reads the fabric only.
mailboxOf(String id) → String
The fabric mailbox for a local agent id. An id containing / is already an absolute mailbox (cross-instance addressing like <sessionId>/main) and passes through unprefixed.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
notePressure(String id, {required int estTokens, required int windowTokens}) → void
Context-pressure stamp (issue #439): the executor calls this at every child turn boundary with the estimated request tokens and the effective window, so task_status can show the wall approaching. In-memory only, like touch — the next boundary refreshes it.
pendingInbox(String id) → Future<List<AgentMessage>>
The unread inbox messages of id (empty without a fabric) — the pending-inbox block of the observe/detail views.
pendingInboxCount(String id) → Future<int>
Counts the unread inbox messages of id (0 without a fabric) — the mail:N indicator in the agents panel.
recordCompaction(String id, {required int freedTokens}) → void
Records a completed proactive compaction (issue #439): bumps the counter, stamps the freed-token count and the time, and persists — unlike notePressure this is a durable lifecycle fact.
recordReply(String id, String text) → Future<void>
Records the child's explicit reply (Phase 3b) on its handle.
register({required String id, required String name, required String agentType, required String task, String context = ''}) → Future<SubagentHandle>
Registers a new subagent — NEVER creates a child session eagerly.
rehydrate() → Future<void>
Rehydrates the registry from the parent session (idempotent).
reset() → void
Drops the registry view so the next rehydrate loads afresh — used when the host switches to a different parent session (each session owns its own registry). Wake bookkeeping is dropped with it: the new session's children start clean.
toString() → String
A string representation of this object.
inherited
touch(String id, {required int tokens, required int requests}) → void
In-flight liveness touch (issue #383 heartbeat): the executor calls this when the child proves provider-side life — a completed assistant response — stamping SubagentHandle.lastActivity and the running turn's usage snapshot. Deliberately in-memory only: the registry snapshot is not rewritten per provider turn (a write storm for a freshness signal the next real update refreshes anyway).
update(String id, {SubagentStatus? status, int? tokens, int? requests, String? modelId, String? error, bool clearError = false}) → Future<void>
Updates a handle's status and emits an event. clearError drops the recorded failure — the resume path (issue #222) clears the old error when a failed child goes back to running.
wakeChildrenWithPendingMail() → Future<int>
Starts a wake for every retained child that (a) holds pending inbox mail and (b) can be woken — SubagentStatus.completed or SubagentStatus.idle. Failed/aborted children stay manual (task_resume semantics); queued/running children consume their inbox at the next turn boundary on their own. Fire-and-forget by design: the sweep rides the host's inbox tick and never blocks it. Returns how many wakes were STARTED. Without wakeChild or a fabric this is a cheap no-op.

Operators

operator ==(Object other) → bool
The equality operator.
inherited
operator [](String id) → SubagentHandle?
Looks up a handle by id.