feat(engine): scope skill loading by session purpose to reduce token overhead
This commit is contained in:
39
packages/engine/src/__tests__/skill-resolver-scoping.test.ts
Normal file
39
packages/engine/src/__tests__/skill-resolver-scoping.test.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { getSkillPurposeFilter } from "../skill-resolver.js";
|
||||
|
||||
describe("getSkillPurposeFilter", () => {
|
||||
it("returns a pass-all filter for executor sessions", () => {
|
||||
const filter = getSkillPurposeFilter("executor");
|
||||
expect(filter("any-skill")).toBe(true);
|
||||
expect(filter("fusion")).toBe(true);
|
||||
expect(filter("deployment")).toBe(true);
|
||||
});
|
||||
|
||||
it("filters to review-relevant skills for reviewer sessions", () => {
|
||||
const filter = getSkillPurposeFilter("reviewer");
|
||||
expect(filter("code-review")).toBe(true);
|
||||
expect(filter("security-review")).toBe(true);
|
||||
expect(filter("review")).toBe(true);
|
||||
expect(filter("deployment")).toBe(false);
|
||||
expect(filter("fusion")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns a pass-all filter for unknown session purposes", () => {
|
||||
const filter = getSkillPurposeFilter("unknown-purpose");
|
||||
expect(filter("any-skill")).toBe(true);
|
||||
});
|
||||
|
||||
it("filters to minimal skills for heartbeat sessions", () => {
|
||||
const filter = getSkillPurposeFilter("heartbeat");
|
||||
expect(filter("monitoring")).toBe(true);
|
||||
expect(filter("heartbeat")).toBe(true);
|
||||
expect(filter("fusion")).toBe(false);
|
||||
expect(filter("code-review")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns a pass-all filter for triage sessions", () => {
|
||||
const filter = getSkillPurposeFilter("triage");
|
||||
expect(filter("any-skill")).toBe(true);
|
||||
expect(filter("fusion")).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -484,3 +484,46 @@ export function createSkillsOverrideFromSelection(
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Skills that are relevant to each session purpose. Executors get all
|
||||
* skills (they do the implementation work). Other roles get a scoped
|
||||
* subset to avoid loading 71KB of reference material for every session.
|
||||
*
|
||||
* The filter matches against the skill's directory name (the last path
|
||||
* segment before SKILL.md).
|
||||
*/
|
||||
const REVIEWER_SKILL_ALLOWLIST = new Set([
|
||||
"code-review",
|
||||
"security-review",
|
||||
"review",
|
||||
]);
|
||||
|
||||
const HEARTBEAT_SKILL_ALLOWLIST = new Set([
|
||||
"monitoring",
|
||||
"heartbeat",
|
||||
]);
|
||||
|
||||
/**
|
||||
* Return a filter function that decides whether a skill (by name) should
|
||||
* be included in a session of the given purpose.
|
||||
*
|
||||
* - "executor" and "triage": all skills (pass-through)
|
||||
* - "reviewer": only review-related skills
|
||||
* - "heartbeat": only monitoring-related skills
|
||||
* - unknown: all skills (safe fallback)
|
||||
*/
|
||||
export function getSkillPurposeFilter(
|
||||
sessionPurpose: string,
|
||||
): (skillName: string) => boolean {
|
||||
switch (sessionPurpose) {
|
||||
case "reviewer":
|
||||
return (name) => REVIEWER_SKILL_ALLOWLIST.has(name);
|
||||
case "heartbeat":
|
||||
return (name) => HEARTBEAT_SKILL_ALLOWLIST.has(name);
|
||||
case "executor":
|
||||
case "triage":
|
||||
default:
|
||||
return () => true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user