Show live background task counts
This commit is contained in:
1 parent
9fd21af4e8
commit
8262ceb786
16 files changed
+157
-26
No files matched your search
@@ -264,7 +264,10 @@ written, and the fold uses that same predicate to decide a reply is settled.
|
||||
- **Claude's background-task level is authority; two edge sources are the
|
||||
fallback.** Since Claude Code 2.1.261,
|
||||
`background_tasks_changed { tasks: [...] }` replaces the live set and repairs
|
||||
a missed ending edge. An adopted CLI is sent a repeated `initialize` to ask
|
||||
a missed ending edge. Its array size is also the measured `backgroundTasks`
|
||||
count exposed on the session row and event stream; the phone draws a nonzero
|
||||
count beside the status rather than deriving one from `waiting` or from the
|
||||
subagent directory. An adopted CLI is sent a repeated `initialize` to ask
|
||||
for the current set. Reconcile only between turns or at a result boundary:
|
||||
a foreground agent is legitimately absent from a background-only snapshot.
|
||||
Older CLIs still need both edge sources: `open_tasks` knows about a
|
||||
|
||||
@@ -858,7 +858,9 @@ the reader was not asking after, and one of them turned out to be a whole shell
|
||||
command drawn as centred prose, because its words came from somewhere with no
|
||||
reason to keep them short. The parent's transcript gets a row for a message a
|
||||
subagent genuinely *sends* it, which arrives by the peer path and already has
|
||||
one.
|
||||
one. The live provider-reported count beside the parent session's status is
|
||||
deliberately the only background-task UI until there is a design for inspecting
|
||||
them.
|
||||
|
||||
**The report goes to whichever record is the only one of it**, and the two cases
|
||||
are different places. A subagent has a transcript of its own, and its closing
|
||||
@@ -874,7 +876,10 @@ the ones a stale "running in background" reads worst on.
|
||||
(2026-09-15). Claude Code 2.1.261 added
|
||||
`background_tasks_changed { tasks: [...] }` with replace semantics expressly so
|
||||
a missed bookend cannot wedge a running indicator. Its ids are not correlated
|
||||
with the edge stream; this side uses the authoritative empty/nonempty level.
|
||||
with the edge stream; this side uses the authoritative empty/nonempty level and
|
||||
its measured size. That size is exposed as `backgroundTasks` in the session row
|
||||
and event stream, and is drawn beside the status; background tasks do not become
|
||||
subagent cards.
|
||||
The driver sends a repeated `initialize` when it adopts a CLI, which prompts a
|
||||
full snapshot without restarting the conversation. A parent already recorded as idle or waiting can
|
||||
apply it immediately; one adopted mid-turn waits for the result boundary,
|
||||
|
||||
+5
-3
@@ -89,9 +89,11 @@ transcript is still being written to and its process is the session's to stop.
|
||||
Since Claude Code 2.1.261, `background_tasks_changed { tasks: [...] }` is
|
||||
the authoritative level beside those edges: its set replaces the previous
|
||||
set, so a missed terminal edge cannot leave a subagent running forever. Its
|
||||
ids are deliberately not correlated with the edge stream; the useful claim
|
||||
here is whether the set is empty. The edges still carry mapping, outcome
|
||||
and closing summary. On adoption the driver sends a repeated `initialize`,
|
||||
ids are deliberately not correlated with the edge stream; the useful claims
|
||||
here are whether the set is empty and its measured size. The session API and
|
||||
stream expose that size as `backgroundTasks`, which the phone draws beside
|
||||
the status without pretending those tasks are subagents. The edges still
|
||||
carry mapping, outcome and closing summary. On adoption the driver sends a repeated `initialize`,
|
||||
which makes a current CLI send the full set; an older CLI accepts it and sends no level,
|
||||
leaving the edge-based path unchanged. A snapshot is reconciled immediately
|
||||
when the persisted parent status proves it is between turns, and otherwise
|
||||
|
||||
@@ -5,6 +5,10 @@ one in place when it turns out to need a decision.
|
||||
|
||||
## App — transcript
|
||||
|
||||
- [ ] Decide how running background tasks can be inspected. For now the session
|
||||
status shows only the provider-reported count; command details stay in
|
||||
their existing tool cards and must not become subagent cards.
|
||||
|
||||
- [ ] Messages received from other agents are inconsistent — sometimes they
|
||||
appear, sometimes they don't. **Needs a rig.** Read the code rather than
|
||||
measured: a live Claude session only learns of a peer message from the
|
||||
|
||||
@@ -225,6 +225,8 @@ data class SessionSummary(
|
||||
val usageProvider: String?,
|
||||
val status: String,
|
||||
val lastActivity: Double,
|
||||
/** Latest measured number of live background tasks; zero also covers older servers. */
|
||||
val backgroundTasks: Int,
|
||||
/**
|
||||
* How many subagents this session has, however their own status now reads.
|
||||
*
|
||||
@@ -263,6 +265,7 @@ private fun parseSession(session: JSONObject) =
|
||||
usageProvider = session.optString("usageProvider").ifEmpty { null },
|
||||
status = session.getString("status"),
|
||||
lastActivity = session.getDouble("lastActivity"),
|
||||
backgroundTasks = session.optInt("backgroundTasks", 0),
|
||||
subagents = session.optInt("subagents", 0),
|
||||
)
|
||||
|
||||
|
||||
@@ -140,6 +140,9 @@ sealed class SessionEvent {
|
||||
/** The same command, handed to the session. */
|
||||
data class CommandSent(val id: String, val text: String) : SessionEvent()
|
||||
|
||||
/** Provider-reported number of background tasks alive now. */
|
||||
data class BackgroundTasks(val count: Int) : SessionEvent()
|
||||
|
||||
data class Status(val state: String) : SessionEvent()
|
||||
|
||||
/**
|
||||
@@ -283,6 +286,7 @@ fun parseSeqEvent(json: String): SeqEvent {
|
||||
"commandQueued" ->
|
||||
SessionEvent.CommandQueued(body.getString("id"), body.getString("text"))
|
||||
"commandSent" -> SessionEvent.CommandSent(body.getString("id"), body.getString("text"))
|
||||
"backgroundTasks" -> SessionEvent.BackgroundTasks(body.getInt("count"))
|
||||
"status" -> SessionEvent.Status(body.getString("state"))
|
||||
"settings" ->
|
||||
SessionEvent.Settings(
|
||||
|
||||
@@ -554,6 +554,14 @@ private fun SessionCard(
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
StatusText(session.status)
|
||||
if (session.backgroundTasks > 0) {
|
||||
Text(
|
||||
backgroundTaskLabel(session.backgroundTasks),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
|
||||
@@ -256,6 +256,8 @@ fun SessionScreen(
|
||||
val topEdgeHeld = remember { TopEdgeHold() }
|
||||
var items by remember { mutableStateOf(listOf<TranscriptItem>()) }
|
||||
var status by remember { mutableStateOf(subagent?.status ?: summary.status) }
|
||||
var backgroundTasks by
|
||||
remember(address) { mutableIntStateOf(if (isSubagent) 0 else summary.backgroundTasks) }
|
||||
// Seeded from the row this screen was opened from, so a conversation already under way says how
|
||||
// much it is holding before any turn happens here. Null is "nobody has measured it", which is a
|
||||
// different answer from an empty context and is drawn differently.
|
||||
@@ -538,6 +540,9 @@ fun SessionScreen(
|
||||
}
|
||||
status = event.state
|
||||
}
|
||||
if (event is SessionEvent.BackgroundTasks && !isSubagent) {
|
||||
backgroundTasks = event.count
|
||||
}
|
||||
if (!isSubagent) loginOpen = authenticationPromptAfter(loginOpen, event)
|
||||
// In order, always: one late event recorded ahead of the backlog would fold a
|
||||
// streamed delta into whatever row happened to be last by then.
|
||||
@@ -1842,6 +1847,7 @@ fun SessionScreen(
|
||||
status = status,
|
||||
compactingFor = compactingFor,
|
||||
contextTokens = contextTokens,
|
||||
backgroundTasks = backgroundTasks,
|
||||
subagent = isSubagent,
|
||||
)
|
||||
|
||||
@@ -2362,6 +2368,8 @@ private fun SessionStatusRow(
|
||||
compactingFor: Long?,
|
||||
/** Context the session is holding, or null where nothing has measured it. */
|
||||
contextTokens: Long?,
|
||||
/** Provider-reported live background work; zero is deliberately not drawn. */
|
||||
backgroundTasks: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
/**
|
||||
* Whether this row is for a subagent rather than a session, which changes only one word:
|
||||
@@ -2395,6 +2403,14 @@ private fun SessionStatusRow(
|
||||
// its own contrast, since the surface under it will not change to rescue it.
|
||||
color = commandColor,
|
||||
)
|
||||
if (backgroundTasks > 0) {
|
||||
Text(
|
||||
backgroundTaskLabel(backgroundTasks),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
)
|
||||
}
|
||||
LinearProgressIndicator(
|
||||
color = commandColor,
|
||||
trackColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
@@ -2415,6 +2431,14 @@ private fun SessionStatusRow(
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
)
|
||||
if (backgroundTasks > 0) {
|
||||
Text(
|
||||
backgroundTaskLabel(backgroundTasks),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
}
|
||||
// Every remaining state says which one it is, including the quiet one. The row used to
|
||||
@@ -2423,13 +2447,22 @@ private fun SessionStatusRow(
|
||||
// showed nothing at all. The words and the colour are `sessionStatusWord`'s and
|
||||
// `sessionStatusColour`'s, shared with the session list so one state is not called two
|
||||
// things -- or drawn two colours -- depending which screen you are on.
|
||||
else ->
|
||||
else -> {
|
||||
Text(
|
||||
sessionStatusWord(status, subagent),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = sessionStatusColour(status),
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
if (backgroundTasks > 0) {
|
||||
Text(
|
||||
backgroundTaskLabel(backgroundTasks),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
// How full the session is, which is the number a reader is asking about -- how much room is
|
||||
// left before the next compaction -- rather than what has been spent getting here.
|
||||
|
||||
@@ -38,6 +38,8 @@ fun sessionStatusWord(status: String, subagent: Boolean = false): String =
|
||||
else -> status
|
||||
}
|
||||
|
||||
fun backgroundTaskLabel(count: Int): String = "$count bg ${if (count == 1) "task" else "tasks"}"
|
||||
|
||||
/**
|
||||
* The colour that goes with [sessionStatusWord]: the accent is spent on the states that are about
|
||||
* to do something or want something, and every quiet one shares the muted colour.
|
||||
|
||||
@@ -517,6 +517,7 @@ fun foldEvent(items: List<TranscriptItem>, entry: SeqEvent): List<TranscriptItem
|
||||
// nothing it belongs above.
|
||||
is SessionEvent.MessageDropped -> items
|
||||
is SessionEvent.Settings -> items
|
||||
is SessionEvent.BackgroundTasks -> items
|
||||
is SessionEvent.Status -> settleReply(items, event.state)
|
||||
is SessionEvent.AuthenticationRequired ->
|
||||
items + TranscriptItem.ErrorMsg(entry.seq, event.message)
|
||||
|
||||
@@ -510,6 +510,10 @@ impl ClaudeDriver {
|
||||
}
|
||||
|
||||
impl Driver for ClaudeDriver {
|
||||
fn background_tasks(&self) -> Option<usize> {
|
||||
self.state.lock().unwrap().background_task_count()
|
||||
}
|
||||
|
||||
fn send_user_message(&self, text: String, attachments: Vec<AttachmentRef>) {
|
||||
let mut content = Vec::new();
|
||||
// An image goes into the message itself; the model looks at it. Any
|
||||
|
||||
@@ -136,8 +136,9 @@ pub(super) struct Translator {
|
||||
/// Claude's level signal for background work, when this CLI is new enough
|
||||
/// to send one. Unlike `open_tasks`, this is a snapshot: each new value
|
||||
/// replaces the old one, so a missed ending edge cannot leave work open
|
||||
/// forever. See [`Translator::translate_background_tasks`].
|
||||
background_tasks: Option<bool>,
|
||||
/// forever. The count is also shown beside the session's status. See
|
||||
/// [`Translator::translate_background_tasks`].
|
||||
background_tasks: Option<usize>,
|
||||
/// Tasks the level signal closed before their ordinary notification
|
||||
/// arrived. That notification still owns the useful summary, so it gets
|
||||
/// one chance to update the transcript or tool card after the status was
|
||||
@@ -649,17 +650,18 @@ impl Translator {
|
||||
};
|
||||
let was_outstanding = self.work_outstanding();
|
||||
// The CLI explicitly says not to correlate these ids with its edge
|
||||
// stream. Their useful claim here is the level: empty or not.
|
||||
self.background_tasks = Some(!tasks.is_empty());
|
||||
// stream. Their useful claims here are the level and its exact size.
|
||||
self.background_tasks = Some(tasks.len());
|
||||
let mut events = vec![Event::BackgroundTasks { count: tasks.len() }];
|
||||
|
||||
// During a turn the snapshot omits a foreground task, so wait for the
|
||||
// result boundary before using it to close anything. Between turns,
|
||||
// every task that can still be alive is background work and the level
|
||||
// can repair a missed notification immediately.
|
||||
if !self.settled || self.in_turn {
|
||||
return Vec::new();
|
||||
return events;
|
||||
}
|
||||
let mut events = self.reconcile_background_tasks();
|
||||
events.extend(self.reconcile_background_tasks());
|
||||
let is_outstanding = self.work_outstanding();
|
||||
if was_outstanding != is_outstanding {
|
||||
events.push(Event::Status {
|
||||
@@ -677,7 +679,7 @@ impl Translator {
|
||||
/// foreground task can remain. Returns updates for background commands;
|
||||
/// subagents carry the same correction in their own status transcript.
|
||||
fn reconcile_background_tasks(&mut self) -> Vec<Event> {
|
||||
let Some(false) = self.background_tasks else {
|
||||
let Some(0) = self.background_tasks else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut events = Vec::new();
|
||||
@@ -717,11 +719,17 @@ impl Translator {
|
||||
/// `session_running` is true by construction: this is only ever asked
|
||||
/// while translating a line the session's process just wrote.
|
||||
fn work_outstanding(&self) -> bool {
|
||||
self.background_tasks == Some(true)
|
||||
self.background_tasks.is_some_and(|count| count > 0)
|
||||
|| !self.open_tasks.is_empty()
|
||||
|| self.subagents.any_open(true)
|
||||
}
|
||||
|
||||
/// The latest count Claude supplied, and `None` until this process has
|
||||
/// supplied its first authoritative snapshot.
|
||||
pub(super) fn background_task_count(&self) -> Option<usize> {
|
||||
self.background_tasks
|
||||
}
|
||||
|
||||
/// A task reporting back, from whichever of the two lines got here first.
|
||||
///
|
||||
/// Handled once. The two shapes can both arrive for one task, and what
|
||||
@@ -1853,23 +1861,30 @@ mod tests {
|
||||
let mut translator = Translator::new(dir.path().to_path_buf(), Arc::clone(&subagents));
|
||||
translator.mark_settled();
|
||||
|
||||
assert!(
|
||||
assert_eq!(
|
||||
translate_lines(
|
||||
&mut translator,
|
||||
&[r#"{"type":"system","subtype":"background_tasks_changed","tasks":["toolu_stale"]}"#],
|
||||
)
|
||||
.is_empty()
|
||||
&[
|
||||
r#"{"type":"system","subtype":"background_tasks_changed","tasks":["toolu_stale","command-2","command-3","command-4","command-5"]}"#
|
||||
],
|
||||
),
|
||||
vec![Event::BackgroundTasks { count: 5 }]
|
||||
);
|
||||
assert_eq!(translator.background_task_count(), Some(5));
|
||||
assert_eq!(subagents.list(true)[0].status, SessionStatus::Running);
|
||||
assert_eq!(
|
||||
translate_lines(
|
||||
&mut translator,
|
||||
&[r#"{"type":"system","subtype":"background_tasks_changed","tasks":[]}"#],
|
||||
),
|
||||
vec![Event::Status {
|
||||
vec![
|
||||
Event::BackgroundTasks { count: 0 },
|
||||
Event::Status {
|
||||
state: SessionStatus::Idle
|
||||
}]
|
||||
}
|
||||
]
|
||||
);
|
||||
assert_eq!(translator.background_task_count(), Some(0));
|
||||
assert_eq!(subagents.list(true)[0].status, SessionStatus::Exited);
|
||||
}
|
||||
|
||||
@@ -1884,12 +1899,12 @@ mod tests {
|
||||
let mut translator = Translator::new(dir.path().to_path_buf(), Arc::clone(&subagents));
|
||||
translator.mark_running();
|
||||
|
||||
assert!(
|
||||
assert_eq!(
|
||||
translate_lines(
|
||||
&mut translator,
|
||||
&[r#"{"type":"system","subtype":"background_tasks_changed","tasks":[]}"#],
|
||||
)
|
||||
.is_empty()
|
||||
),
|
||||
vec![Event::BackgroundTasks { count: 0 }]
|
||||
);
|
||||
assert_eq!(subagents.list(true)[0].status, SessionStatus::Running);
|
||||
|
||||
|
||||
@@ -337,6 +337,14 @@ pub enum Event {
|
||||
id: String,
|
||||
answers: Vec<String>,
|
||||
},
|
||||
/// How many background tasks the provider says are alive now.
|
||||
///
|
||||
/// State rather than a transcript row: the phone draws it beside the
|
||||
/// session status. A distinct event keeps the count current while a
|
||||
/// session is open; `GET /sessions` supplies the opening snapshot.
|
||||
BackgroundTasks {
|
||||
count: usize,
|
||||
},
|
||||
Status {
|
||||
state: SessionStatus,
|
||||
},
|
||||
@@ -596,6 +604,12 @@ pub type EventSink = mpsc::UnboundedSender<Event>;
|
||||
/// with live input injects it at the next tool boundary, while a turn-at-a-time
|
||||
/// dialect queues it for the next child process.
|
||||
pub trait Driver: Send + Sync {
|
||||
/// The provider's latest measured number of live background tasks.
|
||||
/// `None` means it has not reported one, not that the count is zero.
|
||||
fn background_tasks(&self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Takes a message, now or once the session is free for it.
|
||||
///
|
||||
/// Every driver owes exactly one `MessageTaken` per message, at the moment
|
||||
|
||||
@@ -124,6 +124,9 @@ pub struct EchoDriver {
|
||||
/// says it recovered, and a clear leaves it unmeasured. What is real is
|
||||
/// which way the numbers move.
|
||||
context: Arc<AtomicU64>,
|
||||
/// Live background commands, for the same count a real provider reports.
|
||||
/// This is the deterministic UI/session-lifecycle rig for that state.
|
||||
background_tasks: Arc<Mutex<usize>>,
|
||||
/// This session's subagents -- see `SUBAGENTS.md`. `/subagent` is the
|
||||
/// test rig for the same registry the claude driver routes real Task
|
||||
/// calls into.
|
||||
@@ -500,6 +503,12 @@ impl EchoDriver {
|
||||
id: id.clone(),
|
||||
output: format!("Command running in background with ID: {id}"),
|
||||
});
|
||||
let background_tasks = Arc::clone(&self.background_tasks);
|
||||
{
|
||||
let mut count = background_tasks.lock().unwrap();
|
||||
*count += 1;
|
||||
self.emit(Event::BackgroundTasks { count: *count });
|
||||
}
|
||||
for word in "Started it; I'll pick this up when it lands.".split_inclusive(' ') {
|
||||
self.emit(Event::AssistantText {
|
||||
delta: word.to_string(),
|
||||
@@ -513,6 +522,11 @@ impl EchoDriver {
|
||||
let sink = self.sink.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_secs(seconds)).await;
|
||||
{
|
||||
let mut count = background_tasks.lock().unwrap();
|
||||
*count -= 1;
|
||||
let _ = sink.send(Event::BackgroundTasks { count: *count });
|
||||
}
|
||||
let _ = sink.send(Event::ToolUpdate {
|
||||
id,
|
||||
output: format!(r#"Background command "{command}" completed (exit code 0)"#),
|
||||
@@ -526,9 +540,16 @@ impl EchoDriver {
|
||||
});
|
||||
tokio::time::sleep(DELTA_DELAY).await;
|
||||
}
|
||||
{
|
||||
let count = background_tasks.lock().unwrap();
|
||||
let _ = sink.send(Event::Status {
|
||||
state: SessionStatus::Idle,
|
||||
state: if *count == 0 {
|
||||
SessionStatus::Idle
|
||||
} else {
|
||||
SessionStatus::Waiting
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -855,6 +876,7 @@ impl EchoDriver {
|
||||
sink,
|
||||
pending_questions: Mutex::new(Vec::new()),
|
||||
context: Arc::new(AtomicU64::new(0)),
|
||||
background_tasks: Arc::new(Mutex::new(0)),
|
||||
busy: Arc::new(AtomicBool::new(false)),
|
||||
queued: Arc::new(Mutex::new(Vec::new())),
|
||||
session_dir,
|
||||
@@ -1165,6 +1187,10 @@ fn finish_turn(sink: &EventSink, queued: &Mutex<Vec<Held>>, busy: &AtomicBool) {
|
||||
}
|
||||
|
||||
impl Driver for EchoDriver {
|
||||
fn background_tasks(&self) -> Option<usize> {
|
||||
Some(*self.background_tasks.lock().unwrap())
|
||||
}
|
||||
|
||||
fn between_turns(&self) -> bool {
|
||||
!self.busy.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
@@ -272,6 +272,10 @@ pub struct SessionInfo {
|
||||
pub status: SessionStatus,
|
||||
pub last_activity: f64,
|
||||
pub created: f64,
|
||||
/// The provider's latest measured number of live background tasks.
|
||||
/// Absent until it reports one; absence is not a measured zero.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub background_tasks: Option<usize>,
|
||||
/// How many subagents this session has started, from a directory
|
||||
/// listing rather than reading each one's status -- see
|
||||
/// `GET /sessions/{id}/subagents` for that. 0 when it has none, not
|
||||
@@ -608,6 +612,7 @@ impl LiveSession {
|
||||
status: *self.shared.status.lock().unwrap(),
|
||||
last_activity: *self.shared.last_activity.lock().unwrap(),
|
||||
created: self.meta.created,
|
||||
background_tasks: self.driver().and_then(|driver| driver.background_tasks()),
|
||||
subagents: subagent::count(self.dir()),
|
||||
}
|
||||
}
|
||||
@@ -1140,6 +1145,7 @@ impl SessionManager {
|
||||
status: status_of_unlaunched(&self.data_dir.join(&meta.id)),
|
||||
last_activity: meta.created,
|
||||
created: meta.created,
|
||||
background_tasks: None,
|
||||
subagents: subagent::count(&self.data_dir.join(&meta.id)),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -866,6 +866,7 @@ mod tests {
|
||||
id: "q1".into(),
|
||||
answers: vec!["Yes".into()],
|
||||
},
|
||||
Event::BackgroundTasks { count: 5 },
|
||||
Event::Status {
|
||||
state: SessionStatus::Idle,
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user