@zhushanwen/pi-permission 1.6.4 → 1.6.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-permission",
3
- "version": "1.6.4",
3
+ "version": "1.6.5",
4
4
  "description": "Pi permission extension — four permission modes (yolo/auto/approve/strict) with three-layer pipeline (AST + rules + AI classifier).",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -14,6 +14,9 @@
14
14
  * 注册,D3「旧名不保留别名」),不对其迁移后结论作断言;
15
15
  * 5b. 反向混合集(wildcard deny 在前、精确 allow 在后)——与分支5互为镜像,
16
16
  * 区分度弱,承重位置锚 = 精确条目仍在 wildcard 之前。
17
+ * 6. subagents → subagent_batch(2026-10-23 批量工具改名入表)——单目标展开:
18
+ * 展开正确 + 字段保留 + 幂等 + 保序位置锚 + 真实 matcher 结论等价四维度合并
19
+ * 锁定(单元素展开无次序承重,四维度一用例足够)。
17
20
  *
18
21
  * 匹配器行为注释:matchNonBashTool 的 tool 字段经 wildcardToRegExp 全锚定匹配
19
22
  * (精确名 = 精确匹配,'*' = 匹配全部);pattern 对 path 匹配(path 缺省时对
@@ -34,7 +37,7 @@ function userRule(overrides: Partial<Rule> & { id: string; tool: string }): Rule
34
37
  };
35
38
  }
36
39
 
37
- describe("旧工具名精确规则迁移(D3)— 表驱动六用例(五分支 + 分支5b 反向混合集)", () => {
40
+ describe("旧工具名精确规则迁移(D3)— 表驱动七用例(六分支 + 分支5b 反向混合集)", () => {
38
41
  it("分支1:精确旧名展开(subagent 三条 + workflow 两条,展开顺序承重,其余字段保留)", () => {
39
42
  const rules: Rule[] = [
40
43
  userRule({ id: "user-1", tool: "subagent", pattern: "*", action: "deny", description: "no subagent" }),
@@ -196,4 +199,36 @@ describe("旧工具名精确规则迁移(D3)— 表驱动六用例(五分
196
199
  expect(matchNonBashTool("bash", "/x", migrated).action).toBe(matchNonBashTool("bash", "/x", mixed).action);
197
200
  expect(matchNonBashTool("bash", "/x", mixed).action).toBe("deny");
198
201
  });
202
+
203
+ it("分支6:subagents → subagent_batch(2026-10-23 批量工具改名入表)— 展开 + 幂等 + 保序 + matcher 结论等价", () => {
204
+ // 展开正确 + 字段保留:单目标展开(一条旧名规则 → 一条 subagent_batch)
205
+ const rules: Rule[] = [
206
+ userRule({ id: "user-1", tool: "subagents", pattern: "*", action: "deny", description: "no batch" }),
207
+ userRule({ id: "user-2", tool: "*", pattern: "*", action: "allow" }),
208
+ ];
209
+ const migrated = migrateLegacyToolRules(rules);
210
+
211
+ // 保序位置锚:展开条目原位替换,wildcard 仍在后(单目标无展开次序承重)
212
+ expect(migrated).toHaveLength(2);
213
+ expect(migrated.map((r) => r.tool)).toEqual(["subagent_batch", "*"]);
214
+ // pattern/action/source/description 保留(只换 tool 名与 id)
215
+ expect(migrated[0]).toMatchObject({ pattern: "*", action: "deny", source: "user", description: "no batch" });
216
+ // 新 id(D3 契约②):不克隆原 id,形态 user-N
217
+ expect(migrated[0]?.id).not.toBe("user-1");
218
+ expect(migrated[0]?.id).toMatch(/^user-\d+$/);
219
+ // 纯函数:原数组不改写
220
+ expect(rules).toHaveLength(2);
221
+ expect(rules[0]?.tool).toBe("subagents");
222
+
223
+ // 幂等:新名不在旧名集合,再迁移深度相等
224
+ expect(migrateLegacyToolRules(migrated)).toEqual(migrated);
225
+
226
+ // 真实 matcher 结论等价:场景同分支5(尾部 wildcard allow 覆盖精确 deny)——
227
+ // 若实装错误地把展开条目追加到数组尾部,subagent_batch deny 落到 wildcard
228
+ // allow 之后 → 结论翻成 deny ≠ 迁移前 allow,本断言即红灯。
229
+ const beforeSubagents = matchNonBashTool("subagents", "/x", rules).action;
230
+ const afterBatch = matchNonBashTool("subagent_batch", "/x", migrated).action;
231
+ expect(beforeSubagents).toBe("allow");
232
+ expect(afterBatch).toBe(beforeSubagents);
233
+ });
199
234
  });
@@ -5,6 +5,7 @@
5
5
  * subagent_start/subagent_manage/subagent_message 与 workflow_run/workflow_control,
6
6
  * 旧名不保留别名(探针 PB-fork)。用户权限规则按工具名匹配(`Rule.tool`)并持久化
7
7
  * 落盘(config.ts saveConfig),旧名精确规则随改名失配——deny 静默退化成 ask/AI 分类。
8
+ * 批量工具 `subagents` → `subagent_batch` 改名(2026-10-23)同机制入表。
8
9
  *
9
10
  * 迁移语义(D3 两条契约):
10
11
  * ① 展开落位与保序——精确匹配旧名的用户规则**原地替换**为其新名展开(展开顺序
@@ -38,6 +39,7 @@ import { makeNextIdCounter } from "../rule-templates.js";
38
39
  const LEGACY_TOOL_NAME_EXPANSION: Readonly<Record<string, readonly string[]>> = {
39
40
  subagent: ["subagent_start", "subagent_manage", "subagent_message"],
40
41
  workflow: ["workflow_run", "workflow_control"],
42
+ subagents: ["subagent_batch"],
41
43
  };
42
44
 
43
45
  /**