agent-skill-evals 0.1.0 → 0.1.1
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/README.md +14 -145
- package/dist/agent/index.d.mts +2 -3
- package/dist/agent/index.mjs +1 -1
- package/dist/agent-B15R7Xlr.mjs +2105 -0
- package/dist/agent-B15R7Xlr.mjs.map +1 -0
- package/dist/assertions/index.d.mts +6 -0
- package/dist/assertions/index.d.mts.map +1 -1
- package/dist/assertions/index.mjs +164 -449
- package/dist/assertions/index.mjs.map +1 -1
- package/dist/catalog-9Wh-AfRv.mjs +409 -0
- package/dist/catalog-9Wh-AfRv.mjs.map +1 -0
- package/dist/cli/init.d.mts +22 -0
- package/dist/cli/init.d.mts.map +1 -0
- package/dist/cli/init.mjs +369 -0
- package/dist/cli/init.mjs.map +1 -0
- package/dist/index-D9lGL0Qg.d.mts +381 -0
- package/dist/index-D9lGL0Qg.d.mts.map +1 -0
- package/dist/presets-DwfDWckA.mjs +80 -0
- package/dist/presets-DwfDWckA.mjs.map +1 -0
- package/dist/test-generator/index.d.mts +945 -0
- package/dist/test-generator/index.d.mts.map +1 -0
- package/dist/test-generator/index.mjs +2 -0
- package/dist/test-pack-Cu1WOssl.mjs +215 -0
- package/dist/test-pack-Cu1WOssl.mjs.map +1 -0
- package/package.json +16 -9
- package/schema/test-pack.schema.json +871 -0
- package/dist/agent-CM7fIL_C.mjs +0 -1525
- package/dist/agent-CM7fIL_C.mjs.map +0 -1
- package/dist/assertion-entries-CfmNt-fp.d.mts +0 -9
- package/dist/assertion-entries-CfmNt-fp.d.mts.map +0 -1
- package/dist/index-4l7TCFny.d.mts +0 -90
- package/dist/index-4l7TCFny.d.mts.map +0 -1
- package/dist/internal-services-5-mRgNls.mjs +0 -226
- package/dist/internal-services-5-mRgNls.mjs.map +0 -1
- package/dist/internal-services-DbsekQ_K.d.mts +0 -76
- package/dist/internal-services-DbsekQ_K.d.mts.map +0 -1
- package/dist/skill-checks/index.d.mts +0 -113
- package/dist/skill-checks/index.d.mts.map +0 -1
- package/dist/skill-checks/index.mjs +0 -408
- package/dist/skill-checks/index.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"internal-services-5-mRgNls.mjs","names":["parseYaml"],"sources":["../src/runtime-checks/check-set.ts","../src/assertion-entries.ts","../src/internal-services.ts"],"sourcesContent":["export const RUNTIME_CHECK_TYPES = [\n \"verifier.succeeds\",\n \"verifier.fails\",\n \"file.exists\",\n \"file.created\",\n \"file.contains\",\n \"file.not_modified\",\n \"file.changes_outside_scope\",\n \"code.pattern_exists\",\n \"code.no_pattern\",\n \"tool.called\",\n \"tool.not_called\",\n \"skill.loaded\",\n] as const;\n\nexport type RuntimeCheckType = (typeof RUNTIME_CHECK_TYPES)[number];\n\nexport const DOUBLE_NEGATIVE_CHECK_TYPES = [\n \"code.no_pattern\",\n \"file.not_modified\",\n \"tool.not_called\",\n] as const satisfies readonly RuntimeCheckType[];\n\nexport const runtimeCheckTypeSet = new Set<string>(RUNTIME_CHECK_TYPES);\nexport const doubleNegativeCheckTypeSet = new Set<string>(DOUBLE_NEGATIVE_CHECK_TYPES);\n","import * as Either from \"effect/Either\";\nimport * as Schema from \"effect/Schema\";\nimport { doubleNegativeCheckTypeSet } from \"./runtime-checks/check-set.js\";\nimport type { AssertionMode } from \"./internal-types.js\";\n\nexport interface AssertionEntry {\n type: string;\n args: unknown;\n}\n\nexport interface AssertionEntryError {\n field: string;\n index?: number;\n reason: string;\n}\n\nexport interface ParsedAssertionEntries {\n entries: AssertionEntry[];\n errors: AssertionEntryError[];\n}\n\nexport interface RuntimeTestFieldEntries {\n preconditions: AssertionEntry[];\n should: AssertionEntry[];\n should_not: AssertionEntry[];\n errors: AssertionEntryError[];\n}\n\ninterface TypedEntry {\n type?: unknown;\n [key: string]: unknown;\n}\n\nconst RuntimeFieldArraySchema = Schema.Array(Schema.Unknown);\nconst EntryObjectSchema = Schema.Unknown.pipe(\n Schema.filter(\n (value): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value),\n { identifier: \"AssertionEntryObject\" },\n ),\n);\nconst NonEmptyTypeSchema = Schema.String.pipe(\n Schema.filter((value) => value.length > 0, { identifier: \"NonEmptyType\" }),\n);\n\n/**\n * Normalizes Promptfoo `vars.preconditions | should | should_not` entries.\n *\n * Supported forms:\n * - `\"file.exists\"`\n * - `{ type: \"file.exists\", path: \"app.js\" }`\n * - `{ \"file.exists\": { path: \"app.js\" } }`\n */\nexport function parseRuntimeTestFields(\n vars: Record<string, unknown>,\n): RuntimeTestFieldEntries {\n const preconditions = parseAssertionEntries(vars.preconditions, \"preconditions\", {\n allowMissing: true,\n mode: \"precondition\",\n });\n const should = parseAssertionEntries(vars.should, \"should\", {\n allowMissing: true,\n mode: \"should\",\n });\n const shouldNot = parseAssertionEntries(vars.should_not, \"should_not\", {\n allowMissing: true,\n mode: \"should_not\",\n });\n return {\n preconditions: preconditions.entries,\n should: should.entries,\n should_not: shouldNot.entries,\n errors: [...preconditions.errors, ...should.errors, ...shouldNot.errors],\n };\n}\n\nexport function parseAssertionEntries(\n raw: unknown,\n field: string,\n options: { allowMissing?: boolean; mode?: AssertionMode } = {},\n): ParsedAssertionEntries {\n const entries: AssertionEntry[] = [];\n const errors: AssertionEntryError[] = [];\n\n if (raw === undefined || raw === null) {\n if (!options.allowMissing) {\n errors.push({ field, reason: \"must be an array of assertion entries\" });\n }\n return { entries, errors };\n }\n\n const rawEntries = Schema.decodeUnknownEither(RuntimeFieldArraySchema)(raw);\n if (Either.isLeft(rawEntries)) {\n return {\n entries,\n errors: [{ field, reason: \"must be an array of assertion entries\" }],\n };\n }\n\n rawEntries.right.forEach((entry, index) => {\n const parsed = parseAssertionEntry(entry, field, index);\n if (\"error\" in parsed) {\n errors.push(parsed.error);\n } else {\n if (\n options.mode === \"should_not\" &&\n doubleNegativeCheckTypeSet.has(parsed.entry.type)\n ) {\n errors.push({\n field,\n index,\n reason: `\"${parsed.entry.type}\" must be declared under should, not should_not`,\n });\n return;\n }\n entries.push(parsed.entry);\n }\n });\n\n return { entries, errors };\n}\n\nfunction parseAssertionEntry(\n entry: unknown,\n field: string,\n index: number,\n): { entry: AssertionEntry } | { error: AssertionEntryError } {\n if (typeof entry === \"string\") {\n if (Either.isLeft(Schema.decodeUnknownEither(NonEmptyTypeSchema)(entry))) {\n return { error: { field, index, reason: \"string entry must not be empty\" } };\n }\n return { entry: { type: entry, args: {} } };\n }\n\n const decodedObject = Schema.decodeUnknownEither(EntryObjectSchema)(entry);\n if (Either.isLeft(decodedObject)) {\n return {\n error: {\n field,\n index,\n reason: \"entry must be a string, { type: ... }, or shorthand object\",\n },\n };\n }\n\n const candidate = decodedObject.right as TypedEntry;\n if (\"type\" in candidate) {\n const type = Schema.decodeUnknownEither(NonEmptyTypeSchema)(candidate.type);\n if (Either.isLeft(type)) {\n return {\n error: { field, index, reason: \"`type` must be a non-empty string\" },\n };\n }\n return { entry: { type: type.right, args: candidate } };\n }\n\n const keys = Object.keys(candidate);\n if (keys.length !== 1) {\n return {\n error: {\n field,\n index,\n reason: \"shorthand assertion object must have exactly one key\",\n },\n };\n }\n\n const type = keys[0]!;\n const args = candidate[type] ?? {};\n if (args !== null && typeof args !== \"object\") {\n return {\n error: {\n field,\n index,\n reason: `shorthand assertion \"${type}\" value must be an object`,\n },\n };\n }\n return { entry: { type, args } };\n}\n","import { constants } from \"node:fs\";\nimport * as PlatformFileSystem from \"@effect/platform/FileSystem\";\nimport * as NodeFileSystem from \"@effect/platform-node/NodeFileSystem\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport { parse as parseYaml } from \"yaml\";\n\ninterface DirectoryEntry {\n name: string;\n isDirectory(): boolean;\n isFile(): boolean;\n}\n\ninterface FileInfo {\n mode: number;\n isDirectory(): boolean;\n isFile(): boolean;\n}\n\ninterface FileSystemService {\n access(path: string, mode?: number): Effect.Effect<void, unknown>;\n copyDirectory(source: string, destination: string): Effect.Effect<void, unknown>;\n makeDirectory(path: string): Effect.Effect<void, unknown>;\n makeTempDirectory(prefix: string): Effect.Effect<string, unknown>;\n readFile(path: string): Effect.Effect<Buffer, unknown>;\n readText(path: string): Effect.Effect<string, unknown>;\n readDirectory(path: string): Effect.Effect<DirectoryEntry[], unknown>;\n stat(path: string): Effect.Effect<FileInfo, unknown>;\n writeText(path: string, contents: string): Effect.Effect<void, unknown>;\n}\n\ninterface EnvironmentService {\n cwd: Effect.Effect<string>;\n env: Effect.Effect<NodeJS.ProcessEnv>;\n}\n\ninterface YamlService {\n parse(input: string): Effect.Effect<unknown, unknown>;\n}\n\nexport class FileSystem extends Context.Tag(\"agent-skill-evals/promptfoo/FileSystem\")<\n FileSystem,\n FileSystemService\n>() {}\n\nexport class Environment extends Context.Tag(\"agent-skill-evals/promptfoo/Environment\")<\n Environment,\n EnvironmentService\n>() {}\n\nexport class YamlParser extends Context.Tag(\"agent-skill-evals/promptfoo/YamlParser\")<\n YamlParser,\n YamlService\n>() {}\n\nfunction toFileInfo(info: PlatformFileSystem.File.Info): FileInfo {\n return {\n mode: info.mode,\n isDirectory: () => info.type === \"Directory\",\n isFile: () => info.type === \"File\",\n };\n}\n\nfunction executableFromMode(mode: number): boolean {\n return (mode & 0o111) !== 0;\n}\n\nconst PlatformBackedFileSystemLive = Layer.effect(\n FileSystem,\n Effect.gen(function* () {\n const fs = yield* PlatformFileSystem.FileSystem;\n\n const statFile = (path: string) => fs.stat(path).pipe(Effect.map(toFileInfo));\n\n return {\n access: (path: string, mode?: number) => {\n const readable = mode === undefined ? undefined : (mode & constants.R_OK) !== 0;\n const writable = mode === undefined ? undefined : (mode & constants.W_OK) !== 0;\n const executable = mode === undefined ? false : (mode & constants.X_OK) !== 0;\n const baseAccess = fs.access(path, {\n ok: true,\n readable,\n writable,\n });\n if (!executable) return baseAccess;\n return baseAccess.pipe(\n Effect.zipRight(statFile(path)),\n Effect.flatMap((info) =>\n executableFromMode(info.mode)\n ? Effect.void\n : Effect.fail(new Error(`Path is not executable: ${path}`)),\n ),\n );\n },\n copyDirectory: (source: string, destination: string) =>\n fs.copy(source, destination),\n makeDirectory: (path: string) => fs.makeDirectory(path, { recursive: true }),\n makeTempDirectory: (prefix: string) => fs.makeTempDirectory({ prefix }),\n readFile: (path: string) => fs.readFile(path).pipe(Effect.map(Buffer.from)),\n readText: (path: string) => fs.readFileString(path),\n readDirectory: (path: string) =>\n fs.readDirectory(path).pipe(\n Effect.flatMap((names) =>\n Effect.forEach(names, (name) =>\n fs.stat(`${path}/${name}`).pipe(\n Effect.map((info): DirectoryEntry => ({\n name,\n isDirectory: () => info.type === \"Directory\",\n isFile: () => info.type === \"File\",\n })),\n ),\n ),\n ),\n ),\n stat: statFile,\n writeText: (path: string, contents: string) => fs.writeFileString(path, contents),\n };\n }),\n);\n\nexport const NodeFileSystemLive = PlatformBackedFileSystemLive.pipe(\n Layer.provide(NodeFileSystem.layer),\n);\n\nexport const NodeEnvironmentLive = Layer.succeed(Environment, {\n cwd: Effect.sync(() => process.cwd()),\n env: Effect.sync(() => ({ ...process.env })),\n});\n\nexport const YamlParserLive = Layer.succeed(YamlParser, {\n parse: (input) =>\n Effect.try({\n try: () => parseYaml(input),\n catch: (error) => error,\n }),\n});\n\nexport const NodeServicesLive = Layer.mergeAll(\n NodeFileSystemLive,\n NodeEnvironmentLive,\n YamlParserLive,\n);\n\nexport function pathExists(path: string): Effect.Effect<boolean, never, FileSystem> {\n return Effect.gen(function* () {\n const fs = yield* FileSystem;\n return yield* fs.stat(path).pipe(\n Effect.as(true),\n Effect.catchAll(() => Effect.succeed(false)),\n );\n });\n}\n\nexport function pathExecutable(path: string): Effect.Effect<boolean, never, FileSystem> {\n return Effect.gen(function* () {\n const fs = yield* FileSystem;\n return yield* fs.access(path, constants.X_OK).pipe(\n Effect.as(true),\n Effect.catchAll(() => Effect.succeed(false)),\n );\n });\n}\n"],"mappings":"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"}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
//#region src/evidence-types.d.ts
|
|
2
|
-
declare const EVIDENCE_SCHEMA_VERSION = "agent-skill-evals.evidence.v1";
|
|
3
|
-
interface CommandEvent {
|
|
4
|
-
command: string;
|
|
5
|
-
args: string[];
|
|
6
|
-
exitCode: number;
|
|
7
|
-
signal?: string;
|
|
8
|
-
stdout?: string;
|
|
9
|
-
stderr?: string;
|
|
10
|
-
startedAt: number;
|
|
11
|
-
durationMs: number;
|
|
12
|
-
}
|
|
13
|
-
interface FileEvent {
|
|
14
|
-
path: string;
|
|
15
|
-
op: "create" | "modify" | "delete";
|
|
16
|
-
}
|
|
17
|
-
interface ToolCallEvent {
|
|
18
|
-
tool: string;
|
|
19
|
-
provider?: string;
|
|
20
|
-
server?: string;
|
|
21
|
-
args?: unknown;
|
|
22
|
-
result?: unknown;
|
|
23
|
-
startedAt: number;
|
|
24
|
-
durationMs: number;
|
|
25
|
-
}
|
|
26
|
-
interface SkillLoadEvent {
|
|
27
|
-
skill: string;
|
|
28
|
-
delivery: "native" | "mcp";
|
|
29
|
-
provider?: string;
|
|
30
|
-
server?: string;
|
|
31
|
-
source?: string;
|
|
32
|
-
startedAt: number;
|
|
33
|
-
}
|
|
34
|
-
interface Usage {
|
|
35
|
-
inputTokens?: number;
|
|
36
|
-
outputTokens?: number;
|
|
37
|
-
totalTokens?: number;
|
|
38
|
-
cacheReadTokens?: number;
|
|
39
|
-
cacheWriteTokens?: number;
|
|
40
|
-
}
|
|
41
|
-
interface RunSummary {
|
|
42
|
-
runDir: string;
|
|
43
|
-
worldPath: string;
|
|
44
|
-
fixture: string;
|
|
45
|
-
durationMs?: number;
|
|
46
|
-
}
|
|
47
|
-
interface EvidenceSnapshot {
|
|
48
|
-
schemaVersion: typeof EVIDENCE_SCHEMA_VERSION;
|
|
49
|
-
output: string;
|
|
50
|
-
run: RunSummary;
|
|
51
|
-
commands: CommandEvent[];
|
|
52
|
-
filesWritten: FileEvent[];
|
|
53
|
-
toolCalls: ToolCallEvent[];
|
|
54
|
-
skillsLoaded: SkillLoadEvent[];
|
|
55
|
-
usage: Usage;
|
|
56
|
-
extensions?: Record<string, unknown>;
|
|
57
|
-
}
|
|
58
|
-
//#endregion
|
|
59
|
-
//#region src/internal-types.d.ts
|
|
60
|
-
interface AgentSkillEvalsAssertionResult {
|
|
61
|
-
pass: boolean;
|
|
62
|
-
score: number;
|
|
63
|
-
reason: string;
|
|
64
|
-
componentResults?: AgentSkillEvalsAssertionResult[];
|
|
65
|
-
evidence?: unknown;
|
|
66
|
-
}
|
|
67
|
-
interface EvidenceHandle {
|
|
68
|
-
commands(): readonly CommandEvent[];
|
|
69
|
-
filesWritten(): readonly FileEvent[];
|
|
70
|
-
toolCalls(): readonly ToolCallEvent[];
|
|
71
|
-
skillsLoaded(): readonly SkillLoadEvent[];
|
|
72
|
-
usage(): Usage;
|
|
73
|
-
}
|
|
74
|
-
//#endregion
|
|
75
|
-
export { FileEvent as a, Usage as c, EvidenceSnapshot as i, EvidenceHandle as n, SkillLoadEvent as o, CommandEvent as r, ToolCallEvent as s, AgentSkillEvalsAssertionResult as t };
|
|
76
|
-
//# sourceMappingURL=internal-services-DbsekQ_K.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"internal-services-DbsekQ_K.d.mts","names":[],"sources":["../src/evidence-types.ts","../src/internal-types.ts"],"mappings":";cAAa,uBAAA;AAAA,UAEI,YAAA;EACf,OAAA;EACA,IAAA;EACA,QAAA;EACA,MAAA;EACA,MAAA;EACA,MAAA;EACA,SAAA;EACA,UAAA;AAAA;AAAA,UAGe,SAAA;EACf,IAAA;EACA,EAAA;AAAA;AAAA,UAGe,aAAA;EACf,IAAA;EACA,QAAA;EACA,MAAA;EACA,IAAA;EACA,MAAA;EACA,SAAA;EACA,UAAA;AAAA;AAAA,UAGe,cAAA;EACf,KAAA;EACA,QAAA;EACA,QAAA;EACA,MAAA;EACA,MAAA;EACA,SAAA;AAAA;AAAA,UAGe,KAAA;EACf,WAAA;EACA,YAAA;EACA,WAAA;EACA,eAAA;EACA,gBAAA;AAAA;AAAA,UAGe,UAAA;EACf,MAAA;EACA,SAAA;EACA,OAAA;EACA,UAAA;AAAA;AAAA,UAGe,gBAAA;EACf,aAAA,SAAsB,uBAAA;EACtB,MAAA;EACA,GAAA,EAAK,UAAA;EACL,QAAA,EAAU,YAAA;EACV,YAAA,EAAc,SAAA;EACd,SAAA,EAAW,aAAA;EACX,YAAA,EAAc,cAAA;EACd,KAAA,EAAO,KAAA;EACP,UAAA,GAAa,MAAA;AAAA;;;UClDE,8BAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;EACA,gBAAA,GAAmB,8BAAA;EACnB,QAAA;AAAA;AAAA,UAee,cAAA;EACf,QAAA,aAAqB,YAAA;EACrB,YAAA,aAAyB,SAAA;EACzB,SAAA,aAAsB,aAAA;EACtB,YAAA,aAAyB,cAAA;EACzB,KAAA,IAAS,KAAA;AAAA"}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { t as AssertionEntryError } from "../assertion-entries-CfmNt-fp.mjs";
|
|
2
|
-
//#region src/skill-checks/skill.d.ts
|
|
3
|
-
interface SkillFrontmatter {
|
|
4
|
-
name?: string;
|
|
5
|
-
description?: string;
|
|
6
|
-
[key: string]: unknown;
|
|
7
|
-
}
|
|
8
|
-
interface ParsedSkill {
|
|
9
|
-
/** Absolute path to the SKILL.md file. */
|
|
10
|
-
skillMdPath: string;
|
|
11
|
-
/** Absolute path to the skill folder (parent of SKILL.md). */
|
|
12
|
-
skillDir: string;
|
|
13
|
-
frontmatter: SkillFrontmatter;
|
|
14
|
-
/** Raw markdown body (after frontmatter). */
|
|
15
|
-
body: string;
|
|
16
|
-
/** Total lines in SKILL.md (including frontmatter). */
|
|
17
|
-
totalLines: number;
|
|
18
|
-
/** Relative paths referenced from SKILL.md (markdown links + script paths). */
|
|
19
|
-
references: string[];
|
|
20
|
-
/** Subset of references that don't exist on disk. */
|
|
21
|
-
missingReferences: string[];
|
|
22
|
-
}
|
|
23
|
-
declare function parseSkillMd(skillMdPath: string): Promise<ParsedSkill>;
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region src/skill-checks/tests-pack.d.ts
|
|
26
|
-
interface ParsedTest {
|
|
27
|
-
/** Absolute path of the YAML file containing this test. */
|
|
28
|
-
filePath: string;
|
|
29
|
-
description?: string;
|
|
30
|
-
vars: Record<string, unknown>;
|
|
31
|
-
/** Effect-type strings referenced by preconditions/should/should_not. */
|
|
32
|
-
effectTypes: string[];
|
|
33
|
-
/** Whether `vars.fixture` was set (or `fixtureless` flagged). */
|
|
34
|
-
hasFixture: boolean;
|
|
35
|
-
/** True if the test is `kind: negative` (or has no positive should + has should_not). */
|
|
36
|
-
isNegative: boolean;
|
|
37
|
-
/** True if the test declares any precondition. */
|
|
38
|
-
hasPrecondition: boolean;
|
|
39
|
-
/** True if the test declares an Agent Skill Evals token budget assertion. */
|
|
40
|
-
hasTokenBudget: boolean;
|
|
41
|
-
/** True if the file or vars marks the test as draft. */
|
|
42
|
-
isDraft: boolean;
|
|
43
|
-
/** Authoring diagnostics for malformed Agent Skill Evals assertion entries. */
|
|
44
|
-
entryErrors: AssertionEntryError[];
|
|
45
|
-
}
|
|
46
|
-
interface ParsedTestsPack {
|
|
47
|
-
/** YAML files matched by `testsGlob`. */
|
|
48
|
-
matchedFiles: string[];
|
|
49
|
-
/** All test cases discovered. */
|
|
50
|
-
tests: ParsedTest[];
|
|
51
|
-
/** Files that failed to parse (with error messages). */
|
|
52
|
-
parseErrors: Array<{
|
|
53
|
-
filePath: string;
|
|
54
|
-
error: string;
|
|
55
|
-
}>;
|
|
56
|
-
/** Verifier scripts referenced by `verifier.succeeds`/`verifier.fails`. */
|
|
57
|
-
verifierScripts: string[];
|
|
58
|
-
/** Verifier scripts that don't exist on disk. */
|
|
59
|
-
missingVerifierScripts: string[];
|
|
60
|
-
/** Verifier scripts that exist but are not executable by the current user. */
|
|
61
|
-
nonExecutableVerifierScripts: string[];
|
|
62
|
-
/** Fixture paths referenced by tests. */
|
|
63
|
-
fixturePaths: string[];
|
|
64
|
-
/** Fixture paths that don't exist on disk. */
|
|
65
|
-
missingFixturePaths: string[];
|
|
66
|
-
/** Effect types not in the supplied known-types set. */
|
|
67
|
-
unresolvedEffectTypes: string[];
|
|
68
|
-
}
|
|
69
|
-
declare function parseTestsPack(input: {
|
|
70
|
-
testsGlob: string;
|
|
71
|
-
baseDir: string;
|
|
72
|
-
knownEffectTypes: ReadonlySet<string>;
|
|
73
|
-
}): Promise<ParsedTestsPack>;
|
|
74
|
-
//#endregion
|
|
75
|
-
//#region src/skill-checks/index.d.ts
|
|
76
|
-
interface StaticProviderConfig {
|
|
77
|
-
/** Override for the cwd Promptfoo was launched from. */
|
|
78
|
-
baseDir?: string;
|
|
79
|
-
}
|
|
80
|
-
interface PromptfooContext {
|
|
81
|
-
vars?: Record<string, unknown>;
|
|
82
|
-
test?: {
|
|
83
|
-
vars?: Record<string, unknown>;
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
interface ProviderResponse {
|
|
87
|
-
output: string;
|
|
88
|
-
metadata?: Record<string, unknown>;
|
|
89
|
-
error?: string;
|
|
90
|
-
}
|
|
91
|
-
interface StaticProviderMetadata {
|
|
92
|
-
skill: ParsedSkill | null;
|
|
93
|
-
tests: ParsedTestsPack | null;
|
|
94
|
-
/** Combined missing files: skill references + verifier scripts + fixtures. */
|
|
95
|
-
missingFiles: string[];
|
|
96
|
-
/** Effect types referenced by tests but not in `knownEffectTypes`. */
|
|
97
|
-
unresolvedEffectTypes: string[];
|
|
98
|
-
warnings: string[];
|
|
99
|
-
}
|
|
100
|
-
declare class AgentSkillEvalsStaticProvider {
|
|
101
|
-
config: StaticProviderConfig;
|
|
102
|
-
private readonly configError?;
|
|
103
|
-
id: () => string;
|
|
104
|
-
constructor(options?: {
|
|
105
|
-
config?: StaticProviderConfig;
|
|
106
|
-
id?: string;
|
|
107
|
-
});
|
|
108
|
-
callApi(_prompt: string, context?: PromptfooContext): Promise<ProviderResponse>;
|
|
109
|
-
private callApiEffect;
|
|
110
|
-
}
|
|
111
|
-
//#endregion
|
|
112
|
-
export { AgentSkillEvalsStaticProvider, AgentSkillEvalsStaticProvider as default, type ParsedSkill, type ParsedTestsPack, StaticProviderConfig, StaticProviderMetadata, parseSkillMd, parseTestsPack };
|
|
113
|
-
//# sourceMappingURL=index.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/skill-checks/skill.ts","../../src/skill-checks/tests-pack.ts","../../src/skill-checks/index.ts"],"mappings":";;UASiB,gBAAA;EACf,IAAA;EACA,WAAA;EAAA,CACC,GAAA;AAAA;AAAA,UAGc,WAAA;EANgB;EAQ/B,WAAA;EANA;EAQA,QAAA;EACA,WAAA,EAAa,gBAAA;EARD;EAUZ,IAAA;EAP0B;EAS1B,UAAA;EAJ6B;EAM7B,UAAA;EAPA;EASA,iBAAA;AAAA;AAAA,iBAKoB,YAAA,CAAa,WAAA,WAAsB,OAAA,CAAQ,WAAA;;;UChBhD,UAAA;;EAEf,QAAA;EACA,WAAA;EACA,IAAA,EAAM,MAAA;EDZyB;ECc/B,WAAA;EDZA;ECcA,UAAA;EDbY;ECeZ,UAAA;EDZe;ECcf,eAAA;;EAEA,cAAA;EDdA;ECgBA,OAAA;EDbA;ECeA,WAAA,EAAa,mBAAA;AAAA;AAAA,UAGE,eAAA;EDZf;ECcA,YAAA;EDZiB;ECcjB,KAAA,EAAO,UAAA;EDTa;ECWpB,WAAA,EAAa,KAAA;IAAQ,QAAA;IAAkB,KAAA;EAAA;EDXgB;ECavD,eAAA;EDb0E;ECe1E,sBAAA;;EAEA,4BAAA;;EAEA,YAAA;EAnCyB;EAqCzB,mBAAA;EAnBgC;EAqBhC,qBAAA;AAAA;AAAA,iBAGoB,cAAA,CAAe,KAAA;EACnC,SAAA;EACA,OAAA;EACA,gBAAA,EAAkB,WAAA;AAAA,IAChB,OAAA,CAAQ,eAAA;;;UChDK,oBAAA;;EAEf,OAAA;AAAA;AAAA,UAOQ,gBAAA;EACR,IAAA,GAAO,MAAA;EACP,IAAA;IAAS,IAAA,GAAO,MAAA;EAAA;AAAA;AAAA,UAGR,gBAAA;EACR,MAAA;EACA,QAAA,GAAW,MAAA;EACX,KAAA;AAAA;AAAA,UAGe,sBAAA;EACf,KAAA,EAAO,WAAA;EACP,KAAA,EAAO,eAAA;EFjBP;EEmBA,YAAA;EFjBA;EEmBA,qBAAA;EACA,QAAA;AAAA;AAAA,cAGI,6BAAA;EACJ,MAAA,EAAQ,oBAAA;EAAA,iBACS,WAAA;EACjB,EAAA;cAEY,OAAA;IAAW,MAAA,GAAS,oBAAA;IAAsB,EAAA;EAAA;EAYhD,OAAA,CACJ,OAAA,UACA,OAAA,GAAS,gBAAA,GACR,OAAA,CAAQ,gBAAA;EAAA,QAMH,aAAA;AAAA"}
|
|
@@ -1,408 +0,0 @@
|
|
|
1
|
-
import { a as pathExecutable, c as parseRuntimeTestFields, i as YamlParser, l as RUNTIME_CHECK_TYPES, n as FileSystem, o as pathExists, r as NodeServicesLive, s as parseAssertionEntries, t as Environment } from "../internal-services-5-mRgNls.mjs";
|
|
2
|
-
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
-
import * as Either from "effect/Either";
|
|
4
|
-
import * as Effect from "effect/Effect";
|
|
5
|
-
import * as ParseResult from "effect/ParseResult";
|
|
6
|
-
import * as Schema from "effect/Schema";
|
|
7
|
-
//#region src/skill-checks/skill.ts
|
|
8
|
-
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
9
|
-
async function parseSkillMd(skillMdPath) {
|
|
10
|
-
return Effect.runPromise(parseSkillMdEffect(skillMdPath).pipe(Effect.provide(NodeServicesLive)));
|
|
11
|
-
}
|
|
12
|
-
function parseSkillMdEffect(skillMdPath) {
|
|
13
|
-
return Effect.gen(function* () {
|
|
14
|
-
const skillDir = dirname(skillMdPath);
|
|
15
|
-
const fs = yield* FileSystem;
|
|
16
|
-
const yaml = yield* YamlParser;
|
|
17
|
-
const raw = yield* fs.readText(skillMdPath);
|
|
18
|
-
const totalLines = raw.split(/\r?\n/).length;
|
|
19
|
-
let frontmatter = {};
|
|
20
|
-
let body = raw;
|
|
21
|
-
const fmMatch = raw.match(FRONTMATTER_RE);
|
|
22
|
-
if (fmMatch) {
|
|
23
|
-
const parsed = yield* yaml.parse(fmMatch[1]);
|
|
24
|
-
if (parsed && typeof parsed === "object") frontmatter = parsed;
|
|
25
|
-
body = raw.slice(fmMatch[0].length);
|
|
26
|
-
}
|
|
27
|
-
const references = extractReferences(body);
|
|
28
|
-
const missingReferences = [];
|
|
29
|
-
for (const ref of references) if (!(yield* pathExists(isAbsolute(ref) ? ref : resolve(skillDir, ref)))) missingReferences.push(ref);
|
|
30
|
-
return {
|
|
31
|
-
skillMdPath,
|
|
32
|
-
skillDir,
|
|
33
|
-
frontmatter,
|
|
34
|
-
body,
|
|
35
|
-
totalLines,
|
|
36
|
-
references,
|
|
37
|
-
missingReferences
|
|
38
|
-
};
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Extract relative paths from a SKILL.md body. Captures:
|
|
43
|
-
* - Markdown links: [text](path)
|
|
44
|
-
* - Bare relative paths in code/inline-code: ./foo, ../foo, foo/bar.sh
|
|
45
|
-
* Skips http(s):// URLs.
|
|
46
|
-
*/
|
|
47
|
-
function extractReferences(body) {
|
|
48
|
-
const refs = /* @__PURE__ */ new Set();
|
|
49
|
-
const linkRe = /\[[^\]]*\]\(([^)\s]+)\)/g;
|
|
50
|
-
let m;
|
|
51
|
-
while ((m = linkRe.exec(body)) !== null) {
|
|
52
|
-
const target = m[1];
|
|
53
|
-
if (target.startsWith("http://") || target.startsWith("https://")) continue;
|
|
54
|
-
if (target.startsWith("#")) continue;
|
|
55
|
-
refs.add(target);
|
|
56
|
-
}
|
|
57
|
-
const codeRe = /`((?:\.{1,2}\/)?[a-zA-Z0-9_./-]+\.[a-zA-Z0-9]{1,5})`/g;
|
|
58
|
-
while ((m = codeRe.exec(body)) !== null) {
|
|
59
|
-
const target = m[1];
|
|
60
|
-
if (target.includes("://")) continue;
|
|
61
|
-
refs.add(target);
|
|
62
|
-
}
|
|
63
|
-
return [...refs];
|
|
64
|
-
}
|
|
65
|
-
//#endregion
|
|
66
|
-
//#region src/skill-checks/schemas.ts
|
|
67
|
-
const UnknownRecordSchema = Schema.Unknown.pipe(Schema.filter((value) => isRecord(value), {
|
|
68
|
-
identifier: "UnknownRecord",
|
|
69
|
-
message: () => "must be an object"
|
|
70
|
-
}));
|
|
71
|
-
const UnknownArraySchema = Schema.Array(Schema.Unknown);
|
|
72
|
-
function decodeTestPackDocument(value) {
|
|
73
|
-
if (Array.isArray(value)) try {
|
|
74
|
-
return { cases: Schema.decodeUnknownSync(UnknownArraySchema)(value) };
|
|
75
|
-
} catch (err) {
|
|
76
|
-
return {
|
|
77
|
-
cases: [],
|
|
78
|
-
parseError: schemaErrorMessage(err)
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
if (decodeRecord(value) !== null) return { cases: [value] };
|
|
82
|
-
return {
|
|
83
|
-
cases: [],
|
|
84
|
-
parseError: "test pack document must be an object or array of objects"
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
function decodeStaticTestCase(value) {
|
|
88
|
-
const caseRecord = decodeRecord(value);
|
|
89
|
-
if (caseRecord === null) return {
|
|
90
|
-
caseRecord: {},
|
|
91
|
-
vars: {},
|
|
92
|
-
entryErrors: [{
|
|
93
|
-
field: "case",
|
|
94
|
-
reason: "must be an object"
|
|
95
|
-
}]
|
|
96
|
-
};
|
|
97
|
-
const rawVars = caseRecord.vars;
|
|
98
|
-
if (rawVars === void 0 || rawVars === null) return {
|
|
99
|
-
caseRecord,
|
|
100
|
-
vars: {},
|
|
101
|
-
entryErrors: []
|
|
102
|
-
};
|
|
103
|
-
const vars = decodeRecord(rawVars);
|
|
104
|
-
if (vars === null) return {
|
|
105
|
-
caseRecord,
|
|
106
|
-
vars: {},
|
|
107
|
-
entryErrors: [{
|
|
108
|
-
field: "vars",
|
|
109
|
-
reason: "must be an object"
|
|
110
|
-
}]
|
|
111
|
-
};
|
|
112
|
-
return {
|
|
113
|
-
caseRecord,
|
|
114
|
-
vars,
|
|
115
|
-
entryErrors: []
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
function decodeRecord(value) {
|
|
119
|
-
try {
|
|
120
|
-
return Schema.decodeUnknownSync(UnknownRecordSchema)(value);
|
|
121
|
-
} catch {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
function isRecord(value) {
|
|
126
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
127
|
-
}
|
|
128
|
-
function schemaErrorMessage(err) {
|
|
129
|
-
return err instanceof Error ? err.message : String(err);
|
|
130
|
-
}
|
|
131
|
-
//#endregion
|
|
132
|
-
//#region src/skill-checks/tests-pack.ts
|
|
133
|
-
async function parseTestsPack(input) {
|
|
134
|
-
return Effect.runPromise(parseTestsPackEffect(input).pipe(Effect.provide(NodeServicesLive)));
|
|
135
|
-
}
|
|
136
|
-
function parseTestsPackEffect(input) {
|
|
137
|
-
return Effect.gen(function* () {
|
|
138
|
-
const fs = yield* FileSystem;
|
|
139
|
-
const yaml = yield* YamlParser;
|
|
140
|
-
const files = yield* expandGlobEffect(input.testsGlob, input.baseDir);
|
|
141
|
-
const tests = [];
|
|
142
|
-
const parseErrors = [];
|
|
143
|
-
for (const file of files) {
|
|
144
|
-
const parsedFile = yield* Effect.either(Effect.gen(function* () {
|
|
145
|
-
const raw = yield* fs.readText(file);
|
|
146
|
-
const document = decodeTestPackDocument(yield* yaml.parse(raw));
|
|
147
|
-
if (document.parseError) return { error: document.parseError };
|
|
148
|
-
return { cases: document.cases.map((c) => parseCase(file, c)) };
|
|
149
|
-
}));
|
|
150
|
-
if (parsedFile._tag === "Left") parseErrors.push({
|
|
151
|
-
filePath: file,
|
|
152
|
-
error: String(parsedFile.left)
|
|
153
|
-
});
|
|
154
|
-
else if ("error" in parsedFile.right && parsedFile.right.error !== void 0) parseErrors.push({
|
|
155
|
-
filePath: file,
|
|
156
|
-
error: parsedFile.right.error
|
|
157
|
-
});
|
|
158
|
-
else tests.push(...parsedFile.right.cases);
|
|
159
|
-
}
|
|
160
|
-
const verifierScripts = uniq(tests.flatMap((t) => {
|
|
161
|
-
const fixtureRoot = typeof t.vars.fixture === "string" ? isAbsolute(t.vars.fixture) ? t.vars.fixture : resolve(input.baseDir, t.vars.fixture) : input.baseDir;
|
|
162
|
-
return collectVerifierScripts(t.vars).map((p) => absolveScript(p, fixtureRoot));
|
|
163
|
-
}));
|
|
164
|
-
const missingVerifierScripts = [];
|
|
165
|
-
const nonExecutableVerifierScripts = [];
|
|
166
|
-
for (const s of verifierScripts) if (!(yield* pathExists(s))) missingVerifierScripts.push(s);
|
|
167
|
-
else if (!(yield* pathExecutable(s))) nonExecutableVerifierScripts.push(s);
|
|
168
|
-
const fixturePaths = uniq(tests.map((t) => typeof t.vars.fixture === "string" ? t.vars.fixture : null).filter((p) => p !== null).map((p) => isAbsolute(p) ? p : resolve(input.baseDir, p)));
|
|
169
|
-
const missingFixturePaths = [];
|
|
170
|
-
for (const p of fixturePaths) if (!(yield* pathExists(p))) missingFixturePaths.push(p);
|
|
171
|
-
return {
|
|
172
|
-
matchedFiles: files,
|
|
173
|
-
tests,
|
|
174
|
-
parseErrors,
|
|
175
|
-
verifierScripts,
|
|
176
|
-
missingVerifierScripts,
|
|
177
|
-
nonExecutableVerifierScripts,
|
|
178
|
-
fixturePaths,
|
|
179
|
-
missingFixturePaths,
|
|
180
|
-
unresolvedEffectTypes: uniq(tests.flatMap((t) => t.effectTypes)).filter((e) => !input.knownEffectTypes.has(e))
|
|
181
|
-
};
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
function parseCase(filePath, input) {
|
|
185
|
-
const decoded = decodeStaticTestCase(input);
|
|
186
|
-
const c = decoded.caseRecord;
|
|
187
|
-
const vars = decoded.vars;
|
|
188
|
-
const description = typeof c.description === "string" ? c.description : void 0;
|
|
189
|
-
const parsed = parseRuntimeTestFields(vars);
|
|
190
|
-
const effectTypes = uniq([
|
|
191
|
-
...parsed.preconditions,
|
|
192
|
-
...parsed.should,
|
|
193
|
-
...parsed.should_not
|
|
194
|
-
].map((entry) => entry.type));
|
|
195
|
-
const entryErrors = [...decoded.entryErrors, ...parsed.errors];
|
|
196
|
-
const isNegative = vars.kind === "negative" || Array.isArray(vars.should_not) && vars.should_not.length > 0 && (!Array.isArray(vars.should) || vars.should.length === 0);
|
|
197
|
-
return {
|
|
198
|
-
filePath,
|
|
199
|
-
description,
|
|
200
|
-
vars,
|
|
201
|
-
effectTypes,
|
|
202
|
-
hasFixture: typeof vars.fixture === "string" || vars.fixtureless === true,
|
|
203
|
-
isNegative,
|
|
204
|
-
hasPrecondition: Array.isArray(vars.preconditions) && vars.preconditions.length > 0,
|
|
205
|
-
hasTokenBudget: declaresTokenBudget(c.assert),
|
|
206
|
-
isDraft: vars.draft === true || typeof c.metadata === "object" && c.metadata !== null && c.metadata.draft === true,
|
|
207
|
-
entryErrors
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
function declaresTokenBudget(assertions) {
|
|
211
|
-
if (!Array.isArray(assertions)) return false;
|
|
212
|
-
return assertions.some((assertion) => {
|
|
213
|
-
if (!assertion || typeof assertion !== "object" || Array.isArray(assertion)) return false;
|
|
214
|
-
const record = assertion;
|
|
215
|
-
if (record.metric === "skill.budget") return true;
|
|
216
|
-
return (record.config && typeof record.config === "object" && !Array.isArray(record.config) ? record.config : {}).metric === "skill.budget";
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
function collectVerifierScripts(vars) {
|
|
220
|
-
const out = [];
|
|
221
|
-
const parsed = {
|
|
222
|
-
preconditions: parseAssertionEntries(vars.preconditions, "preconditions", { allowMissing: true }),
|
|
223
|
-
should: parseAssertionEntries(vars.should, "should", { allowMissing: true }),
|
|
224
|
-
should_not: parseAssertionEntries(vars.should_not, "should_not", { allowMissing: true })
|
|
225
|
-
};
|
|
226
|
-
for (const entries of Object.values(parsed)) for (const entry of entries.entries) {
|
|
227
|
-
if (!isVerifierEntry(entry)) continue;
|
|
228
|
-
if (entry.args && typeof entry.args === "object" && typeof entry.args.run === "string") out.push(entry.args.run);
|
|
229
|
-
}
|
|
230
|
-
return out;
|
|
231
|
-
}
|
|
232
|
-
function isVerifierEntry(entry) {
|
|
233
|
-
return entry.type === "verifier.succeeds" || entry.type === "verifier.fails";
|
|
234
|
-
}
|
|
235
|
-
function absolveScript(scriptPath, fixtureRoot) {
|
|
236
|
-
if (isAbsolute(scriptPath)) return scriptPath;
|
|
237
|
-
return resolve(fixtureRoot, scriptPath);
|
|
238
|
-
}
|
|
239
|
-
function uniq(xs) {
|
|
240
|
-
return [...new Set(xs)];
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Tiny glob expander. Handles literal paths, `**\/*.yaml`, and a single `*`
|
|
244
|
-
* segment. Sufficient for Agent Skill Evals conventions.
|
|
245
|
-
*/
|
|
246
|
-
function expandGlobEffect(pattern, baseDir) {
|
|
247
|
-
return Effect.gen(function* () {
|
|
248
|
-
const fs = yield* FileSystem;
|
|
249
|
-
const abs = isAbsolute(pattern) ? pattern : resolve(baseDir, pattern);
|
|
250
|
-
const absStat = yield* fs.stat(abs).pipe(Effect.either);
|
|
251
|
-
if (absStat._tag === "Right" && absStat.right.isFile()) return [abs];
|
|
252
|
-
if (absStat._tag === "Right" && absStat.right.isDirectory()) return yield* listYamlEffect(abs);
|
|
253
|
-
const idx = abs.search(/\*\*?|\*/);
|
|
254
|
-
if (idx < 0) return [];
|
|
255
|
-
const root = abs.slice(0, idx).replace(/\/$/, "") || "/";
|
|
256
|
-
const tail = abs.slice(idx);
|
|
257
|
-
const matches = [];
|
|
258
|
-
yield* walkEffect(root, (p) => Effect.sync(() => {
|
|
259
|
-
if (matchesGlob(p, root, tail)) matches.push(p);
|
|
260
|
-
}));
|
|
261
|
-
return matches;
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
function listYamlEffect(dir) {
|
|
265
|
-
return Effect.gen(function* () {
|
|
266
|
-
const out = [];
|
|
267
|
-
yield* walkEffect(dir, (p) => Effect.sync(() => {
|
|
268
|
-
if (p.endsWith(".yaml") || p.endsWith(".yml")) out.push(p);
|
|
269
|
-
}));
|
|
270
|
-
return out;
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
function walkEffect(dir, visit) {
|
|
274
|
-
return Effect.gen(function* () {
|
|
275
|
-
const entries = yield* (yield* FileSystem).readDirectory(dir).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
276
|
-
for (const e of entries) {
|
|
277
|
-
const p = join(dir, e.name);
|
|
278
|
-
if (e.isDirectory()) {
|
|
279
|
-
if (e.name === "node_modules" || e.name === ".git") continue;
|
|
280
|
-
yield* walkEffect(p, visit);
|
|
281
|
-
} else if (e.isFile()) yield* visit(p);
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
function matchesGlob(p, root, tail) {
|
|
286
|
-
if (!p.startsWith(root)) return false;
|
|
287
|
-
const rel = p.slice(root.length).replace(/^\//, "");
|
|
288
|
-
return new RegExp("^" + tail.replace(/^\//, "").replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "(?:.*/)?").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*") + "$").test(rel);
|
|
289
|
-
}
|
|
290
|
-
//#endregion
|
|
291
|
-
//#region src/skill-checks/index.ts
|
|
292
|
-
const StaticProviderConfigSchema = Schema.Struct({ baseDir: Schema.optional(Schema.String) });
|
|
293
|
-
var AgentSkillEvalsStaticProvider = class {
|
|
294
|
-
config;
|
|
295
|
-
configError;
|
|
296
|
-
id;
|
|
297
|
-
constructor(options = {}) {
|
|
298
|
-
const config = decodeStaticProviderConfig(options.config ?? {});
|
|
299
|
-
if ("error" in config) {
|
|
300
|
-
this.config = {};
|
|
301
|
-
this.configError = config.error;
|
|
302
|
-
} else this.config = config;
|
|
303
|
-
const label = options.id ?? "agent-skill-evals-static";
|
|
304
|
-
this.id = () => label;
|
|
305
|
-
}
|
|
306
|
-
async callApi(_prompt, context = {}) {
|
|
307
|
-
return Effect.runPromise(this.callApiEffect(_prompt, context).pipe(Effect.provide(NodeServicesLive)));
|
|
308
|
-
}
|
|
309
|
-
callApiEffect(_prompt, context = {}) {
|
|
310
|
-
const self = this;
|
|
311
|
-
return Effect.gen(function* () {
|
|
312
|
-
if (self.configError) return {
|
|
313
|
-
output: "",
|
|
314
|
-
error: self.configError
|
|
315
|
-
};
|
|
316
|
-
const vars = context.vars ?? context.test?.vars ?? {};
|
|
317
|
-
const cwd = yield* (yield* Environment).cwd;
|
|
318
|
-
const baseDir = self.config.baseDir ?? cwd;
|
|
319
|
-
const knownTypes = new Set(RUNTIME_CHECK_TYPES);
|
|
320
|
-
const skillPath = stringVar(vars, "skillPath");
|
|
321
|
-
const testsGlob = stringVar(vars, "testsGlob");
|
|
322
|
-
const warnings = [];
|
|
323
|
-
if (!skillPath) return {
|
|
324
|
-
output: "",
|
|
325
|
-
error: "skill checks: vars.skillPath is required (example: skillPath: ./skills/bugfix-workflow)"
|
|
326
|
-
};
|
|
327
|
-
if (!testsGlob) return {
|
|
328
|
-
output: "",
|
|
329
|
-
error: "skill checks: vars.testsGlob is required (example: testsGlob: ./tests/bugfix-workflow.yaml)"
|
|
330
|
-
};
|
|
331
|
-
let skill = null;
|
|
332
|
-
const skillAbs = isAbsolute(skillPath) ? skillPath : resolve(baseDir, skillPath);
|
|
333
|
-
const skillMd = yield* ensureSkillMdEffect(skillAbs);
|
|
334
|
-
if (skillMd === null) return {
|
|
335
|
-
output: "",
|
|
336
|
-
error: `skill checks: SKILL.md not found at ${skillAbs}`
|
|
337
|
-
};
|
|
338
|
-
const parsedSkill = yield* Effect.either(parseSkillMdEffect(skillMd));
|
|
339
|
-
if (Either.isLeft(parsedSkill)) return {
|
|
340
|
-
output: "",
|
|
341
|
-
error: `skill checks: failed to parse SKILL.md at ${skillMd}: ${parsedSkill.left instanceof Error ? parsedSkill.left.message : String(parsedSkill.left)}`
|
|
342
|
-
};
|
|
343
|
-
skill = parsedSkill.right;
|
|
344
|
-
let tests = null;
|
|
345
|
-
tests = yield* parseTestsPackEffect({
|
|
346
|
-
testsGlob,
|
|
347
|
-
baseDir,
|
|
348
|
-
knownEffectTypes: knownTypes
|
|
349
|
-
});
|
|
350
|
-
const missingFiles = [];
|
|
351
|
-
if (skill) for (const ref of skill.missingReferences) missingFiles.push(`${skill.skillDir}/${ref}`);
|
|
352
|
-
if (tests) {
|
|
353
|
-
missingFiles.push(...tests.missingVerifierScripts);
|
|
354
|
-
missingFiles.push(...tests.missingFixturePaths);
|
|
355
|
-
}
|
|
356
|
-
const metadata = {
|
|
357
|
-
skill,
|
|
358
|
-
tests,
|
|
359
|
-
missingFiles,
|
|
360
|
-
unresolvedEffectTypes: tests?.unresolvedEffectTypes ?? [],
|
|
361
|
-
warnings
|
|
362
|
-
};
|
|
363
|
-
return {
|
|
364
|
-
output: summarise(metadata),
|
|
365
|
-
metadata
|
|
366
|
-
};
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
function decodeStaticProviderConfig(input) {
|
|
371
|
-
const decoded = Schema.decodeUnknownEither(StaticProviderConfigSchema, { errors: "all" })(input ?? {});
|
|
372
|
-
if (Either.isRight(decoded)) return decoded.right;
|
|
373
|
-
return { error: `skill checks: invalid config: ${ParseResult.TreeFormatter.formatErrorSync(decoded.left)}` };
|
|
374
|
-
}
|
|
375
|
-
function stringVar(vars, key) {
|
|
376
|
-
const value = vars[key];
|
|
377
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
378
|
-
}
|
|
379
|
-
function ensureSkillMdEffect(skillPath) {
|
|
380
|
-
return Effect.gen(function* () {
|
|
381
|
-
const fs = yield* FileSystem;
|
|
382
|
-
const statResult = yield* fs.stat(skillPath).pipe(Effect.either);
|
|
383
|
-
if (Either.isRight(statResult)) {
|
|
384
|
-
const s = statResult.right;
|
|
385
|
-
if (s.isFile()) return skillPath;
|
|
386
|
-
if (s.isDirectory()) {
|
|
387
|
-
const candidate = join(skillPath, "SKILL.md");
|
|
388
|
-
const cs = yield* fs.stat(candidate).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
389
|
-
return cs && cs.isFile() ? candidate : null;
|
|
390
|
-
}
|
|
391
|
-
return null;
|
|
392
|
-
}
|
|
393
|
-
return null;
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
function summarise(m) {
|
|
397
|
-
const lines = ["Agent Skill Evals skill check input loaded:"];
|
|
398
|
-
if (m.skill) lines.push(`- skill: ${m.skill.frontmatter.name ?? "?"} (${m.skill.skillMdPath})`);
|
|
399
|
-
if (m.tests) lines.push(`- tests: ${m.tests.tests.length} case(s) from ${m.tests.matchedFiles.length} file(s)`);
|
|
400
|
-
if (m.missingFiles.length) lines.push(`- missing files: ${m.missingFiles.length}`);
|
|
401
|
-
if (m.unresolvedEffectTypes.length) lines.push(`- unresolved effect types: ${m.unresolvedEffectTypes.length}`);
|
|
402
|
-
lines.push(m.warnings.length ? `- warnings: ${m.warnings.join("; ")}` : "- warnings: none");
|
|
403
|
-
return lines.join("\n");
|
|
404
|
-
}
|
|
405
|
-
//#endregion
|
|
406
|
-
export { AgentSkillEvalsStaticProvider, AgentSkillEvalsStaticProvider as default, parseSkillMd, parseTestsPack };
|
|
407
|
-
|
|
408
|
-
//# sourceMappingURL=index.mjs.map
|