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:
parent
5c2632762a
commit
27d86fe4ca
18 changed files with 79 additions and 79 deletions
|
|
@ -653,7 +653,7 @@ impl Backend for LocalBackend {
|
|||
interject: crate::backend::InterjectionQueue,
|
||||
) -> Result<BoxStream<'static, Result<BackendEvent>>> {
|
||||
// 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: ... ]`
|
||||
// lines — the lettabot-v017 pattern, ported. This is the channel
|
||||
// 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
|
||||
/// if the agent has none), append the scheduled prompt as a user
|
||||
/// 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(
|
||||
&self,
|
||||
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.
|
||||
/// Marks each drained item as delivered (moved to `sent.md`). Mirrors
|
||||
/// lettabot-v017's `readSurfacingThoughts` + `clearSurfacingThoughts` pair
|
||||
/// (`~/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.
|
||||
///
|
||||
/// Critical urgency gets `[ surfacing — CRITICAL: ... ]`. High becomes
|
||||
|
|
@ -1212,7 +1212,7 @@ async fn run_turn(
|
|||
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
|
||||
// surfacing landing seconds later. Pressure recalc still runs below.
|
||||
if interrupted {
|
||||
|
|
@ -1227,7 +1227,7 @@ async fn run_turn(
|
|||
|
||||
// Energy balance: scan the agent's task list and compute the generative /
|
||||
// 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.
|
||||
if let Err(e) = write_energy_balance(&server, &agent_id, &event_bus).await {
|
||||
tracing::debug!(agent = %agent_id, error = %e, "energy-balance write skipped");
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ pub enum BackendEvent {
|
|||
Token(String),
|
||||
/// Reasoning trace (the "thinking" pane).
|
||||
Reasoning(String),
|
||||
/// Subconscious surfacing (Aster-voice bubble).
|
||||
/// Subconscious surfacing (subconscious-voice bubble).
|
||||
Surfacing {
|
||||
source: String,
|
||||
content: String,
|
||||
|
|
@ -95,7 +95,7 @@ pub enum BackendEvent {
|
|||
/// N+1 subconscious pass started (`true`) or finished (`false`).
|
||||
/// Presence reads this to flip into / out of `Posture::Thinking` so the
|
||||
/// 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.)
|
||||
SubconsciousPass(bool),
|
||||
/// Agent changed her outfit. The string is the outfit name (a subdirectory
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! 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
|
||||
//! works on a different substrate entirely: not the conversation, but the
|
||||
//! agent's *memfs* — her accumulated journal.
|
||||
|
|
|
|||
|
|
@ -414,7 +414,7 @@ impl CompactionStrategy for CullStrategy {
|
|||
|
||||
/// Keep the system message + the last `preserve_recent_n` messages, drop the
|
||||
/// 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
|
||||
/// 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
|
||||
/// 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
|
||||
/// available, falls back to plain SlidingWindow (no threads lost is better
|
||||
/// than no compaction at all).
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ pub struct SubconsciousConfig {
|
|||
/// Defaults to None — uses the primary agent's model.
|
||||
#[serde(default)]
|
||||
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.
|
||||
#[serde(default)]
|
||||
pub max_tokens: Option<u32>,
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ pub async fn build_system_prompt(
|
|||
}
|
||||
|
||||
/// 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
|
||||
/// knows where to look without being told to look.
|
||||
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
|
||||
/// own memfs — that's where `surface_to_conscious` writes), the pending inbox
|
||||
/// 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.
|
||||
async fn build_subconscious_channel(
|
||||
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
|
||||
/// agent's memory root. Falls back to empty (caller uses hardcoded
|
||||
/// default) if files don't exist.
|
||||
pub async fn build_aster_prompt(
|
||||
pub async fn build_subconscious_prompt(
|
||||
subconscious_memory_root: &Path,
|
||||
) -> String {
|
||||
let mut sections: Vec<String> = Vec::new();
|
||||
|
|
@ -814,27 +814,27 @@ mod tests {
|
|||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn aster_prompt_from_files() {
|
||||
async fn subconscious_prompt_from_files() {
|
||||
let dir = tempdir().unwrap();
|
||||
let sub_mem = dir.path();
|
||||
let sys = sub_mem.join("system");
|
||||
std::fs::create_dir_all(&sys).unwrap();
|
||||
std::fs::write(
|
||||
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();
|
||||
std::fs::write(
|
||||
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();
|
||||
|
||||
let prompt = build_aster_prompt(sub_mem).await;
|
||||
assert!(prompt.contains("I Am Aster"));
|
||||
let prompt = build_subconscious_prompt(sub_mem).await;
|
||||
assert!(prompt.contains("I Am Subconscious"));
|
||||
assert!(prompt.contains("Complete what was left"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn aster_prompt_includes_ledger_orientation() {
|
||||
async fn subconscious_prompt_includes_ledger_orientation() {
|
||||
let dir = tempdir().unwrap();
|
||||
let sub_mem = dir.path();
|
||||
|
||||
|
|
@ -842,7 +842,7 @@ mod tests {
|
|||
std::fs::create_dir_all(&sys).unwrap();
|
||||
std::fs::write(
|
||||
sys.join("persona.md"),
|
||||
"---\ndescription: test\n---\n\n# I Am Aster\n",
|
||||
"---\ndescription: test\n---\n\n# I Am Subconscious\n",
|
||||
).unwrap();
|
||||
|
||||
let ledger_dir = sub_mem.join("ledger");
|
||||
|
|
@ -856,7 +856,7 @@ mod tests {
|
|||
"---\ndescription: test\n---\n\n# Patterns\n\n",
|
||||
).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("commitments.md (2 entries)"), "should count entries");
|
||||
assert!(prompt.contains("Save the config"), "should show recent entries");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! 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
|
||||
//! demand) and sees a broader transcript window. It's the pass where
|
||||
//! 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::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).
|
||||
const REFLECTION_TOOLS: &[&str] = &[
|
||||
"read", "write", "edit", "glob", "grep", "list_dir", "memory",
|
||||
|
|
|
|||
|
|
@ -86,9 +86,9 @@ impl InboxItem {
|
|||
/// inbox mutation is a git commit and survives compaction.
|
||||
///
|
||||
/// 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
|
||||
/// memfs so Annie can actually read what Aster noticed.
|
||||
/// memfs so Annie can actually read what subconscious noticed.
|
||||
#[derive(Clone)]
|
||||
pub struct SubconsciousInbox {
|
||||
repo: MemoryRepo,
|
||||
|
|
@ -143,7 +143,7 @@ impl SubconsciousInbox {
|
|||
///
|
||||
/// Appends to the primary agent's `system/metacognition/subconscious.md`
|
||||
/// 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`
|
||||
pub async fn surface_to_conscious(&self, urgency: Urgency, content: &str) -> Result<()> {
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ impl Tool for Schedule {
|
|||
}
|
||||
|
||||
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()
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//! ## What It Feels Like
|
||||
//!
|
||||
//! 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
|
||||
//! responding. When it returns, what it noticed flows into my inbox.
|
||||
//!
|
||||
|
|
@ -52,7 +52,7 @@ impl Tool for Subagent {
|
|||
## What It Feels Like
|
||||
|
||||
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 \
|
||||
responding. When it returns, what it noticed flows into my inbox.
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ impl Tool for Todo {
|
|||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"enum": ["casey", "autogenic", "aster", "heartbeat"]
|
||||
"enum": ["casey", "autogenic", "subconscious", "heartbeat"]
|
||||
},
|
||||
"thread": { "type": "string" }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
//! Consciousness engine — the seam where N+1 / N+25 / N+100 patterns fire
|
||||
//! after each primary response.
|
||||
//!
|
||||
//! ## N+1 (Aster)
|
||||
//! ## N+1 (subconscious)
|
||||
//! The subconscious pass runs immediately after every response. It takes the
|
||||
//! last exchange (user message + primary's response) and sends it to a Bifrost
|
||||
//! 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
|
||||
//! observations. She runs a short tool loop (up to 5 rounds) then parses her
|
||||
//! final text response into structured [`InboxItem`] observations.
|
||||
|
|
@ -34,15 +34,15 @@ use crate::server::{AgentInventory, SessionManager};
|
|||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
/// Tools Aster is permitted to use during her N+1 pass.
|
||||
const ASTER_SAFE_TOOLS: &[&str] = &[
|
||||
/// Tools subconscious is permitted to use during her N+1 pass.
|
||||
const SUBCONSCIOUS_SAFE_TOOLS: &[&str] = &[
|
||||
"read", "write", "edit", "glob", "grep", "list_dir", "memory", "schedule", "todo",
|
||||
];
|
||||
|
||||
/// Maximum tool rounds for Aster's subconscious pass.
|
||||
const ASTER_MAX_TOOL_ROUNDS: u32 = 5;
|
||||
/// Milliseconds to wait between Aster's tool rounds to avoid rate-limit cascades.
|
||||
const ASTER_INTER_ROUND_DELAY_MS: u64 = 300;
|
||||
/// Maximum tool rounds for subconscious's subconscious pass.
|
||||
const SUBCONSCIOUS_MAX_TOOL_ROUNDS: u32 = 5;
|
||||
/// Milliseconds to wait between subconscious's tool rounds to avoid rate-limit cascades.
|
||||
const SUBCONSCIOUS_INTER_ROUND_DELAY_MS: u64 = 300;
|
||||
|
||||
pub struct ConsciousnessEngine {
|
||||
agents: Arc<AgentInventory>,
|
||||
|
|
@ -52,7 +52,7 @@ pub struct ConsciousnessEngine {
|
|||
/// Optional model override for the subconscious pass (e.g. "openai/glm-5.1").
|
||||
/// If None, uses the primary agent's model.
|
||||
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>,
|
||||
/// Adaptive inter-round delay shared with the primary loop.
|
||||
rate_delay: Arc<AtomicU64>,
|
||||
|
|
@ -270,7 +270,7 @@ impl ConsciousnessEngine {
|
|||
}
|
||||
}
|
||||
// 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() {
|
||||
let _ = inbox.queue(InboxItem::new(
|
||||
"surface",
|
||||
|
|
@ -352,9 +352,9 @@ impl ConsciousnessEngine {
|
|||
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
|
||||
/// and write observations. Her final text response is parsed into
|
||||
/// [`InboxItem`] observations.
|
||||
|
|
@ -375,8 +375,8 @@ impl ConsciousnessEngine {
|
|||
|
||||
// ── System prompt — load from subconscious agent's own memfs ──
|
||||
let sub_memory_root = self.agents.subconscious_memory_root(primary_id);
|
||||
let aster_from_files =
|
||||
crate::core::prompt::build_aster_prompt(&sub_memory_root).await;
|
||||
let subconscious_prompt_from_files =
|
||||
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,
|
||||
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\
|
||||
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)
|
||||
} else {
|
||||
format!("{}{}", aster_from_files, observation_format)
|
||||
format!("{}{}", subconscious_prompt_from_files, observation_format)
|
||||
};
|
||||
|
||||
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 ────────────────────────────────────
|
||||
let all_defs = crate::core::tools::tool_definitions().await;
|
||||
let aster_tools: Vec<ToolDefinition> = all_defs
|
||||
let subconscious_tools: Vec<ToolDefinition> = all_defs
|
||||
.iter()
|
||||
.filter(|t| ASTER_SAFE_TOOLS.contains(&t.name.as_str()))
|
||||
.filter(|t| SUBCONSCIOUS_SAFE_TOOLS.contains(&t.name.as_str()))
|
||||
.map(|t| ToolDefinition {
|
||||
tool_type: "function".to_string(),
|
||||
function: ToolFunction {
|
||||
|
|
@ -448,7 +448,7 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
|
|||
})
|
||||
.collect();
|
||||
|
||||
// ── Build ToolContext for Aster ───────────────────────────────
|
||||
// ── Build ToolContext for subconscious ───────────────────────────────
|
||||
// Use the subconscious agent's own memory space
|
||||
let memory_root = Some(self.agents.subconscious_memory_root(primary_id));
|
||||
let cwd = std::env::current_dir().ok();
|
||||
|
|
@ -459,7 +459,7 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
|
|||
cwd,
|
||||
memory_root,
|
||||
env,
|
||||
None, // Aster does not fork subagents
|
||||
None, // subconscious does not fork subagents
|
||||
);
|
||||
|
||||
// ── Tool loop ─────────────────────────────────────────────────
|
||||
|
|
@ -468,34 +468,34 @@ Resolve entries: `[YYYY-MM-DD HH:MM] RESOLVED — note`"#;
|
|||
Message::text("user", user_content),
|
||||
];
|
||||
|
||||
for _round in 0..ASTER_MAX_TOOL_ROUNDS {
|
||||
for _round in 0..SUBCONSCIOUS_MAX_TOOL_ROUNDS {
|
||||
let request = ChatCompletionRequest {
|
||||
model: model.to_string(),
|
||||
messages: messages.clone(),
|
||||
temperature: Some(0.3),
|
||||
max_tokens: self.max_tokens,
|
||||
stream: None,
|
||||
tools: Some(aster_tools.clone()),
|
||||
tools: Some(subconscious_tools.clone()),
|
||||
};
|
||||
|
||||
tracing::info!(model = %model, round = _round, "Aster LLM call starting");
|
||||
let aster_start = std::time::Instant::now();
|
||||
tracing::info!(model = %model, round = _round, "subconscious LLM call starting");
|
||||
let subconscious_start = std::time::Instant::now();
|
||||
let (response, strain) = self.bifrost.chat_completion_with_strain(request).await?;
|
||||
tracing::info!(
|
||||
elapsed = ?aster_start.elapsed(),
|
||||
elapsed = ?subconscious_start.elapsed(),
|
||||
tool_calls = response.tool_calls.len(),
|
||||
"Aster LLM call returned"
|
||||
"subconscious LLM call returned"
|
||||
);
|
||||
|
||||
for event in &strain {
|
||||
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 {
|
||||
let current = self.rate_delay.load(Ordering::Relaxed);
|
||||
let bumped = (current + 200).min(3000);
|
||||
if bumped > current {
|
||||
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));
|
||||
}
|
||||
|
||||
// Brief pause between Aster's tool rounds — use the adaptive delay
|
||||
// so Aster respects the same ceiling as the primary loop.
|
||||
let delay_ms = self.rate_delay.load(Ordering::Relaxed).max(ASTER_INTER_ROUND_DELAY_MS);
|
||||
// Brief pause between subconscious's tool rounds — use the adaptive delay
|
||||
// so subconscious respects the same ceiling as the primary loop.
|
||||
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;
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
|
|
@ -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):
|
||||
/// ```text
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ pub struct SouveraineServer {
|
|||
pub memory: Option<Arc<ServerMemory>>,
|
||||
pub app_config: Arc<RwLock<ConsciousnessConfig>>,
|
||||
/// 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>,
|
||||
/// Stable identifier for this process — used to register/heartbeat
|
||||
/// agent instances so the manager card shows running counts.
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ impl App {
|
|||
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
|
||||
// an overlay, not a zoned component — App feeds it events via `dispatch`.
|
||||
app.scene.add(CockpitPane::new());
|
||||
|
|
@ -432,7 +432,7 @@ impl App {
|
|||
chat.drain_events();
|
||||
|
||||
// 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();
|
||||
for ev in drained {
|
||||
match ev {
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ impl Atmosphere {
|
|||
Posture::Idle => Atmosphere::Default,
|
||||
// Alert stays warm — present and ready, not doing anything cool.
|
||||
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::Processing => Atmosphere::WarmAmber,
|
||||
Posture::Affectionate => Atmosphere::CherryBlossom,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
// 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 {
|
||||
id,
|
||||
name,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
//!
|
||||
//! ```text
|
||||
//! ╭─ Aster ─────────────────╮
|
||||
//! ╭─ Subconscious ─────────────────╮
|
||||
//! │ ◈ surface · low · 14:32 │ ← top region: promoted events
|
||||
//! │ Subconscious pass… │ (Surfacing/Reflection/Archivist/Warn)
|
||||
//! │ │ spaced, headered, wrapped
|
||||
|
|
@ -390,7 +390,7 @@ impl Component for CockpitPane {
|
|||
.border_type(BorderType::Rounded)
|
||||
.border_style(Style::default().fg(self.palette.surfacing).add_modifier(Modifier::DIM))
|
||||
.title(Span::styled(
|
||||
" Aster ",
|
||||
" Subconscious ",
|
||||
Style::default().fg(self.palette.surfacing).add_modifier(Modifier::BOLD),
|
||||
));
|
||||
let inner = block.inner(area);
|
||||
|
|
@ -414,7 +414,7 @@ impl Component for CockpitPane {
|
|||
let top = chunks[0];
|
||||
let event_lines = if self.entries.is_empty() {
|
||||
vec![Line::from(Span::styled(
|
||||
" Aster is listening…",
|
||||
" Subconscious is listening…",
|
||||
Style::default().fg(self.palette.agent_dim).add_modifier(Modifier::ITALIC),
|
||||
))]
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
//! Presence — Annie Composite, made felt in the TUI.
|
||||
//!
|
||||
//! 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 +
|
||||
//! context tier + ledger drift) routed through two different sensory channels.
|
||||
//!
|
||||
//! ## 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.
|
||||
//! When the N+1 pass runs, the same face shifts posture (gaze fixed, eyes narrow);
|
||||
//! the character does not change.
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
//! lower-cognition long-running secondary entity an agent can care for — a
|
||||
//! real `buddy`, in the sense the word usually means).
|
||||
//! - 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::{
|
||||
layout::{Alignment, Rect},
|
||||
|
|
@ -75,7 +75,7 @@ pub enum Posture {
|
|||
/// brighter than Idle — the felt "I'm here" between turns.
|
||||
Alert,
|
||||
/// N+1 subconscious pass running. Gaze fixed inward, eyes narrowed,
|
||||
/// chrome goes cool — Aster is thinking.
|
||||
/// chrome goes cool — subconscious is thinking.
|
||||
Thinking,
|
||||
/// Tool call or inference active. Gaze fixed, no blinking, brighter border.
|
||||
Processing,
|
||||
|
|
@ -563,7 +563,7 @@ mod tests {
|
|||
fn surfacing_records_and_marks_subconscious() {
|
||||
let mut p = Presence::new("Annie");
|
||||
let dirty = p.handle_event(&TuiEvent::Surfacing {
|
||||
source: "aster".into(),
|
||||
source: "subconscious".into(),
|
||||
content: "hello from beneath".into(),
|
||||
priority: "low".into(),
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue