feat(agent): 集数待确认的智能体兜底——查看上下文 / 模拟 / 写规则三个能力,写入前强制模拟,接后台调查与授权内恢复

能力 ID 的域名允许单个下划线分词(episode_mapping)。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-03 20:08:20 +08:00
1 parent 45a114263b
commit 7793c7b3bc
27 files changed
+1747 -48

No files matched your search

+6 -1
View File
@@ -16,7 +16,12 @@ describe("能力 ID ↔ 工具名", () => {
expect(capabilityIdToToolName("task.retry_later")).toBe("task__retry_later");
expect(toolNameToCapabilityId("download__submit")).toBe("download.submit");
expect(toolNameToCapabilityId("task__retry_later")).toBe("task.retry_later");
for (const id of ["catalog.lookup", "policy.apply", "organize.execute"]) {
// 域名可用单个下划线分词:按第一个 `__` 切分仍无歧义
expect(capabilityIdToToolName("episode_mapping.apply_rule")).toBe("episode_mapping__apply_rule");
expect(toolNameToCapabilityId("episode_mapping__apply_rule")).toBe("episode_mapping.apply_rule");
expect(() => capabilityIdToToolName("episode__mapping.inspect")).toThrow();
expect(() => capabilityIdToToolName("episode_.inspect")).toThrow();
for (const id of ["catalog.lookup", "policy.apply", "organize.execute", "episode_mapping.inspect"]) {
expect(toolNameToCapabilityId(capabilityIdToToolName(id))).toBe(id);
}
expect(toolNameToCapabilityId("bash")).toBeUndefined();
+12 -1
View File
@@ -50,7 +50,7 @@ describe("加载(Pi harness loadSkills)", () => {
it("正文提到的能力都在 frontmatter 的 capabilities 里,且都是已知能力", () => {
for (const s of skills) {
const mentioned = new Set(s.content.match(/\b[a-z]+\.[a-z_]+\b/g) ?? []);
const mentioned = new Set(s.content.match(/\b[a-z][a-z_]*\.[a-z_]+\b/g) ?? []);
for (const m of mentioned) {
if (!(CAPABILITY_IDS as readonly string[]).includes(m)) continue;
expect(s.capabilities, `${s.name} 正文用了 ${m}`).toContain(m);
@@ -99,6 +99,8 @@ const ROUTING: [string, string][] = [
["下载进度怎么样了", "download-diagnose"],
["帮我建个巡检,每天早上看看有没有失败的下载", "patrol-draft"],
["每隔 6 小时检查一下站点登录", "patrol-draft"],
["药屋少女第二季的集数对不上", "episode-mapping"],
["Lilith 的连续编号帮我对一下", "episode-mapping"],
];
describe("按意图选择", () => {
@@ -107,6 +109,15 @@ describe("按意图选择", () => {
expect(picked[0]?.name).toBe(name);
});
it("集数待确认事项的后台调查载入「集数映射」", () => {
const goal = "调查待处理事项 x(episode_mapping):集数待确认:药屋少女的呢喃 第 1 季 · Lilith-Raws。";
const picked = selectSkills(skills, goal, {
kind: "investigate",
capabilities: ["episode_mapping.inspect", "episode_mapping.simulate", "decision.list"],
});
expect(picked[0]?.name).toBe("episode-mapping");
});
it("闲聊不载入 Skill;最多载入 2 个", () => {
expect(selectSkills(skills, "你好", { kind: "chat" })).toEqual([]);
expect(selectSkills(skills, "巡检一下下载失败和订阅追更,再清理空间", { kind: "chat" }).length).toBe(2);