diff --git a/crates/memory/src/eventlog.rs b/crates/memory/src/eventlog.rs index 87aca355..c6459af6 100644 --- a/crates/memory/src/eventlog.rs +++ b/crates/memory/src/eventlog.rs @@ -39,7 +39,7 @@ pub async fn cmd(m: &Memory, op: LogOp) -> Fallible { LogOp::Materialize { domain } => { let done = m.materialize(domain.as_deref()).await?; for id in &done.behind { - eprintln!("memory: {id} is not at the version the log names"); + eprintln!("amem: {id} is not at the version the log names"); } crate::json(&done) } diff --git a/crates/memory/src/gofannon.rs b/crates/memory/src/gofannon.rs index 67befe85..a0d2505c 100644 --- a/crates/memory/src/gofannon.rs +++ b/crates/memory/src/gofannon.rs @@ -102,7 +102,7 @@ pub async fn cmd(root: &Path, op: GofannonOp) -> Fallible { poll: Duration::from_secs(poll_seconds.max(1)), }; let done = work(&m, &plan).await?; - eprintln!("memory: gofannon worked {done} tasks"); + eprintln!("amem: gofannon worked {done} tasks"); Ok(()) } } @@ -129,7 +129,7 @@ pub async fn work(m: &Arc, plan: &Plan) -> Result, id: String, lease: i64) -> tokio::task: loop { tick.tick().await; if let Err(e) = client.heartbeat(&id, lease).await { - eprintln!("memory: heartbeat on {id}: {e}"); + eprintln!("amem: heartbeat on {id}: {e}"); return; } } @@ -235,10 +235,10 @@ async fn settle( .await?; if done { client.complete(&task.id, &handoff).await?; - eprintln!("memory: {} completed, handoff {handoff}", task.id); + eprintln!("amem: {} completed, handoff {handoff}", task.id); } else { client.release(&task.id, Some(&handoff)).await?; - eprintln!("memory: {} released, handoff {handoff}", task.id); + eprintln!("amem: {} released, handoff {handoff}", task.id); } Ok(()) } diff --git a/crates/memory/src/image.rs b/crates/memory/src/image.rs index 108e96bd..4e7ea446 100644 --- a/crates/memory/src/image.rs +++ b/crates/memory/src/image.rs @@ -70,7 +70,7 @@ pub fn unpack(root: &Path, image: &str) -> Fallible { std::fs::remove_dir_all(&fresh)?; } std::fs::create_dir_all(&fresh)?; - eprintln!("memory: exporting {image} from {rt}"); + eprintln!("amem: exporting {image} from {rt}"); let id = run(rt, &["create", image, "true"])?; let id = id.lines().last().unwrap_or_default().trim().to_string(); let result = (|| -> Fallible<()> { @@ -99,7 +99,7 @@ pub fn unpack(root: &Path, image: &str) -> Fallible { } std::fs::rename(&fresh, &rootfs)?; std::fs::write(rootfs.with_extension("source"), format!("{image}\n"))?; - eprintln!("memory: unpacked into {}", rootfs.display()); + eprintln!("amem: unpacked into {}", rootfs.display()); Ok(rootfs) } diff --git a/crates/memory/src/main.rs b/crates/memory/src/main.rs index ef1d8bb5..bd06e763 100644 --- a/crates/memory/src/main.rs +++ b/crates/memory/src/main.rs @@ -1283,10 +1283,10 @@ fn main() { match outbox { Some(outbox) => { if let Err(e) = hooks::run(&outbox, harness) { - eprintln!("memory hook: {e}"); + eprintln!("amem: hook: {e}"); } } - None => eprintln!("memory hook: no MEMORY_OUTBOX and no HOME"), + None => eprintln!("amem: hook: no MEMORY_OUTBOX and no HOME"), } std::process::exit(0); } @@ -1749,7 +1749,7 @@ fn route_for( )?; if var == "GH_TOKEN" { eprintln!( - "memory: copilot holds your GitHub login's token, which reaches every \ + "amem: copilot holds your GitHub login's token, which reaches every \ repository the login can; a token with only the Copilot Requests \ permission, as COPILOT_GITHUB_TOKEN, keeps that out of the sandbox" ); @@ -2022,12 +2022,12 @@ fn record_reported_usage(p: &Prepared) { .telemetry() .record_reported(p.memory.session_id(), &p.provider, usage) { - eprintln!("memory: could not record reported usage: {e}"); + eprintln!("amem: could not record reported usage: {e}"); } } if let Some((pct, window)) = reported.plan_used_percent { eprintln!( - "memory: the harness reports {pct:.1}% of its plan's {}-hour window used", + "amem: the harness reports {pct:.1}% of its plan's {}-hour window used", window / 60 ); } @@ -2080,7 +2080,7 @@ fn report_usage(egress: &Egress) { if rows.is_empty() { return; } - eprintln!("memory: usage this root, by destination and model:"); + eprintln!("amem: usage this root, by destination and model:"); for r in rows { eprintln!( " {:<10} {:<26} {:<8} calls {:>4} in {:>8} out {:>8} cache {:>8}", @@ -2187,14 +2187,14 @@ fn live_mount(p: &Prepared, harness: Harness) -> Option { match dm_mount::mount(p.memory.clone(), &p.projection, &at) { Ok(m) => { eprintln!( - "memory: the projection at {} is a live mount: every open of an entry is a get", + "amem: the projection at {} is a live mount: every open of an entry is a get", dm_sandbox::INSIDE_PROJECTION ); Some(m) } Err(e) => { eprintln!( - "memory: the projection at {} is a render: {e}", + "amem: the projection at {} is a render: {e}", dm_sandbox::INSIDE_PROJECTION ); None @@ -2406,7 +2406,7 @@ async fn golden_suite( .ok_or_else(|| { format!("the platform holds no verified eval artifact named {name}") })?; - eprintln!("memory: the suite is the platform's {}", a.entry); + eprintln!("amem: the suite is the platform's {}", a.entry); golden::Suite::parse(&a.text) } (None, None) => Err("name a suite, or --platform".into()), @@ -3229,7 +3229,7 @@ fn empty_record(root: &Path) -> Result> { )); std::fs::create_dir_all(&dir)?; Memory::init(&dir, &grants.owner, &domain)?; - eprintln!("memory: empty memory at {}", dir.display()); + eprintln!("amem: empty memory at {}", dir.display()); Ok(dir) } @@ -3407,7 +3407,7 @@ fn announce(runner: &Runner, p: &Prepared, harness: Harness, workdir: &Path) { _ => workdir.to_path_buf(), }; eprintln!( - "memory: {} {how} {} · model calls via {} at ceiling {} · session {}", + "amem: {} {how} {} · model calls via {} at ceiling {} · session {}", harness.binary(), shown.display(), p.ceiling_of, @@ -3450,7 +3450,7 @@ async fn platform_model(memory: &Memory, harness: Harness) -> Option { .flatten()?; let v: serde_json::Value = serde_json::from_str(&a.text).ok()?; let model = v.get("model")?.as_str()?.to_string(); - eprintln!("memory: model {model} from the platform's {}", a.entry); + eprintln!("amem: model {model} from the platform's {}", a.entry); Some(model) } @@ -3667,7 +3667,7 @@ impl Session { via: MineVia, ) -> Result, Box> { let Some(t) = mine::newest_transcript(&self.p.home) else { - eprintln!("memory: nothing to mine; no transcript under the private home"); + eprintln!("amem: nothing to mine; no transcript under the private home"); return Ok(Vec::new()); }; let model = model::resolve(&self.p.egress, &self.p.server.provider, model).await; @@ -3689,7 +3689,7 @@ impl Session { ) .await? }; - eprintln!("memory: mined {} entries from {}", ids.len(), t.display()); + eprintln!("amem: mined {} entries from {}", ids.len(), t.display()); client.close().await.ok(); Ok(ids) } @@ -3811,7 +3811,7 @@ impl Session { at: String::new(), }; if let Err(e) = self.p.egress.telemetry().record_run(&run) { - eprintln!("memory: the run's resources were not kept: {e}"); + eprintln!("amem: the run's resources were not kept: {e}"); } Ok((code, out)) } diff --git a/crates/memory/src/mine.rs b/crates/memory/src/mine.rs index 77aa5029..24513d94 100644 --- a/crates/memory/src/mine.rs +++ b/crates/memory/src/mine.rs @@ -117,7 +117,7 @@ pub async fn write_proposals( domain: None, }) .await?; - eprintln!("memory: mined {} {id}: {}", p.kind, p.title); + eprintln!("amem: mined {} {id}: {}", p.kind, p.title); ids.push(id); } Ok(ids) diff --git a/crates/memory/src/reenact.rs b/crates/memory/src/reenact.rs index ded84b2f..3457eca7 100644 --- a/crates/memory/src/reenact.rs +++ b/crates/memory/src/reenact.rs @@ -323,7 +323,7 @@ fn checkout(repo: &Path, commit: &str, run: &str) -> Fallibly { fn remove_checkout(into: &Path) { if std::fs::remove_dir_all(into).is_err() { - eprintln!("memory: the checkout at {} was left", into.display()); + eprintln!("amem: the checkout at {} was left", into.display()); } } @@ -498,7 +498,7 @@ async fn run_once( session.memory().set_playback(play.clone()); session.memory().disable_read_log(); eprintln!( - "memory: re-enacting {} ({} calls on its tape) as {}", + "amem: re-enacting {} ({} calls on its tape) as {}", args.session, play.played().tape_calls, session.memory().session_id() @@ -540,7 +540,7 @@ fn tree_for( if let Some(c) = commit { return Ok((checkout(repo, c, &format!("{session}-{run}"))?, None)); } - eprintln!("memory: {session} recorded no commit; the re-run works in an empty directory"); + eprintln!("amem: {session} recorded no commit; the re-run works in an empty directory"); // as an empty record is made for an evaluation's baseline let d = std::env::temp_dir().join(format!("amem-reenact-{}-{run}", std::process::id())); std::fs::create_dir_all(&d)?; diff --git a/crates/memory/src/sources.rs b/crates/memory/src/sources.rs index 9044f768..7e0f84b4 100644 --- a/crates/memory/src/sources.rs +++ b/crates/memory/src/sources.rs @@ -77,7 +77,7 @@ pub async fn cmd(m: &Memory, op: SourceOp) -> Fallible { let id = id.strip_prefix(dm_server::sources::CITATION).unwrap_or(&id); let s = m.release_source(id).await?; eprintln!( - "memory: {} released; it carries {}", + "amem: {} released; it carries {}", s.id, if s.findings.is_empty() { "nothing the scan found".to_string() diff --git a/crates/memory/src/stops.rs b/crates/memory/src/stops.rs index 9c7752de..e6d1da73 100644 --- a/crates/memory/src/stops.rs +++ b/crates/memory/src/stops.rs @@ -194,7 +194,7 @@ fn say(signal: i32) -> bool { 2 => "SIGINT", _ => "SIGTERM", }; - let line = format!("memory: {name}: ending the run and cleaning up\n"); + let line = format!("amem: {name}: ending the run and cleaning up\n"); std::io::stderr().write_all(line.as_bytes()).is_ok() } diff --git a/crates/memory/src/taps.rs b/crates/memory/src/taps.rs index 7e50a4e7..4feb80ae 100644 --- a/crates/memory/src/taps.rs +++ b/crates/memory/src/taps.rs @@ -74,7 +74,7 @@ pub async fn run( .and_then(|var| crate::present(std::env::var(var).ok())); if grant.credential.is_some() && credential.is_none() { eprintln!( - "memory: {} is not set; the tap runs without a credential", + "amem: {} is not set; the tap runs without a credential", grant.credential.as_deref().unwrap_or_default() ); } @@ -94,12 +94,12 @@ pub async fn run( (Ok(outcome), _) => println!("{}", serde_json::to_string_pretty(&outcome)?), (Err(e), None) => return Err(e.into()), // on a schedule, one failed pull is reported and the next one still comes - (Err(e), Some(_)) => eprintln!("memory: tap {name}: {e}"), + (Err(e), Some(_)) => eprintln!("amem: tap {name}: {e}"), } let Some(secs) = every else { return Ok(()); }; - eprintln!("memory: tap {name} runs again in {secs}s"); + eprintln!("amem: tap {name} runs again in {secs}s"); tokio::time::sleep(std::time::Duration::from_secs(secs.max(1))).await; } } diff --git a/crates/memory/src/transcripts.rs b/crates/memory/src/transcripts.rs index 693aa64d..fd218e0f 100644 --- a/crates/memory/src/transcripts.rs +++ b/crates/memory/src/transcripts.rs @@ -75,21 +75,21 @@ pub async fn sweep(m: &Memory) -> Result, Box> let swept = m.sweep_transcripts(now).await?; // decision contexts run on the same clock, and go whole: no entry cites them for session in m.sweep_contexts(now).await? { - eprintln!("memory: decision contexts of {session} expired and were dropped"); + eprintln!("amem: decision contexts of {session} expired and were dropped"); } // and the tapes a re-run is answered from, unless pinned for a benchmark for session in m.sweep_tapes(now).await? { // and the reports of its re-runs, which quote what the runs wrote and said crate::reenact::forget_runs(m.root(), &session); - eprintln!("memory: the tape of {session} expired and was dropped, with its re-runs"); + eprintln!("amem: the tape of {session} expired and was dropped, with its re-runs"); } // and so do the sources tools brought in, except what a live entry cites for id in m.sweep_sources(now).await? { - eprintln!("memory: source {id} expired, uncited, and was dropped"); + eprintln!("amem: source {id} expired, uncited, and was dropped"); } for s in &swept { eprintln!( - "memory: transcript {} expired: {} turns kept as {} excerpts, {} dropped", + "amem: transcript {} expired: {} turns kept as {} excerpts, {} dropped", s.session, s.kept.len(), s.excerpts, @@ -147,7 +147,7 @@ pub async fn keep_newest( match keep_file(memory, harness, domain, Some(home), &path).await { Ok(k) => { eprintln!( - "memory: transcript kept as source {} in {}: {} turns, {}, until {}", + "amem: transcript kept as source {} in {}: {} turns, {}, until {}", k.session, k.domain, k.turns, @@ -158,7 +158,7 @@ pub async fn keep_newest( } Err(e) => { eprintln!( - "memory: the transcript at {} was not kept: {e}", + "amem: the transcript at {} was not kept: {e}", path.display() ); None @@ -175,7 +175,7 @@ pub async fn remove_kept(memory: &Memory, home: &Path) -> Vec o, Err(e) => { - eprintln!("memory: the harness's transcripts were left in its home: {e}"); + eprintln!("amem: the harness's transcripts were left in its home: {e}"); return Vec::new(); } }; @@ -188,7 +188,7 @@ pub async fn remove_kept(memory: &Memory, home: &Path) -> Vec) -> Fallible { let (sr, sw) = tokio::io::split(server_io); tokio::spawn(async move { if let Err(e) = mcp::serve_launcher_pipe(memory, sr, sw).await { - eprintln!("memory: in-process server: {e}"); + eprintln!("amem: in-process server: {e}"); } }); let (cr, cw) = tokio::io::split(client_io); @@ -119,7 +119,7 @@ pub async fn file_outbox( }) .await?; eprintln!( - "memory: handoff {id} from the harness's hook ({})", + "amem: handoff {id} from the harness's hook ({})", note.event.suffix() ); std::fs::remove_file(¬e.path).ok(); @@ -193,13 +193,13 @@ pub async fn prompted( let new: Vec<&String> = after.difference(&before).collect(); if new.is_empty() && !no_handoff { eprintln!( - "memory: the run left no handoff; the next session starts from nothing (pass --no-handoff if that is intended)" + "amem: the run left no handoff; the next session starts from nothing (pass --no-handoff if that is intended)" ); client.close().await.ok(); return Ok(if code == 0 { STOPPED } else { code }); } for id in new { - eprintln!("memory: handoff {id}"); + eprintln!("amem: handoff {id}"); } client.close().await.ok(); Ok(code) @@ -275,7 +275,7 @@ pub async fn run(session: &Session, options: &Options) -> Fallible { let Some(task) = claimed else { drop(in_flight); if !options.keep_going { - eprintln!("memory: nothing claimable"); + eprintln!("amem: nothing claimable"); return Ok(if last == NOTHING_TO_DO { NOTHING_TO_DO } else { @@ -306,7 +306,7 @@ pub async fn run(session: &Session, options: &Options) -> Fallible { crate::MineVia::Api }; if let Err(e) = session.mine(plan.model.as_deref(), via).await { - eprintln!("memory: mining failed: {e}"); + eprintln!("amem: mining failed: {e}"); } } if !options.keep_going { @@ -322,7 +322,7 @@ async fn one( options: &Options, ) -> Fallible { let lease_seconds = options.lease_seconds; - eprintln!("memory: claimed {}: {}", task.id, task.title); + eprintln!("amem: claimed {}: {}", task.id, task.title); let holding = Holding::of(&task.id); let memory = session.memory(); let platform = Platform::for_harness(&memory, session.harness()).await; @@ -346,7 +346,7 @@ async fn one( loop { tick.tick().await; if let Err(e) = client.heartbeat(&id, lease_seconds).await { - eprintln!("memory: heartbeat on {id}: {e}"); + eprintln!("amem: heartbeat on {id}: {e}"); return; } } @@ -430,7 +430,7 @@ async fn settle( match now.status.as_str() { "completed" => { eprintln!( - "memory: {} completed, handoff {}", + "amem: {} completed, handoff {}", task.id, now.handoff.as_deref().unwrap_or("-") ); @@ -452,7 +452,7 @@ async fn settle( .await?; client.complete(&task.id, &handoff).await?; eprintln!( - "memory: {} completed with its report as handoff {handoff}", + "amem: {} completed with its report as handoff {handoff}", task.id ); Ok(0) @@ -476,13 +476,13 @@ async fn settle( .await?; client.release(&task.id, Some(&handoff)).await?; eprintln!( - "memory: {} released; its output is filed as handoff {handoff}", + "amem: {} released; its output is filed as handoff {handoff}", task.id ); Ok(STOPPED) } other => { - eprintln!("memory: {} is now {other}", task.id); + eprintln!("amem: {} is now {other}", task.id); Ok(if code == 0 { STOPPED } else { code }) } } @@ -643,7 +643,7 @@ async fn prefetched(memory: &Memory, task: &Task) -> String { match memory.prefetch(&query, &prefetch::Plan::default()).await { Ok(found) => { eprintln!( - "memory: prefetched {} summaries ({} tokens): {}", + "amem: prefetched {} summaries ({} tokens): {}", found.hits.len(), found.tokens, found.ids().join(", ") @@ -651,7 +651,7 @@ async fn prefetched(memory: &Memory, task: &Task) -> String { prefetch_section(&found) } Err(e) => { - eprintln!("memory: no prefetch for {}: {e}", task.id); + eprintln!("amem: no prefetch for {}: {e}", task.id); String::new() } } diff --git a/crates/memory/src/wslc.rs b/crates/memory/src/wslc.rs index e2b5df0e..d3d9fac9 100644 --- a/crates/memory/src/wslc.rs +++ b/crates/memory/src/wslc.rs @@ -469,7 +469,7 @@ fn run_api(l: &Launch) -> Fallible { match ready { Ok((api, storage)) => crate::wslc_api::run(l, &api, &storage), Err(why) if l.via == Via::Auto => { - eprintln!("memory: not over the C API ({why}); driving the wslc command line"); + eprintln!("amem: not over the C API ({why}); driving the wslc command line"); run_cli(l) } Err(why) => Err(why.into()), @@ -637,7 +637,7 @@ fn gateway_up(root: &Path) -> Fallible { let probe = "import urllib.request,sys; urllib.request.urlopen('http://127.0.0.1:4000/health/liveliness', timeout=3)"; for _ in 0..60 { if run_ok(&s(&["exec", GATEWAY_LITELLM, "python3", "-c", probe])).is_ok() { - eprintln!("memory: gateway up inside the VM at http://{GATEWAY_LITELLM}:4000"); + eprintln!("amem: gateway up inside the VM at http://{GATEWAY_LITELLM}:4000"); return Ok(master); } std::thread::sleep(Duration::from_secs(2)); diff --git a/crates/memory/src/wslc_api.rs b/crates/memory/src/wslc_api.rs index ae53e7df..e7d3445d 100644 --- a/crates/memory/src/wslc_api.rs +++ b/crates/memory/src/wslc_api.rs @@ -357,8 +357,8 @@ mod on_windows { } for v in stale { match session.delete_volume(&v) { - Ok(()) => eprintln!("memory: removed {v}, left by a launch that was killed"), - Err(e) => eprintln!("memory: {v}, left by a launch that was killed, stays: {e}"), + Ok(()) => eprintln!("amem: removed {v}, left by a launch that was killed"), + Err(e) => eprintln!("amem: {v}, left by a launch that was killed, stays: {e}"), } } } @@ -374,7 +374,7 @@ mod on_windows { if process_alive(pid) { return false; } - eprintln!("memory: ending {name}, left by process {pid}, which is gone"); + eprintln!("amem: ending {name}, left by process {pid}, which is gone"); let args = ["--session", name, "system", "session", "terminate"].map(String::from); wslc::run_ok(&args).is_ok() } @@ -403,7 +403,7 @@ mod on_windows { let s = session.clone(); Some(rt.spawn(async move { if tokio::signal::ctrl_c().await.is_ok() { - eprintln!("memory: interrupted; ending the session"); + eprintln!("amem: interrupted; ending the session"); s.terminate().ok(); std::fs::remove_dir_all(&projection).ok(); std::process::exit(130); @@ -429,14 +429,14 @@ mod on_windows { match provision(image, &store, refresh, || cli_image_id(image)) { Provision::Present { id } => { eprintln!( - "memory: {image} {} from this session's store; after a rebuild, \ + "amem: {image} {} from this session's store; after a rebuild, \ `amem wslc-api-check --refresh`", id.get(..12).unwrap_or(&id) ); Ok(()) } Provision::Pull => { - eprintln!("memory: pulling {image} into this session's store"); + eprintln!("amem: pulling {image} into this session's store"); session.pull_image(image, None).map_err(Into::into) } Provision::Load { stale } => { @@ -451,7 +451,7 @@ mod on_windows { /// `wslc save` the image to a file beside the session's disk, load it, and remove the /// file. fn load_from_cli(session: &Session, image: &str, scratch: &Path) -> Fallible<()> { - eprintln!("memory: loading {image} into this session's store; once per image"); + eprintln!("amem: loading {image} into this session's store; once per image"); let archive = scratch.join("image.tar"); let started = std::time::Instant::now(); let status = Command::new(wslc::wslc()) @@ -471,7 +471,7 @@ mod on_windows { format!("{image} was loaded but the session's store does not list it").into(), ); } - eprintln!("memory: loaded in {:.0?}", started.elapsed()); + eprintln!("amem: loaded in {:.0?}", started.elapsed()); Ok(()) } diff --git a/crates/memory/tests/message_prefix.rs b/crates/memory/tests/message_prefix.rs new file mode 100644 index 00000000..f86d3717 --- /dev/null +++ b/crates/memory/tests/message_prefix.rs @@ -0,0 +1,233 @@ +//! The binary's own messages start with `amem:`, the command's name, and none starts +//! with `memory:`, the name it had before. The demo selects the lines of `amem work` by +//! that prefix, so a message under the old name is a line the demo drops without a word. +//! This reads the sources, since no run reaches every message. It checks that the old +//! name is gone, not that every line carries the new one: `relay:`, `error:` and the +//! like are prefixes of their own. + +use std::path::Path; + +fn sources(dir: &Path, out: &mut Vec) { + for entry in std::fs::read_dir(dir).expect("crates/ reads") { + let path = entry.expect("an entry").path(); + if path.is_dir() { + if path.file_name().is_some_and(|n| n != "target") { + sources(&path, out); + } + } else if path.extension().is_some_and(|e| e == "rs") { + out.push(path); + } + } +} + +/// Where a message under the old name starts, each as it is spelled in a source line. +fn old_spellings(name: &str) -> Vec { + // after a quote, a backtick, a byte string, or a newline escape; and what follows + // the colon is a space, a tab, a placeholder, a line continuation, or the string's end + let starts = ["\"", "'", "`", "b\"", "\\n"]; + let follows = [" ", "\\t", "{", "\\", "\""]; + let mut old = Vec::new(); + for s in starts { + for f in follows { + old.push(format!("{s}{name}:{f}")); + } + old.push(format!("{s}{name} hook:")); + } + old +} + +/// `text` with every comment blanked and every line break kept, so a line number still +/// points at its source line. A literal is kept whole: `//` or `/*` inside a string or +/// a character opens no comment. +fn without_comments(text: &str) -> String { + let chars: Vec = text.chars().collect(); + let mut out = String::with_capacity(text.len()); + let mut i = 0; + while i < chars.len() { + let rest = &chars[i..]; + if let Some(n) = comment_len(rest) { + out.extend(rest[..n].iter().map(|&c| if c == '\n' { c } else { ' ' })); + i += n; + } else { + let n = literal_len(rest).unwrap_or(1); + out.extend(&rest[..n]); + i += n; + } + } + out +} + +/// The length of the comment that `rest` starts with, if it starts with one. Block +/// comments nest. +fn comment_len(rest: &[char]) -> Option { + if rest.starts_with(&['/', '/']) { + return Some(rest.iter().position(|&c| c == '\n').unwrap_or(rest.len())); + } + if !rest.starts_with(&['/', '*']) { + return None; + } + let mut depth = 0; + let mut i = 0; + while i + 1 < rest.len() { + match (rest[i], rest[i + 1]) { + ('/', '*') => depth += 1, + ('*', '/') => depth -= 1, + _ => { + i += 1; + continue; + } + } + i += 2; + if depth == 0 { + return Some(i); + } + } + Some(rest.len()) +} + +/// The length of the string or character literal that `rest` starts with, if it starts +/// with one. A quote that no character closes is a lifetime. +fn literal_len(rest: &[char]) -> Option { + let up_to = |close: &[char], from: usize| { + rest[from..] + .windows(close.len()) + .position(|w| w == close) + .map_or(rest.len(), |p| from + p + close.len()) + }; + match rest { + ['"', ..] => { + let mut i = 1; + while i < rest.len() && rest[i] != '"' { + i += if rest[i] == '\\' { 2 } else { 1 }; + } + Some((i + 1).min(rest.len())) + } + ['r', tail @ ..] => { + let hashes = tail.iter().take_while(|&&c| c == '#').count(); + let close: Vec = std::iter::once('"') + .chain(std::iter::repeat_n('#', hashes)) + .collect(); + (tail.get(hashes) == Some(&'"')).then(|| up_to(&close, hashes + 2)) + } + ['\'', '\\', ..] => Some(up_to(&['\''], 3)), + ['\'', _, '\'', ..] => Some(3), + _ => None, + } +} + +/// The lines of `text` that start a message under the old name. A comment is not a +/// message, and a line that says it holds the old name on purpose is let through. +fn under_the_old_name(text: &str) -> Vec<(usize, String)> { + // assembled, so this file does not hold what it looks for + let name = ["mem", "ory"].concat(); + let allowed = ["old name", " allowed"].concat(); + let old = old_spellings(&name); + let code = without_comments(text); + let mut found = Vec::new(); + let mut continues = false; + for (n, (line, source)) in code.lines().zip(text.lines()).enumerate() { + let trimmed = line.trim_start(); + // a continuation skips blank lines as it skips indentation + if trimmed.is_empty() { + continue; + } + let held = old.iter().any(|o| line.contains(o.as_str())) + || (continues && trimmed.starts_with(&format!("{name}:"))); + if held && !source.contains(&allowed) { + found.push((n + 1, source.trim_start().to_string())); + } + continues = line.ends_with('\\'); + } + found +} + +#[test] +fn every_spelling_of_the_old_name_is_found_and_only_those() { + let name = ["mem", "ory"].concat(); + let caught = [ + format!("eprintln!(\"{name}: x\");"), + format!("eprintln!(\"{name}:{{e}}\");"), + format!("eprintln!(\"{name}:\\tx\");"), + format!("eprintln!(\"\\n{name}: after a blank line\");"), + format!("let js = r#\"console.warn('{name}: ' + e);\"#;"), + format!("let b = b\"{name}: sandbox: \";"), + format!("eprintln!(\"{name} hook: x\");"), + // what opens a comment inside a literal opens none + format!("let s = \"//\"; eprintln!(\"{name}: x\");"), + format!("let s = \"/*\"; eprintln!(\"{name}: x\"); // */"), + format!("let s = \"a \\\" // b\"; eprintln!(\"{name}: x\");"), + format!("let s = r#\"a \" // \"#; eprintln!(\"{name}: x\");"), + format!("let q = '\"'; let s = \"//\"; eprintln!(\"{name}: x\");"), + format!("let q = '\\''; let s = \"//\"; eprintln!(\"{name}: x\");"), + format!("fn f<'a>(s: &'a str) {{ eprintln!(\"{name}: x\"); }}"), + format!("/* a note */ eprintln!(\"{name}: x\");"), + // a comment ends at its line, and an escaped quote in a char opens no string + format!("// a note\neprintln!(\"{name}: x\");"), + format!("let c = '\\\"'; let s = \"//\"; eprintln!(\"{name}: x\");"), + ]; + for line in &caught { + assert_eq!(under_the_old_name(line).len(), 1, "missed: {line}"); + } + let continued = format!("eprintln!(\"first line\\n\\\n {name}: second line\");"); + assert_eq!( + under_the_old_name(&continued), + vec![(2, format!("{name}: second line\");"))] + ); + // a continuation skips every blank line and all indentation that follows it + for blank in ["", " "] { + let continued = + format!("eprintln!(\"first line\\n\\\n{blank}\n {name}: second line\");"); + assert_eq!( + under_the_old_name(&continued), + vec![(3, format!("{name}: second line\");"))], + "after a line of {blank:?}" + ); + } + let after_a_block = format!("/* a note\n on two lines */ eprintln!(\"{name}: x\");"); + assert_eq!(under_the_old_name(&after_a_block).len(), 1); + let passed = [ + format!("/// the `{name}: ` key"), + format!("// \"{name}: x\""), + format!("let x = 1; // formerly \"{name}: x\""), + format!("/* formerly \"{name}: x\" */"), + format!("let x = 1; /* \"{name}: x\" */ let y = 2;"), + format!("/* /* nested */ \"{name}: x\" */"), + format!("/*\n * formerly \"{name}: x\"\n */"), + format!("/**\n * eprintln!(\"first\\n\\\n {name}: x\");\n */"), + format!("let limits = \"{name}: 1Gi\"; // old name allowed: a pod limit"), + format!("{name}: Arc,"), + "eprintln!(\"amem: x\");".to_string(), + ]; + for line in &passed { + assert!(under_the_old_name(line).is_empty(), "flagged: {line}"); + } +} + +#[test] +fn no_message_starts_with_the_old_name() { + let crates = Path::new(env!("CARGO_MANIFEST_DIR")).join(".."); + let mut files = Vec::new(); + sources(&crates, &mut files); + assert!( + files.len() > 50, + "found only {} sources under {}", + files.len(), + crates.display() + ); + let mut found = Vec::new(); + let mut read = 0; + for file in &files { + let text = std::fs::read_to_string(file).expect("a source reads"); + read += without_comments(&text).matches("\"amem: ").count(); + for (n, line) in under_the_old_name(&text) { + found.push(format!("{}:{n}: {line}", file.display())); + } + } + // The binary has about 80 messages; far fewer seen means comments hid code. + assert!(read > 70, "only {read} messages seen outside comments"); + assert!( + found.is_empty(), + "messages under the old name:\n{}", + found.join("\n") + ); +} diff --git a/crates/sandbox/src/lib.rs b/crates/sandbox/src/lib.rs index 91378b5d..c94f944b 100644 --- a/crates/sandbox/src/lib.rs +++ b/crates/sandbox/src/lib.rs @@ -1190,7 +1190,7 @@ url = "{relay}/mcp" if let Ok(bin) = find_on_path("codex") { if !bin.with_file_name("codex-code-mode-host").is_file() { eprintln!( - "memory: warning: {} has no codex-code-mode-host beside it; Codex's tools, memory among them, will fail. Install it from the same Codex release", + "amem: warning: {} has no codex-code-mode-host beside it; Codex's tools, memory among them, will fail. Install it from the same Codex release", bin.display() ); } @@ -1841,7 +1841,7 @@ function note(event, sessionID, extra) { try { hook({ hook_event_name: event, session_id: sessionID, transcript_path: transcript(sessionID), ...extra }); } catch (err) { - console.warn(`memory: ${event} note not left: ${err.message}`); + console.warn(`amem: ${event} note not left: ${err.message}`); } } diff --git a/crates/sandbox/src/linux.rs b/crates/sandbox/src/linux.rs index 4b9ec176..40cd5414 100644 --- a/crates/sandbox/src/linux.rs +++ b/crates/sandbox/src/linux.rs @@ -502,7 +502,7 @@ fn command(plan: &Plan) -> Result<(Command, PathBuf), Error> { go().map_err(|e| { // std carries only the raw errno back to the parent, so say what // failed here, where stderr is still the terminal - eprintln!("memory: sandbox: {e}"); + eprintln!("amem: sandbox: {e}"); std::io::Error::other(e.to_string()) })?; cloexec_from_three(open_files) @@ -516,7 +516,7 @@ fn command(plan: &Plan) -> Result<(Command, PathBuf), Error> { /// Say what failed between fork and exec, on stderr, without allocating. fn say(what: &[u8]) { - for part in [b"memory: sandbox: ".as_slice(), what, b"\n"] { + for part in [b"amem: sandbox: ".as_slice(), what, b"\n"] { // SAFETY: a write of a buffer with its own length to stderr. unsafe { libc::write(2, part.as_ptr().cast(), part.len()) }; } @@ -647,7 +647,7 @@ fn say_the_relay_died(signal: i32, core_dumped: bool) { } let number = &digits[first..]; let mut buf = [0u8; 96]; - let mut n = put(&mut buf, 0, b"memory: the relay was killed by "); + let mut n = put(&mut buf, 0, b"amem: the relay was killed by "); if let Some(name) = crate::measure::signal_name(signal) { n = put(&mut buf, n, name.as_bytes()); n = put(&mut buf, n, b" (signal "); diff --git a/crates/sandbox/src/measure.rs b/crates/sandbox/src/measure.rs index e026814f..dbb5c7a6 100644 --- a/crates/sandbox/src/measure.rs +++ b/crates/sandbox/src/measure.rs @@ -197,7 +197,7 @@ fn exit_code_as(status: ExitStatus, program: &OsStr, stopped: bool) -> i32 { |name| format!("{name} (signal {signal})"), ); let line = format!( - "memory: {} was killed by {name}{core}\n", + "amem: {} was killed by {name}{core}\n", program.to_string_lossy() ); // one write, so what other threads print does not land inside the line diff --git a/crates/server/src/mcp.rs b/crates/server/src/mcp.rs index 01b1bc42..cfa7822a 100644 --- a/crates/server/src/mcp.rs +++ b/crates/server/src/mcp.rs @@ -935,7 +935,7 @@ impl ServerHandler for McpServer { .tape_call(&tool, &arguments, &value, is_error) .await { - eprintln!("memory: {tool} was not kept on the tape: {e}"); + eprintln!("amem: {tool} was not kept on the tape: {e}"); } result } diff --git a/crates/server/src/projection.rs b/crates/server/src/projection.rs index a8773cc5..cce13243 100644 --- a/crates/server/src/projection.rs +++ b/crates/server/src/projection.rs @@ -138,7 +138,7 @@ impl Memory { if domain == RECALLS { // a domain by that name would sit where the recall pages do eprintln!( - "memory: the domain {RECALLS} is not projected: the name is the recall pages'" + "amem: the domain {RECALLS} is not projected: the name is the recall pages'" ); continue; } @@ -230,7 +230,7 @@ impl Memory { }); match landed { Ok(name) => r.projected = Some(format!("{RECALLS}/{}/{name}", self.session_id)), - Err(e) => eprintln!("memory: projection at {}: {e}", dir.display()), + Err(e) => eprintln!("amem: projection at {}: {e}", dir.display()), } } @@ -274,7 +274,7 @@ impl Memory { pub(crate) async fn refresh_projection(&self) { if let Some(dir) = &self.projection { if let Err(e) = self.render(dir).await { - eprintln!("memory: projection at {}: {e}", dir.display()); + eprintln!("amem: projection at {}: {e}", dir.display()); } } } @@ -370,7 +370,7 @@ impl Memory { pub(crate) async fn refresh_projection_after_erasure(&self) { if let Some(dir) = &self.projection { if let Err(e) = self.sweep_recalls(dir, false) { - eprintln!("memory: projection at {}: {e}", dir.display()); + eprintln!("amem: projection at {}: {e}", dir.display()); } } self.refresh_projection().await; diff --git a/crates/server/src/tape.rs b/crates/server/src/tape.rs index 96aee53e..bbe32422 100644 --- a/crates/server/src/tape.rs +++ b/crates/server/src/tape.rs @@ -505,7 +505,7 @@ impl Memory { ) -> Result, Error> { match self.header_in(domain, session).await { Err(Error::Transcript(e)) => { - eprintln!("memory: the tape of {session} has a header that does not parse, left as it is: {e}"); + eprintln!("amem: the tape of {session} has a header that does not parse, left as it is: {e}"); Ok(None) } other => other, diff --git a/crates/web/src/lib.rs b/crates/web/src/lib.rs index 1f8e03a7..8f03522c 100644 --- a/crates/web/src/lib.rs +++ b/crates/web/src/lib.rs @@ -46,7 +46,7 @@ pub enum Error { #[error("listen: {0}")] Io(#[from] std::io::Error), /// The record. - #[error("memory: {0}")] + #[error("record: {0}")] Memory(#[from] dm_server::Error), } diff --git a/packaging/apparmor/amem b/packaging/apparmor/amem index 5c2afd20..90c0590a 100644 --- a/packaging/apparmor/amem +++ b/packaging/apparmor/amem @@ -16,7 +16,7 @@ # # Without a profile, `amem launch` fails at the first mapping: # -# memory: sandbox: confinement: /proc/self/setgroups: Permission denied +# amem: sandbox: confinement: /proc/self/setgroups: Permission denied # error: confinement: spawn /opt/memory/memory: Invalid argument (os error 22) # # WHAT THIS GRANTS diff --git a/scripts/demo/2-curation.sh b/scripts/demo/2-curation.sh index d6e0b071..9f7fdd78 100755 --- a/scripts/demo/2-curation.sh +++ b/scripts/demo/2-curation.sh @@ -18,7 +18,7 @@ a task list --domain dom-demo --status admitted \ say "A librarian run" "$WORKER_HARNESS on the gateway's $WORKER_MODEL claims the top curation task and does it. An entry rewritten while holding a librarian's task lands one tier up, curated, and never further on an agent's word." top=$(a task list --domain dom-demo --status admitted | j "next((t['id'] for t in d if (t.get('origin') or '').startswith(('librarian:','checker:'))), '')") if [ -n "$top" ]; then - ( cd "$(mktemp -d)" && a work --harness "$WORKER_HARNESS" --auth gateway --model "$WORKER_MODEL" --backend namespaces --lease-seconds 900 --task "$top" 2>&1 | grep -E '^memory: (claimed|task|prefetched)' ) || true + ( cd "$(mktemp -d)" && a work --harness "$WORKER_HARNESS" --auth gateway --model "$WORKER_MODEL" --backend namespaces --lease-seconds 900 --task "$top" 2>&1 | grep -E '^amem: (claimed|task|prefetched)' ) || true else echo " no curation task is admitted: the cycle filed none, or the domain does not admit its own filings (a record from an older demo; make demo-ready RESET=1)" fi diff --git a/scripts/demo/4-fabric.sh b/scripts/demo/4-fabric.sh index d56c71cf..3a183ff7 100755 --- a/scripts/demo/4-fabric.sh +++ b/scripts/demo/4-fabric.sh @@ -43,7 +43,7 @@ if [ -n "${HARNESS:-}" ]; then say "Subagents on the board" "A $HARNESS session is asked to delegate. Each subagent it would spawn is filed as a task of its own instead, with a continuation that waits for them, so the work survives the session and any worker can do it. The original task goes back on the board but waits: nobody may claim it while its continuation is open." parent=$(a task create "Compare two decisions" --domain dom-demo --priority 20 --body "Use two subagents, one per decision, to summarise DR-19 and DR-22 from the memory record; then combine their answers into a handoff and complete this task." | j "d['id']") a task admit "$parent" >/dev/null - ( cd "$(mktemp -d)" && a work --harness "$HARNESS" --auth "${AUTH:-subscription}" $MODEL_ARG --lease-seconds 900 --task "$parent" 2>&1 | grep -E '^memory: (claimed|task|board|filed)' ) || true + ( cd "$(mktemp -d)" && a work --harness "$HARNESS" --auth "${AUTH:-subscription}" $MODEL_ARG --lease-seconds 900 --task "$parent" 2>&1 | grep -E '^amem: (claimed|task|board|filed)' ) || true tree() { a task tree "$parent" | j "'\n'.join(f\" {c['id']} {(c.get('origin') or '').split(':')[0]:<10} {c['status']:<11} {c['title']}\" for c in d.get('children', []))" a task get "$parent" | j "f\" {d['id']} original {d['status']:<11} {d['title']}\" + (' (waits on its continuation)' if d['status'] == 'admitted' else ' (closed by its continuation, with its handoff)' if d['status'] == 'completed' else '')" @@ -53,13 +53,13 @@ if [ -n "${HARNESS:-}" ]; then if [ -n "$kids" ]; then say "The board runs the subagents' tasks" "$WORKER_HARNESS on the gateway's $WORKER_MODEL takes each subagent task, one at a time, and files its report as the handoff." for k in $kids; do - ( cd "$(mktemp -d)" && a work --harness "$WORKER_HARNESS" --auth gateway --model "$WORKER_MODEL" --backend namespaces --lease-seconds 900 --task "$k" 2>&1 | grep -E '^memory: (claimed|task)' ) || true + ( cd "$(mktemp -d)" && a work --harness "$WORKER_HARNESS" --auth gateway --model "$WORKER_MODEL" --backend namespaces --lease-seconds 900 --task "$k" 2>&1 | grep -E '^amem: (claimed|task)' ) || true done cont=$(a task tree "$parent" | j "next((c['id'] for c in d.get('children', []) if (c.get('origin') or '').startswith('continues:') and c['status']=='admitted'), '')") open=$(a task tree "$parent" | j "' '.join(c['id'] for c in d.get('children', []) if (c.get('origin') or '').startswith('task:') and c['status']!='completed')") if [ -n "$cont" ] && [ -z "$open" ]; then say "Fan-in" "The continuation waited for both; it runs now, with their handoffs in its prompt, and completes with a handoff of its own. Its completion closes the original task it resumed, with the same handoff." - ( cd "$(mktemp -d)" && a work --harness "$HARNESS" --auth "${AUTH:-subscription}" $MODEL_ARG --lease-seconds 900 --task "$cont" 2>&1 | grep -E '^memory: (claimed|task)' ) || true + ( cd "$(mktemp -d)" && a work --harness "$HARNESS" --auth "${AUTH:-subscription}" $MODEL_ARG --lease-seconds 900 --task "$cont" 2>&1 | grep -E '^amem: (claimed|task)' ) || true elif [ -n "$cont" ]; then echo " the continuation $cont still waits for: $open" fi diff --git a/scripts/demo/7-platform.sh b/scripts/demo/7-platform.sh index 211c2cb9..3886f4f4 100644 --- a/scripts/demo/7-platform.sh +++ b/scripts/demo/7-platform.sh @@ -49,7 +49,7 @@ if gateway; then say "A task under the definition" "A question filed for $WORKER_HARNESS, launched with no model named: the platform's default model is used, and the verified definition is added to the task's prompt." task=$(a task create "What did DR-22 decide about the format memory entries travel in?" --domain dom-demo --priority 30 --body "Answer from the memory record, in two or three sentences, and file the answer as your handoff." | j "d['id']") a task admit "$task" >/dev/null - ( cd "$(mktemp -d)" && a work --harness "$WORKER_HARNESS" --auth gateway --model "$WORKER_MODEL" --backend namespaces --lease-seconds 900 --task "$task" 2>&1 | grep -E '^memory: (claimed|task|.*confined)' ) || true + ( cd "$(mktemp -d)" && a work --harness "$WORKER_HARNESS" --auth gateway --model "$WORKER_MODEL" --backend namespaces --lease-seconds 900 --task "$task" 2>&1 | grep -E '^amem: (claimed|task|.*confined)' ) || true handoff=$(a task get "$task" | j "d.get('handoff') or ''") if [ -n "$handoff" ]; then a get "$handoff" 2>/dev/null | grep -q "Checked against the platform definition" \ diff --git a/spikes/harness-containers/seatbelt-steps.sh b/spikes/harness-containers/seatbelt-steps.sh index 64265a46..5405c35e 100755 --- a/spikes/harness-containers/seatbelt-steps.sh +++ b/spikes/harness-containers/seatbelt-steps.sh @@ -18,7 +18,7 @@ step() { --workdir "$work" -- "$@" "$work/.out" 2>&1 code=$? printf 'STEP %-22s exit=%s\n' "$name" "$code" - grep -v '^memory: ' "$work/.out" | sed 's/^/ /' | head -20 + grep -v '^amem: ' "$work/.out" | sed 's/^/ /' | head -20 } step true /usr/bin/true step echo /bin/echo inside