M5c-1 plan: task 13, the Honcho worker sends memory writes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
//! Memory writes sent to Honcho (M5c spec, sections 4 and 5), for the worker: a conclusion made
|
||||
//! in the Boxmaker session's memory session, `<id>_memory`; a conclusion deleted by the id its
|
||||
//! making was given; an untrusted note added to the fleet workspace as a message from the agent.
|
||||
//! The text is neutralised here, as it leaves.
|
||||
|
||||
use bxcore::SessionId;
|
||||
use bxcore::sanitize::neutralise;
|
||||
use roncho::{ConclusionBatchCreate, ConclusionCreate, MessageCreate};
|
||||
use serde_json::{Map, json};
|
||||
|
||||
use crate::honcho::client::{Clients, HonchoError, classify};
|
||||
use crate::honcho::ledger::Workspace;
|
||||
use crate::honcho::naming::HonchoSession;
|
||||
use crate::honcho::worker::parts;
|
||||
|
||||
/// What names a Boxmaker session's memory session: `_` is in no Boxmaker id, so the name is no
|
||||
/// other session's.
|
||||
pub const MEMORY_SUFFIX: &str = "_memory";
|
||||
|
||||
/// The memory session of `session`: `<id>_memory`.
|
||||
pub fn memory_session(session: &SessionId) -> String {
|
||||
// `format!("{}{MEMORY_SUFFIX}", session.as_str())`.
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Make conclusion `text` about `peer` (its observer and the observed) in `workspace`, stamped
|
||||
/// with `session`'s memory session, which is made sure of first. Its one id.
|
||||
pub fn conclude(
|
||||
clients: &mut Clients,
|
||||
workspace: Workspace,
|
||||
peer: &str,
|
||||
session: &SessionId,
|
||||
text: &str,
|
||||
) -> Result<Vec<String>, HonchoError> {
|
||||
// `let name = memory_session(session);`, then
|
||||
// `clients.ensure_memory_session(workspace, &name)?;`. The conclusion:
|
||||
// `ConclusionCreate::new(neutralise(text), peer, peer).with_session_id(name)` (observer and
|
||||
// observed both `peer`), in `ConclusionBatchCreate::new(vec![conclusion])`. Then `match
|
||||
// clients.client(workspace).create_conclusions(&batch)`: `Ok(made)` ->
|
||||
// `Ok(made.into_iter().map(|c| c.id).collect())`; `Err(e)` -> `Err(classify(&e))`.
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Delete conclusion `id` in `workspace`; with none to delete, nothing is asked. The ids of what
|
||||
/// was deleted.
|
||||
pub fn unconclude(
|
||||
clients: &Clients,
|
||||
workspace: Workspace,
|
||||
id: Option<&str>,
|
||||
) -> Result<Vec<String>, HonchoError> {
|
||||
// `match id`: `Some(id)` -> `clients.delete_conclusion(workspace, id)?;` then
|
||||
// `Ok(vec![id.to_string()])`; `None` -> `Ok(Vec::new())`: nothing is asked.
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// An untrusted note's messages for the fleet workspace: from the agent's peer, neutralised, in
|
||||
/// parts of at most `MAX_PART` characters, each with the note's metadata.
|
||||
pub fn fleet_batch(text: &str, agent: &str, session: &SessionId, edit: &str) -> Vec<MessageCreate> {
|
||||
// A `Map` `metadata` with `"agent"` -> `json!(agent)`, `"boxmaker_session"` ->
|
||||
// `json!(session.as_str())`, `"edit"` -> `json!(edit)` (in this order). Then
|
||||
// `parts(&neutralise(text)).into_iter().map(|part| MessageCreate::new(part, agent)
|
||||
// .with_metadata(metadata.clone())).collect()`.
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Add `messages` to `session`'s Honcho session in the fleet workspace, which holds only the
|
||||
/// agent's peer, made sure of first. The ids of the messages.
|
||||
pub fn fleet(
|
||||
clients: &mut Clients,
|
||||
session: &SessionId,
|
||||
messages: &[MessageCreate],
|
||||
) -> Result<Vec<String>, HonchoError> {
|
||||
// `let honcho = HonchoSession { name: session.as_str().to_string(), speakers: Vec::new() };`
|
||||
// then `clients.ensure_session(Workspace::Fleet, &honcho)?;`. Then `match
|
||||
// clients.client(Workspace::Fleet).add_messages(&honcho.name, messages)`: `Ok(made)` ->
|
||||
// `Ok(made.into_iter().map(|m| m.id).collect())`; `Err(e)` -> `Err(classify(&e))`.
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//! `memoryd`'s Honcho side at start (M5c spec, sections 5 and 10): the ledger read through, the
|
||||
//! sessions that wait again, the clients with their keys, and the worker's thread.
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use bxcore::SessionId;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::honcho::client::{Clients, Timeouts};
|
||||
use crate::honcho::config::HonchoConfig;
|
||||
use crate::honcho::ledger::Ledger;
|
||||
use crate::honcho::mirror::Mirror;
|
||||
use crate::honcho::state;
|
||||
use crate::honcho::turns::{LEDGER, TurnSync};
|
||||
use crate::honcho::worker::{AskBox, Tuning, Wake, Worker};
|
||||
use crate::serve::{Log, StartError};
|
||||
|
||||
/// With `[honcho]`, at start: the ledger read through, the sessions that wait again, and the
|
||||
/// clients with their keys read. Nothing is bound or started yet.
|
||||
pub struct Opened {
|
||||
pub sync: Arc<Mutex<TurnSync>>,
|
||||
clients: Clients,
|
||||
agent: String,
|
||||
owner: String,
|
||||
}
|
||||
|
||||
impl Opened {
|
||||
/// The memory writes' side of the ledger (task 12).
|
||||
pub fn mirror(&self) -> Mirror {
|
||||
let ledger = self
|
||||
.sync
|
||||
.lock()
|
||||
.unwrap_or_else(|p| p.into_inner())
|
||||
.shared_ledger();
|
||||
Mirror::new(ledger, &self.agent, &self.owner)
|
||||
}
|
||||
}
|
||||
|
||||
/// Open the ledger (a cut-off last line is logged), resume the waiting sessions, and build the
|
||||
/// clients (their key files' warnings are logged). A key that cannot be read is `Secret`.
|
||||
pub fn open(config: &Config, honcho: &HonchoConfig, log: &Log) -> Result<Opened, StartError> {
|
||||
let path = config.memory_dir().join(LEDGER);
|
||||
let ledger = Ledger::open(&path).map_err(StartError::Ledger)?;
|
||||
if ledger.repaired() {
|
||||
(log)(&format!(
|
||||
"memoryd: {}: a last line cut off by a crash was dropped",
|
||||
path.display()
|
||||
));
|
||||
}
|
||||
let mut sync = TurnSync::new(ledger, &honcho.agent, &config.sessions_dir());
|
||||
sync.resume();
|
||||
let env = |name: &str| std::env::var_os(name);
|
||||
let (clients, warnings) =
|
||||
Clients::new(honcho, &env, Timeouts::WRITES).map_err(StartError::Secret)?;
|
||||
for warning in warnings {
|
||||
(log)(&warning);
|
||||
}
|
||||
Ok(Opened {
|
||||
sync: Arc::new(Mutex::new(sync)),
|
||||
clients,
|
||||
agent: honcho.agent.clone(),
|
||||
owner: honcho.owner.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Start the worker's thread, asking `brokerd` on `config`'s state socket.
|
||||
pub fn spawn(opened: Opened, config: &Config, wake: Arc<Wake>, log: Log) -> Result<(), String> {
|
||||
let socket = config.state_socket();
|
||||
let ask: AskBox =
|
||||
Box::new(move |session: &SessionId| state::ask(&socket, session, state::WAIT));
|
||||
let worker = Worker::new(
|
||||
opened.sync,
|
||||
opened.clients,
|
||||
wake,
|
||||
ask,
|
||||
log,
|
||||
Tuning::DEFAULT,
|
||||
&opened.agent,
|
||||
);
|
||||
std::thread::Builder::new()
|
||||
.name("honcho".to_string())
|
||||
.spawn(move || worker.run())
|
||||
.map(|_| ())
|
||||
.map_err(|e| format!("cannot start the Honcho worker: {e}"))
|
||||
}
|
||||
Reference in New Issue
Block a user