Internal Agent
Crucible’s internal agent is a built-in AI assistant that runs locally, logs sessions as markdown, and uses your kiln as its memory. Unlike external agents via ACP, the internal agent has direct access to your files and can persist state across sessions.
Overview
The internal agent provides:
- Session logging - Conversations saved as markdown files
- Task tracking - ACP-style task lists as working memory
- Explicit search - Use
/searchto inject context when needed - Precognition - automatic kiln retrieval before each turn (tunable via the
precognition_select/precognition_formathooks) - Lua hooks - the fourteen
cru.on()events plus session lifecycle hooks — see Event Hooks - Compaction -
/compactsummarizes and continues in a new file
Memory Architecture
The agent operates with two tiers of memory, all stored as plaintext:
┌─────────────────────────────────────────────────┐│ Memory Tiers │├─────────────────────────────────────────────────┤│ ││ Session Memory Current conversation + tasks ││ Logged to markdown file ││ ││ Kiln (via /search) Your notes + embeddings ││ Explicit search injection ││ │└─────────────────────────────────────────────────┘Session Memory
The current conversation, task list, and tool calls. Logged to a markdown file in your personal kiln’s session folder. Session files have index: false frontmatter so they don’t bloat your embedding database.
Kiln Search
Use /search query to find relevant notes and inject them into the conversation. This explicit approach keeps you in control of what context the agent sees.
Session Files
Sessions are markdown notes with special frontmatter. They’re stored in your personal kiln and can be searched, linked, and analyzed like any other note.
Location
Sessions are stored by workspace (directory/repo name):
~/Documents/your-kiln/└── sessions/ └── <WORKSPACE_DIR>/ ├── 2024-12-24_1930.md # Session log ├── 2024-12-25_0900.md # Another session └── ...Where WORKSPACE_DIR is the name of the git repo or working directory you started the chat from.
Session Format
---type: sessionworkspace: cruciblestarted: 2024-12-24T19:30:00Zended: 2024-12-24T21:00:00Z---
# Session
## Tasks
- [x] Read existing agent crates- [~] Design memory architecture- [ ] Implement session logging
## Log
### User 19:30Research internal agent abstractions...
### Agent 19:30:15I'll start by exploring the existing code...
### Tool: semantic_search 19:30:20```json{"query": "agent handle trait", "limit": 5}Result: Found 5 relevant notes…
Session ended
### Frontmatter Fields
| Field | Description ||-------|-------------|| `type: session` | Marks this as a session log || `workspace` | Workspace directory name || `started` | Session start timestamp || `ended` | Session end timestamp (added on close) |
### Embedding Exclusion
The `sessions` folder is configured as an embedding exclusion directory. This means:- **Metadata is indexed** - searchable by properties like `type:session`- **No embeddings generated** - saves vector DB space
```bash# Search sessions by propertycru search --properties "type:session workspace:crucible"
# Text search within sessions foldercru search "error handling" --folder sessions/crucibleCompaction
When sessions get long, use /compact to summarize and continue in a new file:
- Agent generates numbered summary of key points
- Summary appended to current file with link to continuation
- New file created with summary as context
- Logging continues in new file
File Structure
Sessions are folders so agents can write files to session namespace:
~/Documents/your-kiln/sessions/crucible/├── 2024-12-24_1930/ # Session folder│ ├── log.md # Conversation log│ └── ... # Any files agent writes└── 2024-12-24_1930_01/ # After compaction └── log.md
~/.crucible/sessions/ # Hidden state (machine-readable)├── index.json # Session discovery└── state/crucible/ ├── 2024-12-24_1930.json # Full conversation state └── 2024-12-24_1930_01.jsonWhy this structure:
- Session folders let agents write scratch files
- Kiln stays clean markdown
- JSON state hidden, enables full resume
- Index enables fast session discovery
Example
End of 2024-12-24_1930/log.md:
---
**Continued in:** [[2024-12-24_1930_01/log]]New 2024-12-24_1930_01/log.md:
---type: sessionworkspace: cruciblestarted: 2024-12-24T19:30:00Zcontinued_from: 2024-12-24_1930---
# Session (continued)
## Summary
1. Researched internal agent patterns2. Designed session logging with markdown files3. Decided on embedding exclusion approach
## Log
### User 20:15...Task List
The agent uses an ACP-style task list as working memory. Tasks track progress within a session:
## Tasks
- [x] Completed task- [~] In progress task- [ ] Pending taskTask States
| Marker | Status | Description |
|---|---|---|
[ ] | pending | Not started |
[~] | in_progress | Currently working on |
[x] | completed | Finished |
Precognition (Auto Context)
Precognition ships: the daemon searches your kiln before each LLM call and
injects relevant context automatically. /search remains available for
explicit injection, and the precognition_select / precognition_format
hooks let Lua decide which notes survive and how they render — see
Event Hooks.
Lua Integration
Lua hooks and plugin storage ship.
Plugin Storage
cru.storage is a key-value property store, automatically namespaced per
plugin (plugin:<name>) — there is no require("cru.storage") module or
namespace() call:
-- Set a property on an entitycru.storage.set("entity-id", "key", "value")
-- Get a property (nil if missing)local val = cru.storage.get("entity-id", "key")
-- List all properties for an entity ({key = value, ...})local props = cru.storage.list("entity-id")
-- Find entity ids with a matching propertylocal ids = cru.storage.find("status", "active")
-- Delete a property (true if deleted)local ok = cru.storage.delete("entity-id", "key")All calls are async and scoped to the calling plugin’s namespace; before a kiln’s storage is open they are stubs that return nil/empty.
Hook Points
There is no agent:* event namespace — these are the events that actually
fire. See Event Hooks for each one’s payload and return
contract.
| Event | Description |
|---|---|
cru.on_session_start(fn) | Session beginning (can refuse the session) |
cru.on_session_end(fn) | Session closing |
pre_llm_call | Before the LLM call — transform the prompt |
transform_context | Before the LLM call — rewrite the message list |
turn:complete | After the assistant response, can inject a follow-up |
pre_tool_call | Tool about to execute (observe/transform/cancel/handle) |
tool_result | Tool finished, patch what the model receives |
Example: Custom Context Injection
-- Prepend recently modified notes to the prompt. Registered at load time;-- `pre_llm_call` hands the handler `{ prompt, model }` and a returned-- `{ prompt = ... }` replaces it. Returning `{ cancel = true }` cancels the-- turn outright.cru.on("pre_llm_call", { priority = 100 }, function(ctx, event) local recent = cru.kiln.search({ modified_after = os.time() - 86400 -- 24 hours }) if #recent == 0 then return end return { prompt = "## Recent Activity\n" .. table.concat(recent, "\n") .. "\n\n" .. event.prompt, }end)Using the Internal Agent
From CLI
# Start a new session (logs to sessions/<workspace>/<timestamp>.md)cru chat
# Resume most recent open session for this workspacecru chat --resumeSession Commands
During a session:
| Command | Action |
|---|---|
/tasks | Show current task list |
/search | Search kiln and inject context |
/compact | Summarize and continue in new file |
Configuration
The internal agent is configured through the ordinary [chat] and [llm] sections of
~/.config/crucible/config.toml, and stores its sessions in the default kiln:
default_kiln = "notes"
[kilns]notes = "~/Documents/crucible-testing"
[chat]agent_preference = "crucible"See Configuration for every field.
Best Practices
Workspace Organization
Sessions are automatically organized by the workspace directory you run cru chat from:
sessions/├── crucible/ # Work in ~/code/crucible├── my-app/ # Work in ~/code/my-app└── dotfiles/ # Work in ~/dotfilesStart chat from the relevant project directory to keep sessions organized.
When to Use Internal vs External Agents
| Use Internal Agent | Use External Agent (ACP) |
|---|---|
| Local-first operation | Cloud AI services |
| Session persistence | Stateless queries |
| Kiln integration | External tool access |
See Also
- Agent Cards - Define agent personas
- Event Hooks - React to agent events
- Custom Tools - Add tools for agents
- Agents & Protocols - MCP vs ACP
- Language Basics - Lua scripting
- AI Features - All AI capabilities