@gotgenes/pi-permission-system 5.18.1 → 5.18.3

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.
@@ -1,4 +1,3 @@
1
- import assert from "node:assert/strict";
2
1
  import {
3
2
  existsSync,
4
3
  mkdirSync,
@@ -9,7 +8,7 @@ import {
9
8
  } from "node:fs";
10
9
  import { homedir, tmpdir } from "node:os";
11
10
  import { dirname, join, resolve } from "node:path";
12
- import { test } from "vitest";
11
+ import { expect, test } from "vitest";
13
12
 
14
13
  import {
15
14
  createActiveToolsCacheKey,
@@ -230,7 +229,7 @@ async function runToolCall(
230
229
  options: ExtensionHarnessOptions = {},
231
230
  ): Promise<Record<string, unknown>> {
232
231
  const handler = harness.handlers.tool_call;
233
- assert.equal(typeof handler, "function");
232
+ expect(handler).toBeTypeOf("function");
234
233
 
235
234
  const result = await withIsolatedSubagentEnv(async () =>
236
235
  Promise.resolve(
@@ -241,66 +240,58 @@ async function runToolCall(
241
240
  }
242
241
 
243
242
  test("Yolo mode only auto-approves ask-state permissions", () => {
244
- assert.equal(
243
+ expect(
245
244
  shouldAutoApprovePermissionState("ask", DEFAULT_EXTENSION_CONFIG),
246
- false,
247
- );
248
- assert.equal(
245
+ ).toBe(false);
246
+ expect(
249
247
  shouldAutoApprovePermissionState("ask", {
250
248
  ...DEFAULT_EXTENSION_CONFIG,
251
249
  yoloMode: true,
252
250
  }),
253
- true,
254
- );
255
- assert.equal(
251
+ ).toBe(true);
252
+ expect(
256
253
  shouldAutoApprovePermissionState("deny", {
257
254
  ...DEFAULT_EXTENSION_CONFIG,
258
255
  yoloMode: true,
259
256
  }),
260
- false,
261
- );
262
- assert.equal(
257
+ ).toBe(false);
258
+ expect(
263
259
  shouldAutoApprovePermissionState("allow", {
264
260
  ...DEFAULT_EXTENSION_CONFIG,
265
261
  yoloMode: true,
266
262
  }),
267
- false,
268
- );
263
+ ).toBe(false);
269
264
  });
270
265
 
271
266
  test("Yolo mode resolves ask permissions without UI or delegation forwarding", () => {
272
- assert.equal(
267
+ expect(
273
268
  canResolveAskPermissionRequest({
274
269
  config: DEFAULT_EXTENSION_CONFIG,
275
270
  hasUI: false,
276
271
  isSubagent: false,
277
272
  }),
278
- false,
279
- );
280
- assert.equal(
273
+ ).toBe(false);
274
+ expect(
281
275
  canResolveAskPermissionRequest({
282
276
  config: { ...DEFAULT_EXTENSION_CONFIG, yoloMode: true },
283
277
  hasUI: false,
284
278
  isSubagent: false,
285
279
  }),
286
- true,
287
- );
288
- assert.equal(
280
+ ).toBe(true);
281
+ expect(
289
282
  canResolveAskPermissionRequest({
290
283
  config: DEFAULT_EXTENSION_CONFIG,
291
284
  hasUI: false,
292
285
  isSubagent: true,
293
286
  }),
294
- true,
295
- );
287
+ ).toBe(true);
296
288
  });
297
289
 
298
290
  test("Permission-system status is only exposed when yolo mode is enabled", () => {
299
- assert.equal(getPermissionSystemStatus(DEFAULT_EXTENSION_CONFIG), undefined);
300
- assert.equal(
291
+ expect(getPermissionSystemStatus(DEFAULT_EXTENSION_CONFIG)).toBe(undefined);
292
+ expect(
301
293
  getPermissionSystemStatus({ ...DEFAULT_EXTENSION_CONFIG, yoloMode: true }),
302
- "yolo",
303
- );
294
+ ).toBe("yolo");
304
295
  });
305
296
 
306
297
  test("System prompt sanitizer removes the Available tools section and surrounding boilerplate", () => {
@@ -318,11 +309,11 @@ test("System prompt sanitizer removes the Available tools section and surroundin
318
309
 
319
310
  const result = sanitizeAvailableToolsSection(prompt, ["read", "mcp"]);
320
311
 
321
- assert.equal(result.removed, true);
322
- assert.equal(result.prompt.includes("Available tools:"), false);
323
- assert.equal(result.prompt.includes("In addition to the tools above"), false);
324
- assert.match(result.prompt, /Guidelines:/);
325
- assert.match(result.prompt, /Use mcp for MCP discovery first/i);
312
+ expect(result.removed).toBe(true);
313
+ expect(result.prompt).not.toContain("Available tools:");
314
+ expect(result.prompt).not.toContain("In addition to the tools above");
315
+ expect(result.prompt).toMatch(/Guidelines:/);
316
+ expect(result.prompt).toMatch(/Use mcp for MCP discovery first/i);
326
317
  });
327
318
 
328
319
  test("System prompt sanitizer removes denied tool guidelines while keeping global guidance", () => {
@@ -337,13 +328,12 @@ test("System prompt sanitizer removes denied tool guidelines while keeping globa
337
328
 
338
329
  const result = sanitizeAvailableToolsSection(prompt, ["bash", "grep", "mcp"]);
339
330
 
340
- assert.equal(result.removed, true);
341
- assert.equal(result.prompt.includes("Use task when work SHOULD"), false);
342
- assert.match(result.prompt, /Use mcp for MCP discovery first/i);
343
- assert.match(result.prompt, /Prefer grep\/find\/ls tools over bash/i);
344
- assert.match(result.prompt, /Be concise in your responses/);
345
- assert.match(
346
- result.prompt,
331
+ expect(result.removed).toBe(true);
332
+ expect(result.prompt).not.toContain("Use task when work SHOULD");
333
+ expect(result.prompt).toMatch(/Use mcp for MCP discovery first/i);
334
+ expect(result.prompt).toMatch(/Prefer grep\/find\/ls tools over bash/i);
335
+ expect(result.prompt).toMatch(/Be concise in your responses/);
336
+ expect(result.prompt).toMatch(
347
337
  /Show file paths clearly when working with files/,
348
338
  );
349
339
  });
@@ -358,16 +348,14 @@ test("System prompt sanitizer removes inactive built-in write guidance", () => {
358
348
 
359
349
  const result = sanitizeAvailableToolsSection(prompt, ["read"]);
360
350
 
361
- assert.equal(result.removed, true);
362
- assert.equal(
363
- result.prompt.includes("Use write only for new files or complete rewrites"),
364
- false,
351
+ expect(result.removed).toBe(true);
352
+ expect(result.prompt).not.toContain(
353
+ "Use write only for new files or complete rewrites",
365
354
  );
366
- assert.equal(
367
- result.prompt.includes("do NOT use cat or bash to display what you did"),
368
- false,
355
+ expect(result.prompt).not.toContain(
356
+ "do NOT use cat or bash to display what you did",
369
357
  );
370
- assert.match(result.prompt, /Be concise in your responses/);
358
+ expect(result.prompt).toMatch(/Be concise in your responses/);
371
359
  });
372
360
 
373
361
  test("Before-agent-start cache dedupes unchanged active-tool exposure and prompt state", () => {
@@ -381,14 +369,12 @@ test("Before-agent-start cache dedupes unchanged active-tool exposure and prompt
381
369
  allowedToolNames: allowedTools,
382
370
  });
383
371
 
384
- assert.equal(shouldApplyCachedAgentStartState(null, activeToolsKey), true);
385
- assert.equal(
386
- shouldApplyCachedAgentStartState(activeToolsKey, activeToolsKey),
372
+ expect(shouldApplyCachedAgentStartState(null, activeToolsKey)).toBe(true);
373
+ expect(shouldApplyCachedAgentStartState(activeToolsKey, activeToolsKey)).toBe(
387
374
  false,
388
375
  );
389
- assert.equal(shouldApplyCachedAgentStartState(null, promptStateKey), true);
390
- assert.equal(
391
- shouldApplyCachedAgentStartState(promptStateKey, promptStateKey),
376
+ expect(shouldApplyCachedAgentStartState(null, promptStateKey)).toBe(true);
377
+ expect(shouldApplyCachedAgentStartState(promptStateKey, promptStateKey)).toBe(
392
378
  false,
393
379
  );
394
380
  });
@@ -408,11 +394,10 @@ test("Before-agent-start prompt cache invalidates on permission changes while ru
408
394
  allowedToolNames: ["read"],
409
395
  });
410
396
 
411
- assert.equal(
412
- shouldApplyCachedAgentStartState(baselineKey, baselineKey),
397
+ expect(shouldApplyCachedAgentStartState(baselineKey, baselineKey)).toBe(
413
398
  false,
414
399
  );
415
- assert.equal(manager.checkPermission("write", {}, undefined).state, "deny");
400
+ expect(manager.checkPermission("write", {}, undefined).state).toBe("deny");
416
401
 
417
402
  const updatedConfig = `${JSON.stringify(
418
403
  { permission: { "*": "allow", write: "allow" } },
@@ -435,7 +420,7 @@ test("Before-agent-start prompt cache invalidates on permission changes while ru
435
420
  updatedStamp = manager.getPolicyCacheStamp();
436
421
  }
437
422
 
438
- assert.notEqual(updatedStamp, baselineStamp);
423
+ expect(updatedStamp).not.toBe(baselineStamp);
439
424
 
440
425
  const invalidatedKey = createBeforeAgentStartPromptStateKey({
441
426
  agentName: null,
@@ -445,14 +430,10 @@ test("Before-agent-start prompt cache invalidates on permission changes while ru
445
430
  allowedToolNames: ["read", "write"],
446
431
  });
447
432
 
448
- assert.equal(
449
- shouldApplyCachedAgentStartState(baselineKey, invalidatedKey),
433
+ expect(shouldApplyCachedAgentStartState(baselineKey, invalidatedKey)).toBe(
450
434
  true,
451
435
  );
452
- assert.equal(
453
- manager.checkPermission("write", {}, undefined).state,
454
- "allow",
455
- );
436
+ expect(manager.checkPermission("write", {}, undefined).state).toBe("allow");
456
437
  } finally {
457
438
  cleanup();
458
439
  }
@@ -482,16 +463,16 @@ test("Permission-system logger respects debug toggle and keeps review log enable
482
463
  toolName: "write",
483
464
  });
484
465
 
485
- assert.equal(initialDebugWarning, undefined);
486
- assert.equal(reviewWarning, undefined);
487
- assert.equal(existsSync(debugLogPath), false);
488
- assert.equal(existsSync(reviewLogPath), true);
466
+ expect(initialDebugWarning).toBe(undefined);
467
+ expect(reviewWarning).toBe(undefined);
468
+ expect(existsSync(debugLogPath)).toBe(false);
469
+ expect(existsSync(reviewLogPath)).toBe(true);
489
470
 
490
471
  config.debugLog = true;
491
472
  const enabledDebugWarning = logger.debug("debug.enabled", { sample: true });
492
- assert.equal(enabledDebugWarning, undefined);
493
- assert.equal(existsSync(debugLogPath), true);
494
- assert.match(readFileSync(debugLogPath, "utf8"), /debug\.enabled/);
473
+ expect(enabledDebugWarning).toBe(undefined);
474
+ expect(existsSync(debugLogPath)).toBe(true);
475
+ expect(readFileSync(debugLogPath, "utf8")).toMatch(/debug\.enabled/);
495
476
  } finally {
496
477
  rmSync(baseDir, { recursive: true, force: true });
497
478
  }
@@ -504,12 +485,12 @@ test("PermissionManager canonical built-in permission checking", () => {
504
485
 
505
486
  try {
506
487
  const readResult = manager.checkPermission("read", {});
507
- assert.equal(readResult.state, "allow");
508
- assert.equal(readResult.source, "tool");
488
+ expect(readResult.state).toBe("allow");
489
+ expect(readResult.source).toBe("tool");
509
490
 
510
491
  const writeResult = manager.checkPermission("write", {});
511
- assert.equal(writeResult.state, "deny");
512
- assert.equal(writeResult.source, "tool");
492
+ expect(writeResult.state).toBe("deny");
493
+ expect(writeResult.source).toBe("tool");
513
494
  } finally {
514
495
  cleanup();
515
496
  }
@@ -530,7 +511,7 @@ test("multiline bash command resolves to allow via universal fallback", () => {
530
511
  const command =
531
512
  "node -e \"\nimport('x').then(() => {\n console.log('done');\n});\n\"";
532
513
  const result = manager.checkPermission("bash", { command });
533
- assert.equal(result.state, "allow");
514
+ expect(result.state).toBe("allow");
534
515
  } finally {
535
516
  cleanup();
536
517
  }
@@ -548,14 +529,14 @@ test("Bash specific deny patterns override catch-all within the same config", ()
548
529
 
549
530
  try {
550
531
  const denied = manager.checkPermission("bash", { command: "rm -rf build" });
551
- assert.equal(denied.state, "deny");
552
- assert.equal(denied.source, "bash");
553
- assert.equal(denied.matchedPattern, "rm -rf *");
532
+ expect(denied.state).toBe("deny");
533
+ expect(denied.source).toBe("bash");
534
+ expect(denied.matchedPattern).toBe("rm -rf *");
554
535
 
555
536
  const allowed = manager.checkPermission("bash", { command: "echo hello" });
556
- assert.equal(allowed.state, "allow");
557
- assert.equal(allowed.source, "bash");
558
- assert.equal(allowed.matchedPattern, "*");
537
+ expect(allowed.state).toBe("allow");
538
+ expect(allowed.source).toBe("bash");
539
+ expect(allowed.matchedPattern).toBe("*");
559
540
  } finally {
560
541
  cleanup();
561
542
  }
@@ -573,22 +554,22 @@ test("MCP wildcard matching uses the registered mcp tool", () => {
573
554
  const queryDocs = manager.checkPermission("mcp", {
574
555
  tool: "research:query-docs",
575
556
  });
576
- assert.equal(queryDocs.state, "allow");
577
- assert.equal(queryDocs.source, "mcp");
578
- assert.equal(queryDocs.matchedPattern, "research_query-*");
579
- assert.equal(queryDocs.target, "research_query-docs");
557
+ expect(queryDocs.state).toBe("allow");
558
+ expect(queryDocs.source).toBe("mcp");
559
+ expect(queryDocs.matchedPattern).toBe("research_query-*");
560
+ expect(queryDocs.target).toBe("research_query-docs");
580
561
 
581
562
  const resolve2 = manager.checkPermission("mcp", {
582
563
  tool: "research:resolve-context",
583
564
  });
584
- assert.equal(resolve2.state, "ask");
585
- assert.equal(resolve2.matchedPattern, "research_*");
586
- assert.equal(resolve2.target, "research_resolve-context");
565
+ expect(resolve2.state).toBe("ask");
566
+ expect(resolve2.matchedPattern).toBe("research_*");
567
+ expect(resolve2.target).toBe("research_resolve-context");
587
568
 
588
569
  const unknown = manager.checkPermission("mcp", { tool: "search:provider" });
589
- assert.equal(unknown.state, "deny");
590
- assert.equal(unknown.matchedPattern, "*");
591
- assert.equal(unknown.target, "search_provider");
570
+ expect(unknown.state).toBe("deny");
571
+ expect(unknown.matchedPattern).toBe("*");
572
+ expect(unknown.target).toBe("search_provider");
592
573
  } finally {
593
574
  cleanup();
594
575
  }
@@ -605,14 +586,14 @@ test("Arbitrary extension tools use exact-name tool permissions instead of MCP f
605
586
 
606
587
  try {
607
588
  const allowed = manager.checkPermission("third_party_tool", {});
608
- assert.equal(allowed.state, "allow");
609
- assert.equal(allowed.source, "tool");
589
+ expect(allowed.state).toBe("allow");
590
+ expect(allowed.source).toBe("tool");
610
591
 
611
592
  // another_extension_tool has no explicit rule — falls through to the
612
593
  // universal default (permission["*"] = "deny") with source "default".
613
594
  const fallback = manager.checkPermission("another_extension_tool", {});
614
- assert.equal(fallback.state, "deny");
615
- assert.equal(fallback.source, "default");
595
+ expect(fallback.state).toBe("deny");
596
+ expect(fallback.source).toBe("default");
616
597
  } finally {
617
598
  cleanup();
618
599
  }
@@ -634,21 +615,21 @@ test("Skill permission matching", () => {
634
615
  const allowed = manager.checkPermission("skill", {
635
616
  name: "requesting-code-review",
636
617
  });
637
- assert.equal(allowed.state, "allow");
638
- assert.equal(allowed.matchedPattern, "requesting-code-review");
639
- assert.equal(allowed.source, "skill");
618
+ expect(allowed.state).toBe("allow");
619
+ expect(allowed.matchedPattern).toBe("requesting-code-review");
620
+ expect(allowed.source).toBe("skill");
640
621
 
641
622
  const denied = manager.checkPermission("skill", {
642
623
  name: "web-design-guidelines",
643
624
  });
644
- assert.equal(denied.state, "deny");
645
- assert.equal(denied.matchedPattern, "web-*");
625
+ expect(denied.state).toBe("deny");
626
+ expect(denied.matchedPattern).toBe("web-*");
646
627
 
647
628
  const fallback = manager.checkPermission("skill", {
648
629
  name: "unknown-skill",
649
630
  });
650
- assert.equal(fallback.state, "ask");
651
- assert.equal(fallback.matchedPattern, "*");
631
+ expect(fallback.state).toBe("ask");
632
+ expect(fallback.matchedPattern).toBe("*");
652
633
  } finally {
653
634
  cleanup();
654
635
  }
@@ -672,10 +653,10 @@ test("MCP proxy tool infers server-prefixed aliases from configured server names
672
653
  const result = manager.checkPermission("mcp", {
673
654
  tool: "get_code_context_exa",
674
655
  });
675
- assert.equal(result.state, "allow");
676
- assert.equal(result.source, "mcp");
677
- assert.equal(result.matchedPattern, "exa_get_code_context_exa");
678
- assert.equal(result.target, "exa_get_code_context_exa");
656
+ expect(result.state).toBe("allow");
657
+ expect(result.source).toBe("mcp");
658
+ expect(result.matchedPattern).toBe("exa_get_code_context_exa");
659
+ expect(result.target).toBe("exa_get_code_context_exa");
679
660
  } finally {
680
661
  cleanup();
681
662
  }
@@ -715,7 +696,7 @@ test("MCP server names in settings.json are not used — only mcp.json is consul
715
696
  const result = manager.checkPermission("mcp", {
716
697
  tool: "some_tool_legacy-server",
717
698
  });
718
- assert.equal(result.state, "ask");
699
+ expect(result.state).toBe("ask");
719
700
  } finally {
720
701
  rmSync(baseDir, { recursive: true, force: true });
721
702
  }
@@ -740,10 +721,10 @@ test("MCP describe mode normalizes qualified tool names without duplicating serv
740
721
  describe: "exa:web_search_exa",
741
722
  server: "exa",
742
723
  });
743
- assert.equal(result.state, "allow");
744
- assert.equal(result.source, "mcp");
745
- assert.equal(result.matchedPattern, "exa_web_search_exa");
746
- assert.equal(result.target, "exa_web_search_exa");
724
+ expect(result.state).toBe("allow");
725
+ expect(result.source).toBe("mcp");
726
+ expect(result.matchedPattern).toBe("exa_web_search_exa");
727
+ expect(result.target).toBe("exa_web_search_exa");
747
728
  } finally {
748
729
  cleanup();
749
730
  }
@@ -756,12 +737,12 @@ test("Canonical tools map directly without legacy aliases", () => {
756
737
 
757
738
  try {
758
739
  const findResult = manager.checkPermission("find", {});
759
- assert.equal(findResult.state, "allow");
760
- assert.equal(findResult.source, "tool");
740
+ expect(findResult.state).toBe("allow");
741
+ expect(findResult.source).toBe("tool");
761
742
 
762
743
  const lsResult = manager.checkPermission("ls", {});
763
- assert.equal(lsResult.state, "deny");
764
- assert.equal(lsResult.source, "tool");
744
+ expect(lsResult.state).toBe("deny");
745
+ expect(lsResult.source).toBe("tool");
765
746
  } finally {
766
747
  cleanup();
767
748
  }
@@ -788,9 +769,9 @@ permission:
788
769
  { tool: "exa:web_search_exa" },
789
770
  "reviewer",
790
771
  );
791
- assert.equal(result.state, "allow");
792
- assert.equal(result.source, "mcp");
793
- assert.equal(result.target, "exa_web_search_exa");
772
+ expect(result.state).toBe("allow");
773
+ expect(result.source).toBe("mcp");
774
+ expect(result.target).toBe("exa_web_search_exa");
794
775
  } finally {
795
776
  cleanup();
796
777
  }
@@ -822,10 +803,10 @@ permission:
822
803
  { tool: "web_search_exa" },
823
804
  "reviewer",
824
805
  );
825
- assert.equal(result.state, "deny");
826
- assert.equal(result.source, "mcp");
827
- assert.equal(result.matchedPattern, "exa_web_search_exa");
828
- assert.equal(result.target, "exa_web_search_exa");
806
+ expect(result.state).toBe("deny");
807
+ expect(result.source).toBe("mcp");
808
+ expect(result.matchedPattern).toBe("exa_web_search_exa");
809
+ expect(result.target).toBe("exa_web_search_exa");
829
810
  } finally {
830
811
  cleanup();
831
812
  }
@@ -857,19 +838,19 @@ permission:
857
838
  { tool: "web_search_exa" },
858
839
  "reviewer",
859
840
  );
860
- assert.equal(allowed.state, "allow");
861
- assert.equal(allowed.source, "mcp");
862
- assert.equal(allowed.matchedPattern, "exa_web_search_exa");
863
- assert.equal(allowed.target, "exa_web_search_exa");
841
+ expect(allowed.state).toBe("allow");
842
+ expect(allowed.source).toBe("mcp");
843
+ expect(allowed.matchedPattern).toBe("exa_web_search_exa");
844
+ expect(allowed.target).toBe("exa_web_search_exa");
864
845
 
865
846
  const fallback = manager.checkPermission(
866
847
  "mcp",
867
848
  { tool: "other_exa" },
868
849
  "reviewer",
869
850
  );
870
- assert.equal(fallback.state, "deny");
871
- assert.equal(fallback.source, "mcp");
872
- assert.equal(fallback.target, "exa_other_exa");
851
+ expect(fallback.state).toBe("deny");
852
+ expect(fallback.source).toBe("mcp");
853
+ expect(fallback.target).toBe("exa_other_exa");
873
854
  } finally {
874
855
  cleanup();
875
856
  }
@@ -892,16 +873,16 @@ permission:
892
873
 
893
874
  try {
894
875
  const readResult = manager.checkPermission("read", {}, "reviewer");
895
- assert.equal(readResult.state, "deny");
896
- assert.equal(readResult.source, "tool");
876
+ expect(readResult.state).toBe("deny");
877
+ expect(readResult.source).toBe("tool");
897
878
 
898
879
  const mcpResult = manager.checkPermission(
899
880
  "mcp",
900
881
  { tool: "exa:web_search_exa" },
901
882
  "reviewer",
902
883
  );
903
- assert.equal(mcpResult.state, "allow");
904
- assert.equal(mcpResult.source, "mcp");
884
+ expect(mcpResult.state).toBe("allow");
885
+ expect(mcpResult.source).toBe("mcp");
905
886
  } finally {
906
887
  cleanup();
907
888
  }
@@ -925,12 +906,12 @@ permission:
925
906
 
926
907
  try {
927
908
  const findResult = manager.checkPermission("find", {}, "reviewer");
928
- assert.equal(findResult.state, "allow");
929
- assert.equal(findResult.source, "tool");
909
+ expect(findResult.state).toBe("allow");
910
+ expect(findResult.source).toBe("tool");
930
911
 
931
912
  const lsResult = manager.checkPermission("ls", {}, "reviewer");
932
- assert.equal(lsResult.state, "deny");
933
- assert.equal(lsResult.source, "tool");
913
+ expect(lsResult.state).toBe("deny");
914
+ expect(lsResult.source).toBe("tool");
934
915
  } finally {
935
916
  cleanup();
936
917
  }
@@ -955,13 +936,13 @@ permission:
955
936
 
956
937
  try {
957
938
  const findResult = manager.checkPermission("find", {}, "reviewer");
958
- assert.equal(findResult.state, "allow");
959
- assert.equal(findResult.source, "tool");
939
+ expect(findResult.state).toBe("allow");
940
+ expect(findResult.source).toBe("tool");
960
941
 
961
942
  // In flat format any surface key works, including extension tools
962
943
  const taskResult = manager.checkPermission("task", {}, "reviewer");
963
- assert.equal(taskResult.state, "allow");
964
- assert.equal(taskResult.source, "tool");
944
+ expect(taskResult.state).toBe("allow");
945
+ expect(taskResult.source).toBe("tool");
965
946
 
966
947
  // mcp: allow catches all MCP targets
967
948
  const mcpResult = manager.checkPermission(
@@ -969,7 +950,7 @@ permission:
969
950
  { tool: "exa:web_search_exa" },
970
951
  "reviewer",
971
952
  );
972
- assert.equal(mcpResult.state, "allow");
953
+ expect(mcpResult.state).toBe("allow");
973
954
  } finally {
974
955
  cleanup();
975
956
  }
@@ -982,19 +963,19 @@ test("task uses exact-name tool permissions like any registered extension tool",
982
963
 
983
964
  try {
984
965
  const taskResult = manager.checkPermission("task", {});
985
- assert.equal(taskResult.state, "allow");
986
- assert.equal(taskResult.source, "tool");
966
+ expect(taskResult.state).toBe("allow");
967
+ expect(taskResult.source).toBe("tool");
987
968
  } finally {
988
969
  cleanup();
989
970
  }
990
971
  });
991
972
 
992
973
  test("Tool registry resolves event tool names from string and object payloads", () => {
993
- assert.equal(getToolNameFromValue(" read "), "read");
994
- assert.equal(getToolNameFromValue({ toolName: "write" }), "write");
995
- assert.equal(getToolNameFromValue({ name: "find" }), "find");
996
- assert.equal(getToolNameFromValue({ tool: "grep" }), "grep");
997
- assert.equal(getToolNameFromValue({}), null);
974
+ expect(getToolNameFromValue(" read ")).toBe("read");
975
+ expect(getToolNameFromValue({ toolName: "write" })).toBe("write");
976
+ expect(getToolNameFromValue({ name: "find" })).toBe("find");
977
+ expect(getToolNameFromValue({ tool: "grep" })).toBe("grep");
978
+ expect(getToolNameFromValue({})).toBe(null);
998
979
  });
999
980
 
1000
981
  test("Tool registry blocks unregistered tools and handles aliases", () => {
@@ -1008,9 +989,9 @@ test("Tool registry blocks unregistered tools and handles aliases", () => {
1008
989
  "third_party_tool",
1009
990
  registeredTools,
1010
991
  );
1011
- assert.equal(unknownCheck.status, "unregistered");
992
+ expect(unknownCheck.status).toBe("unregistered");
1012
993
  if (unknownCheck.status === "unregistered") {
1013
- assert.deepEqual(unknownCheck.availableToolNames, ["bash", "mcp", "read"]);
994
+ expect(unknownCheck.availableToolNames).toEqual(["bash", "mcp", "read"]);
1014
995
  }
1015
996
 
1016
997
  const aliasCheck = checkRequestedToolRegistration(
@@ -1018,13 +999,13 @@ test("Tool registry blocks unregistered tools and handles aliases", () => {
1018
999
  registeredTools,
1019
1000
  { legacy_read: "read" },
1020
1001
  );
1021
- assert.equal(aliasCheck.status, "registered");
1002
+ expect(aliasCheck.status).toBe("registered");
1022
1003
 
1023
1004
  const missingNameCheck = checkRequestedToolRegistration(
1024
1005
  " ",
1025
1006
  registeredTools,
1026
1007
  );
1027
- assert.equal(missingNameCheck.status, "missing-tool-name");
1008
+ expect(missingNameCheck.status).toBe("missing-tool-name");
1028
1009
  });
1029
1010
 
1030
1011
  test("getToolPermission returns tool-level policy for canonical and extension tools", () => {
@@ -1046,16 +1027,16 @@ permission:
1046
1027
 
1047
1028
  try {
1048
1029
  const bashPermission = manager.getToolPermission("bash", "reviewer");
1049
- assert.equal(bashPermission, "deny");
1030
+ expect(bashPermission).toBe("deny");
1050
1031
 
1051
1032
  const taskPermission = manager.getToolPermission("task", "reviewer");
1052
- assert.equal(taskPermission, "allow");
1033
+ expect(taskPermission).toBe("allow");
1053
1034
 
1054
1035
  const readPermission = manager.getToolPermission("read", "reviewer");
1055
- assert.equal(readPermission, "deny");
1036
+ expect(readPermission).toBe("deny");
1056
1037
 
1057
1038
  const defaultBashPermission = manager.getToolPermission("bash");
1058
- assert.equal(defaultBashPermission, "ask");
1039
+ expect(defaultBashPermission).toBe("ask");
1059
1040
 
1060
1041
  const { manager: manager2, cleanup: cleanup2 } = createManager({
1061
1042
  permission: { "*": "deny", bash: "allow" },
@@ -1063,7 +1044,7 @@ permission:
1063
1044
 
1064
1045
  try {
1065
1046
  const globalBashPermission = manager2.getToolPermission("bash");
1066
- assert.equal(globalBashPermission, "allow");
1047
+ expect(globalBashPermission).toBe("allow");
1067
1048
  } finally {
1068
1049
  cleanup2();
1069
1050
  }
@@ -1079,12 +1060,12 @@ test("getToolPermission supports arbitrary extension tool names", () => {
1079
1060
 
1080
1061
  try {
1081
1062
  const explicitPermission = manager.getToolPermission("third_party_tool");
1082
- assert.equal(explicitPermission, "allow");
1063
+ expect(explicitPermission).toBe("allow");
1083
1064
 
1084
1065
  const fallbackPermission = manager.getToolPermission(
1085
1066
  "missing_extension_tool",
1086
1067
  );
1087
- assert.equal(fallbackPermission, "deny");
1068
+ expect(fallbackPermission).toBe("deny");
1088
1069
  } finally {
1089
1070
  cleanup();
1090
1071
  }
@@ -1098,22 +1079,20 @@ test("Yolo mode bypasses delegated ask routing when no parent forwarding target
1098
1079
  env: {},
1099
1080
  });
1100
1081
 
1101
- assert.equal(targetSessionId, null);
1102
- assert.equal(
1082
+ expect(targetSessionId).toBe(null);
1083
+ expect(
1103
1084
  canResolveAskPermissionRequest({
1104
1085
  config: { ...DEFAULT_EXTENSION_CONFIG, yoloMode: true },
1105
1086
  hasUI: false,
1106
1087
  isSubagent: true,
1107
1088
  }),
1108
- true,
1109
- );
1110
- assert.equal(
1089
+ ).toBe(true);
1090
+ expect(
1111
1091
  shouldAutoApprovePermissionState("ask", {
1112
1092
  ...DEFAULT_EXTENSION_CONFIG,
1113
1093
  yoloMode: true,
1114
1094
  }),
1115
- true,
1116
- );
1095
+ ).toBe(true);
1117
1096
  });
1118
1097
 
1119
1098
  test("Permission forwarding resolves the parent interactive session from subagent runtime env", () => {
@@ -1126,7 +1105,7 @@ test("Permission forwarding resolves the parent interactive session from subagen
1126
1105
  },
1127
1106
  });
1128
1107
 
1129
- assert.equal(targetSessionId, "parent-session");
1108
+ expect(targetSessionId).toBe("parent-session");
1130
1109
  });
1131
1110
 
1132
1111
  test("Permission forwarding does not guess a target session when subagent runtime env is missing", () => {
@@ -1137,7 +1116,7 @@ test("Permission forwarding does not guess a target session when subagent runtim
1137
1116
  env: {},
1138
1117
  });
1139
1118
 
1140
- assert.equal(targetSessionId, null);
1119
+ expect(targetSessionId).toBe(null);
1141
1120
  });
1142
1121
 
1143
1122
  test("Permission forwarding uses session-scoped directories per interactive session", () => {
@@ -1151,26 +1130,24 @@ test("Permission forwarding uses session-scoped directories per interactive sess
1151
1130
  "session-b",
1152
1131
  );
1153
1132
 
1154
- assert.notEqual(sessionA.sessionRootDir, sessionB.sessionRootDir);
1155
- assert.notEqual(sessionA.requestsDir, sessionB.requestsDir);
1156
- assert.notEqual(sessionA.responsesDir, sessionB.responsesDir);
1133
+ expect(sessionA.sessionRootDir).not.toBe(sessionB.sessionRootDir);
1134
+ expect(sessionA.requestsDir).not.toBe(sessionB.requestsDir);
1135
+ expect(sessionA.responsesDir).not.toBe(sessionB.responsesDir);
1157
1136
  });
1158
1137
 
1159
1138
  test("Permission forwarding request routing only matches the intended UI session", () => {
1160
- assert.equal(
1139
+ expect(
1161
1140
  isForwardedPermissionRequestForSession(
1162
1141
  { targetSessionId: "session-a" },
1163
1142
  "session-a",
1164
1143
  ),
1165
- true,
1166
- );
1167
- assert.equal(
1144
+ ).toBe(true);
1145
+ expect(
1168
1146
  isForwardedPermissionRequestForSession(
1169
1147
  { targetSessionId: "session-a" },
1170
1148
  "session-b",
1171
1149
  ),
1172
- false,
1173
- );
1150
+ ).toBe(false);
1174
1151
  });
1175
1152
 
1176
1153
  test("Permission forwarding rejects unresolved sentinel session ids", () => {
@@ -1180,7 +1157,7 @@ test("Permission forwarding rejects unresolved sentinel session ids", () => {
1180
1157
  currentSessionId: "unknown",
1181
1158
  });
1182
1159
 
1183
- assert.equal(targetSessionId, null);
1160
+ expect(targetSessionId).toBe(null);
1184
1161
  });
1185
1162
 
1186
1163
  // ---------------------------------------------------------------------------
@@ -1268,14 +1245,14 @@ test("Project-level config overrides base bash patterns", () => {
1268
1245
  const allowed = manager.checkPermission("bash", {
1269
1246
  command: "rm -rf build",
1270
1247
  });
1271
- assert.equal(allowed.state, "allow");
1272
- assert.equal(allowed.matchedPattern, "rm -rf build");
1248
+ expect(allowed.state).toBe("allow");
1249
+ expect(allowed.matchedPattern).toBe("rm -rf build");
1273
1250
 
1274
1251
  const denied = manager.checkPermission("bash", {
1275
1252
  command: "rm -rf node_modules",
1276
1253
  });
1277
- assert.equal(denied.state, "deny");
1278
- assert.equal(denied.matchedPattern, "rm -rf *");
1254
+ expect(denied.state).toBe("deny");
1255
+ expect(denied.matchedPattern).toBe("rm -rf *");
1279
1256
  } finally {
1280
1257
  cleanup();
1281
1258
  }
@@ -1308,16 +1285,16 @@ permission:
1308
1285
  { command: "git log --oneline" },
1309
1286
  "reviewer",
1310
1287
  );
1311
- assert.equal(allowed.state, "allow");
1312
- assert.equal(allowed.matchedPattern, "git log *");
1288
+ expect(allowed.state).toBe("allow");
1289
+ expect(allowed.matchedPattern).toBe("git log *");
1313
1290
 
1314
1291
  const denied = manager.checkPermission(
1315
1292
  "bash",
1316
1293
  { command: "git status" },
1317
1294
  "reviewer",
1318
1295
  );
1319
- assert.equal(denied.state, "deny");
1320
- assert.equal(denied.matchedPattern, "git *");
1296
+ expect(denied.state).toBe("deny");
1297
+ expect(denied.matchedPattern).toBe("git *");
1321
1298
  } finally {
1322
1299
  cleanup();
1323
1300
  }
@@ -1350,8 +1327,8 @@ permission:
1350
1327
 
1351
1328
  try {
1352
1329
  const result = manager.checkPermission("read", {}, "reviewer");
1353
- assert.equal(result.state, "allow");
1354
- assert.equal(result.source, "tool");
1330
+ expect(result.state).toBe("allow");
1331
+ expect(result.source).toBe("tool");
1355
1332
  } finally {
1356
1333
  cleanup();
1357
1334
  }
@@ -1391,12 +1368,12 @@ permission:
1391
1368
  {},
1392
1369
  "reviewer",
1393
1370
  );
1394
- assert.equal(reviewerResult.state, "deny");
1395
- assert.equal(reviewerResult.source, "default");
1371
+ expect(reviewerResult.state).toBe("deny");
1372
+ expect(reviewerResult.source).toBe("default");
1396
1373
 
1397
1374
  const globalResult = manager.checkPermission("custom_extension_tool", {});
1398
- assert.equal(globalResult.state, "allow");
1399
- assert.equal(globalResult.source, "default");
1375
+ expect(globalResult.state).toBe("allow");
1376
+ expect(globalResult.source).toBe("default");
1400
1377
  } finally {
1401
1378
  cleanup();
1402
1379
  }
@@ -1422,12 +1399,12 @@ permission:
1422
1399
 
1423
1400
  try {
1424
1401
  const agentResult = manager.checkPermission("read", {}, "reviewer");
1425
- assert.equal(agentResult.state, "deny");
1426
- assert.equal(agentResult.source, "tool");
1402
+ expect(agentResult.state).toBe("deny");
1403
+ expect(agentResult.source).toBe("tool");
1427
1404
 
1428
1405
  const globalResult = manager.checkPermission("read", {});
1429
- assert.equal(globalResult.state, "allow");
1430
- assert.equal(globalResult.source, "tool");
1406
+ expect(globalResult.state).toBe("allow");
1407
+ expect(globalResult.source).toBe("tool");
1431
1408
  } finally {
1432
1409
  cleanup();
1433
1410
  }
@@ -1454,10 +1431,10 @@ test("PermissionManager reads config from PI_CODING_AGENT_DIR when set", () => {
1454
1431
  try {
1455
1432
  const manager = new PermissionManager();
1456
1433
  const result = manager.checkPermission("read", {});
1457
- assert.equal(result.state, "allow");
1434
+ expect(result.state).toBe("allow");
1458
1435
 
1459
1436
  const result2 = manager.checkPermission("write", {});
1460
- assert.equal(result2.state, "deny");
1437
+ expect(result2.state).toBe("deny");
1461
1438
  } finally {
1462
1439
  if (original !== undefined) {
1463
1440
  process.env.PI_CODING_AGENT_DIR = original;
@@ -1495,9 +1472,9 @@ test("parseAllSkillPromptSections finds every available_skills block", () => {
1495
1472
 
1496
1473
  const sections = parseAllSkillPromptSections(prompt);
1497
1474
 
1498
- assert.equal(sections.length, 2);
1499
- assert.equal(sections[0].entries[0]?.name, "skill-one");
1500
- assert.equal(sections[1].entries[0]?.name, "skill-two");
1475
+ expect(sections.length).toBe(2);
1476
+ expect(sections[0].entries[0]?.name).toBe("skill-one");
1477
+ expect(sections[1].entries[0]?.name).toBe("skill-two");
1501
1478
  });
1502
1479
 
1503
1480
  test("REGRESSION: resolveSkillPromptEntries sanitizes every available_skills block", () => {
@@ -1536,26 +1513,12 @@ test("REGRESSION: resolveSkillPromptEntries sanitizes every available_skills blo
1536
1513
 
1537
1514
  const result = resolveSkillPromptEntries(prompt, manager, null, "/cwd");
1538
1515
 
1539
- assert.equal(
1540
- result.prompt.includes("denied-skill"),
1541
- false,
1542
- "Denied skill should be removed from every block",
1543
- );
1544
- assert.equal(
1545
- result.prompt.includes("visible-skill"),
1546
- true,
1547
- "Visible skill should remain in the prompt",
1548
- );
1549
- assert.equal(
1550
- (result.prompt.match(/<available_skills>/g) || []).length,
1551
- 1,
1552
- "Fully denied blocks should be removed",
1553
- );
1554
- assert.deepEqual(
1555
- result.entries.map((entry) => entry.name),
1556
- ["visible-skill"],
1557
- "Tracked skill entries should exclude denied skills",
1558
- );
1516
+ expect(result.prompt).not.toContain("denied-skill");
1517
+ expect(result.prompt).toContain("visible-skill");
1518
+ expect((result.prompt.match(/<available_skills>/g) || []).length).toBe(1);
1519
+ expect(result.entries.map((entry) => entry.name)).toEqual([
1520
+ "visible-skill",
1521
+ ]);
1559
1522
  } finally {
1560
1523
  cleanup();
1561
1524
  }
@@ -1602,12 +1565,8 @@ test("REGRESSION: resolveSkillPromptEntries keeps only visible skills available
1602
1565
  result.entries,
1603
1566
  );
1604
1567
 
1605
- assert.equal(matchedVisibleSkill?.name, "visible-skill");
1606
- assert.equal(
1607
- matchedBlockedSkill,
1608
- null,
1609
- "Denied skills should not remain in tracked entries",
1610
- );
1568
+ expect(matchedVisibleSkill?.name).toBe("visible-skill");
1569
+ expect(matchedBlockedSkill).toBe(null);
1611
1570
  } finally {
1612
1571
  cleanup();
1613
1572
  }
@@ -1623,9 +1582,9 @@ test("external_directory permission falls back to universal default when not exp
1623
1582
 
1624
1583
  try {
1625
1584
  const result = manager.checkPermission("external_directory", {});
1626
- assert.equal(result.state, "ask");
1627
- assert.equal(result.source, "special");
1628
- assert.equal(result.matchedPattern, undefined);
1585
+ expect(result.state).toBe("ask");
1586
+ expect(result.source).toBe("special");
1587
+ expect(result.matchedPattern).toBe(undefined);
1629
1588
  } finally {
1630
1589
  cleanup();
1631
1590
  }
@@ -1638,9 +1597,9 @@ test("external_directory permission respects explicit deny", () => {
1638
1597
 
1639
1598
  try {
1640
1599
  const result = manager.checkPermission("external_directory", {});
1641
- assert.equal(result.state, "deny");
1642
- assert.equal(result.source, "special");
1643
- assert.equal(result.matchedPattern, "*");
1600
+ expect(result.state).toBe("deny");
1601
+ expect(result.source).toBe("special");
1602
+ expect(result.matchedPattern).toBe("*");
1644
1603
  } finally {
1645
1604
  cleanup();
1646
1605
  }
@@ -1653,9 +1612,9 @@ test("external_directory permission can be explicitly allowed", () => {
1653
1612
 
1654
1613
  try {
1655
1614
  const result = manager.checkPermission("external_directory", {});
1656
- assert.equal(result.state, "allow");
1657
- assert.equal(result.source, "special");
1658
- assert.equal(result.matchedPattern, "*");
1615
+ expect(result.state).toBe("allow");
1616
+ expect(result.source).toBe("special");
1617
+ expect(result.matchedPattern).toBe("*");
1659
1618
  } finally {
1660
1619
  cleanup();
1661
1620
  }
@@ -1679,7 +1638,7 @@ permission:
1679
1638
  try {
1680
1639
  // Global policy denies external_directory
1681
1640
  const globalResult = manager.checkPermission("external_directory", {});
1682
- assert.equal(globalResult.state, "deny");
1641
+ expect(globalResult.state).toBe("deny");
1683
1642
 
1684
1643
  // Trusted agent overrides to allow
1685
1644
  const agentResult = manager.checkPermission(
@@ -1687,8 +1646,8 @@ permission:
1687
1646
  {},
1688
1647
  "trusted",
1689
1648
  );
1690
- assert.equal(agentResult.state, "allow");
1691
- assert.equal(agentResult.source, "special");
1649
+ expect(agentResult.state).toBe("allow");
1650
+ expect(agentResult.source).toBe("special");
1692
1651
  } finally {
1693
1652
  cleanup();
1694
1653
  }
@@ -1704,8 +1663,8 @@ test("external_directory permission is not affected by unrelated surface keys",
1704
1663
  try {
1705
1664
  // external_directory still resolves from its own entry
1706
1665
  const extResult = manager.checkPermission("external_directory", {});
1707
- assert.equal(extResult.state, "allow");
1708
- assert.equal(extResult.matchedPattern, "*");
1666
+ expect(extResult.state).toBe("allow");
1667
+ expect(extResult.matchedPattern).toBe("*");
1709
1668
  } finally {
1710
1669
  cleanup();
1711
1670
  }
@@ -1735,9 +1694,9 @@ permission:
1735
1694
  { name: "pi-code-review" },
1736
1695
  "reviewer",
1737
1696
  );
1738
- assert.equal(allowed.state, "allow");
1739
- assert.equal(allowed.matchedPattern, "pi-*");
1740
- assert.equal(allowed.source, "skill");
1697
+ expect(allowed.state).toBe("allow");
1698
+ expect(allowed.matchedPattern).toBe("pi-*");
1699
+ expect(allowed.source).toBe("skill");
1741
1700
 
1742
1701
  // Falls through to agent frontmatter catch-all
1743
1702
  const asked = manager.checkPermission(
@@ -1745,13 +1704,13 @@ permission:
1745
1704
  { name: "other-skill" },
1746
1705
  "reviewer",
1747
1706
  );
1748
- assert.equal(asked.state, "ask");
1749
- assert.equal(asked.matchedPattern, "*");
1707
+ expect(asked.state).toBe("ask");
1708
+ expect(asked.matchedPattern).toBe("*");
1750
1709
 
1751
1710
  // No agent override — global deny applies
1752
1711
  const denied = manager.checkPermission("skill", { name: "pi-code-review" });
1753
- assert.equal(denied.state, "deny");
1754
- assert.equal(denied.source, "skill");
1712
+ expect(denied.state).toBe("deny");
1713
+ expect(denied.source).toBe("skill");
1755
1714
  } finally {
1756
1715
  cleanup();
1757
1716
  }
@@ -1781,9 +1740,9 @@ permission:
1781
1740
  { path: `${homedir()}/Downloads/file.txt` },
1782
1741
  "trusted",
1783
1742
  );
1784
- assert.equal(allowed.state, "allow");
1785
- assert.equal(allowed.matchedPattern, "~/Downloads/*");
1786
- assert.equal(allowed.source, "special");
1743
+ expect(allowed.state).toBe("allow");
1744
+ expect(allowed.matchedPattern).toBe("~/Downloads/*");
1745
+ expect(allowed.source).toBe("special");
1787
1746
 
1788
1747
  // Falls through to agent frontmatter catch-all deny
1789
1748
  const denied = manager.checkPermission(
@@ -1791,13 +1750,13 @@ permission:
1791
1750
  { path: `${homedir()}/Documents/secret.txt` },
1792
1751
  "trusted",
1793
1752
  );
1794
- assert.equal(denied.state, "deny");
1795
- assert.equal(denied.matchedPattern, "*");
1753
+ expect(denied.state).toBe("deny");
1754
+ expect(denied.matchedPattern).toBe("*");
1796
1755
 
1797
1756
  // No agent override — global deny applies
1798
1757
  const globalDenied = manager.checkPermission("external_directory", {});
1799
- assert.equal(globalDenied.state, "deny");
1800
- assert.equal(globalDenied.source, "special");
1758
+ expect(globalDenied.state).toBe("deny");
1759
+ expect(globalDenied.source).toBe("special");
1801
1760
  } finally {
1802
1761
  cleanup();
1803
1762
  }
@@ -1838,8 +1797,8 @@ permission:
1838
1797
  { name: "pi-code-review" },
1839
1798
  "analyst",
1840
1799
  );
1841
- assert.equal(allowed.state, "allow");
1842
- assert.equal(allowed.matchedPattern, "pi-*");
1800
+ expect(allowed.state).toBe("allow");
1801
+ expect(allowed.matchedPattern).toBe("pi-*");
1843
1802
 
1844
1803
  // Project-agent *: deny wins over global-agent *: ask
1845
1804
  const denied = manager.checkPermission(
@@ -1847,8 +1806,8 @@ permission:
1847
1806
  { name: "other-skill" },
1848
1807
  "analyst",
1849
1808
  );
1850
- assert.equal(denied.state, "deny");
1851
- assert.equal(denied.matchedPattern, "*");
1809
+ expect(denied.state).toBe("deny");
1810
+ expect(denied.matchedPattern).toBe("*");
1852
1811
  } finally {
1853
1812
  cleanup();
1854
1813
  }
@@ -1882,12 +1841,12 @@ permission:
1882
1841
  try {
1883
1842
  // Project-agent allow wins over global-agent ask
1884
1843
  const result = manager.checkPermission("external_directory", {}, "analyst");
1885
- assert.equal(result.state, "allow");
1886
- assert.equal(result.source, "special");
1844
+ expect(result.state).toBe("allow");
1845
+ expect(result.source).toBe("special");
1887
1846
 
1888
1847
  // Without agent context, global config deny applies
1889
1848
  const globalResult = manager.checkPermission("external_directory", {});
1890
- assert.equal(globalResult.state, "deny");
1849
+ expect(globalResult.state).toBe("deny");
1891
1850
  } finally {
1892
1851
  cleanup();
1893
1852
  }
@@ -1914,12 +1873,11 @@ test("tool_call blocks path-bearing tools outside cwd when external_directory is
1914
1873
  input: { path: siblingPath },
1915
1874
  });
1916
1875
 
1917
- assert.equal(result.block, true);
1918
- assert.match(
1919
- String(result.reason),
1876
+ expect(result.block).toBe(true);
1877
+ expect(String(result.reason)).toMatch(
1920
1878
  /external directory permission denial/i,
1921
1879
  );
1922
- assert.match(String(result.reason), /repo-sibling/);
1880
+ expect(String(result.reason)).toMatch(/repo-sibling/);
1923
1881
  } finally {
1924
1882
  await harness.cleanup();
1925
1883
  rmSync(rootDir, { recursive: true, force: true });
@@ -1941,8 +1899,8 @@ test("tool_call allows path-bearing tools inside cwd without external_directory
1941
1899
  input: { path: join(harness.cwd, "src", "index.ts") },
1942
1900
  });
1943
1901
 
1944
- assert.deepEqual(result, {});
1945
- assert.deepEqual(harness.prompts, []);
1902
+ expect(result).toEqual({});
1903
+ expect(harness.prompts).toEqual([]);
1946
1904
  } finally {
1947
1905
  await harness.cleanup();
1948
1906
  }
@@ -1966,9 +1924,8 @@ test("tool_call blocks external_directory ask when no confirmation channel is av
1966
1924
  },
1967
1925
  });
1968
1926
 
1969
- assert.equal(result.block, true);
1970
- assert.match(
1971
- String(result.reason),
1927
+ expect(result.block).toBe(true);
1928
+ expect(String(result.reason)).toMatch(
1972
1929
  /requires approval, but no interactive UI is available/i,
1973
1930
  );
1974
1931
  } finally {
@@ -1996,11 +1953,11 @@ test("tool_call prompts for external_directory and then falls through to normal
1996
1953
  { hasUI: true, selectResponse: "Yes" },
1997
1954
  );
1998
1955
 
1999
- assert.deepEqual(result, {});
2000
- assert.equal(harness.prompts.length, 1);
2001
- assert.match(harness.prompts[0], /external directory access/i);
2002
- assert.match(harness.prompts[0], /grep/);
2003
- assert.match(harness.prompts[0], /external-search-root/);
1956
+ expect(result).toEqual({});
1957
+ expect(harness.prompts.length).toBe(1);
1958
+ expect(harness.prompts[0]).toMatch(/external directory access/i);
1959
+ expect(harness.prompts[0]).toMatch(/grep/);
1960
+ expect(harness.prompts[0]).toMatch(/external-search-root/);
2004
1961
  } finally {
2005
1962
  await harness.cleanup();
2006
1963
  }
@@ -2021,8 +1978,8 @@ test("tool_call skips external_directory checks for optional path tools without
2021
1978
  input: { pattern: "*.ts" },
2022
1979
  });
2023
1980
 
2024
- assert.deepEqual(result, {});
2025
- assert.deepEqual(harness.prompts, []);
1981
+ expect(result).toEqual({});
1982
+ expect(harness.prompts).toEqual([]);
2026
1983
  } finally {
2027
1984
  await harness.cleanup();
2028
1985
  }
@@ -2045,12 +2002,11 @@ test("tool_call blocks bash command with external path when external_directory i
2045
2002
  input: { command: "cat /etc/hosts" },
2046
2003
  });
2047
2004
 
2048
- assert.equal(result.block, true);
2049
- assert.match(
2050
- String(result.reason),
2005
+ expect(result.block).toBe(true);
2006
+ expect(String(result.reason)).toMatch(
2051
2007
  /external directory permission denial/i,
2052
2008
  );
2053
- assert.match(String(result.reason), /\/etc\/hosts/);
2009
+ expect(String(result.reason)).toMatch(/\/etc\/hosts/);
2054
2010
  } finally {
2055
2011
  await harness.cleanup();
2056
2012
  }
@@ -2071,7 +2027,7 @@ test("tool_call allows bash command with only internal paths when external_direc
2071
2027
  input: { command: "cat src/index.ts" },
2072
2028
  });
2073
2029
 
2074
- assert.deepEqual(result, {});
2030
+ expect(result).toEqual({});
2075
2031
  } finally {
2076
2032
  await harness.cleanup();
2077
2033
  }
@@ -2093,9 +2049,8 @@ test("tool_call prompts for bash command with external path when external_direct
2093
2049
  });
2094
2050
 
2095
2051
  // No UI available in default harness, so it should block
2096
- assert.equal(result.block, true);
2097
- assert.match(
2098
- String(result.reason),
2052
+ expect(result.block).toBe(true);
2053
+ expect(String(result.reason)).toMatch(
2099
2054
  /requires approval.*no interactive UI/i,
2100
2055
  );
2101
2056
  } finally {
@@ -2119,7 +2074,7 @@ test("tool_call allows bash command with external path when external_directory i
2119
2074
  });
2120
2075
 
2121
2076
  // Should pass through to normal bash permission (which is also allow)
2122
- assert.deepEqual(result, {});
2077
+ expect(result).toEqual({});
2123
2078
  } finally {
2124
2079
  await harness.cleanup();
2125
2080
  }
@@ -2145,8 +2100,8 @@ test("tool_call applies bash pattern permissions after external_directory allow"
2145
2100
  });
2146
2101
 
2147
2102
  // external_directory allows, but bash pattern denies
2148
- assert.equal(result.block, true);
2149
- assert.match(String(result.reason), /not permitted/i);
2103
+ expect(result.block).toBe(true);
2104
+ expect(String(result.reason)).toMatch(/not permitted/i);
2150
2105
  } finally {
2151
2106
  await harness.cleanup();
2152
2107
  }
@@ -2171,10 +2126,10 @@ test("generic ask prompts include serialized tool input for informed approval",
2171
2126
  { hasUI: true, selectResponse: "No" },
2172
2127
  );
2173
2128
 
2174
- assert.equal(result.block, true);
2175
- assert.equal(harness.prompts.length, 1);
2176
- assert.match(harness.prompts[0], /weather_lookup/);
2177
- assert.match(harness.prompts[0], /\{"city":"Chicago","units":"metric"\}/);
2129
+ expect(result.block).toBe(true);
2130
+ expect(harness.prompts.length).toBe(1);
2131
+ expect(harness.prompts[0]).toMatch(/weather_lookup/);
2132
+ expect(harness.prompts[0]).toMatch(/\{"city":"Chicago","units":"metric"\}/);
2178
2133
  } finally {
2179
2134
  await harness.cleanup();
2180
2135
  }
@@ -2203,14 +2158,14 @@ test("getResolvedPolicyPaths returns correct paths and existence when files exis
2203
2158
 
2204
2159
  const result = pm.getResolvedPolicyPaths();
2205
2160
 
2206
- assert.equal(result.globalConfigPath, globalConfigPath);
2207
- assert.equal(result.globalConfigExists, true);
2208
- assert.equal(result.projectConfigPath, projectConfigPath);
2209
- assert.equal(result.projectConfigExists, true);
2210
- assert.equal(result.agentsDir, agentsDir);
2211
- assert.equal(result.agentsDirExists, true);
2212
- assert.equal(result.projectAgentsDir, projectAgentsDir);
2213
- assert.equal(result.projectAgentsDirExists, true);
2161
+ expect(result.globalConfigPath).toBe(globalConfigPath);
2162
+ expect(result.globalConfigExists).toBe(true);
2163
+ expect(result.projectConfigPath).toBe(projectConfigPath);
2164
+ expect(result.projectConfigExists).toBe(true);
2165
+ expect(result.agentsDir).toBe(agentsDir);
2166
+ expect(result.agentsDirExists).toBe(true);
2167
+ expect(result.projectAgentsDir).toBe(projectAgentsDir);
2168
+ expect(result.projectAgentsDirExists).toBe(true);
2214
2169
  } finally {
2215
2170
  rmSync(tempDir, { recursive: true, force: true });
2216
2171
  }
@@ -2229,14 +2184,14 @@ test("getResolvedPolicyPaths returns false for missing files and null for absent
2229
2184
 
2230
2185
  const result = pm.getResolvedPolicyPaths();
2231
2186
 
2232
- assert.equal(result.globalConfigPath, globalConfigPath);
2233
- assert.equal(result.globalConfigExists, false);
2234
- assert.equal(result.projectConfigPath, null);
2235
- assert.equal(result.projectConfigExists, false);
2236
- assert.equal(result.agentsDir, agentsDir);
2237
- assert.equal(result.agentsDirExists, false);
2238
- assert.equal(result.projectAgentsDir, null);
2239
- assert.equal(result.projectAgentsDirExists, false);
2187
+ expect(result.globalConfigPath).toBe(globalConfigPath);
2188
+ expect(result.globalConfigExists).toBe(false);
2189
+ expect(result.projectConfigPath).toBe(null);
2190
+ expect(result.projectConfigExists).toBe(false);
2191
+ expect(result.agentsDir).toBe(agentsDir);
2192
+ expect(result.agentsDirExists).toBe(false);
2193
+ expect(result.projectAgentsDir).toBe(null);
2194
+ expect(result.projectAgentsDirExists).toBe(false);
2240
2195
  } finally {
2241
2196
  rmSync(tempDir, { recursive: true, force: true });
2242
2197
  }
@@ -2251,7 +2206,7 @@ test("PermissionManager.getConfigIssues returns empty array for clean config", (
2251
2206
  const { manager, cleanup } = createManager(config);
2252
2207
  try {
2253
2208
  const issues = manager.getConfigIssues();
2254
- assert.equal(issues.length, 0);
2209
+ expect(issues.length).toBe(0);
2255
2210
  } finally {
2256
2211
  cleanup();
2257
2212
  }
@@ -2261,7 +2216,7 @@ test("PermissionManager.getConfigIssues returns empty array for empty config", (
2261
2216
  const { manager, cleanup } = createManager({});
2262
2217
  try {
2263
2218
  const issues = manager.getConfigIssues();
2264
- assert.equal(issues.length, 0);
2219
+ expect(issues.length).toBe(0);
2265
2220
  } finally {
2266
2221
  cleanup();
2267
2222
  }
@@ -2297,8 +2252,8 @@ test("session approval: first prompt with 'Yes, for this session' skips subseque
2297
2252
  },
2298
2253
  { hasUI: true, selectResponse: "Yes, for this session" },
2299
2254
  );
2300
- assert.deepEqual(result1, {});
2301
- assert.equal(harness.prompts.length, 1);
2255
+ expect(result1).toEqual({});
2256
+ expect(harness.prompts.length).toBe(1);
2302
2257
 
2303
2258
  // Second access under same prefix — should skip prompt
2304
2259
  const result2 = await runToolCall(
@@ -2310,9 +2265,9 @@ test("session approval: first prompt with 'Yes, for this session' skips subseque
2310
2265
  },
2311
2266
  { hasUI: true, selectResponse: "Yes, for this session" },
2312
2267
  );
2313
- assert.deepEqual(result2, {});
2268
+ expect(result2).toEqual({});
2314
2269
  // No new prompt — still just the original one
2315
- assert.equal(harness.prompts.length, 1);
2270
+ expect(harness.prompts.length).toBe(1);
2316
2271
 
2317
2272
  // Third access with different tool under same prefix — also skipped
2318
2273
  const result3 = await runToolCall(
@@ -2324,8 +2279,8 @@ test("session approval: first prompt with 'Yes, for this session' skips subseque
2324
2279
  },
2325
2280
  { hasUI: true, selectResponse: "Yes, for this session" },
2326
2281
  );
2327
- assert.deepEqual(result3, {});
2328
- assert.equal(harness.prompts.length, 1);
2282
+ expect(result3).toEqual({});
2283
+ expect(harness.prompts.length).toBe(1);
2329
2284
  } finally {
2330
2285
  await harness.cleanup();
2331
2286
  rmSync(rootDir, { recursive: true, force: true });
@@ -2360,7 +2315,7 @@ test("session approval: different directory prefix still prompts", async () => {
2360
2315
  },
2361
2316
  { hasUI: true, selectResponse: "Yes, for this session" },
2362
2317
  );
2363
- assert.equal(harness.prompts.length, 1);
2318
+ expect(harness.prompts.length).toBe(1);
2364
2319
 
2365
2320
  // Access sibling-b — different prefix, should prompt again
2366
2321
  await runToolCall(
@@ -2372,7 +2327,7 @@ test("session approval: different directory prefix still prompts", async () => {
2372
2327
  },
2373
2328
  { hasUI: true, selectResponse: "Yes" },
2374
2329
  );
2375
- assert.equal(harness.prompts.length, 2);
2330
+ expect(harness.prompts.length).toBe(2);
2376
2331
  } finally {
2377
2332
  await harness.cleanup();
2378
2333
  rmSync(rootDir, { recursive: true, force: true });
@@ -2405,7 +2360,7 @@ test("session approval: session_shutdown clears session approvals", async () =>
2405
2360
  },
2406
2361
  { hasUI: true, selectResponse: "Yes, for this session" },
2407
2362
  );
2408
- assert.equal(harness.prompts.length, 1);
2363
+ expect(harness.prompts.length).toBe(1);
2409
2364
 
2410
2365
  // Trigger session_shutdown (clears cache)
2411
2366
  const shutdownCtx = createMockContext(cwd, harness.prompts, {
@@ -2424,8 +2379,8 @@ test("session approval: session_shutdown clears session approvals", async () =>
2424
2379
  },
2425
2380
  { hasUI: true, selectResponse: "Yes" },
2426
2381
  );
2427
- assert.deepEqual(result, {});
2428
- assert.equal(harness.prompts.length, 2);
2382
+ expect(result).toEqual({});
2383
+ expect(harness.prompts.length).toBe(2);
2429
2384
  } finally {
2430
2385
  await harness.cleanup();
2431
2386
  rmSync(rootDir, { recursive: true, force: true });
@@ -2457,8 +2412,8 @@ test("session approval: bash external directory with 'Yes, for this session' ski
2457
2412
  },
2458
2413
  { hasUI: true, selectResponse: "Yes, for this session" },
2459
2414
  );
2460
- assert.deepEqual(result1, {});
2461
- assert.equal(harness.prompts.length, 1);
2415
+ expect(result1).toEqual({});
2416
+ expect(harness.prompts.length).toBe(1);
2462
2417
 
2463
2418
  // Second bash command referencing path under same prefix — skips prompt
2464
2419
  const result2 = await runToolCall(
@@ -2470,8 +2425,8 @@ test("session approval: bash external directory with 'Yes, for this session' ski
2470
2425
  },
2471
2426
  { hasUI: true, selectResponse: "Yes, for this session" },
2472
2427
  );
2473
- assert.deepEqual(result2, {});
2474
- assert.equal(harness.prompts.length, 1);
2428
+ expect(result2).toEqual({});
2429
+ expect(harness.prompts.length).toBe(1);
2475
2430
  } finally {
2476
2431
  await harness.cleanup();
2477
2432
  rmSync(rootDir, { recursive: true, force: true });
@@ -2504,7 +2459,7 @@ test("session approval: regular 'Yes' does not create session approval", async (
2504
2459
  },
2505
2460
  { hasUI: true, selectResponse: "Yes" },
2506
2461
  );
2507
- assert.equal(harness.prompts.length, 1);
2462
+ expect(harness.prompts.length).toBe(1);
2508
2463
 
2509
2464
  // Same prefix — should still prompt since we used "Yes" not session
2510
2465
  await runToolCall(
@@ -2516,7 +2471,7 @@ test("session approval: regular 'Yes' does not create session approval", async (
2516
2471
  },
2517
2472
  { hasUI: true, selectResponse: "Yes" },
2518
2473
  );
2519
- assert.equal(harness.prompts.length, 2);
2474
+ expect(harness.prompts.length).toBe(2);
2520
2475
  } finally {
2521
2476
  await harness.cleanup();
2522
2477
  rmSync(rootDir, { recursive: true, force: true });
@@ -2549,9 +2504,9 @@ test("checkPermission returns source 'session' when session rules cover the exte
2549
2504
  undefined,
2550
2505
  sessionRules,
2551
2506
  );
2552
- assert.equal(result.state, "allow");
2553
- assert.equal(result.source, "session");
2554
- assert.equal(result.matchedPattern, "/other/project/*");
2507
+ expect(result.state).toBe("allow");
2508
+ expect(result.source).toBe("session");
2509
+ expect(result.matchedPattern).toBe("/other/project/*");
2555
2510
  } finally {
2556
2511
  cleanup();
2557
2512
  }
@@ -2580,8 +2535,8 @@ test("checkPermission falls back to config policy when session rules do not cove
2580
2535
  undefined,
2581
2536
  sessionRules,
2582
2537
  );
2583
- assert.equal(result.state, "deny");
2584
- assert.equal(result.source, "special");
2538
+ expect(result.state).toBe("deny");
2539
+ expect(result.source).toBe("special");
2585
2540
  } finally {
2586
2541
  cleanup();
2587
2542
  }
@@ -2609,8 +2564,8 @@ test("checkPermission with empty session rules is identical to call without sess
2609
2564
  source: "special",
2610
2565
  origin: "global",
2611
2566
  };
2612
- assert.deepEqual(withEmpty, expected);
2613
- assert.deepEqual(withoutArg, expected);
2567
+ expect(withEmpty).toEqual(expected);
2568
+ expect(withoutArg).toEqual(expected);
2614
2569
  } finally {
2615
2570
  cleanup();
2616
2571
  }
@@ -2640,8 +2595,8 @@ test("session rules for one surface do not affect checks on other surfaces", ()
2640
2595
  undefined,
2641
2596
  sessionRules,
2642
2597
  );
2643
- assert.equal(bashResult.state, "ask");
2644
- assert.equal(bashResult.source, "bash");
2598
+ expect(bashResult.state).toBe("ask");
2599
+ expect(bashResult.source).toBe("bash");
2645
2600
 
2646
2601
  // MCP check — session rules should not affect MCP decisions.
2647
2602
  const mcpResult = manager.checkPermission(
@@ -2650,8 +2605,8 @@ test("session rules for one surface do not affect checks on other surfaces", ()
2650
2605
  undefined,
2651
2606
  sessionRules,
2652
2607
  );
2653
- assert.equal(mcpResult.state, "ask");
2654
- assert.equal(mcpResult.source, "default");
2608
+ expect(mcpResult.state).toBe("ask");
2609
+ expect(mcpResult.source).toBe("default");
2655
2610
  } finally {
2656
2611
  cleanup();
2657
2612
  }
@@ -2680,8 +2635,8 @@ test("session rules override config deny for external_directory", () => {
2680
2635
  undefined,
2681
2636
  sessionRules,
2682
2637
  );
2683
- assert.equal(result.state, "allow");
2684
- assert.equal(result.source, "session");
2638
+ expect(result.state).toBe("allow");
2639
+ expect(result.source).toBe("session");
2685
2640
  } finally {
2686
2641
  cleanup();
2687
2642
  }
@@ -2709,9 +2664,9 @@ test("checkPermission returns source 'session' for bash when session rules match
2709
2664
  undefined,
2710
2665
  sessionRules,
2711
2666
  );
2712
- assert.equal(result.state, "allow");
2713
- assert.equal(result.source, "session");
2714
- assert.equal(result.matchedPattern, "git *");
2667
+ expect(result.state).toBe("allow");
2668
+ expect(result.source).toBe("session");
2669
+ expect(result.matchedPattern).toBe("git *");
2715
2670
  } finally {
2716
2671
  cleanup();
2717
2672
  }
@@ -2737,8 +2692,8 @@ test("checkPermission returns source 'session' for bash when session rule is exa
2737
2692
  undefined,
2738
2693
  sessionRules,
2739
2694
  );
2740
- assert.equal(result.state, "allow");
2741
- assert.equal(result.source, "session");
2695
+ expect(result.state).toBe("allow");
2696
+ expect(result.source).toBe("session");
2742
2697
  } finally {
2743
2698
  cleanup();
2744
2699
  }
@@ -2764,8 +2719,8 @@ test("checkPermission falls back to config for bash when session rules do not ma
2764
2719
  undefined,
2765
2720
  sessionRules,
2766
2721
  );
2767
- assert.equal(result.state, "deny");
2768
- assert.equal(result.source, "bash");
2722
+ expect(result.state).toBe("deny");
2723
+ expect(result.source).toBe("bash");
2769
2724
  } finally {
2770
2725
  cleanup();
2771
2726
  }
@@ -2791,8 +2746,8 @@ test("checkPermission returns source 'session' for mcp when session rules match
2791
2746
  undefined,
2792
2747
  sessionRules,
2793
2748
  );
2794
- assert.equal(result.state, "allow");
2795
- assert.equal(result.source, "session");
2749
+ expect(result.state).toBe("allow");
2750
+ expect(result.source).toBe("session");
2796
2751
  } finally {
2797
2752
  cleanup();
2798
2753
  }
@@ -2818,9 +2773,9 @@ test("checkPermission returns source 'session' for skill when session rules matc
2818
2773
  undefined,
2819
2774
  sessionRules,
2820
2775
  );
2821
- assert.equal(result.state, "allow");
2822
- assert.equal(result.source, "session");
2823
- assert.equal(result.matchedPattern, "librarian");
2776
+ expect(result.state).toBe("allow");
2777
+ expect(result.source).toBe("session");
2778
+ expect(result.matchedPattern).toBe("librarian");
2824
2779
  } finally {
2825
2780
  cleanup();
2826
2781
  }
@@ -2841,8 +2796,8 @@ test("checkPermission returns source 'session' for tool surface when session rul
2841
2796
  ];
2842
2797
 
2843
2798
  const result = manager.checkPermission("read", {}, undefined, sessionRules);
2844
- assert.equal(result.state, "allow");
2845
- assert.equal(result.source, "session");
2799
+ expect(result.state).toBe("allow");
2800
+ expect(result.source).toBe("session");
2846
2801
  } finally {
2847
2802
  cleanup();
2848
2803
  }
@@ -2869,7 +2824,7 @@ test("bash session rules do not bleed into mcp checks", () => {
2869
2824
  sessionRules,
2870
2825
  );
2871
2826
  // bash session rule must not affect mcp surface
2872
- assert.notEqual(result.source, "session");
2827
+ expect(result.source).not.toBe("session");
2873
2828
  } finally {
2874
2829
  cleanup();
2875
2830
  }