swarm: keep an agent's terminal rows and replay them on attach
swarm-controller now creates `term-main-<agent>` for every live agent, in the same one-minute pass and with the same config style as `term-sub-<agent>` (#4897): subjects `$SWARM.term.<agent>` only, max_age 24h, max_bytes 64 MiB, discard Old, max_message_size from the queue's max_payload. An existing stream is opened as it is. The agent keeps its plain publish on `$SWARM.term.<agent>` and gets no JetStream subject. `GET /api/agents/{name}/term/stream` creates an ephemeral push consumer on that stream starting 100 rows back, so a swarm-ui terminal opens with recent history and then follows new rows, in order, from one subscription. Each SSE event carries the row's stream sequence as its id, and a reconnect's Last-Event-ID resumes after it. Without the stream the route falls back to the live core subscription it used before. The stream is `term-main-<agent>`, not `term-<agent>`: an agent may be named `sub-x`, whose `term-<agent>` would be agent `x`'s subagent stream. Its one literal subject cannot match `$SWARM.term.<agent>.sub.>`, which needs more tokens, so the two streams never overlap. The reader gains `$JS.API.CONSUMER.CREATE.*.*.$SWARM.term.*`, the extended create form whose last tokens are the consumer's filter. Checked against nats-server 2.14.4, the trailing `*` also admits a literal `*` or `>` filter, so a consumer under it reaches any terminal row, main or subagent; those are rows the reader already subscribes to and reads with `STREAM.MSG.GET.*`. Closes #4826
This commit is contained in:
parent
609657f1ff
commit
ae9ca4331e
12 changed files with 533 additions and 38 deletions
|
|
@ -5,7 +5,8 @@
|
|||
//! `$SWARM.term.{agent}.sub.{subagent}` under the agent's own queue credential,
|
||||
//! which may publish there and nothing else of these streams. [`spawn`]
|
||||
//! creates the stream `term-sub-{agent}` for every live agent, with the config
|
||||
//! `swarm_queue_client::subagent_term` spells. Subagents are spawned on demand
|
||||
//! `swarm_queue_client::subagent_term` spells, and in the same pass the
|
||||
//! agent's own terminal stream (`swarm_queue_client::agent_term`). Subagents are spawned on demand
|
||||
//! under caller-chosen names, so the list is the set of subjects that stream
|
||||
//! holds rows for, read with `STREAM.INFO`. A missing stream lists nothing.
|
||||
//! Each subagent carries the stream's own publish time of its newest row, so
|
||||
|
|
@ -30,7 +31,7 @@ use axum::http::StatusCode;
|
|||
use axum::response::sse::{Event, KeepAlive, Sse};
|
||||
use futures_util::{Stream, StreamExt as _, TryStreamExt as _};
|
||||
use serde::Serialize;
|
||||
use swarm_queue_client::subagent_term;
|
||||
use swarm_queue_client::{agent_term, subagent_term};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::AppState;
|
||||
|
|
@ -49,8 +50,9 @@ const LOOKUP_TIMEOUT: Duration = Duration::from_secs(2);
|
|||
/// How many newest-row lookups [`list_subagents`] has in flight at once.
|
||||
const LOOKUP_CONCURRENCY: usize = 16;
|
||||
|
||||
/// Create the subagent stream of every live agent that has none, now and every
|
||||
/// [`ENSURE_INTERVAL`] after. An existing stream is left as it is.
|
||||
/// Create the subagent stream and the terminal stream (`agent_term`) of every
|
||||
/// live agent that lacks one, now and every [`ENSURE_INTERVAL`] after. An
|
||||
/// existing stream is left as it is.
|
||||
///
|
||||
/// The live agents are the ones some hive's wanted-state declaration names in
|
||||
/// a state other than `Destroyed` (`agent_renewal::live_agents`). A pass skips
|
||||
|
|
@ -63,7 +65,7 @@ pub(crate) fn spawn(client: async_nats::Client, wanted: Arc<WantedWriter>, hives
|
|||
loop {
|
||||
ticker.tick().await;
|
||||
if swarm_queue_client::ensure_connected(&client).is_err() {
|
||||
tracing::debug!("subagent streams: queue not connected; next pass");
|
||||
tracing::debug!("terminal streams: queue not connected; next pass");
|
||||
continue;
|
||||
}
|
||||
for agent in live_agents(&wanted, &hives).await {
|
||||
|
|
@ -74,6 +76,13 @@ pub(crate) fn spawn(client: async_nats::Client, wanted: Arc<WantedWriter>, hives
|
|||
"subagent streams: creating the agent's stream failed; next pass"
|
||||
);
|
||||
}
|
||||
if let Err(e) = agent_term::open_or_create(&client, &agent).await {
|
||||
tracing::warn!(
|
||||
%agent,
|
||||
error = %swarm_queue_client::chain(&e),
|
||||
"terminal streams: creating the agent's stream failed; next pass"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue