0.167.4
Changed
- Навыки, создаваемые агентом через
skill_manage, — только индивидуальные (ADR 2026-08-03). Инструментskill_manageагента ранее создавал глобальные навыки (store.CreateSkillсOwnerAgentID = nil) и лишь привязывал их к текущему агенту. Это противоречило модели владения и создавало IDOR-подобные уязвимости:updateAction/deleteActionработали поskill_idбез проверки принадлежности,listActionпоказывал все навыки (включая индивидуальные других агентов), аgetActionпозволял читать чужой приватный контент.
Изменения (internal/agent/skill_manage_tool.go):
createAction: теперь вызываетstore.CreateAgentOwnedSkill(ставитowner_agent_id = agentID) вместоCreateSkill+AddAgentSkill. Навык становится индивидуальным (приватным для агента) с момента создания.- Автоматический префикс имени: имя создаваемого/переименовываемого навыка формируется как
<agentSlug>-<shortName>, гдеagentSlug— результатNameSlug(Agent.Name). Гарантирует уникальность на системном UNIQUE-индексеskills.name. Примеры: агент «Семён» →semen-my-skill; «Дмитрий» →dmitriy-my-skill; «My Agent» →my-agent-.... Полное имя валидируется^[a-z][a-z0-9-]{2,40}$. updateAction/deleteAction: добавлена ownership-проверка (ownedByCurrentAgent) — операция разрешена только еслиskill.OwnerAgentID == agentID. Builtin, глобальные и чужие навыки недоступны; при отказе возвращаетсяskill not found(fail-closed, не палит существование — согласовано сgetAction).getAction: добавлена проверкаreadableByCurrentAgent— builtin и глобальные общедоступны для чтения, индивидуальные — только свои. Чужой индивидуальный навык возвращаетskill not found.listAction:ListSkillsзаменён наListAgentSkills(agentID)— показывает только навыки, подключённые к текущему агенту (симметрично блоку «Навыки агента» в UI). Чужие индивидуальные навыки не видны.- Обработка ошибок БД:
GetSkill/GetSkillByNameразличают not-found (store.IsNotFoundError) от реальных ошибок БД; UNIQUE-нарушения при create/rename (TOCTOU) переводятся в дружелюбное «skill with name ‘…’ already exists» черезstore.IsUniqueViolationError.
Транслитерация кириллицы (internal/agent/slug.go, новая утилита): NameSlug переводит имя агента в slug фонетической схемой (русский алфавит + Ё): «Семён»→semen, «Дмитрий»→dmitriy, «Юрий»→yuriy, «Щорс»→shchors. Твёрдый/мягкий знак опускаются. Fallback agent для пустого результата (эмодзи/пробелы). Отдельна от workspace.SanitizeName намеренно — изменение SanitizeName рассинхронило бы существующие workspace-каталоги (см. ADR, альтернатива A).
Builtin-навык skill-creator (миграция 105, SQLite + PG): обновлены description и content — упоминается индивидуальность навыков, автоматический префикс имени, ownership-правила.
Тесты: 32 unit-теста TestNameSlug (positive/negative/edge) + 11 integration-тестов TestSkillManage_* через реальный SQLiteStore (create/update/delete/list/get, ownership, prefix, duplicate, validation).
Известные ограничения (зафиксированы в ADR): agents.name не имеет UNIQUE-констрейнта — два агента с именами, дающими одинаковый slug (например «Ivan» и «Иван»), коллизируют на skills.name; обрабатывается дружелобной ошибкой. Предсуществующая не-атомарность CreateAgentOwnedSkill (orphan при сбое linking) вынесена в отдельное.
Связанный техдолг: (P3) — обернуть CreateAgentOwnedSkill в транзакцию.