sync from monorepo @ 2452e92e
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
//! `MetaEventsBackend` impl for `JsonlBackend`.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use chrono::Utc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::backend::MetaEventsBackend;
|
||||
use crate::backends::jsonl::backend::JsonlBackend;
|
||||
use crate::error::{ArchivistError, Result};
|
||||
use crate::storage::{append_ndjson, read_json, read_ndjson, write_json};
|
||||
use crate::types::{
|
||||
MetaEventRecord, SessionCompleteness, SessionKind, SessionMetadata,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
impl MetaEventsBackend for JsonlBackend {
|
||||
async fn append_meta_events(
|
||||
&self,
|
||||
scroll_id: Uuid,
|
||||
events: Vec<MetaEventRecord>,
|
||||
) -> Result<()> {
|
||||
// Ensure session directory exists
|
||||
self.paths.ensure_dirs(scroll_id).await?;
|
||||
|
||||
// Append each event to events.jsonl
|
||||
let events_path = self.paths.events_path(scroll_id);
|
||||
for event in &events {
|
||||
append_ndjson(&events_path, event).await?;
|
||||
}
|
||||
|
||||
// Update session.json timestamp
|
||||
let session_json_path = self.paths.session_json(scroll_id);
|
||||
let now = Utc::now();
|
||||
|
||||
let session_metadata = match read_json::<SessionMetadata>(&session_json_path).await {
|
||||
Ok(mut metadata) => {
|
||||
metadata.updated_at = now;
|
||||
metadata
|
||||
}
|
||||
Err(_) => {
|
||||
// session.json doesn't exist, this shouldn't happen for meta sessions
|
||||
// but we'll handle it gracefully
|
||||
tracing::warn!(
|
||||
scroll_id = %scroll_id,
|
||||
"session.json missing when appending meta events, creating minimal metadata"
|
||||
);
|
||||
|
||||
SessionMetadata {
|
||||
version: 1,
|
||||
scroll_id,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
title: None,
|
||||
connector_uid: scroll_id, // Use scroll_id as placeholder
|
||||
native_session_id: None,
|
||||
agent_id: None,
|
||||
parent_scroll_id: None,
|
||||
continuation: None,
|
||||
tags: Vec::new(),
|
||||
metadata: serde_json::json!({}),
|
||||
no_update: false,
|
||||
kind: SessionKind::AcpConnection,
|
||||
acp_client_id: None,
|
||||
is_connected: None,
|
||||
current_session_id: None,
|
||||
models: None,
|
||||
modes: None,
|
||||
config_options: None,
|
||||
completeness: SessionCompleteness::default(),
|
||||
matrix_room_id: None,
|
||||
matrix_sharing_active: false,
|
||||
matrix_shared_at: None,
|
||||
is_subagent: false,
|
||||
subagent_type: None,
|
||||
spawning_tool_use_id: None,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
write_json(&session_json_path, &session_metadata).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_meta_events(&self, scroll_id: Uuid) -> Result<Vec<MetaEventRecord>> {
|
||||
let events_path = self.paths.events_path(scroll_id);
|
||||
|
||||
// Read events from events.jsonl
|
||||
let mut events: Vec<MetaEventRecord> = read_ndjson(&events_path)
|
||||
.await
|
||||
.unwrap_or_else(|_| Vec::new());
|
||||
|
||||
// Sort by timestamp then event_id for stable ordering
|
||||
events.sort_by(|a, b| {
|
||||
a.ts.cmp(&b.ts).then_with(|| a.event_id.cmp(&b.event_id))
|
||||
});
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
async fn update_meta_session_status(
|
||||
&self,
|
||||
scroll_id: Uuid,
|
||||
is_connected: bool,
|
||||
current_session_id: Option<Uuid>,
|
||||
) -> Result<()> {
|
||||
// Load existing session metadata
|
||||
let session_json_path = self.paths.session_json(scroll_id);
|
||||
|
||||
let mut session_metadata: SessionMetadata = match read_json(&session_json_path).await {
|
||||
Ok(metadata) => metadata,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
return Err(ArchivistError::SessionUnknown(scroll_id));
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
|
||||
// Update connection status fields
|
||||
session_metadata.is_connected = Some(is_connected);
|
||||
session_metadata.current_session_id = current_session_id;
|
||||
session_metadata.updated_at = Utc::now();
|
||||
|
||||
// Write updated metadata back to disk
|
||||
write_json(&session_json_path, &session_metadata).await?;
|
||||
|
||||
tracing::info!(
|
||||
scroll_id = %scroll_id,
|
||||
is_connected = %is_connected,
|
||||
current_session_id = ?current_session_id,
|
||||
"Updated meta session status"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn list_meta_sessions(&self) -> Result<Vec<SessionMetadata>> {
|
||||
// Scan .contexts/ directory for all session.json files
|
||||
let contexts_dir = self.paths.root().join(".contexts");
|
||||
|
||||
if !contexts_dir.exists() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let mut meta_sessions = Vec::new();
|
||||
|
||||
// Read all session directories
|
||||
let mut entries = tokio::fs::read_dir(&contexts_dir).await?;
|
||||
|
||||
while let Some(entry) = entries.next_entry().await? {
|
||||
if !entry.file_type().await?.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let session_json_path = entry.path().join("session.json");
|
||||
|
||||
// Try to read session.json
|
||||
match read_json::<SessionMetadata>(&session_json_path).await {
|
||||
Ok(metadata) => {
|
||||
// Filter to only AcpConnection sessions
|
||||
if metadata.kind == SessionKind::AcpConnection {
|
||||
meta_sessions.push(metadata);
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
// Skip missing session files
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
path = ?session_json_path,
|
||||
error = %e,
|
||||
"Failed to read session.json while listing meta sessions"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by updated_at descending (newest first)
|
||||
meta_sessions.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
|
||||
|
||||
Ok(meta_sessions)
|
||||
}
|
||||
|
||||
async fn find_meta_session_by_client(
|
||||
&self,
|
||||
client_id: &str,
|
||||
) -> Result<Option<SessionMetadata>> {
|
||||
// Use list_meta_sessions and filter by acp_client_id
|
||||
let meta_sessions = self.list_meta_sessions().await?;
|
||||
|
||||
let result = meta_sessions
|
||||
.into_iter()
|
||||
.find(|session| {
|
||||
session.acp_client_id.as_deref() == Some(client_id)
|
||||
});
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user