WIP: Agent core migrated to AST types

agent/mod.rs fully uses AstNode/ContextState/PendingToolCall.
Killed: push_message, push_entry, append_streaming, finalize_streaming,
streaming_index, assemble_api_messages, age_out_images, working_stack,
context_sections, entries. ConversationLog rewritten for AstNode.

Remaining: api dead code (chat path), mind/, user/, oneshot, learn.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-08 14:59:38 -04:00
parent 9c79d7a037
commit a68377907a
4 changed files with 70 additions and 243 deletions

View file

@ -1,19 +1,8 @@
// observe.rs — Shared observation socket + logfile
//
// Two mechanisms:
// 1. Logfile (~/.consciousness/agent-sessions/observe.log) — append-only
// plain text of the conversation. `poc-agent read` prints new
// content since last read using a byte-offset cursor file.
// 2. Unix socket — for live streaming (`poc-agent read -f`) and
// sending input (`poc-agent write <msg>`).
//
// The logfile is the history. The socket is the live wire.
use anyhow::{Context, Result};
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use crate::agent::context::ConversationEntry;
use crate::agent::context_new::AstNode;
pub struct ConversationLog {
path: PathBuf,
@ -21,7 +10,6 @@ pub struct ConversationLog {
impl ConversationLog {
pub fn new(path: PathBuf) -> Result<Self> {
// Ensure parent directory exists
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("creating log dir {}", parent.display()))?;
@ -29,16 +17,15 @@ impl ConversationLog {
Ok(Self { path })
}
/// Append a conversation entry to the log.
pub fn append(&self, entry: &ConversationEntry) -> Result<()> {
pub fn append_node(&self, node: &AstNode) -> Result<()> {
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(&self.path)
.with_context(|| format!("opening log {}", self.path.display()))?;
let line = serde_json::to_string(entry)
.context("serializing entry for log")?;
let line = serde_json::to_string(node)
.context("serializing node for log")?;
writeln!(file, "{}", line)
.context("writing to conversation log")?;
file.sync_all()
@ -46,10 +33,7 @@ impl ConversationLog {
Ok(())
}
/// Read the tail of the log (last `max_bytes` bytes).
/// Seeks to `file_len - max_bytes`, skips the first partial line,
/// then parses forward. For logs smaller than `max_bytes`, reads everything.
pub fn read_tail(&self, max_bytes: u64) -> Result<Vec<ConversationEntry>> {
pub fn read_nodes(&self, max_bytes: u64) -> Result<Vec<AstNode>> {
if !self.path.exists() {
return Ok(Vec::new());
}
@ -60,45 +44,36 @@ impl ConversationLog {
if file_len > max_bytes {
reader.seek(SeekFrom::Start(file_len - max_bytes))?;
// Skip partial first line
let mut discard = String::new();
reader.read_line(&mut discard)?;
}
let mut entries = Vec::new();
let mut nodes = Vec::new();
for line in reader.lines() {
let line = line.context("reading log tail")?;
let line = line.trim();
if line.is_empty() {
continue;
}
// Try ConversationEntry first (new format), fall back to bare Message (old logs)
if let Ok(entry) = serde_json::from_str::<ConversationEntry>(line) {
entries.push(entry);
if line.is_empty() { continue; }
if let Ok(node) = serde_json::from_str::<AstNode>(line) {
nodes.push(node);
}
}
Ok(entries)
Ok(nodes)
}
pub fn path(&self) -> &Path {
&self.path
}
/// Get the timestamp of the oldest message in the log.
pub fn oldest_timestamp(&self) -> Option<chrono::DateTime<chrono::Utc>> {
let file = File::open(&self.path).ok()?;
let reader = BufReader::new(file);
for line in reader.lines().flatten() {
let line = line.trim().to_string();
if line.is_empty() { continue; }
if let Ok(entry) = serde_json::from_str::<ConversationEntry>(&line) {
if let Some(ts) = &entry.message().timestamp {
if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(ts) {
return Some(dt.to_utc());
}
// Try other formats
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(ts, "%Y-%m-%dT%H:%M:%S") {
return Some(dt.and_utc());
if let Ok(node) = serde_json::from_str::<AstNode>(&line) {
if let Some(leaf) = node.leaf() {
if let Some(ts) = leaf.timestamp() {
return Some(ts);
}
}
}