@ykurilov/agent-chat-skills 0.58.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +3 -0
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/types/create-skills.d.ts +3 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-skills
|
|
2
2
|
|
|
3
|
+
## 0.59.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fc1f387: Поддельные служебные блоки в ответе модели вырезаются сами. Один список форматов: ядро (`CORE_SERVICE_BLOCKS` — сводка, шаги ответа, место на экране, служебные пометки), пакеты (память `<agent_memory>`, вложения `<attachment>`, навыки `<skill>` и их список, строки сценария) и приложение — `agent.serviceBlocks` (`{ tag }` или `{ name, line, match?, until? }`, ошибка — `TypeError` при создании службы). Блок с начала строки вырезается в потоке до экрана, записи и следующего шага; в прошлых ответах — только в запросе, байт в байт одинаково (кэш). Код, цитаты и упоминания посреди фразы не трогаются, пустым ответ не остаётся. Журнал: `[служебный блок вырезан] тег=… чат=… ответ=… модель=… провайдер=…`.
|
|
8
|
+
|
|
9
|
+
### Как обновить приложение
|
|
10
|
+
|
|
11
|
+
Ничего делать не нужно. Своя вырезка служебных блоков в приложении больше не нужна — свои теги передайте в `agent.serviceBlocks`.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [fc1f387]
|
|
16
|
+
- @ykurilov/agent-chat-core@0.59.0
|
|
17
|
+
|
|
3
18
|
## 0.58.0
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -36,6 +36,9 @@ mountChat(app, createSkillsHandler({ skills, resolveUser })); // адреса э
|
|
|
36
36
|
приложения. Совпадение имени — ошибка при запуске. Next.js: `createNextRouteHandlers(skillsHandler)` в
|
|
37
37
|
`app/api/skills/[[...path]]/route.ts`.
|
|
38
38
|
|
|
39
|
+
Ещё `withSkills` добавляет к `agent.serviceBlocks` форматы навыков (`skills.serviceBlocks`): тело `<skill …>` и список
|
|
40
|
+
навыков с заголовком `listHeading`. Если модель повторит их в ответе, ядро вырежет их до экрана и записи.
|
|
41
|
+
|
|
39
42
|
Настройки `createSkills`: `maxBodyChars` (по умолчанию 50 000), `modelTexts` (`englishSkillsModelTexts` для
|
|
40
43
|
англоязычного агента), `log` и `logError` (по умолчанию `console`).
|
|
41
44
|
|
package/dist/index.js
CHANGED
|
@@ -164,7 +164,9 @@ ${skill.body}
|
|
|
164
164
|
toolNames: [USE_SKILL_TOOL],
|
|
165
165
|
isAdmin,
|
|
166
166
|
maxBodyChars,
|
|
167
|
-
manage: createSkillsManager({ store, maxBodyChars, now })
|
|
167
|
+
manage: createSkillsManager({ store, maxBodyChars, now }),
|
|
168
|
+
// Тело навыка — тег `<skill …>`; список — заголовок `listHeading` и строки навыков до пустой строки.
|
|
169
|
+
serviceBlocks: [{ tag: "skill" }, { name: "skills_list", line: texts.listHeading, until: "paragraph" }]
|
|
168
170
|
};
|
|
169
171
|
}
|
|
170
172
|
|
|
@@ -180,6 +182,7 @@ function withSkills(agent, skills) {
|
|
|
180
182
|
const appBlocks = agent.contextBlocks;
|
|
181
183
|
return {
|
|
182
184
|
...agent,
|
|
185
|
+
serviceBlocks: [...agent.serviceBlocks ?? [], ...skills.serviceBlocks],
|
|
183
186
|
tools: (model) => {
|
|
184
187
|
const own = typeof appTools === "function" ? appTools(model) : appTools ?? {};
|
|
185
188
|
const name = clash(own);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/create-skills.ts","../src/loaded-skills.ts","../src/skill-texts.ts","../src/skills-manager.ts","../src/skill-visibility.ts","../src/with-skills.ts","../src/store/memory.ts","../src/skills-handler.ts"],"sourcesContent":["import { tool } from \"ai\";\nimport type { ToolSet } from \"ai\";\nimport { z } from \"zod\";\nimport type { AgentToolsModel, ContextBlock, ContextBlocksInput } from \"@ykurilov/agent-chat-core\";\nimport { isSkillVisibleInHistory, skillHeader } from \"./loaded-skills\";\nimport type { BranchStorage } from \"./loaded-skills\";\nimport { createSkillsManager } from \"./skills-manager\";\nimport type { SkillsManager } from \"./skills-manager\";\nimport { DEFAULT_MAX_BODY_CHARS } from \"./skill-rules\";\nimport { russianSkillsModelTexts, SKILLS_BLOCK_KEY, USE_SKILL_TOOL } from \"./skill-texts\";\nimport type { SkillsModelTexts } from \"./skill-texts\";\nimport { visibleStatuses } from \"./skill-visibility\";\nimport type { IsSkillsAdmin, SkillsTurn } from \"./skill-visibility\";\nimport { byName } from \"./store/skill-store\";\nimport type { SkillBrief, SkillStore } from \"./types\";\n\nexport interface CreateSkillsOptions {\n store: SkillStore;\n /** Админ ли человек — для экрана навыков. Навыки «только админам» в ходе видны по `viewer.isAdmin` (`agent.viewer`). */\n isAdmin: IsSkillsAdmin;\n /** Хранилище чатов службы: `use_skill` смотрит, видно ли тело навыка в истории чата. */\n chatStorage: BranchStorage;\n /** Предел тела навыка в знаках; по умолчанию 50 000. */\n maxBodyChars?: number;\n /** Тексты для модели; по умолчанию русские (`englishSkillsModelTexts` — английские). */\n modelTexts?: SkillsModelTexts;\n /** Строка в журнал о каждой загрузке тела; по умолчанию `console.info`. */\n log?: (line: string) => void;\n logError?: (message: string) => void;\n now?: () => Date;\n}\n\n/** Навыки агента: список в контексте, инструмент загрузки и управление для админ-экрана. */\nexport interface Skills {\n /** Для `agent.contextBlocks`: блок `skills` со списком видимых навыков, без тел. */\n contextBlocks(input: ContextBlocksInput): Promise<ContextBlock[]>;\n /** Для `agent.tools`: инструмент `use_skill`. Описание одно на все чаты, видимость проверяется при вызове. */\n tools(model: AgentToolsModel): ToolSet;\n toolNames: readonly string[];\n isAdmin: IsSkillsAdmin;\n /** Предел тела навыка в знаках: по нему же адреса экрана считают предел запроса. */\n maxBodyChars: number;\n /** Действия админ-экрана: с проверкой полей, автором и временем. */\n manage: SkillsManager;\n}\n\nexport function createSkills(options: CreateSkillsOptions): Skills {\n const { store, isAdmin, chatStorage } = options;\n const texts = options.modelTexts ?? russianSkillsModelTexts;\n const log = options.log ?? ((line: string) => console.info(line));\n const logError = options.logError ?? ((message: string) => console.error(message));\n const now = options.now ?? (() => new Date());\n const maxBodyChars = options.maxBodyChars ?? DEFAULT_MAX_BODY_CHARS;\n let viewerHintShown = false;\n\n /** Видимые статусы хода; есть админские навыки, а прав хода нет — один раз подсказка в журнал. */\n function statusesOf(turn: SkillsTurn, skills: readonly SkillBrief[]) {\n if (turn.viewer === undefined && !viewerHintShown && skills.some((skill) => skill.status === \"admins\")) {\n viewerHintShown = true;\n log(\"skills: навыки «только админам» не видны: задайте agent.viewer, который возвращает { isAdmin }\");\n }\n return visibleStatuses(turn);\n }\n\n async function contextBlocks(input: ContextBlocksInput): Promise<ContextBlock[]> {\n const all = await store.listBrief();\n const statuses = statusesOf(input, all);\n const skills = all.filter((skill) => statuses.has(skill.status)).sort(byName);\n if (skills.length === 0) return [];\n // Текст зависит только от списка: пока навыки не правят, блок тот же байт в байт и ядро его не шлёт заново.\n const lines = skills.map((skill) => `- ${skill.name}: ${skill.whenToUse}`);\n return [{ key: SKILLS_BLOCK_KEY, text: [texts.listHeading, ...lines].join(\"\\n\") }];\n }\n\n /** `given` — заголовки тел, уже отданных в этой попытке хода: два вызова в одном шаге модели истории ещё не видят. */\n async function useSkill(model: AgentToolsModel, name: string, given: Set<string>): Promise<string> {\n const skill = await store.get(name.trim());\n if (!skill || !statusesOf(model, [skill]).has(skill.status)) return texts.notFound(name);\n\n const header = skillHeader(skill);\n if (given.has(header)) return texts.alreadyLoaded(skill.name, skill.version);\n given.add(header);\n let visible = false;\n try {\n visible = await isSkillVisibleInHistory(chatStorage, model.chatId, header);\n } catch (error) {\n // Не прочитали историю — отдаём тело: лишний раз загрузить лучше, чем оставить агента без указаний.\n logError(`skills: history of chat ${model.chatId} not read: ${String(error)}`);\n }\n if (visible) return texts.alreadyLoaded(skill.name, skill.version);\n\n const turnId = model.turnId ?? model.messageId ?? \"\";\n try {\n await store.recordLoad({ name: skill.name, version: skill.version, chatId: model.chatId, ownerId: model.ownerId, turnId, loadedAt: now().toISOString() });\n } catch (error) {\n logError(`skills: load of \"${skill.name}\" not recorded: ${String(error)}`);\n }\n log(`skills: loaded \"${skill.name}\" v${skill.version} chat=${model.chatId} owner=${model.ownerId} turn=${turnId}`);\n return `${header}\\n${skill.body}\\n</skill>`;\n }\n\n return {\n contextBlocks,\n // Ядро собирает инструменты заново на каждую попытку хода: повтор после сбоя начинает учёт отданных тел с нуля.\n tools: (model) => {\n const given = new Set<string>();\n return {\n [USE_SKILL_TOOL]: tool({\n description: texts.toolDescription,\n inputSchema: z.object({ name: z.string().describe(texts.nameDescription) }),\n execute: ({ name }) => useSkill(model, name, given),\n }),\n };\n },\n toolNames: [USE_SKILL_TOOL],\n isAdmin,\n maxBodyChars,\n manage: createSkillsManager({ store, maxBodyChars, now }),\n };\n}\n","import { createHash } from \"node:crypto\";\nimport { activeCompaction, loadCurrentBranch } from \"@ykurilov/agent-chat-core\";\nimport type { ChatStorage } from \"@ykurilov/agent-chat-core\";\nimport { USE_SKILL_TOOL } from \"./skill-texts\";\n\nexport type BranchStorage = Pick<ChatStorage, \"listMessages\" | \"listCompactions\" | \"listBranchSelections\">;\n\n/**\n * Начало результата `use_skill` с телом: по нему видно, какой навык и какой версии уже перед глазами модели.\n * Отпечаток тела отличает навык, созданный заново с тем же именем: его версия снова 1, а текст другой.\n */\nexport function skillHeader(skill: { name: string; version: number; body: string }): string {\n const hash = createHash(\"sha256\").update(skill.body).digest(\"hex\").slice(0, 12);\n return `<skill name=\"${skill.name}\" version=\"${skill.version}\" hash=\"${hash}\">`;\n}\n\n/**\n * Тело навыка с этим заголовком (`skillHeader`) уже видно модели: оно в результате `use_skill` на текущей ветке после сжатой части.\n * Другая ветка («Переделать», правка вопроса) и сжатая часть не в счёт — там тело модели не видно.\n */\nexport async function isSkillVisibleInHistory(storage: BranchStorage, chatId: string, header: string): Promise<boolean> {\n const branch = await loadCurrentBranch(storage, chatId);\n const active = activeCompaction(branch, await storage.listCompactions(chatId));\n return branch.slice(active ? active.throughIndex + 1 : 0).some((message) =>\n message.parts.some((part) => {\n if (part.type !== `tool-${USE_SKILL_TOOL}`) return false;\n const call = part as { state?: string; output?: unknown };\n return call.state === \"output-available\" && typeof call.output === \"string\" && call.output.startsWith(header);\n }),\n );\n}\n","/** Имя инструмента загрузки навыка. */\nexport const USE_SKILL_TOOL = \"use_skill\";\n\n/** Ключ блока контекста со списком навыков. */\nexport const SKILLS_BLOCK_KEY = \"skills\";\n\n/** Тексты для модели. Постоянные: от них зависит кэш, поэтому в них нет ни списка, ни времени. */\nexport interface SkillsModelTexts {\n /** Первая строка блока со списком: что такое навыки и когда их грузить. */\n listHeading: string;\n /** Описание инструмента `use_skill`. */\n toolDescription: string;\n /** Описание поля `name`. */\n nameDescription: string;\n /** Тело этой версии уже есть выше в переписке. */\n alreadyLoaded(name: string, version: number): string;\n /** Навыка нет, он выключен или не виден в этом чате — ответ один, чтобы не выдать чужой навык. */\n notFound(name: string): string;\n}\n\nexport const russianSkillsModelTexts: SkillsModelTexts = {\n listHeading:\n \"Навыки — готовые указания для отдельных задач. Если задача человека подходит под навык, до начала работы загрузи его указания инструментом use_skill и следуй им. Не подходит — не загружай.\",\n toolDescription:\n \"Загружает указания навыка из списка «Навыки» в контексте. Зови, когда задача подходит под навык, до начала работы. Загруженные указания остаются в переписке: повторно звать не нужно.\",\n nameDescription: \"Имя навыка из списка — как в списке, до двоеточия.\",\n alreadyLoaded: (name, version) => `Навык «${name}» версии ${version} уже загружен выше в переписке — следуй тем указаниям.`,\n notFound: (name) => `Навыка «${name}» нет. Бери навыки только из списка «Навыки» в контексте.`,\n};\n\nexport const englishSkillsModelTexts: SkillsModelTexts = {\n listHeading:\n \"Skills are ready-made instructions for specific tasks. If the person's task matches a skill, load its instructions with the use_skill tool before you start and follow them. If it doesn't match, don't load it.\",\n toolDescription:\n \"Loads the instructions of a skill from the “Skills” list in the context. Call it before starting when the task matches a skill. Loaded instructions stay in the conversation: no need to call it again.\",\n nameDescription: \"Skill name exactly as in the list, before the colon.\",\n alreadyLoaded: (name, version) => `Skill “${name}” version ${version} is already loaded above in the conversation — follow those instructions.`,\n notFound: (name) => `There is no skill “${name}”. Only use skills from the “Skills” list in the context.`,\n};\n","import { checkSkillName, checkSkillStatus, checkSkillText } from \"./skill-rules\";\nimport { SkillNotFoundError } from \"./types\";\nimport type { Skill, SkillBrief, SkillLoadStats, SkillStatus, SkillStore, SkillVersion } from \"./types\";\n\n/** Сколько последних загрузок показывает карточка навыка. */\nexport const RECENT_LOADS_LIMIT = 20;\n\nexport interface SkillSummary extends SkillBrief {\n /** Сколько раз тело загружали в чатах. */\n loads: number;\n}\n\nexport interface SkillsManager {\n list(): Promise<SkillSummary[]>;\n /** Навык и учёт загрузок; нет — `null`. */\n get(name: string): Promise<{ skill: Skill; loads: SkillLoadStats } | null>;\n create(input: { name: string; whenToUse: string; body: string; status?: string }, author: string): Promise<Skill>;\n updateText(name: string, edit: { whenToUse: string; body: string; expectedVersion: number }, author: string): Promise<Skill>;\n setStatus(name: string, status: string, author: string): Promise<Skill>;\n versions(name: string): Promise<SkillVersion[]>;\n /** Откат — новая версия с текстом старой; текст проверяется нынешними правилами; история не переписывается. */\n rollback(name: string, version: number, expectedVersion: number, author: string): Promise<Skill>;\n delete(name: string): Promise<boolean>;\n}\n\nexport function createSkillsManager({ store, maxBodyChars, now }: { store: SkillStore; maxBodyChars: number; now: () => Date }): SkillsManager {\n const at = () => now().toISOString();\n return {\n async list() {\n const [skills, counts] = await Promise.all([store.listBrief(), store.loadCounts()]);\n return skills.map((skill) => ({ ...skill, loads: counts[skill.name] ?? 0 }));\n },\n async get(name) {\n const skill = await store.get(name);\n return skill ? { skill, loads: await store.loadStats(name, RECENT_LOADS_LIMIT) } : null;\n },\n async create(input, author) {\n checkSkillName(input.name);\n const status: SkillStatus = input.status === undefined ? \"off\" : checkSkillStatus(input.status);\n return store.create({ name: input.name, ...checkSkillText(input, maxBodyChars), status, author }, at());\n },\n async updateText(name, edit, author) {\n return store.updateText(name, { ...checkSkillText(edit, maxBodyChars), author, expectedVersion: edit.expectedVersion }, at());\n },\n async setStatus(name, status, author) {\n return store.setStatus(name, checkSkillStatus(status), author, at());\n },\n versions: (name) => store.listVersions(name),\n async rollback(name, version, expectedVersion, author) {\n const old = await store.getVersion(name, version);\n if (!old) throw new SkillNotFoundError(name);\n return store.updateText(name, { ...checkSkillText(old, maxBodyChars), author, expectedVersion }, at());\n },\n delete: (name) => store.delete(name),\n };\n}\n","import type { AgentAudience, AgentViewer } from \"@ykurilov/agent-chat-core\";\nimport type { SkillStatus } from \"./types\";\n\n/** Решение приложения: админ ли этот человек — для экрана навыков. Запрос есть у вызовов из HTTP-адресов навыков. */\nexport type IsSkillsAdmin = (user: { id: string }, request?: Request) => boolean | Promise<boolean>;\n\n/** Ход: с кем говорит агент и права человека на ход (`agent.viewer` ядра). */\nexport interface SkillsTurn {\n audience?: AgentAudience;\n viewer?: AgentViewer;\n}\n\n/**\n * Какие статусы видны в этом ходе. Всем — `everyone`. Админские — по `viewer.isAdmin` (права из базы приложения, один\n * раз в начале хода; в копии чата человека — права человека) и не в группе: там их видели бы все участники.\n */\nexport function visibleStatuses(turn: SkillsTurn): ReadonlySet<SkillStatus> {\n return turn.audience !== \"group\" && turn.viewer?.isAdmin === true ? new Set([\"everyone\", \"admins\"]) : new Set([\"everyone\"]);\n}\n","import type { ToolSet } from \"ai\";\nimport type { AgentSettings } from \"@ykurilov/agent-chat-core\";\nimport type { Skills } from \"./create-skills\";\nimport { SKILLS_BLOCK_KEY } from \"./skill-texts\";\n\n/**\n * Настройки агента с навыками: блок `skills` после блоков приложения, `use_skill` рядом с инструментами приложения.\n * Совпадение имени инструмента или ключа блока — ошибка: тихо подменить чужое нельзя.\n */\nexport function withSkills(agent: AgentSettings, skills: Skills): AgentSettings {\n const clash = (own: ToolSet) => skills.toolNames.find((name) => Object.hasOwn(own, name));\n const clashMessage = (name: string) => `Инструмент «${name}» задан и приложением, и навыками: переименуйте инструмент приложения`;\n const appTools = agent.tools;\n if (appTools && typeof appTools !== \"function\") {\n const name = clash(appTools);\n if (name) throw new Error(clashMessage(name));\n }\n const appBlocks = agent.contextBlocks;\n return {\n ...agent,\n tools: (model) => {\n const own = typeof appTools === \"function\" ? appTools(model) : (appTools ?? {});\n const name = clash(own);\n if (name) throw new Error(clashMessage(name));\n return { ...own, ...skills.tools(model) };\n },\n contextBlocks: async (input) => {\n const [own, fromSkills] = await Promise.all([appBlocks ? appBlocks(input) : [], skills.contextBlocks(input)]);\n if (own?.some((block) => block.key === SKILLS_BLOCK_KEY)) {\n throw new Error(`Ключ блока «${SKILLS_BLOCK_KEY}» занят навыками: переименуйте блок приложения`);\n }\n return [...(own ?? []), ...fromSkills];\n },\n };\n}\n","import { SkillExistsError, SkillNotFoundError, SkillVersionConflictError } from \"../types\";\nimport type { Skill, SkillLoad, SkillStore, SkillVersion } from \"../types\";\nimport { brief, byName, sameText, storedText } from \"./skill-store\";\n\n/** Навыки в памяти процесса: для тестов и проб, живут до перезапуска. */\nexport function memorySkillStore(): SkillStore {\n const skills = new Map<string, Skill>();\n const versions = new Map<string, SkillVersion[]>();\n let loads: SkillLoad[] = [];\n\n const existing = (name: string): Skill => {\n const skill = skills.get(name);\n if (!skill) throw new SkillNotFoundError(name);\n return skill;\n };\n\n return {\n async list() {\n return [...skills.values()].sort(byName).map((skill) => ({ ...skill }));\n },\n async listBrief() {\n return [...skills.values()].sort(byName).map(brief);\n },\n async get(name) {\n const skill = skills.get(name);\n return skill ? { ...skill } : null;\n },\n async create(input, at) {\n if (skills.has(input.name)) throw new SkillExistsError(input.name);\n const skill: Skill = {\n name: input.name,\n ...storedText(input),\n status: input.status,\n version: 1,\n createdBy: input.author,\n createdAt: at,\n updatedBy: input.author,\n updatedAt: at,\n };\n skills.set(skill.name, skill);\n versions.set(skill.name, [{ name: skill.name, version: 1, whenToUse: skill.whenToUse, body: skill.body, author: input.author, createdAt: at }]);\n return { ...skill };\n },\n async updateText(name, edit, at) {\n const skill = existing(name);\n if (skill.version !== edit.expectedVersion) throw new SkillVersionConflictError(name, skill.version);\n const text = storedText(edit);\n if (sameText(skill, text)) return { ...skill };\n const next: Skill = { ...skill, ...text, version: skill.version + 1, updatedBy: edit.author, updatedAt: at };\n skills.set(name, next);\n versions.get(name)!.push({ name, version: next.version, whenToUse: next.whenToUse, body: next.body, author: edit.author, createdAt: at });\n return { ...next };\n },\n async setStatus(name, status, author, at) {\n const next: Skill = { ...existing(name), status, updatedBy: author, updatedAt: at };\n skills.set(name, next);\n return { ...next };\n },\n async delete(name) {\n const had = skills.delete(name);\n versions.delete(name);\n loads = loads.filter((load) => load.name !== name);\n return had;\n },\n async listVersions(name) {\n return [...(versions.get(name) ?? [])].reverse().map((version) => ({ ...version }));\n },\n async getVersion(name, version) {\n const found = versions.get(name)?.find((candidate) => candidate.version === version);\n return found ? { ...found } : null;\n },\n async recordLoad(load) {\n loads.push({ ...load });\n },\n async loadStats(name, limit) {\n const own = loads.filter((load) => load.name === name);\n const recent = [...own].sort((left, right) => (left.loadedAt < right.loadedAt ? 1 : left.loadedAt > right.loadedAt ? -1 : 0)).slice(0, limit);\n return { total: own.length, recent: recent.map((load) => ({ ...load })) };\n },\n async loadCounts() {\n const counts: Record<string, number> = {};\n for (const load of loads) counts[load.name] = (counts[load.name] ?? 0) + 1;\n return counts;\n },\n };\n}\n","import { allowedUser, createRequestOriginCheck, isSafeMethod, originRefusalHint } from \"@ykurilov/agent-chat-core\";\nimport type { ResolvedUser } from \"@ykurilov/agent-chat-core\";\nimport type { Skills } from \"./create-skills\";\nimport { InvalidSkillError, MAX_WHEN_TO_USE_CHARS } from \"./skill-rules\";\nimport { SkillExistsError, SkillNotFoundError, SkillVersionConflictError } from \"./types\";\n\nexport interface CreateSkillsHandlerOptions {\n skills: Skills;\n /** Тот же, что у чата приложения: кто делает запрос. Админ ли он — решает `isAdmin` из `createSkills`. */\n resolveUser(request: Request): ResolvedUser | Promise<ResolvedUser>;\n /** По умолчанию `/api/skills`. */\n basePath?: string;\n /** Чужие сайты, которым можно менять навыки с куками админа (как `allowedOrigins` чата). */\n allowedOrigins?: readonly string[];\n logError?: (message: string) => void;\n}\n\nexport interface SkillsHandler {\n basePath: string;\n fetch(request: Request): Promise<Response>;\n}\n\n/**\n * Самое большое тело запроса: тело и `whenToUse` в пределе — до 6 байт на знак (так JSON пишет управляющие символы),\n * плюс запас на остальные поля; не меньше 256 КБ.\n */\nfunction maxRequestBytes(maxBodyChars: number): number {\n return Math.max(256 * 1024, (maxBodyChars + MAX_WHEN_TO_USE_CHARS) * 6 + 16 * 1024);\n}\n\nclass BadRequestError extends Error {}\nclass BodyTooLargeError extends Error {}\n\n/**\n * Адреса админ-экрана навыков. Не-админ, гость и отказ приложения получают 404 на любом адресе: чужой не узнает, что\n * навыки есть. Запись с чужого сайта — 403 (только админу, после проверки). Ошибки — `{ error: { kind, message } }`.\n */\nexport function createSkillsHandler(options: CreateSkillsHandlerOptions): SkillsHandler {\n const { skills } = options;\n const manage = skills.manage;\n const requestLimit = maxRequestBytes(skills.maxBodyChars);\n const basePath = (options.basePath ?? \"/api/skills\").replace(/\\/+$/, \"\");\n const logError = options.logError ?? ((message: string) => console.error(message));\n const allowsOrigin = createRequestOriginCheck({\n allowedOrigins: options.allowedOrigins,\n onRefused: (refusal) => logError(`Навыки: ${originRefusalHint(refusal)}`),\n });\n\n async function route(request: Request, segments: string[], author: string): Promise<Response | null> {\n const [name, action, ...rest] = segments;\n if (rest.length > 0) return null;\n const method = request.method;\n if (name === undefined) {\n if (method === \"GET\") {\n return Response.json({ skills: await manage.list() });\n }\n if (method === \"POST\") {\n const input = await readObject(request, requestLimit);\n const skill = await manage.create(\n { name: text(input, \"name\"), whenToUse: text(input, \"whenToUse\"), body: text(input, \"body\"), status: optionalText(input, \"status\") },\n author,\n );\n return Response.json({ skill }, { status: 201 });\n }\n return null;\n }\n if (action === undefined) {\n if (method === \"GET\") {\n const found = await manage.get(name);\n return found ? Response.json(found) : notFound();\n }\n if (method === \"PUT\") {\n const input = await readObject(request, requestLimit);\n const skill = await manage.updateText(name, { whenToUse: text(input, \"whenToUse\"), body: text(input, \"body\"), expectedVersion: version(input, \"expectedVersion\") }, author);\n return Response.json({ skill });\n }\n if (method === \"DELETE\") return (await manage.delete(name)) ? new Response(null, { status: 204 }) : notFound();\n return null;\n }\n if (action === \"status\" && method === \"PUT\") {\n return Response.json({ skill: await manage.setStatus(name, text(await readObject(request, requestLimit), \"status\"), author) });\n }\n if (action === \"versions\" && method === \"GET\") {\n if (!(await manage.get(name))) return notFound();\n return Response.json({ versions: await manage.versions(name) });\n }\n if (action === \"rollback\" && method === \"POST\") {\n const input = await readObject(request, requestLimit);\n return Response.json({ skill: await manage.rollback(name, version(input, \"version\"), version(input, \"expectedVersion\"), author) });\n }\n return null;\n }\n\n return {\n basePath,\n async fetch(request) {\n const segments = pathSegments(new URL(request.url).pathname, basePath);\n if (!segments) return notFound();\n try {\n const user = allowedUser(await options.resolveUser(request));\n if (!user || !(await skills.isAdmin(user, request))) return notFound();\n // Источник проверяется после админа: не-админ всегда получает 404, как будто навыков нет.\n if (!isSafeMethod(request.method) && !allowsOrigin(request)) return Response.json({ error: { kind: \"forbidden_origin\" } }, { status: 403 });\n return (await route(request, segments, user.id)) ?? notFound();\n } catch (error) {\n return errorToResponse(error, request, logError);\n }\n },\n };\n}\n\nfunction notFound(): Response {\n return Response.json({ error: { kind: \"not_found\" } }, { status: 404 });\n}\n\nfunction errorToResponse(error: unknown, request: Request, logError: (message: string) => void): Response {\n const fail = (status: number, body: Record<string, unknown>) => Response.json({ error: body }, { status });\n if (error instanceof SkillNotFoundError) return notFound();\n if (error instanceof InvalidSkillError) return fail(400, { kind: \"invalid_skill\", field: error.field, reason: error.reason, message: error.message });\n if (error instanceof SkillExistsError) return fail(409, { kind: \"skill_exists\", message: error.message });\n if (error instanceof SkillVersionConflictError) return fail(409, { kind: \"version_conflict\", currentVersion: error.currentVersion, message: error.message });\n if (error instanceof BadRequestError) return fail(400, { kind: \"bad_request\", message: error.message });\n if (error instanceof BodyTooLargeError) return fail(413, { kind: \"too_large\", message: \"Request body is too large\" });\n logError(`Навыки: сбой на ${request.method} ${new URL(request.url).pathname}: ${error instanceof Error ? `${error.name}: ${error.message}` : String(error)}`);\n return fail(500, { kind: \"other\", message: \"Internal error\" });\n}\n\nfunction pathSegments(pathname: string, basePath: string): string[] | null {\n if (pathname !== basePath && !pathname.startsWith(`${basePath}/`)) return null;\n try {\n return pathname\n .slice(basePath.length)\n .split(\"/\")\n .filter((segment) => segment.length > 0)\n .map((segment) => decodeURIComponent(segment));\n } catch {\n // Битая кодировка (например, `%E0`): такого адреса нет.\n return null;\n }\n}\n\nasync function readObject(request: Request, limit: number): Promise<Record<string, unknown>> {\n const declared = Number(request.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > limit) {\n await request.body?.cancel().catch(() => {});\n throw new BodyTooLargeError();\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(await readLimitedText(request, limit));\n } catch (error) {\n if (error instanceof BodyTooLargeError) throw error;\n throw new BadRequestError(\"Body is not JSON\");\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) throw new BadRequestError(\"Body must be a JSON object\");\n return parsed as Record<string, unknown>;\n}\n\n/** Тело потоком: после предела чтение обрывается, поток закрывается. */\nasync function readLimitedText(request: Request, limit: number): Promise<string> {\n if (!request.body) return \"\";\n const reader = request.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > limit) {\n await reader.cancel().catch(() => {});\n throw new BodyTooLargeError();\n }\n chunks.push(value);\n }\n return Buffer.concat(chunks).toString(\"utf8\");\n}\n\nfunction text(input: Record<string, unknown>, field: string): string {\n const value = input[field];\n if (typeof value !== \"string\") throw new BadRequestError(`Field \"${field}\" must be a string`);\n return value;\n}\n\nfunction optionalText(input: Record<string, unknown>, field: string): string | undefined {\n return input[field] === undefined ? undefined : text(input, field);\n}\n\nfunction version(input: Record<string, unknown>, field: string): number {\n const value = input[field];\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 1) throw new BadRequestError(`Field \"${field}\" must be a version number`);\n return value;\n}\n"],"mappings":"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","names":["version","version","text","version"]}
|
|
1
|
+
{"version":3,"sources":["../src/create-skills.ts","../src/loaded-skills.ts","../src/skill-texts.ts","../src/skills-manager.ts","../src/skill-visibility.ts","../src/with-skills.ts","../src/store/memory.ts","../src/skills-handler.ts"],"sourcesContent":["import { tool } from \"ai\";\nimport type { ToolSet } from \"ai\";\nimport { z } from \"zod\";\nimport type { AgentToolsModel, ContextBlock, ContextBlocksInput, ServiceBlockFormat } from \"@ykurilov/agent-chat-core\";\nimport { isSkillVisibleInHistory, skillHeader } from \"./loaded-skills\";\nimport type { BranchStorage } from \"./loaded-skills\";\nimport { createSkillsManager } from \"./skills-manager\";\nimport type { SkillsManager } from \"./skills-manager\";\nimport { DEFAULT_MAX_BODY_CHARS } from \"./skill-rules\";\nimport { russianSkillsModelTexts, SKILLS_BLOCK_KEY, USE_SKILL_TOOL } from \"./skill-texts\";\nimport type { SkillsModelTexts } from \"./skill-texts\";\nimport { visibleStatuses } from \"./skill-visibility\";\nimport type { IsSkillsAdmin, SkillsTurn } from \"./skill-visibility\";\nimport { byName } from \"./store/skill-store\";\nimport type { SkillBrief, SkillStore } from \"./types\";\n\nexport interface CreateSkillsOptions {\n store: SkillStore;\n /** Админ ли человек — для экрана навыков. Навыки «только админам» в ходе видны по `viewer.isAdmin` (`agent.viewer`). */\n isAdmin: IsSkillsAdmin;\n /** Хранилище чатов службы: `use_skill` смотрит, видно ли тело навыка в истории чата. */\n chatStorage: BranchStorage;\n /** Предел тела навыка в знаках; по умолчанию 50 000. */\n maxBodyChars?: number;\n /** Тексты для модели; по умолчанию русские (`englishSkillsModelTexts` — английские). */\n modelTexts?: SkillsModelTexts;\n /** Строка в журнал о каждой загрузке тела; по умолчанию `console.info`. */\n log?: (line: string) => void;\n logError?: (message: string) => void;\n now?: () => Date;\n}\n\n/** Навыки агента: список в контексте, инструмент загрузки и управление для админ-экрана. */\nexport interface Skills {\n /** Для `agent.contextBlocks`: блок `skills` со списком видимых навыков, без тел. */\n contextBlocks(input: ContextBlocksInput): Promise<ContextBlock[]>;\n /** Для `agent.tools`: инструмент `use_skill`. Описание одно на все чаты, видимость проверяется при вызове. */\n tools(model: AgentToolsModel): ToolSet;\n toolNames: readonly string[];\n isAdmin: IsSkillsAdmin;\n /** Предел тела навыка в знаках: по нему же адреса экрана считают предел запроса. */\n maxBodyChars: number;\n /** Действия админ-экрана: с проверкой полей, автором и временем. */\n manage: SkillsManager;\n /** Для `agent.serviceBlocks` (`withSkills` добавляет сам): тело навыка `<skill …>` и список навыков. */\n serviceBlocks: readonly ServiceBlockFormat[];\n}\n\nexport function createSkills(options: CreateSkillsOptions): Skills {\n const { store, isAdmin, chatStorage } = options;\n const texts = options.modelTexts ?? russianSkillsModelTexts;\n const log = options.log ?? ((line: string) => console.info(line));\n const logError = options.logError ?? ((message: string) => console.error(message));\n const now = options.now ?? (() => new Date());\n const maxBodyChars = options.maxBodyChars ?? DEFAULT_MAX_BODY_CHARS;\n let viewerHintShown = false;\n\n /** Видимые статусы хода; есть админские навыки, а прав хода нет — один раз подсказка в журнал. */\n function statusesOf(turn: SkillsTurn, skills: readonly SkillBrief[]) {\n if (turn.viewer === undefined && !viewerHintShown && skills.some((skill) => skill.status === \"admins\")) {\n viewerHintShown = true;\n log(\"skills: навыки «только админам» не видны: задайте agent.viewer, который возвращает { isAdmin }\");\n }\n return visibleStatuses(turn);\n }\n\n async function contextBlocks(input: ContextBlocksInput): Promise<ContextBlock[]> {\n const all = await store.listBrief();\n const statuses = statusesOf(input, all);\n const skills = all.filter((skill) => statuses.has(skill.status)).sort(byName);\n if (skills.length === 0) return [];\n // Текст зависит только от списка: пока навыки не правят, блок тот же байт в байт и ядро его не шлёт заново.\n const lines = skills.map((skill) => `- ${skill.name}: ${skill.whenToUse}`);\n return [{ key: SKILLS_BLOCK_KEY, text: [texts.listHeading, ...lines].join(\"\\n\") }];\n }\n\n /** `given` — заголовки тел, уже отданных в этой попытке хода: два вызова в одном шаге модели истории ещё не видят. */\n async function useSkill(model: AgentToolsModel, name: string, given: Set<string>): Promise<string> {\n const skill = await store.get(name.trim());\n if (!skill || !statusesOf(model, [skill]).has(skill.status)) return texts.notFound(name);\n\n const header = skillHeader(skill);\n if (given.has(header)) return texts.alreadyLoaded(skill.name, skill.version);\n given.add(header);\n let visible = false;\n try {\n visible = await isSkillVisibleInHistory(chatStorage, model.chatId, header);\n } catch (error) {\n // Не прочитали историю — отдаём тело: лишний раз загрузить лучше, чем оставить агента без указаний.\n logError(`skills: history of chat ${model.chatId} not read: ${String(error)}`);\n }\n if (visible) return texts.alreadyLoaded(skill.name, skill.version);\n\n const turnId = model.turnId ?? model.messageId ?? \"\";\n try {\n await store.recordLoad({ name: skill.name, version: skill.version, chatId: model.chatId, ownerId: model.ownerId, turnId, loadedAt: now().toISOString() });\n } catch (error) {\n logError(`skills: load of \"${skill.name}\" not recorded: ${String(error)}`);\n }\n log(`skills: loaded \"${skill.name}\" v${skill.version} chat=${model.chatId} owner=${model.ownerId} turn=${turnId}`);\n return `${header}\\n${skill.body}\\n</skill>`;\n }\n\n return {\n contextBlocks,\n // Ядро собирает инструменты заново на каждую попытку хода: повтор после сбоя начинает учёт отданных тел с нуля.\n tools: (model) => {\n const given = new Set<string>();\n return {\n [USE_SKILL_TOOL]: tool({\n description: texts.toolDescription,\n inputSchema: z.object({ name: z.string().describe(texts.nameDescription) }),\n execute: ({ name }) => useSkill(model, name, given),\n }),\n };\n },\n toolNames: [USE_SKILL_TOOL],\n isAdmin,\n maxBodyChars,\n manage: createSkillsManager({ store, maxBodyChars, now }),\n // Тело навыка — тег `<skill …>`; список — заголовок `listHeading` и строки навыков до пустой строки.\n serviceBlocks: [{ tag: \"skill\" }, { name: \"skills_list\", line: texts.listHeading, until: \"paragraph\" }],\n };\n}\n","import { createHash } from \"node:crypto\";\nimport { activeCompaction, loadCurrentBranch } from \"@ykurilov/agent-chat-core\";\nimport type { ChatStorage } from \"@ykurilov/agent-chat-core\";\nimport { USE_SKILL_TOOL } from \"./skill-texts\";\n\nexport type BranchStorage = Pick<ChatStorage, \"listMessages\" | \"listCompactions\" | \"listBranchSelections\">;\n\n/**\n * Начало результата `use_skill` с телом: по нему видно, какой навык и какой версии уже перед глазами модели.\n * Отпечаток тела отличает навык, созданный заново с тем же именем: его версия снова 1, а текст другой.\n */\nexport function skillHeader(skill: { name: string; version: number; body: string }): string {\n const hash = createHash(\"sha256\").update(skill.body).digest(\"hex\").slice(0, 12);\n return `<skill name=\"${skill.name}\" version=\"${skill.version}\" hash=\"${hash}\">`;\n}\n\n/**\n * Тело навыка с этим заголовком (`skillHeader`) уже видно модели: оно в результате `use_skill` на текущей ветке после сжатой части.\n * Другая ветка («Переделать», правка вопроса) и сжатая часть не в счёт — там тело модели не видно.\n */\nexport async function isSkillVisibleInHistory(storage: BranchStorage, chatId: string, header: string): Promise<boolean> {\n const branch = await loadCurrentBranch(storage, chatId);\n const active = activeCompaction(branch, await storage.listCompactions(chatId));\n return branch.slice(active ? active.throughIndex + 1 : 0).some((message) =>\n message.parts.some((part) => {\n if (part.type !== `tool-${USE_SKILL_TOOL}`) return false;\n const call = part as { state?: string; output?: unknown };\n return call.state === \"output-available\" && typeof call.output === \"string\" && call.output.startsWith(header);\n }),\n );\n}\n","/** Имя инструмента загрузки навыка. */\nexport const USE_SKILL_TOOL = \"use_skill\";\n\n/** Ключ блока контекста со списком навыков. */\nexport const SKILLS_BLOCK_KEY = \"skills\";\n\n/** Тексты для модели. Постоянные: от них зависит кэш, поэтому в них нет ни списка, ни времени. */\nexport interface SkillsModelTexts {\n /** Первая строка блока со списком: что такое навыки и когда их грузить. */\n listHeading: string;\n /** Описание инструмента `use_skill`. */\n toolDescription: string;\n /** Описание поля `name`. */\n nameDescription: string;\n /** Тело этой версии уже есть выше в переписке. */\n alreadyLoaded(name: string, version: number): string;\n /** Навыка нет, он выключен или не виден в этом чате — ответ один, чтобы не выдать чужой навык. */\n notFound(name: string): string;\n}\n\nexport const russianSkillsModelTexts: SkillsModelTexts = {\n listHeading:\n \"Навыки — готовые указания для отдельных задач. Если задача человека подходит под навык, до начала работы загрузи его указания инструментом use_skill и следуй им. Не подходит — не загружай.\",\n toolDescription:\n \"Загружает указания навыка из списка «Навыки» в контексте. Зови, когда задача подходит под навык, до начала работы. Загруженные указания остаются в переписке: повторно звать не нужно.\",\n nameDescription: \"Имя навыка из списка — как в списке, до двоеточия.\",\n alreadyLoaded: (name, version) => `Навык «${name}» версии ${version} уже загружен выше в переписке — следуй тем указаниям.`,\n notFound: (name) => `Навыка «${name}» нет. Бери навыки только из списка «Навыки» в контексте.`,\n};\n\nexport const englishSkillsModelTexts: SkillsModelTexts = {\n listHeading:\n \"Skills are ready-made instructions for specific tasks. If the person's task matches a skill, load its instructions with the use_skill tool before you start and follow them. If it doesn't match, don't load it.\",\n toolDescription:\n \"Loads the instructions of a skill from the “Skills” list in the context. Call it before starting when the task matches a skill. Loaded instructions stay in the conversation: no need to call it again.\",\n nameDescription: \"Skill name exactly as in the list, before the colon.\",\n alreadyLoaded: (name, version) => `Skill “${name}” version ${version} is already loaded above in the conversation — follow those instructions.`,\n notFound: (name) => `There is no skill “${name}”. Only use skills from the “Skills” list in the context.`,\n};\n","import { checkSkillName, checkSkillStatus, checkSkillText } from \"./skill-rules\";\nimport { SkillNotFoundError } from \"./types\";\nimport type { Skill, SkillBrief, SkillLoadStats, SkillStatus, SkillStore, SkillVersion } from \"./types\";\n\n/** Сколько последних загрузок показывает карточка навыка. */\nexport const RECENT_LOADS_LIMIT = 20;\n\nexport interface SkillSummary extends SkillBrief {\n /** Сколько раз тело загружали в чатах. */\n loads: number;\n}\n\nexport interface SkillsManager {\n list(): Promise<SkillSummary[]>;\n /** Навык и учёт загрузок; нет — `null`. */\n get(name: string): Promise<{ skill: Skill; loads: SkillLoadStats } | null>;\n create(input: { name: string; whenToUse: string; body: string; status?: string }, author: string): Promise<Skill>;\n updateText(name: string, edit: { whenToUse: string; body: string; expectedVersion: number }, author: string): Promise<Skill>;\n setStatus(name: string, status: string, author: string): Promise<Skill>;\n versions(name: string): Promise<SkillVersion[]>;\n /** Откат — новая версия с текстом старой; текст проверяется нынешними правилами; история не переписывается. */\n rollback(name: string, version: number, expectedVersion: number, author: string): Promise<Skill>;\n delete(name: string): Promise<boolean>;\n}\n\nexport function createSkillsManager({ store, maxBodyChars, now }: { store: SkillStore; maxBodyChars: number; now: () => Date }): SkillsManager {\n const at = () => now().toISOString();\n return {\n async list() {\n const [skills, counts] = await Promise.all([store.listBrief(), store.loadCounts()]);\n return skills.map((skill) => ({ ...skill, loads: counts[skill.name] ?? 0 }));\n },\n async get(name) {\n const skill = await store.get(name);\n return skill ? { skill, loads: await store.loadStats(name, RECENT_LOADS_LIMIT) } : null;\n },\n async create(input, author) {\n checkSkillName(input.name);\n const status: SkillStatus = input.status === undefined ? \"off\" : checkSkillStatus(input.status);\n return store.create({ name: input.name, ...checkSkillText(input, maxBodyChars), status, author }, at());\n },\n async updateText(name, edit, author) {\n return store.updateText(name, { ...checkSkillText(edit, maxBodyChars), author, expectedVersion: edit.expectedVersion }, at());\n },\n async setStatus(name, status, author) {\n return store.setStatus(name, checkSkillStatus(status), author, at());\n },\n versions: (name) => store.listVersions(name),\n async rollback(name, version, expectedVersion, author) {\n const old = await store.getVersion(name, version);\n if (!old) throw new SkillNotFoundError(name);\n return store.updateText(name, { ...checkSkillText(old, maxBodyChars), author, expectedVersion }, at());\n },\n delete: (name) => store.delete(name),\n };\n}\n","import type { AgentAudience, AgentViewer } from \"@ykurilov/agent-chat-core\";\nimport type { SkillStatus } from \"./types\";\n\n/** Решение приложения: админ ли этот человек — для экрана навыков. Запрос есть у вызовов из HTTP-адресов навыков. */\nexport type IsSkillsAdmin = (user: { id: string }, request?: Request) => boolean | Promise<boolean>;\n\n/** Ход: с кем говорит агент и права человека на ход (`agent.viewer` ядра). */\nexport interface SkillsTurn {\n audience?: AgentAudience;\n viewer?: AgentViewer;\n}\n\n/**\n * Какие статусы видны в этом ходе. Всем — `everyone`. Админские — по `viewer.isAdmin` (права из базы приложения, один\n * раз в начале хода; в копии чата человека — права человека) и не в группе: там их видели бы все участники.\n */\nexport function visibleStatuses(turn: SkillsTurn): ReadonlySet<SkillStatus> {\n return turn.audience !== \"group\" && turn.viewer?.isAdmin === true ? new Set([\"everyone\", \"admins\"]) : new Set([\"everyone\"]);\n}\n","import type { ToolSet } from \"ai\";\nimport type { AgentSettings } from \"@ykurilov/agent-chat-core\";\nimport type { Skills } from \"./create-skills\";\nimport { SKILLS_BLOCK_KEY } from \"./skill-texts\";\n\n/**\n * Настройки агента с навыками: блок `skills` после блоков приложения, `use_skill` рядом с инструментами приложения.\n * Совпадение имени инструмента или ключа блока — ошибка: тихо подменить чужое нельзя.\n */\nexport function withSkills(agent: AgentSettings, skills: Skills): AgentSettings {\n const clash = (own: ToolSet) => skills.toolNames.find((name) => Object.hasOwn(own, name));\n const clashMessage = (name: string) => `Инструмент «${name}» задан и приложением, и навыками: переименуйте инструмент приложения`;\n const appTools = agent.tools;\n if (appTools && typeof appTools !== \"function\") {\n const name = clash(appTools);\n if (name) throw new Error(clashMessage(name));\n }\n const appBlocks = agent.contextBlocks;\n return {\n ...agent,\n serviceBlocks: [...(agent.serviceBlocks ?? []), ...skills.serviceBlocks],\n tools: (model) => {\n const own = typeof appTools === \"function\" ? appTools(model) : (appTools ?? {});\n const name = clash(own);\n if (name) throw new Error(clashMessage(name));\n return { ...own, ...skills.tools(model) };\n },\n contextBlocks: async (input) => {\n const [own, fromSkills] = await Promise.all([appBlocks ? appBlocks(input) : [], skills.contextBlocks(input)]);\n if (own?.some((block) => block.key === SKILLS_BLOCK_KEY)) {\n throw new Error(`Ключ блока «${SKILLS_BLOCK_KEY}» занят навыками: переименуйте блок приложения`);\n }\n return [...(own ?? []), ...fromSkills];\n },\n };\n}\n","import { SkillExistsError, SkillNotFoundError, SkillVersionConflictError } from \"../types\";\nimport type { Skill, SkillLoad, SkillStore, SkillVersion } from \"../types\";\nimport { brief, byName, sameText, storedText } from \"./skill-store\";\n\n/** Навыки в памяти процесса: для тестов и проб, живут до перезапуска. */\nexport function memorySkillStore(): SkillStore {\n const skills = new Map<string, Skill>();\n const versions = new Map<string, SkillVersion[]>();\n let loads: SkillLoad[] = [];\n\n const existing = (name: string): Skill => {\n const skill = skills.get(name);\n if (!skill) throw new SkillNotFoundError(name);\n return skill;\n };\n\n return {\n async list() {\n return [...skills.values()].sort(byName).map((skill) => ({ ...skill }));\n },\n async listBrief() {\n return [...skills.values()].sort(byName).map(brief);\n },\n async get(name) {\n const skill = skills.get(name);\n return skill ? { ...skill } : null;\n },\n async create(input, at) {\n if (skills.has(input.name)) throw new SkillExistsError(input.name);\n const skill: Skill = {\n name: input.name,\n ...storedText(input),\n status: input.status,\n version: 1,\n createdBy: input.author,\n createdAt: at,\n updatedBy: input.author,\n updatedAt: at,\n };\n skills.set(skill.name, skill);\n versions.set(skill.name, [{ name: skill.name, version: 1, whenToUse: skill.whenToUse, body: skill.body, author: input.author, createdAt: at }]);\n return { ...skill };\n },\n async updateText(name, edit, at) {\n const skill = existing(name);\n if (skill.version !== edit.expectedVersion) throw new SkillVersionConflictError(name, skill.version);\n const text = storedText(edit);\n if (sameText(skill, text)) return { ...skill };\n const next: Skill = { ...skill, ...text, version: skill.version + 1, updatedBy: edit.author, updatedAt: at };\n skills.set(name, next);\n versions.get(name)!.push({ name, version: next.version, whenToUse: next.whenToUse, body: next.body, author: edit.author, createdAt: at });\n return { ...next };\n },\n async setStatus(name, status, author, at) {\n const next: Skill = { ...existing(name), status, updatedBy: author, updatedAt: at };\n skills.set(name, next);\n return { ...next };\n },\n async delete(name) {\n const had = skills.delete(name);\n versions.delete(name);\n loads = loads.filter((load) => load.name !== name);\n return had;\n },\n async listVersions(name) {\n return [...(versions.get(name) ?? [])].reverse().map((version) => ({ ...version }));\n },\n async getVersion(name, version) {\n const found = versions.get(name)?.find((candidate) => candidate.version === version);\n return found ? { ...found } : null;\n },\n async recordLoad(load) {\n loads.push({ ...load });\n },\n async loadStats(name, limit) {\n const own = loads.filter((load) => load.name === name);\n const recent = [...own].sort((left, right) => (left.loadedAt < right.loadedAt ? 1 : left.loadedAt > right.loadedAt ? -1 : 0)).slice(0, limit);\n return { total: own.length, recent: recent.map((load) => ({ ...load })) };\n },\n async loadCounts() {\n const counts: Record<string, number> = {};\n for (const load of loads) counts[load.name] = (counts[load.name] ?? 0) + 1;\n return counts;\n },\n };\n}\n","import { allowedUser, createRequestOriginCheck, isSafeMethod, originRefusalHint } from \"@ykurilov/agent-chat-core\";\nimport type { ResolvedUser } from \"@ykurilov/agent-chat-core\";\nimport type { Skills } from \"./create-skills\";\nimport { InvalidSkillError, MAX_WHEN_TO_USE_CHARS } from \"./skill-rules\";\nimport { SkillExistsError, SkillNotFoundError, SkillVersionConflictError } from \"./types\";\n\nexport interface CreateSkillsHandlerOptions {\n skills: Skills;\n /** Тот же, что у чата приложения: кто делает запрос. Админ ли он — решает `isAdmin` из `createSkills`. */\n resolveUser(request: Request): ResolvedUser | Promise<ResolvedUser>;\n /** По умолчанию `/api/skills`. */\n basePath?: string;\n /** Чужие сайты, которым можно менять навыки с куками админа (как `allowedOrigins` чата). */\n allowedOrigins?: readonly string[];\n logError?: (message: string) => void;\n}\n\nexport interface SkillsHandler {\n basePath: string;\n fetch(request: Request): Promise<Response>;\n}\n\n/**\n * Самое большое тело запроса: тело и `whenToUse` в пределе — до 6 байт на знак (так JSON пишет управляющие символы),\n * плюс запас на остальные поля; не меньше 256 КБ.\n */\nfunction maxRequestBytes(maxBodyChars: number): number {\n return Math.max(256 * 1024, (maxBodyChars + MAX_WHEN_TO_USE_CHARS) * 6 + 16 * 1024);\n}\n\nclass BadRequestError extends Error {}\nclass BodyTooLargeError extends Error {}\n\n/**\n * Адреса админ-экрана навыков. Не-админ, гость и отказ приложения получают 404 на любом адресе: чужой не узнает, что\n * навыки есть. Запись с чужого сайта — 403 (только админу, после проверки). Ошибки — `{ error: { kind, message } }`.\n */\nexport function createSkillsHandler(options: CreateSkillsHandlerOptions): SkillsHandler {\n const { skills } = options;\n const manage = skills.manage;\n const requestLimit = maxRequestBytes(skills.maxBodyChars);\n const basePath = (options.basePath ?? \"/api/skills\").replace(/\\/+$/, \"\");\n const logError = options.logError ?? ((message: string) => console.error(message));\n const allowsOrigin = createRequestOriginCheck({\n allowedOrigins: options.allowedOrigins,\n onRefused: (refusal) => logError(`Навыки: ${originRefusalHint(refusal)}`),\n });\n\n async function route(request: Request, segments: string[], author: string): Promise<Response | null> {\n const [name, action, ...rest] = segments;\n if (rest.length > 0) return null;\n const method = request.method;\n if (name === undefined) {\n if (method === \"GET\") {\n return Response.json({ skills: await manage.list() });\n }\n if (method === \"POST\") {\n const input = await readObject(request, requestLimit);\n const skill = await manage.create(\n { name: text(input, \"name\"), whenToUse: text(input, \"whenToUse\"), body: text(input, \"body\"), status: optionalText(input, \"status\") },\n author,\n );\n return Response.json({ skill }, { status: 201 });\n }\n return null;\n }\n if (action === undefined) {\n if (method === \"GET\") {\n const found = await manage.get(name);\n return found ? Response.json(found) : notFound();\n }\n if (method === \"PUT\") {\n const input = await readObject(request, requestLimit);\n const skill = await manage.updateText(name, { whenToUse: text(input, \"whenToUse\"), body: text(input, \"body\"), expectedVersion: version(input, \"expectedVersion\") }, author);\n return Response.json({ skill });\n }\n if (method === \"DELETE\") return (await manage.delete(name)) ? new Response(null, { status: 204 }) : notFound();\n return null;\n }\n if (action === \"status\" && method === \"PUT\") {\n return Response.json({ skill: await manage.setStatus(name, text(await readObject(request, requestLimit), \"status\"), author) });\n }\n if (action === \"versions\" && method === \"GET\") {\n if (!(await manage.get(name))) return notFound();\n return Response.json({ versions: await manage.versions(name) });\n }\n if (action === \"rollback\" && method === \"POST\") {\n const input = await readObject(request, requestLimit);\n return Response.json({ skill: await manage.rollback(name, version(input, \"version\"), version(input, \"expectedVersion\"), author) });\n }\n return null;\n }\n\n return {\n basePath,\n async fetch(request) {\n const segments = pathSegments(new URL(request.url).pathname, basePath);\n if (!segments) return notFound();\n try {\n const user = allowedUser(await options.resolveUser(request));\n if (!user || !(await skills.isAdmin(user, request))) return notFound();\n // Источник проверяется после админа: не-админ всегда получает 404, как будто навыков нет.\n if (!isSafeMethod(request.method) && !allowsOrigin(request)) return Response.json({ error: { kind: \"forbidden_origin\" } }, { status: 403 });\n return (await route(request, segments, user.id)) ?? notFound();\n } catch (error) {\n return errorToResponse(error, request, logError);\n }\n },\n };\n}\n\nfunction notFound(): Response {\n return Response.json({ error: { kind: \"not_found\" } }, { status: 404 });\n}\n\nfunction errorToResponse(error: unknown, request: Request, logError: (message: string) => void): Response {\n const fail = (status: number, body: Record<string, unknown>) => Response.json({ error: body }, { status });\n if (error instanceof SkillNotFoundError) return notFound();\n if (error instanceof InvalidSkillError) return fail(400, { kind: \"invalid_skill\", field: error.field, reason: error.reason, message: error.message });\n if (error instanceof SkillExistsError) return fail(409, { kind: \"skill_exists\", message: error.message });\n if (error instanceof SkillVersionConflictError) return fail(409, { kind: \"version_conflict\", currentVersion: error.currentVersion, message: error.message });\n if (error instanceof BadRequestError) return fail(400, { kind: \"bad_request\", message: error.message });\n if (error instanceof BodyTooLargeError) return fail(413, { kind: \"too_large\", message: \"Request body is too large\" });\n logError(`Навыки: сбой на ${request.method} ${new URL(request.url).pathname}: ${error instanceof Error ? `${error.name}: ${error.message}` : String(error)}`);\n return fail(500, { kind: \"other\", message: \"Internal error\" });\n}\n\nfunction pathSegments(pathname: string, basePath: string): string[] | null {\n if (pathname !== basePath && !pathname.startsWith(`${basePath}/`)) return null;\n try {\n return pathname\n .slice(basePath.length)\n .split(\"/\")\n .filter((segment) => segment.length > 0)\n .map((segment) => decodeURIComponent(segment));\n } catch {\n // Битая кодировка (например, `%E0`): такого адреса нет.\n return null;\n }\n}\n\nasync function readObject(request: Request, limit: number): Promise<Record<string, unknown>> {\n const declared = Number(request.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > limit) {\n await request.body?.cancel().catch(() => {});\n throw new BodyTooLargeError();\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(await readLimitedText(request, limit));\n } catch (error) {\n if (error instanceof BodyTooLargeError) throw error;\n throw new BadRequestError(\"Body is not JSON\");\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) throw new BadRequestError(\"Body must be a JSON object\");\n return parsed as Record<string, unknown>;\n}\n\n/** Тело потоком: после предела чтение обрывается, поток закрывается. */\nasync function readLimitedText(request: Request, limit: number): Promise<string> {\n if (!request.body) return \"\";\n const reader = request.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > limit) {\n await reader.cancel().catch(() => {});\n throw new BodyTooLargeError();\n }\n chunks.push(value);\n }\n return Buffer.concat(chunks).toString(\"utf8\");\n}\n\nfunction text(input: Record<string, unknown>, field: string): string {\n const value = input[field];\n if (typeof value !== \"string\") throw new BadRequestError(`Field \"${field}\" must be a string`);\n return value;\n}\n\nfunction optionalText(input: Record<string, unknown>, field: string): string | undefined {\n return input[field] === undefined ? undefined : text(input, field);\n}\n\nfunction version(input: Record<string, unknown>, field: string): number {\n const value = input[field];\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 1) throw new BadRequestError(`Field \"${field}\" must be a version number`);\n return value;\n}\n"],"mappings":"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","names":["version","version","text","version"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ToolSet } from "ai";
|
|
2
|
-
import type { AgentToolsModel, ContextBlock, ContextBlocksInput } from "@ykurilov/agent-chat-core";
|
|
2
|
+
import type { AgentToolsModel, ContextBlock, ContextBlocksInput, ServiceBlockFormat } from "@ykurilov/agent-chat-core";
|
|
3
3
|
import type { BranchStorage } from "./loaded-skills.js";
|
|
4
4
|
import type { SkillsManager } from "./skills-manager.js";
|
|
5
5
|
import type { SkillsModelTexts } from "./skill-texts.js";
|
|
@@ -32,5 +32,7 @@ export interface Skills {
|
|
|
32
32
|
maxBodyChars: number;
|
|
33
33
|
/** Действия админ-экрана: с проверкой полей, автором и временем. */
|
|
34
34
|
manage: SkillsManager;
|
|
35
|
+
/** Для `agent.serviceBlocks` (`withSkills` добавляет сам): тело навыка `<skill …>` и список навыков. */
|
|
36
|
+
serviceBlocks: readonly ServiceBlockFormat[];
|
|
35
37
|
}
|
|
36
38
|
export declare function createSkills(options: CreateSkillsOptions): Skills;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ykurilov/agent-chat-skills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"ai": "7.0.122",
|
|
54
54
|
"drizzle-orm": "^0.45.3",
|
|
55
55
|
"zod": "^4.6.5",
|
|
56
|
-
"@ykurilov/agent-chat-core": "0.
|
|
56
|
+
"@ykurilov/agent-chat-core": "0.59.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"better-sqlite3": "^13.0.3",
|