文本登记与严格头解析
策略的具体实现如下。
ts
export function defineTextSkillLoader(
definition: TextSkillLoaderDefinition,
): import('../structs/kits/text-skills.js').TextSkillLoader {
const { maxInstructionChars, maxDescriptionChars, maxIdChars = 64 } = definition;
breakWhile([
{
occur: () =>
![maxInstructionChars, maxDescriptionChars, maxIdChars].every(Number.isSafeInteger),
error: 'Invalid text skill limits',
tags: ['integer'],
},
{
occur: () => Math.min(maxInstructionChars, maxDescriptionChars, maxIdChars) < 1,
error: 'Invalid text skill limits',
tags: ['range'],
},
]);
const idPattern = new RegExp(`^[a-z][a-z0-9-]{0,${maxIdChars - 1}}$`);
const entryFor = definition.entryFor?.bind(definition) ?? ((id) => `${id}/SKILL.md`);
const parseHeader = definition.parseHeader?.bind(definition) ?? strictHeader;
const catalogError =
(typeof definition.errors?.catalog === 'function'
? definition.errors.catalog.bind(definition.errors)
: definition.errors?.catalog) ?? 'Text skill catalog must be an array';
const registrationError =
(typeof definition.errors?.registration === 'function'
? definition.errors.registration.bind(definition.errors)
: definition.errors?.registration) ?? 'Invalid text skill registration';
const documentError =
(typeof definition.errors?.document === 'function'
? definition.errors.document.bind(definition.errors)
: definition.errors?.document) ?? 'Invalid text skill document';
const tags = [...(definition.tags ?? [])];
return Object.freeze({
load(input: { entries: unknown }, ports: TextSkillReadPorts): readonly TextSkill[] {
breakWhile(!input || typeof input !== 'object', catalogError);
const entries = input.entries,
read = ports.read.bind(ports);
breakWhile(!Array.isArray(entries), catalogError);
const ids = new Set<string>();
return Object.freeze(
(entries as Record<string, unknown>[]).map((raw) => {
breakWhile(!raw || typeof raw !== 'object' || Array.isArray(raw), registrationError, [
...tags,
'registration',
'shape',
]);
const entry = {
id: raw.id,
entry: raw.entry,
kind: raw.kind,
toolIds: raw.toolIds,
} as unknown as TextSkillRegistration;
breakWhile(
[
{
occur: () => !entry || typeof entry !== 'object',
error: registrationError,
tags: ['shape'],
},
{
occur: [
() => typeof entry.id !== 'string' || !idPattern.test(entry.id),
() => ids.has(entry.id),
],
error: registrationError,
tags: ['id'],
},
{
occur: [
() =>
entry.entry !==
synchronous(entryFor(entry.id), 'Skill entry selectors must be synchronous'),
() => entry.kind !== 'text-workflow',
],
error: registrationError,
tags: ['workflow'],
},
{
occur: [
() => !Array.isArray(entry.toolIds),
() => entry.toolIds.some((tool) => typeof tool !== 'string' || !tool),
() => new Set(entry.toolIds).size !== entry.toolIds.length,
],
error: registrationError,
tags: ['tools'],
},
],
[...tags, 'registration'],
);
ids.add(entry.id);
const instructions = synchronous(read(entry.entry), 'Skill reads must be synchronous');
breakWhile(
typeof instructions !== 'string' || instructions.length > maxInstructionChars,
documentError,
[...tags, 'document', 'instructions'],
);
const header = synchronous(
parseHeader(instructions),
'Skill header parsing must be synchronous',
);
breakWhile(
[
{
occur: [() => !header, () => header!.id !== entry.id],
error: documentError,
tags: ['identity'],
},
{
occur: () =>
!header!.description.trim() || header!.description.length > maxDescriptionChars,
error: documentError,
tags: ['description'],
},
],
[...tags, 'document'],
);
return Object.freeze({
id: entry.id,
entry: entry.entry,
kind: entry.kind,
toolIds: Object.freeze([...entry.toolIds]),
description: header!.description.trim(),
instructions,
});
}),
);
},
});
}
function strictHeader(instructions: string): TextSkillHeader | undefined {
const header = instructions.match(
/^---\r?\nname: ([a-z][a-z0-9-]*)\r?\ndescription: ([^\r\n]+)\r?\n---(?:\r?\n|$)/,
);
return header ? { id: header[1], description: header[2] } : undefined;
}