Event.Error уже эмитился в live-SSE, но если клиент подключился
после провала хода (или опрашивает историю через getMessages
вместо SSE), он видел только user-сообщение без следа, что ход
провалился. Это и нужно было поправить.
* :proto
- Message.Error(id, message, code?, date) — терминальная
персистентная проекция Event.Error. Audit-only; в live-стриме
по-прежнему приходит Event.Error.
* :standalone
- MessageRecord.Error с тем же контрактом.
- SqliteMessageStore: kind "error", JSON-payload {message, code};
encoding-ошибки round-trip покрыты тестом (с code и без).
- ChatConversation.failTurn(message, code?): пишет MessageRecord.Error
в audit (кроме temp-диалогов), затем эмитит Event.Error + Event.End.
Все три exit-точки из runTurn (пустой parts, init-catch,
stream-catch) теперь через failTurn.
- ChatConversation: при ошибке стрима живой LiteConversation
сбрасывается — следующий send пересоберёт его из working_memory.
Раньше оставляли битую инстанцию вопреки KDoc класса.
- toProto: маппит MessageRecord.Error → Message.Error.
* docs
- STANDALONE.md: добавлен Message.Error в таблицу dual-log + абзац
про персист ошибок (audit/working_memory, сброс liteConv).
- ARCHITECTURE.md: Message.Error в сигнатуре Message, failTurn
в ChatConversation, Message.Error в audit log.
- Поправлен пример describe() в доке тулов (реальный формат —
OpenAI-style function wrapper, а не голый JSON Schema).
Тесты:
- ChatAgentTest: LLM failure → Event.Error + Event.End + Error
record в audit + backfill через getMessages.
- PersistenceTest: MessageRecord.Error round-trip (с code и без).
- :standalone jvmTest 70 (было 69).
E2E: неверный model id (HTTP 400) → polling GET /messages теперь
возвращает user_message + error (без assistant_message).
Пользовательские инструкции («навыки») живут в указанной папке
(AGENTIK_SKILLS_DIR), рекурсивно читаются при старте и попадают в
системный промпт в сжатом виде: только имя + краткое описание.
Полный текст модель подгружает по требованию, вызывая встроенный
инструмент read_skill(name).
*:skills
- SkillCatalog + SkillPrompt (commonMain): рендер секции системного
промпта; тело навыка в промпт не течёт.
- SkillParser.parseAuto(): теперь читает и opencode-стиль SKILL.md
(YAML frontmatter + markdown тело), и голый *.yaml/*.yml
(поля name, description, опц. body). parseOrThrow для strict-путей.
- SkillParseError.render(): человекочитаемое описание ошибки для
логов и диагностики.
- SkillLoader (jvmMain): рекурсивный обход папки, детерминированный
порядок (по пути), ошибки отдельных файлов не валят загрузку;
дубликаты имён → ошибка, выигрывает первый по пути.
* :standalone
- AgentikConfig.skillsDir + env AGENTIK_SKILLS_DIR.
- ChatAgent: параметр skills (SkillCatalog); системный промпт
автоматически дополняется секцией «## Навыки» и в working memory
сидится вместе с базовым промптом.
- При непустом каталоге в tools автоматически добавляется
SkillReadTool (имя read_skill) — модель может загрузить полный
текст навыка, как обычный LiteTool.
- Main.kt: загружает навыки и шумно логирует ошибки загрузки в stderr.
* docs
- STANDALONE.md: секция «Навыки (skills)», env-переменная в таблице.
- Формат SKILL.md (opencode frontmatter) + голый *.yaml/*.yml.
Тесты: :skills jvmTest 34, :standalone jvmTest 69 (новые — состав
системного промпта, регистрация read_skill, навыки не утекают в
промпт телом).