TIC said nothing about what the agent does, and named the wrong thing: the tick belongs to the scheduler, which is generic and lives outside it. The agent's only decision is whether an incoming event deserves an interruption — it sorts, it never acts — so it is now event-triage, matching the functional naming of the two memory lints. - agents/tic/ -> agents/event-triage/, module tic/ -> event_triage/, TicManager -> EventTriageManager, TicConfig -> EventTriageConfig - agent id and chat source: "tic" -> "event-triage" - config keys: tic.* -> event_triage.*, and the config.yml section tic: -> event_triage: (greenfield: previously set values fall back to defaults) - i18n en/it/fr: Event triage / Triage eventi / Tri des evenements; dropped the stale "TIC sessions" mention from the debug-pages description - docs/system-agents.md, docs/index.md, docs/settings.md, CLAUDE.md, SKALD.md
This commit is contained in:
@@ -38,7 +38,7 @@ pub struct ChatMessage {
|
||||
pub status: String,
|
||||
pub input_tokens: Option<i64>,
|
||||
pub output_tokens: Option<i64>,
|
||||
/// True for messages injected synthetically (e.g. TIC notifications) — not
|
||||
/// True for messages injected synthetically (e.g. event triage notifications) — not
|
||||
/// typed by a real user. Stored in DB so the UI can skip them on reload.
|
||||
pub is_synthetic: bool,
|
||||
/// Chain-of-thought from reasoning models (e.g. DeepSeek thinking mode).
|
||||
|
||||
@@ -5,9 +5,9 @@ pub struct ChatSession {
|
||||
pub source: String,
|
||||
pub agent_id: String,
|
||||
/// True when a real user is actively participating (web, telegram).
|
||||
/// False for fully automated sessions (cron, tic).
|
||||
/// False for fully automated sessions (cron, event-triage).
|
||||
pub is_interactive: bool,
|
||||
/// True for short-lived task sessions (cron, tic) with no long-term
|
||||
/// True for short-lived task sessions (cron, event-triage) with no long-term
|
||||
/// conversational value. May be used to skip memory / analytics sinks.
|
||||
pub is_ephemeral: bool,
|
||||
/// Optional RunContext JSON blob assigned to this session.
|
||||
|
||||
@@ -58,7 +58,7 @@ pub async fn mark_processed(pool: &SqlitePool, ids: &[i64]) -> Result<()> {
|
||||
// ── Read ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Oldest N pending (unprocessed) events, ordered oldest-first.
|
||||
/// Used by TicManager to fetch a bounded batch each tick.
|
||||
/// Used by EventTriageManager to fetch a bounded batch each pass.
|
||||
pub async fn pending_limited(pool: &SqlitePool, limit: i64) -> Result<Vec<McpEvent>> {
|
||||
let rows = sqlx::query_as::<_, (i64, String, String, String, bool, Option<String>, String)>(
|
||||
"SELECT id, source, method, payload, processed, processed_at, created_at
|
||||
|
||||
@@ -898,14 +898,14 @@ pub async fn create_owner_tables(pool: &SqlitePool) -> Result<()> {
|
||||
.await?;
|
||||
|
||||
// Execution log of the **system agents** — the background agents the instance
|
||||
// runs on a user's behalf without being asked (TIC is the first and, today,
|
||||
// the only one). The sibling of `job_runs`: same shape, but keyed on the agent
|
||||
// instead of a scheduled job, because a system agent has no user-authored row
|
||||
// to point at.
|
||||
// runs on a user's behalf without being asked (event triage is the first and,
|
||||
// today, the only one). The sibling of `job_runs`: same shape, but keyed on the
|
||||
// agent instead of a scheduled job, because a system agent has no user-authored
|
||||
// row to point at.
|
||||
//
|
||||
// Owner table, and that is the whole privacy story: a run of TIC summarises
|
||||
// what landed in this user's inbox, so it belongs in *their* encrypted file
|
||||
// and nowhere else. There is deliberately no `user_id` column — the file is
|
||||
// Owner table, and that is the whole privacy story: an event-triage run
|
||||
// summarises what landed in this user's inbox, so it belongs in *their*
|
||||
// encrypted file and nowhere else. There is deliberately no `user_id` column — the file is
|
||||
// the owner (§5.1). An admin reading `system.db` learns nothing about it.
|
||||
//
|
||||
// A user whose database is still locked is skipped by the scheduler and
|
||||
@@ -948,8 +948,8 @@ pub async fn create_owner_tables(pool: &SqlitePool) -> Result<()> {
|
||||
// productive run aged out.
|
||||
//
|
||||
// Persisting it is what makes a long interval survive a restart. An in-memory
|
||||
// deadline is fine at TIC's scale — a few minutes, re-armed on boot — but a
|
||||
// weekly agent on a machine rebooted every few days would have its deadline
|
||||
// deadline is fine at event triage's scale — a few minutes, re-armed on boot —
|
||||
// but a weekly agent on a machine rebooted every few days would have its deadline
|
||||
// reset before it ever fired, and would simply never run.
|
||||
//
|
||||
// Owner table for the same reason as the run log: when an agent last ran for
|
||||
@@ -1166,7 +1166,7 @@ mod tests {
|
||||
one("INSERT INTO scheduled_jobs (id, title, cron, prompt, session_id) VALUES (1, 't', '* * * * *', 'p', 1)")
|
||||
.await.unwrap();
|
||||
one("INSERT INTO job_runs (job_id, started_at, status) VALUES (1, 'now', 'completed')").await.unwrap();
|
||||
one("INSERT INTO system_agent_runs (agent_id, started_at, status) VALUES ('tic', 'now', 'running')").await.unwrap();
|
||||
one("INSERT INTO system_agent_runs (agent_id, started_at, status) VALUES ('event-triage', 'now', 'running')").await.unwrap();
|
||||
// Owner table with a BARE `catalog_name` ref — proves it stands alone with
|
||||
// FKs on (an owner→registry FK here would die on this INSERT).
|
||||
one("INSERT INTO mcp_user_servers (name, catalog_name, source) VALUES ('u', 'whatsapp', 'local_script')").await.unwrap();
|
||||
|
||||
@@ -28,7 +28,7 @@ pub struct SystemAgentRun {
|
||||
pub completed_at: Option<String>,
|
||||
pub duration_ms: Option<i64>,
|
||||
pub status: String,
|
||||
/// Free-form JSON with the agent's own counters (TIC: events processed,
|
||||
/// Free-form JSON with the agent's own counters (event triage: events processed,
|
||||
/// notifications emitted). Never the event contents.
|
||||
pub stats: Option<String>,
|
||||
pub error: Option<String>,
|
||||
|
||||
@@ -65,19 +65,19 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn never_attempted_reads_as_due() {
|
||||
let pool = pool().await;
|
||||
assert!(last_attempt_at(&pool, "tic").await.unwrap().is_none());
|
||||
assert!(seconds_since_attempt(&pool, "tic").await.unwrap().is_none());
|
||||
assert!(last_attempt_at(&pool, "event-triage").await.unwrap().is_none());
|
||||
assert!(seconds_since_attempt(&pool, "event-triage").await.unwrap().is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn an_attempt_is_recorded_and_then_overwritten() {
|
||||
let pool = pool().await;
|
||||
mark_attempt(&pool, "tic").await.unwrap();
|
||||
let first = last_attempt_at(&pool, "tic").await.unwrap().unwrap();
|
||||
mark_attempt(&pool, "event-triage").await.unwrap();
|
||||
let first = last_attempt_at(&pool, "event-triage").await.unwrap().unwrap();
|
||||
|
||||
// Fresh attempt: still one row for this agent, and the age is small.
|
||||
mark_attempt(&pool, "tic").await.unwrap();
|
||||
assert!(seconds_since_attempt(&pool, "tic").await.unwrap().unwrap() < 5);
|
||||
mark_attempt(&pool, "event-triage").await.unwrap();
|
||||
assert!(seconds_since_attempt(&pool, "event-triage").await.unwrap().unwrap() < 5);
|
||||
assert!(!first.is_empty());
|
||||
|
||||
let rows = sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM system_agent_state")
|
||||
@@ -90,8 +90,8 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn agents_do_not_share_a_row() {
|
||||
let pool = pool().await;
|
||||
mark_attempt(&pool, "tic").await.unwrap();
|
||||
assert!(seconds_since_attempt(&pool, "tic").await.unwrap().is_some());
|
||||
mark_attempt(&pool, "event-triage").await.unwrap();
|
||||
assert!(seconds_since_attempt(&pool, "event-triage").await.unwrap().is_some());
|
||||
assert!(seconds_since_attempt(&pool, "memory-lint").await.unwrap().is_none());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user