Add project documentation and reference materials
Include Luna AI Assistant design docs covering channels, configuration, core architecture, memory, scheduler, and skills. Add reference docs from OpenClaw and ZeroClaw projects, plus Mistral and OpenAI API specs.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"file-explorer": true,
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": true,
|
||||
"backlink": true,
|
||||
"canvas": true,
|
||||
"outgoing-link": true,
|
||||
"tag-pane": true,
|
||||
"footnotes": false,
|
||||
"properties": true,
|
||||
"page-preview": true,
|
||||
"daily-notes": true,
|
||||
"templates": true,
|
||||
"note-composer": true,
|
||||
"command-palette": true,
|
||||
"slash-command": false,
|
||||
"editor-status": true,
|
||||
"bookmarks": true,
|
||||
"markdown-importer": false,
|
||||
"zk-prefixer": false,
|
||||
"random-note": false,
|
||||
"outline": true,
|
||||
"word-count": true,
|
||||
"slides": false,
|
||||
"audio-recorder": false,
|
||||
"workspaces": false,
|
||||
"file-recovery": true,
|
||||
"publish": false,
|
||||
"sync": true,
|
||||
"bases": true,
|
||||
"webviewer": false
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"collapse-filter": true,
|
||||
"search": "",
|
||||
"showTags": false,
|
||||
"showAttachments": false,
|
||||
"hideUnresolved": false,
|
||||
"showOrphans": true,
|
||||
"collapse-color-groups": true,
|
||||
"colorGroups": [],
|
||||
"collapse-display": true,
|
||||
"showArrow": false,
|
||||
"textFadeMultiplier": 0,
|
||||
"nodeSizeMultiplier": 1,
|
||||
"lineSizeMultiplier": 1,
|
||||
"collapse-forces": true,
|
||||
"centerStrength": 0.518713248970312,
|
||||
"repelStrength": 10,
|
||||
"linkStrength": 1,
|
||||
"linkDistance": 250,
|
||||
"scale": 1,
|
||||
"close": true
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
{
|
||||
"main": {
|
||||
"id": "b89020bafd288606",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "3b084bb6aea7bf06",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "27fd26dcd453c61f",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "Channels/Web Interface Channel.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "Web Interface Channel"
|
||||
},
|
||||
"group": "47a210897f386c25"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7b841bf992949f81",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "5056061ce0a62aac",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "Channels/Web Interface Channel.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "Web Interface Channel"
|
||||
},
|
||||
"group": "47a210897f386c25"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
},
|
||||
"left": {
|
||||
"id": "d65ed31b4cf140a8",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "cb79094e37f1bfbf",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "fcd6318fb0efd8c3",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "file-explorer",
|
||||
"state": {
|
||||
"sortOrder": "alphabetical",
|
||||
"autoReveal": false
|
||||
},
|
||||
"icon": "lucide-folder-closed",
|
||||
"title": "Files"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "1bbb51a5f3e6fe42",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "search",
|
||||
"state": {
|
||||
"query": "",
|
||||
"matchingCase": false,
|
||||
"explainSearch": false,
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical"
|
||||
},
|
||||
"icon": "lucide-search",
|
||||
"title": "Search"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bb387a4f03db1101",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "bookmarks",
|
||||
"state": {},
|
||||
"icon": "lucide-bookmark",
|
||||
"title": "Bookmarks"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300
|
||||
},
|
||||
"right": {
|
||||
"id": "be153589260b27a6",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "b9a0a82b097773e2",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "99660be9c41b8d54",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"file": "Channels/Web Interface Channel.md",
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
"showSearch": false,
|
||||
"searchQuery": "",
|
||||
"backlinkCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
},
|
||||
"icon": "links-coming-in",
|
||||
"title": "Backlinks for Web Interface Channel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "e5acfa574174fe3a",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outgoing-link",
|
||||
"state": {
|
||||
"file": "Core.md",
|
||||
"linksCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
},
|
||||
"icon": "links-going-out",
|
||||
"title": "Outgoing links from Core"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "3693f3ae811b7b80",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "tag",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"useHierarchy": true,
|
||||
"showSearch": false,
|
||||
"searchQuery": ""
|
||||
},
|
||||
"icon": "lucide-tags",
|
||||
"title": "Tags"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "018d06694edd9d01",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "all-properties",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"showSearch": false,
|
||||
"searchQuery": ""
|
||||
},
|
||||
"icon": "lucide-archive",
|
||||
"title": "All properties"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "b9b058ee8cd7d7a6",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {
|
||||
"file": "Core.md",
|
||||
"followCursor": false,
|
||||
"showSearch": false,
|
||||
"searchQuery": ""
|
||||
},
|
||||
"icon": "lucide-list",
|
||||
"title": "Outline of Core"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300,
|
||||
"collapsed": true
|
||||
},
|
||||
"left-ribbon": {
|
||||
"hiddenItems": {
|
||||
"switcher:Open quick switcher": false,
|
||||
"graph:Open graph view": false,
|
||||
"canvas:Create new canvas": false,
|
||||
"daily-notes:Open today's daily note": false,
|
||||
"templates:Insert template": false,
|
||||
"command-palette:Open command palette": false,
|
||||
"bases:Create new base": false
|
||||
}
|
||||
},
|
||||
"active": "5056061ce0a62aac",
|
||||
"lastOpenFiles": [
|
||||
"Core.md",
|
||||
"Channels/Web Interface Channel.md",
|
||||
"Channels.md",
|
||||
"Channels/Telegram Channel.md",
|
||||
"Channels/CLI Channel.md",
|
||||
"Channels/Channels.md",
|
||||
"Channels",
|
||||
"Skills.md",
|
||||
"Configuration.md",
|
||||
"Scheduler.md",
|
||||
"Memory.md",
|
||||
"Project Plan.md",
|
||||
"Welcome.md"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# CLI Channel
|
||||
|
||||
The CLI connects to Luna via SignalR. `CliChannel` is a server-side adapter that bridges the SignalR hub to the `IChannel` interface defined in [[Channels]].
|
||||
|
||||
## How It Works
|
||||
|
||||
`CliChannel` wraps `IChatHubContext` from [[Core]] to send streaming responses back to the connected SignalR client. The flow:
|
||||
|
||||
1. The `ChatHub` receives a message from the CLI client over SignalR.
|
||||
2. `ChatHub` calls `RaiseMessageReceived` on the `CliChannel` instance.
|
||||
3. The [[Channels|ChannelManager]] picks up the event and routes it through `SessionManager.RouteMessagesAsync`.
|
||||
4. The resulting `IAsyncEnumerable<ChatStreamUpdate>` is passed back to `CliChannel.SendStreamingMessageAsync`.
|
||||
5. `CliChannel` forwards the stream to the SignalR client via `IChatHubContext`.
|
||||
|
||||
## Key Characteristics
|
||||
|
||||
- **Server-side adapter**: `CliChannel` lives on the server; the actual CLI is a separate SignalR client.
|
||||
- **Single connection**: One `CliChannel` instance maps to the SignalR hub connection.
|
||||
- **No polling**: Unlike [[Telegram Channel]], the CLI uses persistent WebSocket connections via SignalR.
|
||||
|
||||
## Namespace
|
||||
|
||||
`Luna.Channels.Cli`
|
||||
@@ -0,0 +1,101 @@
|
||||
# Channels
|
||||
|
||||
The Channels module provides a unified abstraction for communication platforms like the CLI and Telegram. It handles message transport, while [[Core]] manages AI context and session state.
|
||||
|
||||
## Luna.Channels.Abstractions
|
||||
|
||||
This project defines the contracts and event models for all channel implementations.
|
||||
|
||||
### IChannel
|
||||
The primary interface for any communication transport. There is no abstract base class; `IChannel` is the complete contract.
|
||||
|
||||
```csharp
|
||||
public interface IChannel : IDisposable
|
||||
{
|
||||
string ChannelId { get; }
|
||||
string ChannelType { get; }
|
||||
string DisplayName { get; }
|
||||
bool IsConnected { get; }
|
||||
|
||||
event ChannelMessageReceivedEventHandler? MessageReceived;
|
||||
event EventHandler<ChannelConnectionEventArgs>? ConnectionStateChanged;
|
||||
|
||||
Task SendMessageAsync(ChannelMessage message, CancellationToken ct = default);
|
||||
Task SendStreamingMessageAsync(IAsyncEnumerable<ChatStreamUpdate> messageStream, CancellationToken ct = default);
|
||||
}
|
||||
```
|
||||
|
||||
### IChannelManager
|
||||
Coordinates registered channels and handles message routing.
|
||||
|
||||
```csharp
|
||||
public interface IChannelManager
|
||||
{
|
||||
void RegisterChannel(IChannel channel);
|
||||
void UnregisterChannel(string channelId);
|
||||
IChannel? GetChannel(string channelId);
|
||||
IReadOnlyList<IChannel> GetAllChannels();
|
||||
event EventHandler<ChannelMessageReceivedEventArgs>? MessageRouted;
|
||||
}
|
||||
```
|
||||
|
||||
### Supporting Types
|
||||
- **ChannelType**: A static class providing constants like `cli` and `telegram`.
|
||||
- **ChannelMessageReceivedEventHandler**: Delegate for handling incoming messages.
|
||||
`Task ChannelMessageReceivedEventHandler(object? sender, ChannelMessageReceivedEventArgs e)`
|
||||
- **ChannelMessageReceivedEventArgs**: Contains the `Channel`, `Message`, and an `IsHandled` flag.
|
||||
- **ChannelConnectionEventArgs**: Contains the `Channel` and `IsConnected` status.
|
||||
|
||||
## Luna.Channels
|
||||
|
||||
This project contains the concrete management logic and specific channel implementations.
|
||||
|
||||
### ChannelManager
|
||||
The `ChannelManager` implements `IChannelManager` and acts as the central hub for message traffic. It injects `ISessionManager` from [[Core]] and `ILogger<ChannelManager>`.
|
||||
|
||||
When a channel is registered via `RegisterChannel`, the manager subscribes to its `MessageReceived` event. The `OnMessageReceivedAsync` handler performs the following:
|
||||
1. Calls `sessionManager.RouteMessagesAsync(message.Content, message.ConversationId, channel.ChannelId)`.
|
||||
2. Pipes the resulting `IAsyncEnumerable<ChatStreamUpdate>` back to the channel via `channel.SendStreamingMessageAsync`.
|
||||
|
||||
### Channel Implementations
|
||||
- [[CLI Channel]] — SignalR-based adapter for the command-line interface.
|
||||
- [[Telegram Channel]] — Telegram Bot API adapter with message splitting and streaming.
|
||||
- [[Web Interface Channel]] — Browser-based chat UI (not yet implemented).
|
||||
|
||||
## Routing Flow
|
||||
|
||||
1. `Channel.MessageReceived` event triggers.
|
||||
2. `ChannelManager` catches the event and identifies the sender.
|
||||
3. Manager calls `SessionManager.RouteMessagesAsync`.
|
||||
4. `SessionManager` returns an `IAsyncEnumerable<ChatStreamUpdate>`.
|
||||
5. `ChannelManager` passes this stream to `Channel.SendStreamingMessageAsync`.
|
||||
6. The channel implementation handles the physical transport of the stream.
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
- **Separation of Concerns**: `ChannelManager` handles transport and routing. `SessionManager` handles AI context and conversation logic.
|
||||
- **Conversation Scoping**: Sessions are scoped to conversations, not specific channels. This allows for potential cross-channel persistence.
|
||||
- **Unified DTOs**: All message data uses the `ChannelMessage` DTO from `Luna.Shared`.
|
||||
- **Registration**: Channels are registered via the `AddChannels()` DI extension method, following the [[Configuration]] patterns.
|
||||
- **Options Pattern**: Implementations use `IOptions<TOptions>` (e.g., `TelegramOptions`) for configuration.
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Project References
|
||||
- `Luna.Channels.Abstractions`
|
||||
- `Luna.Configuration`
|
||||
- `Luna.Core.Abstractions`
|
||||
- `Luna.Shared`
|
||||
|
||||
### NuGet Packages
|
||||
- `Telegram.Bot`
|
||||
- `Microsoft.Extensions.Hosting.Abstractions`
|
||||
- `Microsoft.Extensions.Logging.Abstractions`
|
||||
|
||||
## Adding a New Channel
|
||||
|
||||
To implement a new channel:
|
||||
1. Create a class implementing `IChannel`.
|
||||
2. Ensure it handles both `SendMessageAsync` and `SendStreamingMessageAsync`.
|
||||
3. Raise `MessageReceived` when the external platform sends a message.
|
||||
4. Register the channel with `IChannelManager` during startup or via a background adapter (like `TelegramAdapter`).
|
||||
@@ -0,0 +1,39 @@
|
||||
# Telegram Channel
|
||||
|
||||
The Telegram integration consists of two classes: `TelegramChannel` (the `IChannel` implementation) and `TelegramAdapter` (the hosted service that manages channel lifecycles). Both live in the `Luna.Channels.Telegram` namespace.
|
||||
|
||||
## TelegramChannel
|
||||
|
||||
Handles interaction with the Telegram Bot API, implementing the `IChannel` interface defined in [[Channels]].
|
||||
|
||||
### Message Splitting
|
||||
Telegram enforces a 4096-character limit per message. `TelegramChannel` automatically splits long responses into sequential chunks that respect this limit.
|
||||
|
||||
### Streaming
|
||||
Rather than forwarding every `ChatStreamUpdate` individually (which would hit Telegram's rate limits), the channel accumulates streaming updates and sends them in periodic batches.
|
||||
|
||||
### Typing Indicators
|
||||
Uses `KeepTypingAsync` to maintain a "typing..." indicator in the Telegram chat while the AI generates a response. This runs as a background loop until the response completes.
|
||||
|
||||
### User Filtering
|
||||
`RaiseMessageReceived` filters incoming updates by `AllowedUserIds` (configured via `TelegramOptions` in [[Configuration]]). Messages from unauthorized users or with empty content are silently dropped.
|
||||
|
||||
## TelegramAdapter
|
||||
|
||||
An `IHostedService` and `IUpdateHandler` that polls Telegram for updates using long polling.
|
||||
|
||||
### Lifecycle
|
||||
1. On startup, begins polling the Telegram Bot API.
|
||||
2. For each incoming update, identifies the chat ID.
|
||||
3. Creates a new `TelegramChannel` instance for each unique chat (if one doesn't already exist).
|
||||
4. Registers the channel with `IChannelManager` from [[Channels]].
|
||||
5. Routes the update to the appropriate `TelegramChannel`.
|
||||
|
||||
### Configuration
|
||||
Configured via `TelegramOptions` (see [[Configuration]]), which includes:
|
||||
- `BotToken` — Telegram Bot API token.
|
||||
- `AllowedUserIds` — Whitelist of Telegram user IDs permitted to interact with Luna.
|
||||
|
||||
## Namespace
|
||||
|
||||
`Luna.Channels.Telegram`
|
||||
@@ -0,0 +1,41 @@
|
||||
# Web Interface Channel
|
||||
|
||||
> [!info] Status: Not Yet Implemented
|
||||
|
||||
The Web Interface channel will provide a browser-based chat UI for interacting with Luna directly, without requiring the CLI or Telegram. It implements the `IChannel` interface defined in [[Channels]].
|
||||
|
||||
## Planned Approach
|
||||
|
||||
### WebInterfaceChannel
|
||||
|
||||
A server-side `IChannel` implementation that bridges the web frontend to Luna's channel system. Similar to [[CLI Channel]], it will likely use SignalR for real-time bidirectional communication.
|
||||
|
||||
**Key responsibilities:**
|
||||
- Accept messages from authenticated browser sessions.
|
||||
- Stream `ChatStreamUpdate` responses back to the frontend in real time.
|
||||
- Manage connection lifecycle (connect, disconnect, reconnect).
|
||||
|
||||
### WebInterfaceAdapter
|
||||
|
||||
An `IHostedService` (similar to `TelegramAdapter` in [[Telegram Channel]]) responsible for:
|
||||
- Registering `WebInterfaceChannel` instances with `IChannelManager` from [[Channels]].
|
||||
- Managing per-user or per-session channel lifecycle.
|
||||
|
||||
## Configuration
|
||||
|
||||
Will follow the existing [[Configuration]] options pattern with a `WebInterfaceOptions` class containing settings such as:
|
||||
- Authentication / authorization settings.
|
||||
- CORS policy.
|
||||
- Session timeout.
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Expected Project References
|
||||
- `Luna.Channels.Abstractions`
|
||||
- `Luna.Configuration`
|
||||
- `Luna.Core.Abstractions`
|
||||
- `Luna.Shared`
|
||||
|
||||
## Namespace
|
||||
|
||||
`Luna.Channels.Web`
|
||||
@@ -0,0 +1,112 @@
|
||||
# Configuration
|
||||
|
||||
## Overview
|
||||
Luna uses the standard .NET Options pattern for managing application settings. The configuration system relies on `IOptions<T>` and `IOptionsMonitor<T>` to provide typed access to settings defined in `appsettings.json`. This approach ensures type safety and allows for easy validation at startup.
|
||||
|
||||
The configuration is centralized in the `Luna.Configuration` project. Settings are bound during application startup using the `.BindConfiguration().ValidateDataAnnotations().ValidateOnStart()` pattern.
|
||||
|
||||
## Options Classes
|
||||
|
||||
### AgentOptions
|
||||
These settings define the behavior and identity of AI agents like the [[Core]] agent or the Librarian. Agents retrieve their specific configuration using `IOptionsMonitor<AgentOptions>.Get(name)`.
|
||||
|
||||
```csharp
|
||||
public class AgentOptions
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
public string? DisplayName { get; init; }
|
||||
public string? Description { get; init; }
|
||||
public required string Provider { get; init; }
|
||||
public required string ModelId { get; init; }
|
||||
public required string Instructions { get; init; }
|
||||
public required int MaxContextTokens { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
### ProviderOptions
|
||||
Configures the connection details for AI model providers such as Mistral or OpenAI.
|
||||
|
||||
```csharp
|
||||
public class ProviderOptions
|
||||
{
|
||||
public required string ApiKey { get; init; }
|
||||
public required string ApiUrl { get; init; }
|
||||
public required string[] Models { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
### SessionOptions
|
||||
Controls how [[Memory]] and conversation sessions are managed. It specifically dictates when the session context should be compacted to save tokens.
|
||||
|
||||
```csharp
|
||||
public class SessionOptions
|
||||
{
|
||||
public required float ContextTokenThreshold { get; init; }
|
||||
public required int RetainedMessagesAfterCompacting { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
### TelegramOptions
|
||||
Specific settings for the Telegram [[Channels]] adapter, including bot authentication and user access control.
|
||||
|
||||
```csharp
|
||||
public class TelegramOptions
|
||||
{
|
||||
public required string BotToken { get; init; }
|
||||
public string? WebhookUrl { get; init; }
|
||||
public int PollingTimeoutSeconds { get; init; } = 30;
|
||||
public string[] AllowedUserIds { get; init; } = [];
|
||||
}
|
||||
```
|
||||
|
||||
## Example Configuration
|
||||
|
||||
The following `appsettings.json` structure demonstrates how these options are populated:
|
||||
|
||||
```json
|
||||
{
|
||||
"Agents": {
|
||||
"Core": {
|
||||
"Name": "Core",
|
||||
"Provider": "Mistral",
|
||||
"ModelId": "mistral-small-latest",
|
||||
"Instructions": "You are Luna, a helpful AI assistant.",
|
||||
"MaxContextTokens": 8192
|
||||
},
|
||||
"Librarian": {
|
||||
"Name": "Librarian",
|
||||
"Provider": "Mistral",
|
||||
"ModelId": "mistral-small-latest",
|
||||
"Instructions": "Summarize conversations concisely.",
|
||||
"MaxContextTokens": 4096
|
||||
}
|
||||
},
|
||||
"Providers": {
|
||||
"Mistral": {
|
||||
"ApiKey": "YOUR_API_KEY",
|
||||
"ApiUrl": "https://api.mistral.ai",
|
||||
"Models": ["mistral-small-latest"]
|
||||
}
|
||||
},
|
||||
"Session": {
|
||||
"ContextTokenThreshold": 0.7,
|
||||
"RetainedMessagesAfterCompacting": 5
|
||||
},
|
||||
"Channels": {
|
||||
"Telegram": {
|
||||
"BotToken": "YOUR_BOT_TOKEN",
|
||||
"PollingTimeoutSeconds": 30,
|
||||
"AllowedUserIds": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
The configuration module depends on the following NuGet packages:
|
||||
|
||||
- Microsoft.Extensions.DependencyInjection.Abstractions
|
||||
- Microsoft.Extensions.FileProviders.Embedded
|
||||
- Microsoft.Extensions.Options
|
||||
- Tomlyn (for parsing embedded TOML resources)
|
||||
@@ -0,0 +1,106 @@
|
||||
# Context Compaction Research
|
||||
|
||||
This document captures a research analysis comparing Claude Code's compaction engine patterns against Luna's current implementation, with recommendations for adoption. Reference source: https://barazany.dev/blog/claude-codes-compaction-engine
|
||||
|
||||
## Luna's Current Compaction Architecture
|
||||
|
||||
| Component | Current Approach |
|
||||
|---|---|
|
||||
| Trigger | Token threshold: `session.TokenAmount > MaxContextTokens * 0.75` |
|
||||
| Token estimation | Naive `text.Length / 4` heuristic (`TokenEstimator`) |
|
||||
| Compaction method | Single LLM call via `LibrarianAgent` (Mistral Small) — plain bullet-point summary |
|
||||
| Retained context | Last N messages (default: 2) carried forward |
|
||||
| Post-compaction reconstruction | Summary wrapped in `MEMORY BEGIN/END` markers as an Assistant message, then retained messages appended |
|
||||
| Persistent memory | File-based `MemoryStore` — raw conversation logs to disk, last 10 read back as system message |
|
||||
| Tiers | None — only full LLM summarization |
|
||||
| Cache awareness | None |
|
||||
| Tool result management | None — tool outputs accumulate until full compaction |
|
||||
|
||||
## Claude Code's Three-Tier Pattern
|
||||
|
||||
Claude Code employs a tiered strategy to manage context while maximizing cache efficiency:
|
||||
|
||||
- **Tier 1**: Lightweight deterministic cleanup before every API call. This process clears old tool results (retaining only the last 5) and replaces them with placeholders. No LLM is involved at this stage.
|
||||
- **Tier 2**: API-level server-side strategies for token management using Anthropic-specific infrastructure.
|
||||
- **Tier 3**: Full LLM summarization as a last resort. This involves a structured 9-section summary with a chain-of-thought scratchpad. Post-compaction reconstruction includes a boundary marker, the summary, the 5 most recently read files (capped at 50K tokens), re-injected skills, tool definitions, and session hooks.
|
||||
|
||||
The key architectural insight is that cache economics drive every decision. This includes using `cache_edits` for surgical server-side deletions and ensuring the summarization call reuses the same cache key.
|
||||
|
||||
## Pattern Evaluation
|
||||
|
||||
### 1. Tier 1 — Deterministic Tool Result Cleanup
|
||||
**Gap**: Luna currently has zero tool result management.
|
||||
**Recommendation**: ADOPT.
|
||||
**Effort**: Low.
|
||||
**Details**: Implement a pre-call sanitizer that trims old tool results before each API call to prevent context bloating from large tool outputs.
|
||||
|
||||
### 2. Structured Compaction Prompt
|
||||
**Gap**: Luna uses a simple "max 12 bullet points" prompt.
|
||||
**Recommendation**: ADOPT.
|
||||
**Effort**: Low.
|
||||
**Details**: Replace the current prompt with a structured template that forces categorized output, including user intent, key decisions, unresolved tasks, relevant facts, and technical context.
|
||||
|
||||
### 3. Tiered Compaction (Delay LLM Summarization)
|
||||
**Gap**: Luna only supports full LLM summarization.
|
||||
**Recommendation**: ADOPT.
|
||||
**Effort**: Moderate.
|
||||
**Details**: Implement a 2-tier system. Tier 1 performs deterministic cleanup on every call, while Tier 2 triggers LLM summarization only when Tier 1 is insufficient. The Anthropic-specific server-side tier will be skipped.
|
||||
|
||||
### 4. Post-Compaction Reconstruction
|
||||
**Gap**: Luna's reconstruction logic is basic and uses Assistant messages for summaries.
|
||||
**Recommendation**: PARTIALLY ADOPT.
|
||||
**Effort**: Moderate.
|
||||
**Details**: Place the summary as a System message instead of an Assistant message. Re-inject agent instructions and add a boundary marker with metadata (timestamp, pre-compaction message count). Include a continuation message so the agent does not treat the summary as something to respond to.
|
||||
|
||||
### 5. Improved Token Estimation
|
||||
**Gap**: Luna relies on a `text.Length / 4` heuristic.
|
||||
**Recommendation**: ADOPT.
|
||||
**Effort**: Low.
|
||||
**Details**: Replace the current heuristic with a proper tokenizer, such as `Microsoft.ML.Tokenizers`, or a significantly improved heuristic.
|
||||
|
||||
### 6. Autonomous Continuation After Compaction
|
||||
**Gap**: Compaction can disrupt the conversation flow.
|
||||
**Recommendation**: ADOPT LIGHTLY.
|
||||
**Effort**: Trivial.
|
||||
**Details**: Prepend a brief system message after compaction, such as "Context was compacted. Continue naturally."
|
||||
|
||||
### 7. Cache-Aware `cache_edits`
|
||||
**Gap**: This is specific to Anthropic's API.
|
||||
**Recommendation**: NOT APPLICABLE.
|
||||
**Effort**: N/A.
|
||||
**Details**: This could be revisited if an Anthropic provider is added to `IProvider` in the future.
|
||||
|
||||
### 8. Same-Cache-Key Summarization
|
||||
**Gap**: Luna uses a cheaper model (Mistral Small) for compaction.
|
||||
**Recommendation**: NOT APPLICABLE.
|
||||
**Effort**: N/A.
|
||||
**Details**: Luna's current approach is effective when prompt caching is not a primary factor.
|
||||
|
||||
## Priority Adoption Matrix
|
||||
|
||||
| Priority | Pattern | Effort | Impact |
|
||||
|---|---|---|---|
|
||||
| P0 | Tier 1 — Deterministic tool result cleanup | Low | High |
|
||||
| P0 | Structured compaction prompt | Low | High |
|
||||
| P1 | Tiered compaction (delay LLM summarization) | Moderate | High |
|
||||
| P1 | Post-compaction reconstruction improvements | Moderate | Medium |
|
||||
| P1 | Improved token estimation | Low | Medium |
|
||||
| P2 | Continuation message after compaction | Trivial | Low-Medium |
|
||||
| N/A | Cache-aware `cache_edits` | — | Not applicable (Mistral) |
|
||||
| N/A | Same-cache-key summarization | — | Not applicable |
|
||||
|
||||
## Known Issues Found During Analysis
|
||||
|
||||
There is a bug in `SessionManager.SaveSessionLogAsync()` at line 97:
|
||||
|
||||
```csharp
|
||||
.Where(m => m.Role != ChatRole.System || m.Role != ChatRole.Tool)
|
||||
```
|
||||
|
||||
This condition is always true due to De Morgan's law. It should use `&&` instead of `||` to correctly filter out system and tool messages.
|
||||
|
||||
## Cross-References
|
||||
|
||||
- [[Core]] — SessionManager, compaction flow, token estimation
|
||||
- [[Memory]] — MemoryStore, persistent conversation logs
|
||||
- [[Configuration]] — SessionOptions (ContextTokenThreshold, RetainedMessagesAfterCompacting)
|
||||
@@ -0,0 +1,137 @@
|
||||
# Core Module
|
||||
|
||||
The Core module serves as the central orchestration engine for Luna, managing sessions, message routing, context compaction, and tool integration. It is split into `Luna.Core.Abstractions` for interfaces and `Luna.Core` for the primary implementation.
|
||||
|
||||
## Luna.Core.Abstractions
|
||||
|
||||
This project defines the contracts used by the Core and other modules.
|
||||
|
||||
### ISessionManager
|
||||
The primary interface for managing chat sessions and routing messages.
|
||||
```csharp
|
||||
public interface ISessionManager
|
||||
{
|
||||
IAsyncEnumerable<ChatStreamUpdate> RouteMessagesAsync(string content, string conversationId, string connectionId, CancellationToken ct);
|
||||
Task ClientDisconnectedAsync(string connectionId);
|
||||
}
|
||||
```
|
||||
|
||||
### IChatHubContext
|
||||
Interface for sending responses back to clients via SignalR.
|
||||
```csharp
|
||||
public interface IChatHubContext
|
||||
{
|
||||
Task SendResponseAsync(string connectionId, ChannelMessage message, CancellationToken ct);
|
||||
Task SendStreamingResponseAsync(string connectionId, IAsyncEnumerable<ChatStreamUpdate> messageStream, CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
## Luna.Core
|
||||
|
||||
The main implementation project containing the session logic and SignalR hubs.
|
||||
|
||||
### Session
|
||||
The `Session` class manages the state of an active conversation.
|
||||
```csharp
|
||||
public class Session
|
||||
{
|
||||
public string SessionId { get; set; }
|
||||
public List<ChatMessage> Messages { get; } = new();
|
||||
public int TokenAmount => TokenEstimator.EstimateTokens(Messages);
|
||||
}
|
||||
```
|
||||
|
||||
### SessionManager
|
||||
Implements `ISessionManager`. It coordinates between agents, memory, and the core LLM processing.
|
||||
- **Injected Services**:
|
||||
- `[FromKeyedServices("Core")] IAgent coreAgent`
|
||||
- `[FromKeyedServices("Librarian")] IAgent librarianAgent`
|
||||
- `IOptions<SessionOptions> options`
|
||||
- `IChatStreamUpdateBuilder streamUpdateBuilder`
|
||||
- `IMemoryStore memoryStore`
|
||||
- **State Management**: Maintains an in-memory `Dictionary<string, Session>` for sessions and a `ConcurrentDictionary<string, string>` for mapping connection IDs to session IDs.
|
||||
|
||||
#### Message Routing Flow
|
||||
1. **Session Initialization**: Creates a new session if one does not exist and loads existing [[Memory]] via `memoryStore.GetMemoriesAsync()`.
|
||||
2. **Token Check**: Evaluates current token usage against `MaxContextTokens * ContextTokenThreshold`.
|
||||
3. **Compaction**: If the threshold is exceeded, triggers `CompactSessionAsync`.
|
||||
4. **Processing**: Appends the user message, streams the response from the `coreAgent` via `ProcessStreamingAsync`, and converts AI content to `ChatStreamUpdate` using the `IChatStreamUpdateBuilder`.
|
||||
5. **Persistence**: Appends the assistant response to the session history.
|
||||
|
||||
#### Compaction Strategy
|
||||
When a session exceeds the token threshold, the system:
|
||||
1. Retains the last $N$ messages (defined by `SessionOptions.RetainedMessagesAfterCompacting`).
|
||||
2. Sends all older messages to the `librarianAgent` for summarization.
|
||||
3. Wraps the resulting summary in `<---- MEMORY BEGIN ---->` and `<---- MEMORY END ---->` markers and inserts it at the beginning of the message list.
|
||||
|
||||
#### Disconnect Flow
|
||||
When `ClientDisconnectedAsync` is called:
|
||||
1. The conversation log is saved to the `IMemoryStore`.
|
||||
2. The session and connection mappings are cleaned up.
|
||||
|
||||
### Hubs and Contexts
|
||||
|
||||
#### ChatHub
|
||||
A SignalR hub that serves as the entry point for real-time communication.
|
||||
- **OnConnected**: Creates a `CliChannel` and registers it with the `IChannelManager`.
|
||||
- **OnDisconnected**: Unregisters the channel.
|
||||
- **OnMessageReceived**: Delegates message handling to the `CliChannel.RaiseMessageReceived`.
|
||||
|
||||
#### ChatHubContext
|
||||
Implements `IChatHubContext` using `IHubContext<ChatHub>`. It handles the actual transmission of data to SignalR clients, supporting both discrete and streaming responses.
|
||||
|
||||
### Tools System
|
||||
|
||||
#### IToolbox
|
||||
Provides a mechanism for discovering and exposing tools to the AI.
|
||||
- **Implementation**: Uses reflection to find methods decorated with `[ToolAttribute]`.
|
||||
- **Function Creation**: Generates `AITool` instances using `AIFunctionFactory.Create`.
|
||||
|
||||
#### IToolsProvider
|
||||
Exposes the collection of discovered tools.
|
||||
```csharp
|
||||
public interface IToolsProvider
|
||||
{
|
||||
IEnumerable<AITool> GetTools();
|
||||
}
|
||||
```
|
||||
|
||||
### Token Estimation
|
||||
The `TokenEstimator` provides a heuristic-based token count:
|
||||
- **Calculation**: Number of characters divided by 4.
|
||||
|
||||
## Architecture Flow
|
||||
|
||||
The following flow describes how a message moves through the Core module:
|
||||
|
||||
```text
|
||||
Channel.MessageReceived → ChannelManager → SessionManager.RouteMessagesAsync
|
||||
→ Token Check → Compaction if needed (LibrarianAgent)
|
||||
→ CoreAgent.ProcessStreamingAsync → IChatClient Streaming
|
||||
→ ChatStreamUpdateBuilder → IAsyncEnumerable<ChatStreamUpdate>
|
||||
→ Channel.SendStreamingMessageAsync → Client
|
||||
```
|
||||
|
||||
## Cross-References
|
||||
- [[Channels]]: Management of communication pathways.
|
||||
- [[Memory]]: Long-term and short-term state persistence.
|
||||
- [[Configuration]]: `SessionOptions` and system settings.
|
||||
- [[Skills]]: Integration of specialized capabilities.
|
||||
- [[Scheduler]]: Task timing and execution.
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Project References
|
||||
- Luna.Agents.Abstractions
|
||||
- Luna.Channels
|
||||
- Luna.Channels.Abstractions
|
||||
- [[Configuration]] (Luna.Configuration)
|
||||
- Luna.Core.Abstractions
|
||||
- [[Memory]] (Luna.Memory)
|
||||
- Luna.Providers
|
||||
- Luna.Providers.Abstractions
|
||||
- Luna.Shared
|
||||
|
||||
### NuGet Packages
|
||||
- Microsoft.AspNetCore.OpenApi
|
||||
- Microsoft.Extensions.AI
|
||||
@@ -0,0 +1,84 @@
|
||||
# Memory Module
|
||||
|
||||
## Overview
|
||||
|
||||
The Memory module provides persistent conversation storage for the Luna AI Assistant. It uses a simple **filesystem-based** approach — conversation logs are written as plain text files and read back to seed new sessions with prior context.
|
||||
|
||||
The module consists of a single project: `Luna.Memory`.
|
||||
|
||||
---
|
||||
|
||||
## Interface
|
||||
|
||||
```csharp
|
||||
public interface IMemoryStore
|
||||
{
|
||||
Task AddMemoryAsync(string memory);
|
||||
Task<string> GetMemoriesAsync();
|
||||
}
|
||||
```
|
||||
|
||||
- `AddMemoryAsync` — persists a conversation log or compaction summary to storage.
|
||||
- `GetMemoriesAsync` — retrieves recent memories as a single concatenated string, used to seed new conversations.
|
||||
|
||||
---
|
||||
|
||||
## Implementation
|
||||
|
||||
### MemoryStore
|
||||
|
||||
`MemoryStore` is the concrete `IMemoryStore` implementation. It stores conversation logs as individual files on disk.
|
||||
|
||||
**Storage path**: `~/.luna/memory/conversations/`
|
||||
|
||||
**File naming**: `Luna_Conversation_Log_{yyyyMMddHHmmss}`
|
||||
|
||||
```csharp
|
||||
public class MemoryStore : IMemoryStore
|
||||
```
|
||||
|
||||
| Method | Behavior |
|
||||
|--------|----------|
|
||||
| `AddMemoryAsync` | Creates the storage directory if it does not exist, then writes the memory string to a new timestamped file. |
|
||||
| `GetMemoriesAsync` | Reads the **last 10 files** (ordered by timestamp descending, parsed from the filename), concatenates their contents, and returns the result. Returns an empty string if no files exist. |
|
||||
|
||||
There is no database, no Redis, and no SQLite involved — persistence is purely filesystem-based.
|
||||
|
||||
---
|
||||
|
||||
## Integration with the System
|
||||
|
||||
The `SessionManager` in [[Core]] injects `IMemoryStore` and uses it at three points:
|
||||
|
||||
1. **Session creation** — On the first message in a new conversation, `SessionManager` calls `GetMemoriesAsync()` and, if non-empty, prepends the result as a `ChatMessage` with `ChatRole.System`. This gives the agent prior conversational context.
|
||||
|
||||
2. **Client disconnect** — When a client disconnects, `SessionManager.ClientDisconnectedAsync` triggers `SaveSessionLogAsync`, which formats all user and assistant messages from the session and calls `AddMemoryAsync` to persist the log.
|
||||
|
||||
3. **Compaction** — When the session's token count exceeds the configured threshold (see [[Configuration]] `SessionOptions`), `SessionManager` uses the Librarian agent to summarize older messages. The summary is retained in-session as an assistant message wrapped in:
|
||||
```
|
||||
<---- MEMORY BEGIN ---->
|
||||
[Meta] Conversation Recorded at: {timestamp}
|
||||
{summary}
|
||||
<---- MEMORY END ---->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DI Registration
|
||||
|
||||
Memory services are registered via the `AddMemory()` extension method on `IServiceCollection`.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
**Project references**: None (standalone module).
|
||||
|
||||
**NuGet packages**:
|
||||
- `Microsoft.Extensions.Caching.Memory`
|
||||
- `Microsoft.Extensions.DependencyInjection.Abstractions`
|
||||
- `Microsoft.Extensions.Logging.Abstractions`
|
||||
- `Microsoft.Extensions.Options`
|
||||
- `StackExchange.Redis` (present in csproj, currently unused)
|
||||
- `Microsoft.Data.Sqlite` (present in csproj, currently unused)
|
||||
- `Microsoft.EntityFrameworkCore.Sqlite` (present in csproj, currently unused)
|
||||
@@ -0,0 +1,24 @@
|
||||
# Scheduler
|
||||
|
||||
> [!info] Status: Not Yet Implemented
|
||||
> This module is planned for a future development phase.
|
||||
|
||||
The Scheduler provides a mechanism for Luna to handle time-based operations and periodic tasks. It acts as a temporal bridge between [[Core]] and [[Memory]], ensuring that scheduled actions are executed reliably and within the specified context.
|
||||
|
||||
## Purpose
|
||||
The primary role of the Scheduler is to periodically scan [[Memory]] for pending tasks and coordinate their execution. It manages the lifecycle of long-running or deferred operations, ensuring they are handed off to the appropriate Agents at the right time.
|
||||
|
||||
## Planned Features
|
||||
- **Recurring Tasks**: Support for daily, weekly, or interval-based execution patterns.
|
||||
- **Task Dependencies**: Ability to chain tasks so that one starts only after another completes successfully.
|
||||
- **Priority Queue**: Management of task urgency to ensure critical system operations take precedence.
|
||||
- **Cron Scheduling**: Standardized string-based scheduling for precise control over execution times.
|
||||
|
||||
## Architecture & Integration
|
||||
The Scheduler is expected to integrate deeply with the following modules:
|
||||
- **[[Core]]**: For task execution logic and agent coordination.
|
||||
- **[[Memory]]**: To persist task states and schedules.
|
||||
|
||||
## Likely Dependencies
|
||||
To ensure robust background execution, the module will likely utilize:
|
||||
- `Microsoft.Extensions.Hosting`: Leveraging `IHostedService` or `BackgroundService` for long-lived process management within the .NET ecosystem.
|
||||
@@ -0,0 +1,23 @@
|
||||
# Skills
|
||||
|
||||
> [!info] Status: Not Yet Implemented
|
||||
> This module is planned for a future development phase.
|
||||
|
||||
The Skills module is designed as a plugin system to extend Luna with third-party integrations and specialized tools. It provides a modular framework for adding new capabilities without modifying the core system.
|
||||
|
||||
## Existing Tools System
|
||||
It is important to note that a basic tools system already exists in [[Core]]. This existing system includes:
|
||||
- `IToolbox`: A registry for managing available tools.
|
||||
- `IToolsProvider`: An interface for supplying tools to the assistant.
|
||||
- `ToolAttribute`: Used for reflective discovery of tools within the codebase.
|
||||
|
||||
The Skills module will build upon or complement this system by allowing for more complex, external integrations.
|
||||
|
||||
## Planned Features
|
||||
- **Dynamic Skill Loading**: Support for loading and unloading skills at runtime without requiring a system restart.
|
||||
- **Skill Discovery**: Automatic detection of new skills within designated plugin directories.
|
||||
- **Capability-Based Security**: A granular permission model where skills must declare and be granted specific capabilities (e.g., network access, file system access).
|
||||
- **Third-Party Integrations**: A standardized interface for connecting Luna to external services and APIs.
|
||||
|
||||
## Integration
|
||||
- **[[Core]]**: The Skills module will interface with the existing tools system in the Core to expose its capabilities to the AI Agents.
|
||||
Reference in New Issue
Block a user