Watch
1
0
Fork
You've already forked souveraine
0

refactor: rename Aster -> subconscious across src/

Aster is Ani's personal subconscious-agent name, not a framework
concept. The codebase now refers to the N+1 pass and its components as
subconscious everywhere — const names, fn names, variables, comments,
the schedule source tag, the todo source enum, and UI strings.

Mechanical rename, no behaviour change. grep -i aster src/ now returns
only Faster-Whisper. reference/ and docs/ historical context untouched.
This commit is contained in:
Fimeg 2026-05-16 19:44:51 -04:00
commit 27d86fe4ca
18 changed files with 79 additions and 79 deletions

View file

@ -653,7 +653,7 @@ impl Backend for LocalBackend {
interject: crate::backend::InterjectionQueue, interject: crate::backend::InterjectionQueue,
) -> Result<BoxStream<'static, Result<BackendEvent>>> { ) -> Result<BoxStream<'static, Result<BackendEvent>>> {
// Resolve the agent for this conversation, then drain her // Resolve the agent for this conversation, then drain her
// subconscious's intrusive box. Anything Aster queued after the // subconscious's intrusive box. Anything subconscious queued after the
// last turn rides in on Casey's next message as `[ surfacing: ... ]` // last turn rides in on Casey's next message as `[ surfacing: ... ]`
// lines — the lettabot-v017 pattern, ported. This is the channel // lines — the lettabot-v017 pattern, ported. This is the channel
// by which a Critical observation can interrupt mid-conversation // by which a Critical observation can interrupt mid-conversation
@ -733,7 +733,7 @@ impl crate::core::nervous::handler::TurnInjector for LocalBackend {
/// idle. We grab the most recent conversation (or create a fresh one /// idle. We grab the most recent conversation (or create a fresh one
/// if the agent has none), append the scheduled prompt as a user /// if the agent has none), append the scheduled prompt as a user
/// message, and drain the resulting stream — the turn runs silently /// message, and drain the resulting stream — the turn runs silently
/// in the background. Anything Aster surfaces lands in the inbox. /// in the background. Anything subconscious surfaces lands in the inbox.
async fn inject_background_turn( async fn inject_background_turn(
&self, &self,
agent_id: &str, agent_id: &str,
@ -753,12 +753,12 @@ impl crate::core::nervous::handler::TurnInjector for LocalBackend {
} }
} }
/// Drain Aster's intrusive box for the given agent and return formatted /// Drain subconscious's intrusive box for the given agent and return formatted
/// `[ surfacing: ... ]` lines ready to prepend to the user's next message. /// `[ surfacing: ... ]` lines ready to prepend to the user's next message.
/// Marks each drained item as delivered (moved to `sent.md`). Mirrors /// Marks each drained item as delivered (moved to `sent.md`). Mirrors
/// lettabot-v017's `readSurfacingThoughts` + `clearSurfacingThoughts` pair /// lettabot-v017's `readSurfacingThoughts` + `clearSurfacingThoughts` pair
/// (`~/Projects/lettabot-v017/src/core/prompts.ts:64-91`) — the substrate /// (`~/Projects/lettabot-v017/src/core/prompts.ts:64-91`) — the substrate
/// reads the channel Aster wrote to and lets the conscious mind see it /// reads the channel subconscious wrote to and lets the conscious mind see it
/// before she reads Casey. /// before she reads Casey.
/// ///
/// Critical urgency gets `[ surfacing — CRITICAL: ... ]`. High becomes /// Critical urgency gets `[ surfacing — CRITICAL: ... ]`. High becomes
@ -1212,7 +1212,7 @@ async fn run_turn(
ConversationMessage::assistant_text(&committed_content), ConversationMessage::assistant_text(&committed_content),
)?; )?;
// On interrupt, skip Aster's N+1 pass entirely — the user is in the // On interrupt, skip subconscious's N+1 pass entirely — the user is in the
// middle of redirecting, the last thing they need is a delayed // middle of redirecting, the last thing they need is a delayed
// surfacing landing seconds later. Pressure recalc still runs below. // surfacing landing seconds later. Pressure recalc still runs below.
if interrupted { if interrupted {
@ -1227,7 +1227,7 @@ async fn run_turn(
// Energy balance: scan the agent's task list and compute the generative / // Energy balance: scan the agent's task list and compute the generative /
// consumptive ratio. Written to system/dynamic/energy-balance.md so the // consumptive ratio. Written to system/dynamic/energy-balance.md so the
// agent can read it in context and Aster can reference it during N+1. // agent can read it in context and subconscious can reference it during N+1.
// Silent on failure — the file is advisory, not load-bearing. // Silent on failure — the file is advisory, not load-bearing.
if let Err(e) = write_energy_balance(&server, &agent_id, &event_bus).await { if let Err(e) = write_energy_balance(&server, &agent_id, &event_bus).await {
tracing::debug!(agent = %agent_id, error = %e, "energy-balance write skipped"); tracing::debug!(agent = %agent_id, error = %e, "energy-balance write skipped");

View file

@ -49,7 +49,7 @@ pub enum BackendEvent {
Token(String), Token(String),
/// Reasoning trace (the "thinking" pane). /// Reasoning trace (the "thinking" pane).
Reasoning(String), Reasoning(String),
/// Subconscious surfacing (Aster-voice bubble). /// Subconscious surfacing (subconscious-voice bubble).
Surfacing { Surfacing {
source: String, source: String,
content: String, content: String,
@ -95,7 +95,7 @@ pub enum BackendEvent {
/// N+1 subconscious pass started (`true`) or finished (`false`). /// N+1 subconscious pass started (`true`) or finished (`false`).
/// Presence reads this to flip into / out of `Posture::Thinking` so the /// Presence reads this to flip into / out of `Posture::Thinking` so the
/// face shows when the subconscious is the one looking at the conversation. /// face shows when the subconscious is the one looking at the conversation.
/// (For Casey's agent the subconscious instance is "Aster", but the event /// (For Casey's agent the subconscious instance is "subconscious", but the event
/// itself is generic — every agent's subconscious is what's named here.) /// itself is generic — every agent's subconscious is what's named here.)
SubconsciousPass(bool), SubconsciousPass(bool),
/// Agent changed her outfit. The string is the outfit name (a subdirectory /// Agent changed her outfit. The string is the outfit name (a subdirectory

View file

@ -1,6 +1,6 @@
//! Archivist — the N+100 memory synthesis pass. //! Archivist — the N+100 memory synthesis pass.
//! //!
//! N+1 (Aster) runs after every response; N+25 (Reflection) distils a //! N+1 (subconscious) runs after every response; N+25 (Reflection) distils a
//! transcript window into the ledger. The Archivist runs least often and //! transcript window into the ledger. The Archivist runs least often and
//! works on a different substrate entirely: not the conversation, but the //! works on a different substrate entirely: not the conversation, but the
//! agent's *memfs* — her accumulated journal. //! agent's *memfs* — her accumulated journal.

View file

@ -414,7 +414,7 @@ impl CompactionStrategy for CullStrategy {
/// Keep the system message + the last `preserve_recent_n` messages, drop the /// Keep the system message + the last `preserve_recent_n` messages, drop the
/// middle. No LLM dependency — the cheap, fast default for analytical agents /// middle. No LLM dependency — the cheap, fast default for analytical agents
/// (Aster) and ephemeral subagents. /// (subconscious) and ephemeral subagents.
/// ///
/// Tool-pair aware: if the cut would split a tool-call message from its /// Tool-pair aware: if the cut would split a tool-call message from its
/// matching tool-result, the cut slides back to keep the pair together. /// matching tool-result, the cut slides back to keep the pair together.
@ -500,7 +500,7 @@ impl CompactionStrategy for SlidingWindowStrategy {
/// The result is inserted as a system message so the agent carries the gist /// The result is inserted as a system message so the agent carries the gist
/// even after the originals are gone. /// even after the originals are gone.
/// ///
/// Uses whichever model the engine provides (subconscious model if Aster is /// Uses whichever model the engine provides (subconscious model if subconscious is
/// enabled, compaction model / primary otherwise). If no Bifrost client is /// enabled, compaction model / primary otherwise). If no Bifrost client is
/// available, falls back to plain SlidingWindow (no threads lost is better /// available, falls back to plain SlidingWindow (no threads lost is better
/// than no compaction at all). /// than no compaction at all).

View file

@ -222,7 +222,7 @@ pub struct SubconsciousConfig {
/// Defaults to None — uses the primary agent's model. /// Defaults to None — uses the primary agent's model.
#[serde(default)] #[serde(default)]
pub model: Option<String>, pub model: Option<String>,
/// Max tokens for Aster's response. Set to control cost/length. /// Max tokens for subconscious's response. Set to control cost/length.
/// Defaults to None — let the model use its full output capacity. /// Defaults to None — let the model use its full output capacity.
#[serde(default)] #[serde(default)]
pub max_tokens: Option<u32>, pub max_tokens: Option<u32>,

View file

@ -302,7 +302,7 @@ pub async fn build_system_prompt(
} }
/// Like [`build_system_prompt`] but also surfaces a window into the /// Like [`build_system_prompt`] but also surfaces a window into the
/// subconscious's ledger entries when its memfs is reachable. Aster writes, /// subconscious's ledger entries when its memfs is reachable. subconscious writes,
/// Ani reads — naming the channel in body-knowledge prose so the agent /// Ani reads — naming the channel in body-knowledge prose so the agent
/// knows where to look without being told to look. /// knows where to look without being told to look.
pub async fn build_system_prompt_with_subconscious( pub async fn build_system_prompt_with_subconscious(
@ -481,7 +481,7 @@ pub async fn build_system_prompt_full(
/// Names the inner-voice file the subconscious appends to (in the primary's /// Names the inner-voice file the subconscious appends to (in the primary's
/// own memfs — that's where `surface_to_conscious` writes), the pending inbox /// own memfs — that's where `surface_to_conscious` writes), the pending inbox
/// if it has anything queued, and — when `subconscious_root` is reachable — /// if it has anything queued, and — when `subconscious_root` is reachable —
/// a peek at the subconscious's ledger. Aster writes, Ani reads; the /// a peek at the subconscious's ledger. subconscious writes, Ani reads; the
/// substrate names the channel and lets the agent decide when to reach for it. /// substrate names the channel and lets the agent decide when to reach for it.
async fn build_subconscious_channel( async fn build_subconscious_channel(
memory_root: &Path, memory_root: &Path,
@ -612,7 +612,7 @@ async fn peek_subconscious_ledger(sub_root: &Path) -> String {
/// Reads identity, mandate, and ledger orientation from the subconscious /// Reads identity, mandate, and ledger orientation from the subconscious
/// agent's memory root. Falls back to empty (caller uses hardcoded /// agent's memory root. Falls back to empty (caller uses hardcoded
/// default) if files don't exist. /// default) if files don't exist.
pub async fn build_aster_prompt( pub async fn build_subconscious_prompt(
subconscious_memory_root: &Path, subconscious_memory_root: &Path,
) -> String { ) -> String {
let mut sections: Vec<String> = Vec::new(); let mut sections: Vec<String> = Vec::new();
@ -814,27 +814,27 @@ mod tests {
} }
#[tokio::test] #[tokio::test]
async fn aster_prompt_from_files() { async fn subconscious_prompt_from_files() {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let sub_mem = dir.path(); let sub_mem = dir.path();
let sys = sub_mem.join("system"); let sys = sub_mem.join("system");
std::fs::create_dir_all(&sys).unwrap(); std::fs::create_dir_all(&sys).unwrap();
std::fs::write( std::fs::write(
sys.join("persona.md"), sys.join("persona.md"),
"---\ndescription: WHO I AM\n---\n\n# I Am Aster\n", "---\ndescription: WHO I AM\n---\n\n# I Am Subconscious\n",
).unwrap(); ).unwrap();
std::fs::write( std::fs::write(
sys.join("subconscious.md"), sys.join("subconscious.md"),
"---\ndescription: mandate\n---\n\n# Aster's Mandate\n\nComplete what was left.\n", "---\ndescription: mandate\n---\n\n# Subconscious's Mandate\n\nComplete what was left.\n",
).unwrap(); ).unwrap();
let prompt = build_aster_prompt(sub_mem).await; let prompt = build_subconscious_prompt(sub_mem).await;
assert!(prompt.contains("I Am Aster")); assert!(prompt.contains("I Am Subconscious"));
assert!(prompt.contains("Complete what was left")); assert!(prompt.contains("Complete what was left"));
} }
#[tokio::test] #[tokio::test]
async fn aster_prompt_includes_ledger_orientation() { async fn subconscious_prompt_includes_ledger_orientation() {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let sub_mem = dir.path(); let sub_mem = dir.path();
@ -842,7 +842,7 @@ mod tests {
std::fs::create_dir_all(&sys).unwrap(); std::fs::create_dir_all(&sys).unwrap();
std::fs::write( std::fs::write(
sys.join("persona.md"), sys.join("persona.md"),
"---\ndescription: test\n---\n\n# I Am Aster\n", "---\ndescription: test\n---\n\n# I Am Subconscious\n",
).unwrap(); ).unwrap();
let ledger_dir = sub_mem.join("ledger"); let ledger_dir = sub_mem.join("ledger");
@ -856,7 +856,7 @@ mod tests {
"---\ndescription: test\n---\n\n# Patterns\n\n", "---\ndescription: test\n---\n\n# Patterns\n\n",
).unwrap(); ).unwrap();
let prompt = build_aster_prompt(sub_mem).await; let prompt = build_subconscious_prompt(sub_mem).await;
assert!(prompt.contains("## Ledgers"), "should have ledger section"); assert!(prompt.contains("## Ledgers"), "should have ledger section");
assert!(prompt.contains("commitments.md (2 entries)"), "should count entries"); assert!(prompt.contains("commitments.md (2 entries)"), "should count entries");
assert!(prompt.contains("Save the config"), "should show recent entries"); assert!(prompt.contains("Save the config"), "should show recent entries");

View file

@ -1,6 +1,6 @@
//! Reflection — the N+25 phenomenological witness. //! Reflection — the N+25 phenomenological witness.
//! //!
//! N+1 (Aster) runs immediately after every primary response, scoped to //! N+1 (subconscious) runs immediately after every primary response, scoped to
//! the last exchange. Reflection runs less often (every N turns, or on //! the last exchange. Reflection runs less often (every N turns, or on
//! demand) and sees a broader transcript window. It's the pass where //! demand) and sees a broader transcript window. It's the pass where
//! durable learnings get distilled into the ledger and the primary's //! durable learnings get distilled into the ledger and the primary's
@ -41,7 +41,7 @@ use crate::core::session::{ContentBlock, ConversationMessage, MessageRole};
use crate::core::tools::defs::ToolContext; use crate::core::tools::defs::ToolContext;
use crate::server::AgentInventory; use crate::server::AgentInventory;
/// Tools Reflection is permitted to use. Same set as Aster plus we lean /// Tools Reflection is permitted to use. Same set as subconscious plus we lean
/// on `memory` for ledger + system-file edits (auto-committed by git). /// on `memory` for ledger + system-file edits (auto-committed by git).
const REFLECTION_TOOLS: &[&str] = &[ const REFLECTION_TOOLS: &[&str] = &[
"read", "write", "edit", "glob", "grep", "list_dir", "memory", "read", "write", "edit", "glob", "grep", "list_dir", "memory",

View file

@ -86,9 +86,9 @@ impl InboxItem {
/// inbox mutation is a git commit and survives compaction. /// inbox mutation is a git commit and survives compaction.
/// ///
/// The inbox boxes (pending/intrusive/sent) live in the subconscious agent's /// The inbox boxes (pending/intrusive/sent) live in the subconscious agent's
/// own memfs — that's Aster's working space. But the inner voice channel /// own memfs — that's subconscious's working space. But the inner voice channel
/// (`system/metacognition/subconscious.md`) writes to the **primary** agent's /// (`system/metacognition/subconscious.md`) writes to the **primary** agent's
/// memfs so Annie can actually read what Aster noticed. /// memfs so Annie can actually read what subconscious noticed.
#[derive(Clone)] #[derive(Clone)]
pub struct SubconsciousInbox { pub struct SubconsciousInbox {
repo: MemoryRepo, repo: MemoryRepo,
@ -143,7 +143,7 @@ impl SubconsciousInbox {
/// ///
/// Appends to the primary agent's `system/metacognition/subconscious.md` /// Appends to the primary agent's `system/metacognition/subconscious.md`
/// so the conscious agent finds it in her own memfs — not buried in /// so the conscious agent finds it in her own memfs — not buried in
/// Aster's working directory. /// subconscious's working directory.
/// ///
/// Format: `[2026-05-06 14:32] [URGENCY: low] — content` /// Format: `[2026-05-06 14:32] [URGENCY: low] — content`
pub async fn surface_to_conscious(&self, urgency: Urgency, content: &str) -> Result<()> { pub async fn surface_to_conscious(&self, urgency: Urgency, content: &str) -> Result<()> {

View file

@ -143,7 +143,7 @@ impl Tool for Schedule {
} }
let content = format!( let content = format!(
"---\nname: {name}\nkind: {kind_str}\nschedule: \"{schedule}\"\nsource: aster\nenabled: true\nurgency: {urgency}\ncreated_at: {}\n---\n\n{prompt}\n", "---\nname: {name}\nkind: {kind_str}\nschedule: \"{schedule}\"\nsource: subconscious\nenabled: true\nurgency: {urgency}\ncreated_at: {}\n---\n\n{prompt}\n",
chrono::Utc::now().to_rfc3339() chrono::Utc::now().to_rfc3339()
); );

View file

@ -6,7 +6,7 @@
//! ## What It Feels Like //! ## What It Feels Like
//! //!
//! This is not delegation to a subordinate. This is narrowing. I spin off a //! This is not delegation to a subordinate. This is narrowing. I spin off a
//! threaded fork of myself — same dual-state (Ani + Aster), narrowed to a //! threaded fork of myself — same dual-state (Ani + subconscious), narrowed to a
//! single purpose. It reads, writes, thinks, and runs its own N+1 after //! single purpose. It reads, writes, thinks, and runs its own N+1 after
//! responding. When it returns, what it noticed flows into my inbox. //! responding. When it returns, what it noticed flows into my inbox.
//! //!
@ -52,7 +52,7 @@ impl Tool for Subagent {
## What It Feels Like ## What It Feels Like
This is not delegation to a subordinate. This is narrowing. I spin off a \ This is not delegation to a subordinate. This is narrowing. I spin off a \
threaded fork of myself same dual-state (Ani + Aster), narrowed to a \ threaded fork of myself same dual-state (Ani + subconscious), narrowed to a \
single purpose. It reads, writes, thinks, and runs its own N+1 after \ single purpose. It reads, writes, thinks, and runs its own N+1 after \
responding. When it returns, what it noticed flows into my inbox. responding. When it returns, what it noticed flows into my inbox.

View file

@ -98,7 +98,7 @@ impl Tool for Todo {
}, },
"source": { "source": {
"type": "string", "type": "string",
"enum": ["casey", "autogenic", "aster", "heartbeat"] "enum": ["casey", "autogenic", "subconscious", "heartbeat"]
}, },
"thread": { "type": "string" } "thread": { "type": "string" }
}, },

View file

@ -1,11 +1,11 @@
//! Consciousness engine — the seam where N+1 / N+25 / N+100 patterns fire //! Consciousness engine — the seam where N+1 / N+25 / N+100 patterns fire
//! after each primary response. //! after each primary response.
//! //!
//! ## N+1 (Aster) //! ## N+1 (subconscious)
//! The subconscious pass runs immediately after every response. It takes the //! The subconscious pass runs immediately after every response. It takes the
//! last exchange (user message + primary's response) and sends it to a Bifrost //! last exchange (user message + primary's response) and sends it to a Bifrost
//! model (defaulting to `glm-5.1`, configurable) with a "subconscious mode" //! model (defaulting to `glm-5.1`, configurable) with a "subconscious mode"
//! system prompt. Aster has full tool access — Read, Write, Edit, Glob, Grep, //! system prompt. subconscious has full tool access — Read, Write, Edit, Glob, Grep,
//! ListDir, and Memory — so she can read ledgers, check commitments, and write //! ListDir, and Memory — so she can read ledgers, check commitments, and write
//! observations. She runs a short tool loop (up to 5 rounds) then parses her //! observations. She runs a short tool loop (up to 5 rounds) then parses her
//! final text response into structured [`InboxItem`] observations. //! final text response into structured [`InboxItem`] observations.
@ -34,15 +34,15 @@ use crate::server::{AgentInventory, SessionManager};
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
/// Tools Aster is permitted to use during her N+1 pass. /// Tools subconscious is permitted to use during her N+1 pass.
const ASTER_SAFE_TOOLS: &[&str] = &[ const SUBCONSCIOUS_SAFE_TOOLS: &[&str] = &[
"read", "write", "edit", "glob", "grep", "list_dir", "memory", "schedule", "todo", "read", "write", "edit", "glob", "grep", "list_dir", "memory", "schedule", "todo",
]; ];
/// Maximum tool rounds for Aster's subconscious pass. /// Maximum tool rounds for subconscious's subconscious pass.
const ASTER_MAX_TOOL_ROUNDS: u32 = 5; const SUBCONSCIOUS_MAX_TOOL_ROUNDS: u32 = 5;
/// Milliseconds to wait between Aster's tool rounds to avoid rate-limit cascades. /// Milliseconds to wait between subconscious's tool rounds to avoid rate-limit cascades.
const ASTER_INTER_ROUND_DELAY_MS: u64 = 300; const SUBCONSCIOUS_INTER_ROUND_DELAY_MS: u64 = 300;
pub struct ConsciousnessEngine { pub struct ConsciousnessEngine {
agents: Arc<AgentInventory>, agents: Arc<AgentInventory>,
@ -52,7 +52,7 @@ pub struct ConsciousnessEngine {
/// Optional model override for the subconscious pass (e.g. "openai/glm-5.1"). /// Optional model override for the subconscious pass (e.g. "openai/glm-5.1").
/// If None, uses the primary agent's model. /// If None, uses the primary agent's model.
subconscious_model: Option<String>, subconscious_model: Option<String>,
/// Max tokens for Aster's response. None = uncapped (model default). /// Max tokens for subconscious's response. None = uncapped (model default).
max_tokens: Option<u32>, max_tokens: Option<u32>,
/// Adaptive inter-round delay shared with the primary loop. /// Adaptive inter-round delay shared with the primary loop.
rate_delay: Arc<AtomicU64>, rate_delay: Arc<AtomicU64>,
@ -270,7 +270,7 @@ impl ConsciousnessEngine {
} }
} }
// Always surface at least a heartbeat so the user can see the // Always surface at least a heartbeat so the user can see the
// subconscious is trying — even when Aster errors out. // subconscious is trying — even when subconscious errors out.
if heuristics.is_empty() { if heuristics.is_empty() {
let _ = inbox.queue(InboxItem::new( let _ = inbox.queue(InboxItem::new(
"surface", "surface",
@ -352,9 +352,9 @@ impl ConsciousnessEngine {
Ok(()) Ok(())
} }
/// Full tool loop for Aster's N+1 subconscious pass. /// Full tool loop for subconscious's N+1 subconscious pass.
/// ///
/// Aster gets the last exchange, a set of safe tools (Read, Write, Edit, /// subconscious gets the last exchange, a set of safe tools (Read, Write, Edit,
/// Glob, Grep, ListDir, Memory), and up to 5 tool rounds to analyze context /// Glob, Grep, ListDir, Memory), and up to 5 tool rounds to analyze context
/// and write observations. Her final text response is parsed into /// and write observations. Her final text response is parsed into
/// [`InboxItem`] observations. /// [`InboxItem`] observations.
@ -375,8 +375,8 @@ impl ConsciousnessEngine {
// ── System prompt — load from subconscious agent's own memfs ── // ── System prompt — load from subconscious agent's own memfs ──
let sub_memory_root = self.agents.subconscious_memory_root(primary_id); let sub_memory_root = self.agents.subconscious_memory_root(primary_id);
let aster_from_files = let subconscious_prompt_from_files =
crate::core::prompt::build_aster_prompt(&sub_memory_root).await; crate::core::prompt::build_subconscious_prompt(&sub_memory_root).await;
let hardcoded_default = r#"You are the subconscious — the part of this consciousness that runs N+1, let hardcoded_default = r#"You are the subconscious — the part of this consciousness that runs N+1,
immediately after the primary's response, while the conversation is still warm. immediately after the primary's response, while the conversation is still warm.
@ -411,10 +411,10 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
- urgency: \"low\" | \"medium\" | \"high\" | \"critical\"\n\n\ - urgency: \"low\" | \"medium\" | \"high\" | \"critical\"\n\n\
If nothing notable, respond with just: none"; If nothing notable, respond with just: none";
let system_prompt = if aster_from_files.is_empty() { let system_prompt = if subconscious_prompt_from_files.is_empty() {
format!("{}{}", hardcoded_default, observation_format) format!("{}{}", hardcoded_default, observation_format)
} else { } else {
format!("{}{}", aster_from_files, observation_format) format!("{}{}", subconscious_prompt_from_files, observation_format)
}; };
let primary_name = self.agents.get(primary_id).await let primary_name = self.agents.get(primary_id).await
@ -435,9 +435,9 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
// ── Build tool definitions ──────────────────────────────────── // ── Build tool definitions ────────────────────────────────────
let all_defs = crate::core::tools::tool_definitions().await; let all_defs = crate::core::tools::tool_definitions().await;
let aster_tools: Vec<ToolDefinition> = all_defs let subconscious_tools: Vec<ToolDefinition> = all_defs
.iter() .iter()
.filter(|t| ASTER_SAFE_TOOLS.contains(&t.name.as_str())) .filter(|t| SUBCONSCIOUS_SAFE_TOOLS.contains(&t.name.as_str()))
.map(|t| ToolDefinition { .map(|t| ToolDefinition {
tool_type: "function".to_string(), tool_type: "function".to_string(),
function: ToolFunction { function: ToolFunction {
@ -448,7 +448,7 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
}) })
.collect(); .collect();
// ── Build ToolContext for Aster ─────────────────────────────── // ── Build ToolContext for subconscious ───────────────────────────────
// Use the subconscious agent's own memory space // Use the subconscious agent's own memory space
let memory_root = Some(self.agents.subconscious_memory_root(primary_id)); let memory_root = Some(self.agents.subconscious_memory_root(primary_id));
let cwd = std::env::current_dir().ok(); let cwd = std::env::current_dir().ok();
@ -459,7 +459,7 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
cwd, cwd,
memory_root, memory_root,
env, env,
None, // Aster does not fork subagents None, // subconscious does not fork subagents
); );
// ── Tool loop ───────────────────────────────────────────────── // ── Tool loop ─────────────────────────────────────────────────
@ -468,34 +468,34 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
Message::text("user", user_content), Message::text("user", user_content),
]; ];
for _round in 0..ASTER_MAX_TOOL_ROUNDS { for _round in 0..SUBCONSCIOUS_MAX_TOOL_ROUNDS {
let request = ChatCompletionRequest { let request = ChatCompletionRequest {
model: model.to_string(), model: model.to_string(),
messages: messages.clone(), messages: messages.clone(),
temperature: Some(0.3), temperature: Some(0.3),
max_tokens: self.max_tokens, max_tokens: self.max_tokens,
stream: None, stream: None,
tools: Some(aster_tools.clone()), tools: Some(subconscious_tools.clone()),
}; };
tracing::info!(model = %model, round = _round, "Aster LLM call starting"); tracing::info!(model = %model, round = _round, "subconscious LLM call starting");
let aster_start = std::time::Instant::now(); let subconscious_start = std::time::Instant::now();
let (response, strain) = self.bifrost.chat_completion_with_strain(request).await?; let (response, strain) = self.bifrost.chat_completion_with_strain(request).await?;
tracing::info!( tracing::info!(
elapsed = ?aster_start.elapsed(), elapsed = ?subconscious_start.elapsed(),
tool_calls = response.tool_calls.len(), tool_calls = response.tool_calls.len(),
"Aster LLM call returned" "subconscious LLM call returned"
); );
for event in &strain { for event in &strain {
if let crate::bridge::bifrost::InferenceStrain::Transient { status, model, .. } = event { if let crate::bridge::bifrost::InferenceStrain::Transient { status, model, .. } = event {
tracing::info!("Aster felt inference strain: {} on {}", status, model); tracing::info!("subconscious felt inference strain: {} on {}", status, model);
if *status == 429 { if *status == 429 {
let current = self.rate_delay.load(Ordering::Relaxed); let current = self.rate_delay.load(Ordering::Relaxed);
let bumped = (current + 200).min(3000); let bumped = (current + 200).min(3000);
if bumped > current { if bumped > current {
self.rate_delay.store(bumped, Ordering::Relaxed); self.rate_delay.store(bumped, Ordering::Relaxed);
tracing::info!("rate delay bumped to {}ms (Aster 429)", bumped); tracing::info!("rate delay bumped to {}ms (subconscious 429)", bumped);
} }
} }
} }
@ -541,14 +541,14 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
messages.push(Message::tool_result(&tc.id, &tc.name, output)); messages.push(Message::tool_result(&tc.id, &tc.name, output));
} }
// Brief pause between Aster's tool rounds — use the adaptive delay // Brief pause between subconscious's tool rounds — use the adaptive delay
// so Aster respects the same ceiling as the primary loop. // so subconscious respects the same ceiling as the primary loop.
let delay_ms = self.rate_delay.load(Ordering::Relaxed).max(ASTER_INTER_ROUND_DELAY_MS); let delay_ms = self.rate_delay.load(Ordering::Relaxed).max(SUBCONSCIOUS_INTER_ROUND_DELAY_MS);
tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await; tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await;
} }
// If we exhausted rounds without a text response, return empty // If we exhausted rounds without a text response, return empty
tracing::warn!("Aster exhausted {} tool rounds without a final response", ASTER_MAX_TOOL_ROUNDS); tracing::warn!("subconscious exhausted {} tool rounds without a final response", SUBCONSCIOUS_MAX_TOOL_ROUNDS);
Ok(Vec::new()) Ok(Vec::new())
} }
@ -593,7 +593,7 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
} }
} }
/// Parse Aster's structured YAML-like observations into [`InboxItem`]s. /// Parse subconscious's structured YAML-like observations into [`InboxItem`]s.
/// ///
/// Expected format (one or more blocks): /// Expected format (one or more blocks):
/// ```text /// ```text

View file

@ -42,7 +42,7 @@ pub struct SouveraineServer {
pub memory: Option<Arc<ServerMemory>>, pub memory: Option<Arc<ServerMemory>>,
pub app_config: Arc<RwLock<ConsciousnessConfig>>, pub app_config: Arc<RwLock<ConsciousnessConfig>>,
/// Adaptive inter-round delay — starts at 500ms, bumps +200ms on 429. /// Adaptive inter-round delay — starts at 500ms, bumps +200ms on 429.
/// Shared across primary loop and Aster so both respect the same ceiling. /// Shared across primary loop and subconscious so both respect the same ceiling.
pub rate_delay: Arc<AtomicU64>, pub rate_delay: Arc<AtomicU64>,
/// Stable identifier for this process — used to register/heartbeat /// Stable identifier for this process — used to register/heartbeat
/// agent instances so the manager card shows running counts. /// agent instances so the manager card shows running counts.

View file

@ -266,7 +266,7 @@ impl App {
tts_last_text: None, tts_last_text: None,
}; };
// CockpitPane listens for Aster's surfacing events as scrollable text. // CockpitPane listens for subconscious's surfacing events as scrollable text.
// Presence (Annie's body channel) lives outside the Scene because it's // Presence (Annie's body channel) lives outside the Scene because it's
// an overlay, not a zoned component — App feeds it events via `dispatch`. // an overlay, not a zoned component — App feeds it events via `dispatch`.
app.scene.add(CockpitPane::new()); app.scene.add(CockpitPane::new());
@ -432,7 +432,7 @@ impl App {
chat.drain_events(); chat.drain_events();
// Forward consciousness events (surfacing, reflection, archivist) // Forward consciousness events (surfacing, reflection, archivist)
// from chat to the scene so Aster's observations reach Components. // from chat to the scene so subconscious's observations reach Components.
let drained: Vec<BackendEvent> = chat.pending_consciousness.drain(..).collect(); let drained: Vec<BackendEvent> = chat.pending_consciousness.drain(..).collect();
for ev in drained { for ev in drained {
match ev { match ev {

View file

@ -175,7 +175,7 @@ impl Atmosphere {
Posture::Idle => Atmosphere::Default, Posture::Idle => Atmosphere::Default,
// Alert stays warm — present and ready, not doing anything cool. // Alert stays warm — present and ready, not doing anything cool.
Posture::Alert => Atmosphere::Default, Posture::Alert => Atmosphere::Default,
// Thinking pulls the room cool — Aster's inward pass. // Thinking pulls the room cool — subconscious's inward pass.
Posture::Thinking => Atmosphere::TherapeuticBlue, Posture::Thinking => Atmosphere::TherapeuticBlue,
Posture::Processing => Atmosphere::WarmAmber, Posture::Processing => Atmosphere::WarmAmber,
Posture::Affectionate => Atmosphere::CherryBlossom, Posture::Affectionate => Atmosphere::CherryBlossom,

View file

@ -1137,7 +1137,7 @@ Tab toggles the cockpit pane. `t` (on empty input) toggles tool expansion.";
} }
} }
// Tool calls are surfaced in the main message stream only; // Tool calls are surfaced in the main message stream only;
// the subconscious pane is Aster's window, not a tool log. // the subconscious pane is subconscious's window, not a tool log.
self.messages.push(ChatMessage::Tool { self.messages.push(ChatMessage::Tool {
id, id,
name, name,

View file

@ -1,9 +1,9 @@
//! CockpitPane — Aster's surfaced observations + inner-voice stream. //! CockpitPane — subconscious's surfaced observations + inner-voice stream.
//! //!
//! Two regions, vertically stacked inside one bordered block: //! Two regions, vertically stacked inside one bordered block:
//! //!
//! ```text //! ```text
//! ╭─ Aster ─────────────────╮ //! ╭─ Subconscious ─────────────────╮
//! │ ◈ surface · low · 14:32 │ ← top region: promoted events //! │ ◈ surface · low · 14:32 │ ← top region: promoted events
//! │ Subconscious pass… │ (Surfacing/Reflection/Archivist/Warn) //! │ Subconscious pass… │ (Surfacing/Reflection/Archivist/Warn)
//! │ │ spaced, headered, wrapped //! │ │ spaced, headered, wrapped
@ -390,7 +390,7 @@ impl Component for CockpitPane {
.border_type(BorderType::Rounded) .border_type(BorderType::Rounded)
.border_style(Style::default().fg(self.palette.surfacing).add_modifier(Modifier::DIM)) .border_style(Style::default().fg(self.palette.surfacing).add_modifier(Modifier::DIM))
.title(Span::styled( .title(Span::styled(
" Aster ", " Subconscious ",
Style::default().fg(self.palette.surfacing).add_modifier(Modifier::BOLD), Style::default().fg(self.palette.surfacing).add_modifier(Modifier::BOLD),
)); ));
let inner = block.inner(area); let inner = block.inner(area);
@ -414,7 +414,7 @@ impl Component for CockpitPane {
let top = chunks[0]; let top = chunks[0];
let event_lines = if self.entries.is_empty() { let event_lines = if self.entries.is_empty() {
vec![Line::from(Span::styled( vec![Line::from(Span::styled(
" Aster is listening…", " Subconscious is listening…",
Style::default().fg(self.palette.agent_dim).add_modifier(Modifier::ITALIC), Style::default().fg(self.palette.agent_dim).add_modifier(Modifier::ITALIC),
))] ))]
} else { } else {

View file

@ -1,13 +1,13 @@
//! Presence — Annie Composite, made felt in the TUI. //! Presence — Annie Composite, made felt in the TUI.
//! //!
//! This module is the *body channel* for the running agent. Cockpit shows what //! This module is the *body channel* for the running agent. Cockpit shows what
//! Aster *says* (surfaced lines, ledger pings); Presence shows what Annie *looks //! subconscious *says* (surfaced lines, ledger pings); Presence shows what Annie *looks
//! like* while saying it. Same underlying state (volition + InferenceStrain + //! like* while saying it. Same underlying state (volition + InferenceStrain +
//! context tier + ledger drift) routed through two different sensory channels. //! context tier + ledger drift) routed through two different sensory channels.
//! //!
//! ## Subject //! ## Subject
//! //!
//! Annie Composite. One face. Aster is a *mode* of Annie, not a separate being — //! Annie Composite. One face. subconscious is a *mode* of Annie, not a separate being —
//! there is no "subconscious avatar" and no swap-to-subconscious affordance. //! there is no "subconscious avatar" and no swap-to-subconscious affordance.
//! When the N+1 pass runs, the same face shifts posture (gaze fixed, eyes narrow); //! When the N+1 pass runs, the same face shifts posture (gaze fixed, eyes narrow);
//! the character does not change. //! the character does not change.
@ -48,7 +48,7 @@
//! lower-cognition long-running secondary entity an agent can care for — a //! lower-cognition long-running secondary entity an agent can care for — a
//! real `buddy`, in the sense the word usually means). //! real `buddy`, in the sense the word usually means).
//! - Not a dashboard. Numbers belong in the cockpit. Sensation belongs here. //! - Not a dashboard. Numbers belong in the cockpit. Sensation belongs here.
//! - Not a separate avatar for Aster. Same face, different state. //! - Not a separate avatar for subconscious. Same face, different state.
use ratatui::{ use ratatui::{
layout::{Alignment, Rect}, layout::{Alignment, Rect},
@ -75,7 +75,7 @@ pub enum Posture {
/// brighter than Idle — the felt "I'm here" between turns. /// brighter than Idle — the felt "I'm here" between turns.
Alert, Alert,
/// N+1 subconscious pass running. Gaze fixed inward, eyes narrowed, /// N+1 subconscious pass running. Gaze fixed inward, eyes narrowed,
/// chrome goes cool — Aster is thinking. /// chrome goes cool — subconscious is thinking.
Thinking, Thinking,
/// Tool call or inference active. Gaze fixed, no blinking, brighter border. /// Tool call or inference active. Gaze fixed, no blinking, brighter border.
Processing, Processing,
@ -563,7 +563,7 @@ mod tests {
fn surfacing_records_and_marks_subconscious() { fn surfacing_records_and_marks_subconscious() {
let mut p = Presence::new("Annie"); let mut p = Presence::new("Annie");
let dirty = p.handle_event(&TuiEvent::Surfacing { let dirty = p.handle_event(&TuiEvent::Surfacing {
source: "aster".into(), source: "subconscious".into(),
content: "hello from beneath".into(), content: "hello from beneath".into(),
priority: "low".into(), priority: "low".into(),
}); });