Skip to content

Memory & Skills

Agents have persistent storage that survives across conversations and restarts. This is split into file memory for human-readable notes, typed memory for searchable records, and skills for structured capabilities.

workspace/
├── memory/
│ ├── MEMORY.md # Long-term memory
│ └── 2026-02-23.md # Daily notes (date-stamped)
├── skills/
│ └── my-skill/
│ └── SKILL.md # Skill definition
├── images/ # Generated images
├── HEARTBEAT.md # Periodic task instructions (optional)
└── ... # Any other files the agent creates

The workspace path defaults to /opt/baal-agent/workspace and is configured via the WORKSPACE_PATH environment variable.

The agent has two memory paths. File memory is edited with normal file tools. Typed memory is written and searched with dedicated tools.

workspace/memory/MEMORY.md stores persistent knowledge that the agent should always have access to: user preferences, project context, important facts, and recurring patterns.

The agent is instructed (via its system prompt) to save important information here. For example, if a user says “I prefer Python over JavaScript,” the agent might append that to MEMORY.md so it remembers in future conversations.

workspace/memory/YYYY-MM-DD.md files hold session-specific notes tied to a particular day. These are useful for tracking ongoing work, temporary context, and daily task lists.

Only today’s daily notes are loaded into context. Older daily notes remain on disk and can be read on demand, but are not injected automatically.

Typed memory records are stored in the agent SQLite database with:

  • kind — category such as user_fact, repo_convention, test_command, or decision
  • content — compact declarative memory text
  • source — where the record came from
  • optional metadata
  • archive state and timestamps

The agent can create records with remember_fact and retrieve them with search_memory. Recent active records are injected under Typed Memory on each turn, after being passed through the same prompt-injection scanner used for file memory.

On each conversation turn, the context builder reads:

  1. memory/MEMORY.md — included under a “Long-term Memory” heading
  2. memory/<today>.md — included under a “Today’s Notes” heading
  3. recent typed memory records — included under a “Typed Memory” heading

These are injected as dynamic context near the end of the message list (just before the latest user message) to preserve KV cache efficiency. See Context & Compaction for details.

Before memory, daily notes, user profile, project context files, skill summaries, or active TODO context are injected, Baal scans them for obvious prompt-injection payloads. Hidden HTML instructions, attempts to override system/developer instructions, secret exfiltration snippets, and tool-policy bypass attempts are redacted with a [BLOCKED: ...] marker.

Skills are structured capability definitions that teach the agent how to perform specific tasks. Each skill lives in its own directory under workspace/skills/.

workspace/skills/my-skill/
└── SKILL.md

A SKILL.md file can use either format:

Frontmatter format (recommended):

---
name: "Data Analysis"
description: "Analyze CSV data and generate charts"
requires_tools: ["read_file", "execute_code"]
platforms: ["api", "telegram"]
---
## Instructions
When asked to analyze data:
1. Read the CSV file
2. Identify key metrics
3. Generate a summary with charts
...

Legacy format (heading + first paragraph):

# Data Analysis
Analyze CSV data and generate charts.
## Instructions
...

The context builder scans workspace/skills/*/SKILL.md and extracts a summary for each skill:

  • From frontmatter: uses the name and description fields
  • If present, requires_tools hides the skill unless all required tools are available
  • If present, platforms hides the skill unless the current channel matches
  • From legacy format: uses the directory name and the first non-heading paragraph

These summaries are included in the dynamic context as a bullet list:

## Available Skills
- **Data Analysis**: Analyze CSV data and generate charts (read `workspace/skills/data-analysis/SKILL.md` for details)
- **Web Scraping**: Extract structured data from websites (read `workspace/skills/web-scraping/SKILL.md` for details)

The agent sees the summary of every skill on every turn, but the full skill content is only loaded when the agent explicitly reads the SKILL.md file. This keeps the context small while making skills discoverable.

Skill summaries are cached and invalidated when a SKILL.md file is added, removed, or changes size/mtime, so large skill directories do not need to be fully rescanned on every prompt build.

The runtime records skill lifecycle events in the durable event log:

  • skill.considered when a turn builds the visible skill list
  • skill.loaded when the agent reads workspace/skills/<id>/SKILL.md
  • skill.draft.proposed when a high-tool-count task produces a reusable skill draft

Draft proposals are telemetry only in this phase. The agent does not automatically write or update skill files from the proposal.

A skill directory can contain any files the agent needs — not just SKILL.md. For example, a skill might include template files, configuration snippets, or reference data. The SKILL.md file serves as the entry point.

The heartbeat system allows agents to perform periodic background work. If HEARTBEAT_INTERVAL is set to a value greater than 0 (default: 1800 seconds / 30 minutes), the agent checks workspace/HEARTBEAT.md at that interval.

If the file contains actionable content (non-empty, non-comment lines or unchecked checkboxes), the agent runs a turn to process the instructions. Results are delivered to the owner as pending messages.

The heartbeat file supports:

  • Plain text instructions
  • Markdown task lists (- [ ] task for pending, - [x] task for completed)
  • Comments (<!-- ... -->) and headings are ignored when checking for actionable content

Agents can also poll workspace/watchers.json from the same scheduler loop. Each watcher records its first observed file or directory state, then triggers a background job when that path changes:

[
{
"id": "docs",
"path": "docs/research",
"task": "Review changed research notes and summarize any new decisions.",
"enabled": true,
"debounce": 60
}
]

Watcher jobs run with IDs like watcher:docs. State is stored in workspace/.watcher_state.json; deleting that file resets the first-observed baseline. Watcher checks emit runtime events named watcher.checked, watcher.skipped, watcher.triggered, and watcher.error.