๐Ÿ“ components/memory/ ยท 05_auto_dreaming_controls.md

Chapter 5: Auto-Dreaming Controls

๐Ÿ“„ components/memory/05_auto_dreaming_controls.md

Chapter 5: Auto-Dreaming Controls

Welcome to the final chapter of our memory system tutorial!

In the previous chapter, Path Contextualization, we learned how to make file paths look friendly and readable.

Now, we tackle the most advanced concept: Auto-Dreaming.

In our system, "Dreaming" is a background process. Just as humans consolidate memories while they sleep, our AI consolidates scattered notes into organized documentation when it is idle.

This chapter explains how we build the Control Panel for this robotic process.

The Problem: The Invisible Worker

Imagine you have hired a robotic archivist to clean your office at night.

  1. The Switch: You need a way to tell it not to clean (maybe you are working late).
  2. The Status: You need to know if it is currently cleaning (so you don't trip over it).
  3. The Log: You want to know when it last finished cleaning.

Without a UI, this process happens invisibly in the background. You wouldn't know if it was working, broken, or turned off.

The Solution: The Status Dashboard

We add a specific line to our Terminal Interaction Layer that acts as a comprehensive dashboard.

It looks like this:

Auto-dream: on ยท running

or

Auto-dream: on ยท last ran 5 mins ago

It combines Control (toggling on/off) with Feedback (running state and history).

Concept 1: The Dependency

First, we need to understand a rule: You can't dream if you don't have memory.

The Auto-Dream controls only appear if the main "Auto-memory" system is enabled. In the code, we manage this visibility state.

// MemoryFileSelector.tsx
// Only show the dream row if memory is enabled
const [showDreamRow] = useState(isAutoMemoryEnabled);

// Later in the render...
{showDreamRow && (
  <ListItem>...</ListItem>
)}

Explanation:

Concept 2: Listening for the "Heartbeat"

How does the UI know if the background process is running? It needs to listen to the application's "brain" (the State).

We use a hook to check the list of active tasks.

// subscribe to the global state
const isDreamRunning = useAppState(state =>
  // Look through all tasks
  Object.values(state.tasks).some(
    // Is there a task of type 'dream' running?
    t => t.type === 'dream' && t.status === 'running'
  )
);

Explanation:

If isDreamRunning becomes true, the UI instantly re-renders to show "running".

Concept 3: The History Timestamp

If the robot isn't working now, when did it finish last? We read a timestamp from a special "lock file" or log.

// State to hold the timestamp
const [lastDreamAt, setLastDreamAt] = useState<number | null>(null);

useEffect(() => {
  if (!showDreamRow) return;
  
  // Asynchronously read the file from disk
  readLastConsolidatedAt().then(setLastDreamAt);
}, [showDreamRow, isDreamRunning]);

Explanation:

Interaction Flow: The Lifecycle of a Dream

Let's visualize how the user interacts with this system and how the system responds.

sequenceDiagram participant User participant UI as Control Panel participant Backend as Auto-Dream Process participant File as Timestamp Log User->>UI: Toggles "Auto-dream: on" UI->>Backend: Enables Service Note over Backend: Time passes... triggers run Backend->>UI: State change: "Running" UI-->>User: Shows "Auto-dream: on ยท running" Backend->>File: Writes completion time Backend->>UI: State change: "Idle" UI->>File: Reads timestamp UI-->>User: Shows "Auto-dream: on ยท last ran 1m ago"

Internal Implementation: Rendering the Status Line

Now we combine the Switch, the Heartbeat, and the History into one visual line of text using Ink components.

This logic resides in MemoryFileSelector.tsx.

The Status Logic

We calculate a simple text string (dreamStatus) based on the complex state.

const dreamStatus = isDreamRunning
  ? "running" // Priority 1: It is happening now
  : lastDreamAt === null
    ? ""      // Priority 2: We don't know yet
    : `last ran ${formatRelativeTimeAgo(new Date(lastDreamAt))}`;

Explanation:

The Visual Output

Finally, we render the line. We use conditional coloring to help the user focus.

<ListItem isFocused={focusedToggle === 1}>
  <Text color={focusedToggle === 1 ? "suggestion" : undefined}>
    Auto-dream: {autoDreamOn ? "on" : "off"}
    
    {/* The status text we calculated above */}
    {dreamStatus && (
      <Text dimColor={true}> ยท {dreamStatus}</Text>
    )}
  </Text>
</ListItem>

Explanation:

Putting it all together

By combining these elements, we have created a "Living Interface."

  1. Passive: It shows you the history ("last ran 1 hour ago").
  2. Active: It shows you current activity ("running").
  3. Interactive: You can toggle it on/off.

This completes the loop of Trust. The user trusts the automatic memory system because they can see it working and control it when necessary.

Series Conclusion

Congratulations! You have completed the Memory System Tutorial.

Let's review what we have built:

  1. Memory Hierarchy Interface: We structured our files (User vs. Project).
  2. Dynamic Agent Scope: We created magic folders for AI agents.
  3. Terminal Interaction Layer: We built a keyboard-driven dashboard.
  4. Path Contextualization: We made file paths human-readable.
  5. Auto-Dreaming Controls: We added visibility to background processes.

You now understand the architecture behind a modern, interactive AI memory system. You have moved from static text files to a dynamic, living application that manages context intelligently.

Happy coding!


Generated by Code IQ