skilld-harness 3.6.2 → 3.6.4
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 +4 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +93 -48
- package/dist/index.mjs.map +1 -1
- package/dist/skills/generate-package-skill/SKILL.md +36 -11
- package/dist/skills/generate-package-skill/assets/harness-request.md +3 -1
- package/dist/skills/generate-package-skill/scripts/extract-blocks.mjs +94 -0
- package/dist/skills/generate-package-skill/scripts/serve-fixture.mjs +43 -19
- package/dist/skills/review-skill/SKILL.md +14 -3
- package/dist/skills/review-skill/assets/harness-request.md +2 -1
- package/dist/skills/skilld/SKILL.md +17 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,11 +48,14 @@ Both `Ok` and `Err` carry a `report`, so a failed run still reports its cost.
|
|
|
48
48
|
A token count is `undefined` when the adapter does not report it.
|
|
49
49
|
If the run fails before the Agent starts, `steps` is 0.
|
|
50
50
|
`report.warnings` names each source file the Harness left out for size, each `onEvent` failure, and each cleanup problem.
|
|
51
|
-
|
|
51
|
+
If the output fails a check, the Harness sends the issues back to the Agent in the same session, up to two times.
|
|
52
|
+
Each repair turn adds its steps and usage to the report, and a warning names the issues.
|
|
53
|
+
An `InvalidSkill` error lists each check that still fails in `issues`.
|
|
52
54
|
|
|
53
55
|
The Harness checks the rules that the generation Skills state:
|
|
54
56
|
|
|
55
57
|
- The frontmatter contains only `name` and `description`.
|
|
58
|
+
- The description is one plain YAML line without double quotes, backticks, or `%`.
|
|
56
59
|
- `SKILL.md` links every file under `references/`, by a Markdown link or an inline code path.
|
|
57
60
|
- A package Skill keeps `SKILL.md` under 500 lines and writes at most eight reference files.
|
|
58
61
|
- A project Skill points only at project files and gives at least one search command.
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/harness.ts"],"mappings":";;UAGiB;WACN;WACA;;KAGC;WAEC;WACA;;WAGA;WACA;WACA;;KAGD;WAEC;WACA,QAAQ;WACR,aAAa;;WAGb;WACA;WACA,aAAa;;WAGb;WACA;;UAGI;WACN;WACA;WACA;WACA;WACA;WACA;;;UAIM;;WAEN;;WAEA;WACA;;;KAIC;WACK;WAA4B;;WAE9B;WACA;WACA;WACA;WACA;;WAGA;WACA;WACA;WACA,OAAO;;UAGL;WACN,SAAS;;;;;WAKT,WAAW,OAAO;;UAGZ;WACN;WACA;;UAGM;WACN;WACA;WACA;;UAGM;WACN;WACA;WACA;WACA,OAAO,cAAc;WACrB,gBAAgB,cAAc;;UAGxB;WACN;WACA;WACA;WACA;;UAGM;WACN;WACA;WACA,UAAU,cAAc;;KAGvB;WACK;WAA+B;;WAEjC;WACA;WACA,UAAU,cAAc;WACxB;;WAEE;WAA8B;WAA0B;;WACxD;WAA+B;WAA0B,QAAQ;;WACjE;WAAmC;WAA0B;;WAC7D;WAA6B;WAA0B;;WACvD;WAAkC;WAA0B;WAAuB;;WACnF;WAA4B;;;UAG5B;WACN,cAAc;WACd,cAAc;WACd,aAAa;;;;;;UAOP;;WAEN,OAAO;;WAEP;;;;;WAKA,UAAU;;KAGT,eAAe,YAAY,mBAAmB;WACzC;WAAqB,OAAO,eAAe;WAAe,QAAQ;;WAClE;WAAsB,OAAO;WAAwB,QAAQ;;UAE7D;WACN,MAAM,YAAY,UAAU,OAAO,KAAK,UAAU,oBAAoB,QAAQ,eAAe;;UAGvF;WACN,SAAS;;WAET,SAAS;;WAET,gBAAgB;WAChB,eAAe,QAAQ;;WAEvB,SAAS,gBAAgB,MAAM,SAAS,OAAO,gBAAgB,QAAQ;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/harness.ts"],"mappings":";;UAGiB;WACN;WACA;;KAGC;WAEC;WACA;;WAGA;WACA;WACA;;KAGD;WAEC;WACA,QAAQ;WACR,aAAa;;WAGb;WACA;WACA,aAAa;;WAGb;WACA;;UAGI;WACN;WACA;WACA;WACA;WACA;WACA;;;UAIM;;WAEN;;WAEA;WACA;;;KAIC;WACK;WAA4B;;WAE9B;WACA;WACA;WACA;WACA;;WAGA;WACA;WACA;WACA,OAAO;;UAGL;WACN,SAAS;;;;;WAKT,WAAW,OAAO;;UAGZ;WACN;WACA;;UAGM;WACN;WACA;WACA;;UAGM;WACN;WACA;WACA;WACA,OAAO,cAAc;WACrB,gBAAgB,cAAc;;UAGxB;WACN;WACA;WACA;WACA;;UAGM;WACN;WACA;WACA,UAAU,cAAc;;KAGvB;WACK;WAA+B;;WAEjC;WACA;WACA,UAAU,cAAc;WACxB;;WAEE;WAA8B;WAA0B;;WACxD;WAA+B;WAA0B,QAAQ;;WACjE;WAAmC;WAA0B;;WAC7D;WAA6B;WAA0B;;WACvD;WAAkC;WAA0B;WAAuB;;WACnF;WAA4B;;;UAG5B;WACN,cAAc;WACd,cAAc;WACd,aAAa;;;;;;UAOP;;WAEN,OAAO;;WAEP;;;;;WAKA,UAAU;;KAGT,eAAe,YAAY,mBAAmB;WACzC;WAAqB,OAAO,eAAe;WAAe,QAAQ;;WAClE;WAAsB,OAAO;WAAwB,QAAQ;;UAE7D;WACN,MAAM,YAAY,UAAU,OAAO,KAAK,UAAU,oBAAoB,QAAQ,eAAe;;UAGvF;WACN,SAAS;;WAET,SAAS;;WAET,gBAAgB;WAChB,eAAe,QAAQ;;WAEvB,SAAS,gBAAgB,MAAM,SAAS,OAAO,gBAAgB,QAAQ;;wBC6HlE,mBAAmB,SAAS,4BAA4B"}
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { lstat, mkdir, open, opendir, realpath, rename, rm, rmdir, unlink, write
|
|
|
3
3
|
import { basename, dirname, join, posix, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { HarnessAgent } from "@ai-sdk/harness/agent";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
|
-
import { parseDocument } from "yaml";
|
|
6
|
+
import { Scalar, isScalar, parseDocument } from "yaml";
|
|
7
7
|
import { constants } from "node:fs";
|
|
8
8
|
import { Readable } from "node:stream";
|
|
9
9
|
import { createGunzip } from "node:zlib";
|
|
@@ -362,6 +362,7 @@ function checkProjectSkill(input) {
|
|
|
362
362
|
const allowedFrontmatter = /* @__PURE__ */ new Set(["name", "description"]);
|
|
363
363
|
const packageSkillLineLimit = 500;
|
|
364
364
|
const packageSkillReferenceLimit = 8;
|
|
365
|
+
const riskyDescription = /["`%]/;
|
|
365
366
|
const invalid = (issues) => err({
|
|
366
367
|
_tag: "InvalidSkill",
|
|
367
368
|
message: "Skill output failed deterministic checks.",
|
|
@@ -428,6 +429,8 @@ const validateGeneratedSkill = (name, files, contract) => {
|
|
|
428
429
|
if (frontmatter.name !== name) issues.push("Frontmatter name must match the Skill directory name.");
|
|
429
430
|
if (typeof frontmatter.name !== "string" || !isSkillName(frontmatter.name)) issues.push("Frontmatter name is invalid.");
|
|
430
431
|
if (typeof frontmatter.description !== "string" || frontmatter.description.trim().length === 0 || frontmatter.description.length > 1024) issues.push("Frontmatter description must contain 1 to 1024 characters.");
|
|
432
|
+
const description = document.get("description", true);
|
|
433
|
+
if (typeof frontmatter.description === "string" && (!isScalar(description) || description.type !== Scalar.PLAIN || riskyDescription.test(frontmatter.description))) issues.push("Frontmatter description must be one plain line without double quotes, backticks, or %. Name symptoms in plain words.");
|
|
431
434
|
issues.push(...structureIssues(source, files, contract));
|
|
432
435
|
return issues.length === 0 ? ok(void 0) : invalid(issues);
|
|
433
436
|
};
|
|
@@ -1146,11 +1149,23 @@ function toUsage(usage) {
|
|
|
1146
1149
|
outputTokens: usage.outputTokens
|
|
1147
1150
|
};
|
|
1148
1151
|
}
|
|
1149
|
-
function
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1152
|
+
function sumUsage(a, b) {
|
|
1153
|
+
const sum = (x, y) => x === void 0 || y === void 0 ? void 0 : x + y;
|
|
1154
|
+
return {
|
|
1155
|
+
inputTokens: sum(a.inputTokens, b.inputTokens),
|
|
1156
|
+
cachedInputTokens: sum(a.cachedInputTokens, b.cachedInputTokens),
|
|
1157
|
+
outputTokens: sum(a.outputTokens, b.outputTokens)
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
const OUTPUT_REPAIR_TURNS = 2;
|
|
1161
|
+
function repairRequest(outputPath, issues) {
|
|
1162
|
+
return [
|
|
1163
|
+
`The Harness checked the output at \`${outputPath}\` and found these problems:`,
|
|
1164
|
+
"",
|
|
1165
|
+
...issues.map((issue) => `- ${issue}`),
|
|
1166
|
+
"",
|
|
1167
|
+
`Fix each problem in place. Write no files outside \`${outputPath}\`. Finish when the output passes these checks.`
|
|
1168
|
+
].join("\n");
|
|
1154
1169
|
}
|
|
1155
1170
|
async function prepareCurrentSkill(destination, policy, signal) {
|
|
1156
1171
|
const root = resolve(destination.rootDir);
|
|
@@ -1301,12 +1316,18 @@ async function writePreparedSource(active, prepared, signal) {
|
|
|
1301
1316
|
abortSignal: signal
|
|
1302
1317
|
});
|
|
1303
1318
|
}
|
|
1304
|
-
function
|
|
1305
|
-
return signal?.aborted ?
|
|
1319
|
+
function agentError(cause, signal) {
|
|
1320
|
+
return signal?.aborted ? {
|
|
1321
|
+
_tag: "Cancelled",
|
|
1322
|
+
message: "Skill run was cancelled."
|
|
1323
|
+
} : {
|
|
1306
1324
|
_tag: "AgentFailed",
|
|
1307
1325
|
message: "Harness Agent failed during the Skill run.",
|
|
1308
1326
|
cause
|
|
1309
|
-
}
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
function toAgentError(cause, signal) {
|
|
1330
|
+
return err(agentError(cause, signal));
|
|
1310
1331
|
}
|
|
1311
1332
|
function createSkillHarness(options) {
|
|
1312
1333
|
const policy = parseOutputPolicy({
|
|
@@ -1380,47 +1401,71 @@ function createSkillHarness(options) {
|
|
|
1380
1401
|
const currentSkillPath = posix.join(active.workDir, "input/current-skill");
|
|
1381
1402
|
const outputPath = posix.join(active.workDir, "skilld-output", prepared.outputName);
|
|
1382
1403
|
const prompt = renderRequest(requestContent(skill), sourcePath, currentSkillPath, outputPath, prepared.outputName);
|
|
1404
|
+
const outputSandbox = active.sandbox;
|
|
1383
1405
|
let step = 0;
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
step
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1406
|
+
let stepOffset = 0;
|
|
1407
|
+
let usage;
|
|
1408
|
+
const turn = async (text) => {
|
|
1409
|
+
const generated = await agent.generate({
|
|
1410
|
+
session,
|
|
1411
|
+
prompt: text,
|
|
1412
|
+
abortSignal: signal,
|
|
1413
|
+
onStepStart: (event) => {
|
|
1414
|
+
step = stepOffset + event.stepNumber;
|
|
1415
|
+
log.emit({
|
|
1416
|
+
_tag: "StepStart",
|
|
1417
|
+
step
|
|
1418
|
+
});
|
|
1419
|
+
},
|
|
1420
|
+
onToolExecutionStart: ({ toolCall }) => {
|
|
1421
|
+
log.emit({
|
|
1422
|
+
_tag: "ToolCall",
|
|
1423
|
+
step,
|
|
1424
|
+
toolName: toolCall.toolName,
|
|
1425
|
+
toolCallId: toolCall.toolCallId,
|
|
1426
|
+
input: toolCall.input
|
|
1427
|
+
});
|
|
1428
|
+
},
|
|
1429
|
+
onStepEnd: (result) => {
|
|
1430
|
+
log.emit({
|
|
1431
|
+
_tag: "StepFinish",
|
|
1432
|
+
step: stepOffset + result.stepNumber,
|
|
1433
|
+
finishReason: result.finishReason,
|
|
1434
|
+
usage: toUsage(result.usage)
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
}).then(ok, (cause) => err(cause));
|
|
1438
|
+
if (generated._tag === "Err") return err(agentError(generated.error, signal));
|
|
1439
|
+
const turnUsage = toUsage(generated.value.totalUsage);
|
|
1440
|
+
usage = usage ? sumUsage(usage, turnUsage) : turnUsage;
|
|
1441
|
+
stepOffset += generated.value.steps.length;
|
|
1442
|
+
log.settle(usage, stepOffset);
|
|
1443
|
+
return signal?.aborted ? err({
|
|
1444
|
+
_tag: "Cancelled",
|
|
1445
|
+
message: "Skill run was cancelled."
|
|
1446
|
+
}) : ok(void 0);
|
|
1447
|
+
};
|
|
1448
|
+
const check = async () => {
|
|
1449
|
+
const collected = await collectSandboxOutput(outputSandbox, outputPath, policy, signal);
|
|
1450
|
+
if (collected._tag === "Err") return collected;
|
|
1451
|
+
const validated = prepared.skillName === "review-skill" ? validateSkillReview(collected.value) : validateGeneratedSkill(prepared.outputName, collected.value, prepared.skillName === "generate-project-skill" ? {
|
|
1452
|
+
_tag: "ProjectSkill",
|
|
1453
|
+
projectPaths: prepared.source.files.map((file) => file.path)
|
|
1454
|
+
} : { _tag: "PackageSkill" });
|
|
1455
|
+
return validated._tag === "Err" ? validated : ok(collected.value);
|
|
1456
|
+
};
|
|
1457
|
+
const first = await turn(prompt);
|
|
1458
|
+
if (first._tag === "Err") return first;
|
|
1459
|
+
let checked = await check();
|
|
1460
|
+
for (let repair = 1; repair <= OUTPUT_REPAIR_TURNS && checked._tag === "Err" && checked.error._tag === "InvalidSkill"; repair++) {
|
|
1461
|
+
log.warn(`Output checks failed; repair turn ${repair} of ${OUTPUT_REPAIR_TURNS}: ${checked.error.issues.join(" ")}`);
|
|
1462
|
+
const repaired = await turn(repairRequest(outputPath, checked.error.issues));
|
|
1463
|
+
if (repaired._tag === "Err") return repaired;
|
|
1464
|
+
checked = await check();
|
|
1465
|
+
}
|
|
1466
|
+
if (checked._tag === "Err") return checked;
|
|
1467
|
+
const collected = checked;
|
|
1418
1468
|
if (prepared.skillName === "review-skill") return validateSkillReview(collected.value);
|
|
1419
|
-
const validated = validateGeneratedSkill(prepared.outputName, collected.value, prepared.skillName === "generate-project-skill" ? {
|
|
1420
|
-
_tag: "ProjectSkill",
|
|
1421
|
-
projectPaths: prepared.source.files.map((file) => file.path)
|
|
1422
|
-
} : { _tag: "PackageSkill" });
|
|
1423
|
-
if (validated._tag === "Err") return validated;
|
|
1424
1469
|
if (!prepared.destination) return err({
|
|
1425
1470
|
_tag: "InvalidInput",
|
|
1426
1471
|
message: "Skill destination is required."
|