Конфигурация и БД
Архитектура конфигурации Creo: структура config.json, model resolution, XDG paths,
миграции и валидация. Архитектура SQLite-хранилища: схема, таблицы, FTS5, миграции БД.
Конфигурация
Структура Config
1type Config struct {
2 Schema string // JSON Schema reference
3 Agent Agent // Модельная конфигурация (обязательно)
4 Locale *string // Язык интерфейса: "ru", "en"
5 Scheduler Scheduler // dreaming, scheduled prompts
6 Skillhub Skillhub // Реестр навыков
7 Tui Tui // Настройки TUI
8 Version string // Версия схемы (управляется мигратором)
9}
Agent
1type Agent struct {
2 Current string // Ключ модели для основных сессий
3 Mini string // Ключ модели для фоновых задач
4 ModelBindings []ModelBinding // CWD-based переопределения
5 Models AgentModels // Map: имя → Model
6}
Scheduler
1type Scheduler struct {
2 DreamModel *string // Явная модель для dreaming (пусто = mini)
3 Dreaming bool // Включить dreaming (default: true)
4 RetentionDays int // Дни хранения soft-deleted (default: 30, 0 = ∞)
5 ScheduleModel *string // Явная модель для scheduled prompts (пусто = current)
6}
Tui
1type Tui struct {
2 BottomPanelVisible *bool // Видимость нижней панели (Ctrl+Y)
3 Mini *bool // Compact mode
4 Shell TuiShell // Windows: "powershell" | "cmd"
5 SidebarVisible *bool // Видимость sidebar (Ctrl+B)
6 Theme string // Цветовая схема (default: "onedark")
7}
Model Resolution
Creo разрешает модель для конкретной задачи через иерархию fallback'ов:
ResolveModel(cwd) → model_bindings (first match) → agent.current
DreamModelName() → scheduler.dream_model → agent.mini
ScheduleModelName() → scheduler.schedule_model → agent.current
MiniModel() → agent.Models[agent.mini]
CurrentModel() → agent.Models[agent.current]
ResolveModel — контекстно-зависимый выбор
1func (c *Config) ResolveModel(cwd string) string {
2 for _, b := range c.Agent.ModelBindings {
3 if matchCwdPattern(b.Cwd, cwd) {
4 if b.Model != nil && *b.Model != "" {
5 if _, ok := c.Agent.Models[*b.Model]; ok {
6 return *b.Model
7 }
8 }
9 break // даже если model невалидна — не пытаться дальше
10 }
11 }
12 return c.Agent.Current
13}
Паттерны model_bindings поддерживают glob (*, ?), суффикс /* для поддиректорий.
Первый матч выигрывает.
Dream/Schedule — явное переопределение
1func (c *Config) DreamModelName() string {
2 if c.Scheduler.DreamModel != nil && *c.Scheduler.DreamModel != "" {
3 if _, ok := c.Agent.Models[*c.Scheduler.DreamModel]; ok {
4 return *c.Scheduler.DreamModel
5 }
6 }
7 return c.Agent.Mini // fallback
8}
XDG Paths
Все пути следуют XDG Base Directory Specification:
| Функция | Переменная | Fallback | Описание |
|---|---|---|---|
ConfigDir() |
$XDG_CONFIG_HOME/creo |
~/.config/creo |
Конфигурация |
ConfigPath() |
— | — | $ConfigDir/config.json |
DataDir() |
$XDG_DATA_HOME/creo |
~/.local/share/creo |
БД сессий |
StateDir() |
$XDG_STATE_HOME/creo |
~/.local/state/creo |
Логи, state |
DatabasePath() |
— | — | $DataDir/sessions.db |
CommandsDir() |
— | — | $ConfigDir/commands |
MemoryProfilePath() |
— | — | $ConfigDir/memory/profile |
MemoryEnvironmentPath(cwd) |
— | — | $cwd/.creo/memory/environment |
SkillsGlobalDir() |
— | — | $ConfigDir/skills |
SkillsProjectDir(cwd) |
— | — | $cwd/.creo/skills |
AutoskillsGlobalDir() |
— | — | $ConfigDir/autoskills |
AutoskillsProjectDir(cwd) |
— | — | $cwd/.creo/autoskills |
ConfigDir использует os.UserConfigDir() (→ ~/.config на Linux, ~/Library/Application Support на macOS).
DataDir и StateDir — ~/.local. Разделение соответствует XDG spec.
Миграции
Два пакета: migrator (фреймворк) и migrations (миграции).
migrator/migrator.go — фреймворк
1type Migrator struct {
2 migrations map[Version]Migration
3 versions []Version // сортированный список
4}
5
6type Version string // "1.0.0", "2.0.0"
7type Migration func(cfg map[string]any) error
Алгоритм Migrate:
- Парсить JSON →
map[string]any - Прочитать
cfg["version"](default:"0.0.0") - Найти все версии > currentVersion (сортированный порядок)
- Применить каждую миграцию последовательно
- Записать
cfg["version"] = latest - Сериализовать обратно в JSON
Семантическое сравнение версий: major → minor → patch.
Миграция 1.0.0
Единственная миграция: из старого плоского формата в секционированный.
Было (v0.0.0):
1{
2 "current": "my-model",
3 "mini": "my-mini",
4 "models": { ... },
5 "model_bindings": [ ... ]
6}
Стало (v1.0.0):
1{
2 "agent": {
3 "current": "my-model",
4 "mini": "my-mini",
5 "models": { ... },
6 "model_bindings": [ ... ]
7 },
8 "scheduler": {
9 "dreaming": true,
10 "dream_model": "my-mini",
11 "schedule_model": "my-model"
12 }
13}
Конфиги без поля "version" считаются 0.0.0 и всегда проходят миграцию.
Загрузка и сохранение
Load()
Read config.json
↓
migrations.Migrator.Migrate(data) ← raw JSON transformation
↓ (если миграция была)
Save(cfg) ← persist обновлённый конфиг
↓
json.Unmarshal → *Config
↓
cfg.validate()
↓
return cfg, nil
Save() — атомарная запись
json.MarshalIndentс отступами- Создание temp-файла (
.creo-config-*.tmp) - Write +
chmod 0644 os.Rename— POSIX-атомарный rename поверх temp-файла
Даже если процесс упадёт посреди записи, config.json останется нетронутым.
Валидация
Двухуровневая:
Модель: для openai-провайдера требуется api_key. Для ollama — допустимо без ключа.
Конфиг:
- Каждая модель проходит
Model.validate() currentиminiдолжны существовать вAgent.Modelsdream_modelиschedule_model(если заданы явно) должны существоватьmodel_bindings:cwdобязателен,model/mini(если заданы) должны существовать
Params (через generated UnmarshalJSON):
frequency_penalty: -2 ≤ x ≤ 2presence_penalty: -2 ≤ x ≤ 2repeat_penalty: x ≥ 0temperature: 0 ≤ x ≤ 2top_k: x ≥ 0top_p: 0 ≤ x ≤ 1context_length: x ≥ 1locale: паттерн^[a-zA-Z]{2,3}(-[a-zA-Z0-9]+)*$
SQLite-хранилище
Единый файл: $XDG_DATA_HOME/creo/sessions.db.
DSN и connection pooling
1dsn := "file:" + dbPath + "?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)"
2conn.SetMaxOpenConns(128)
- WAL mode: reader не блокирует writer, несколько concurrent readers
- busy_timeout=5000: writer-конкуренция ждёт до 5 секунд
- SetMaxOpenConns(128): пул соединений для параллельных reads при стриминге.
Исторически
SetMaxOpenConns(1)вызывал deadlock — pending-сообщение блокировалось вdatabase/sqlpool.
Таблицы
| Таблица | Назначение | Ключевые поля |
|---|---|---|
sessions |
Метаданные сессий | id, cwd, parent_session, created_at, leaf_id, deleted_at, last_active_at, session_type, name_source |
entries |
Сообщения и стримы | id, session_id, parent_id, type, timestamp, data |
session_meta |
Key-value пары сессии | session_id, key, value (PK: оба) |
entries_fts |
FTS5-индекс для полнотекстового поиска | text (virtual table) |
todos |
Todo-листы (JSON-массив) | session_id, data, updated_at |
pending_messages |
Очередь сообщений | id, session_id, message_id, data, created_at |
scheduler_tasks |
Задачи планировщика | id, type, session_id, prompt, scheduled_for, repeat_spec, status, locked_at, locked_by, created_at, last_error |
kv_store |
Глобальное key-value | key, value, updated_at |
multiagent_children |
Дочерние агенты | child_id, task_id, parent_id, role, model, status, trace_id, created_at, completed_at |
Схема sessions
1CREATE TABLE sessions (
2 id TEXT PRIMARY KEY,
3 version INTEGER NOT NULL,
4 cwd TEXT NOT NULL,
5 parent_session TEXT,
6 created_at TEXT NOT NULL,
7 leaf_id TEXT,
8 name TEXT DEFAULT '',
9 deleted_at TEXT, -- миграция: soft-delete
10 locked_at TEXT, -- миграция: PID lock
11 locked_pid INTEGER, -- миграция: PID locker
12 running INTEGER DEFAULT 0, -- миграция: activity flag
13 session_type TEXT NOT NULL DEFAULT 'interactive', -- миграция
14 name_source TEXT DEFAULT '', -- миграция
15 last_active_at TEXT -- миграция: сортировка "most recent"
16);
Индексы
1CREATE INDEX idx_entries_session ON entries(session_id);
2CREATE INDEX idx_entries_parent ON entries(parent_id);
3CREATE INDEX idx_pending_session ON pending_messages(session_id);
4CREATE INDEX idx_scheduler_due ON scheduler_tasks(scheduled_for);
5CREATE INDEX idx_scheduler_status ON scheduler_tasks(status);
6CREATE INDEX idx_multiagent_parent ON multiagent_children(parent_id);
7CREATE INDEX idx_multiagent_task ON multiagent_children(task_id);
Миграции БД
Шесть inline-миграций в Open(), применяются при открытии. Все идемпотентны:
проверяют PRAGMA table_info перед ALTER TABLE.
| Миграция | Добавляет | Назначение |
|---|---|---|
migrateDeletedAt |
deleted_at TEXT |
Soft-delete сессий |
migrateLockColumns |
locked_at, locked_pid, running |
Process locking |
migrateSessionType |
session_type TEXT |
Batch vs interactive |
migrateNameSource |
name_source TEXT |
User-set vs auto name |
migrateLastActiveAt |
last_active_at TEXT + backfill |
Сортировка по свежести |
migrateGoalIDToTaskID |
переименование goal_id → task_id |
Ясность семантики |
FTS5 — полнотекстовый поиск
entries_fts — virtual table FTS5 для поиска по содержимому entries.data.
Поддержка:
- Булевы операторы:
AND(неявный),OR,NOT - Prefix matching:
config*→ «configuration», «configure» - Фразовый поиск:
"git reset"— точное совпадение - Комбинирование:
bug OR fix AND "user input"
Результаты включают:
- Bookends — первые 2 и последние 2 сообщения сессии (цель → результат)
- Контекст — ±3 сообщения вокруг совпадения с маркером
>>>
KV Store
kv_store — глобальное key-value хранилище для operational state между сессиями:
курсоры, last-processed IDs, чекпоинты обработки.
1CREATE TABLE kv_store (
2 key TEXT PRIMARY KEY,
3 value TEXT,
4 updated_at TEXT
5);
Ключи глобальные, namespaced: email:last_id, sync:cursor, task:cleanup:last_run.
Файлы
Config
| Файл | Назначение |
|---|---|
internal/config/config.go |
Load, Save, CurrentModel, MiniModel, DreamModelName, ScheduleModelName, ResolveModel, validate |
internal/config/config.gen.go |
Generated from JSON Schema (go-jsonschema) |
internal/config/paths.go |
XDG path resolution |
internal/config/migrator/migrator.go |
Version, Migrator, Register, Migrate |
internal/config/migrations/migrations.go |
init() → register("1.0.0", migrateToV1_0_0) |
schemes/config.schema.json |
JSON Schema для автодополнения в редакторе |
Storage
| Файл | Назначение |
|---|---|
internal/storage/db.go |
Open, schema, inline migrations, DSN |
internal/storage/session_repo.go |
SessionRepo — CRUD сессий |
internal/storage/session_entries.go |
Entries — сообщения внутри сессий |
internal/storage/fts.go |
FTS5 index management |
internal/storage/search.go |
Полнотекстовый поиск (search_sessions) |
internal/storage/search_extras.go |
Bookends, context around match |
internal/storage/scheduler_tasks.go |
Scheduler tasks CRUD |
internal/storage/pending_messages.go |
Pending messages queue |
internal/storage/kv_store.go |
KV store CRUD |
internal/storage/session_stats.go |
Статистика сессий |
internal/storage/session_lock.go |
Session locking (lock/unlock/heartbeat) |
internal/storage/session_lock_unix.go |
Unix-specific lock implementation |
internal/storage/session_lock_windows.go |
Windows-specific lock implementation |