feat(ai-judge): show dialog advice widget with verdict and focus

- add AdvicePresenter rendering a pi-native setWidget panel while a
  permission dialog is up: judgment verdict with reason, high-risk skip
  with category, or an unavailable cause
- highlight the decision-relevant command fragment via a focus cascade:
  high-risk match, triggering unit, then executed unit
- clear the widget when permissions:decision resolves the same request
- add dialogAdvice config key (default true, invalid falls back with a
  diagnostic) and cover formatting, lifecycle, and config in tests
This commit is contained in:
2026-09-17 23:42:57 +08:00
parent 77ef5483ed
commit 7190bffd86
7 changed files with 592 additions and 7 deletions
@@ -253,7 +253,7 @@ describe("AI judge lifecycle", () => {
return currentModel;
},
modelRegistry: { complete },
ui: { notify: vi.fn() },
ui: { notify: vi.fn(), setWidget: vi.fn() },
} as unknown as ExtensionContext;
const harness = createFakePi();
@@ -342,7 +342,7 @@ describe("AI judge lifecycle", () => {
api: "openai-codex-responses",
} as Model<any>,
modelRegistry: { complete },
ui: { notify: vi.fn() },
ui: { notify: vi.fn(), setWidget: vi.fn() },
} as unknown as ExtensionContext;
const harness = createFakePi();
@@ -406,7 +406,7 @@ describe("AI judge lifecycle", () => {
sessionManager,
model,
modelRegistry: { complete },
ui: { notify: vi.fn() },
ui: { notify: vi.fn(), setWidget: vi.fn() },
} as unknown as ExtensionContext;
const harness = createFakePi();
@@ -492,7 +492,7 @@ describe("AI judge lifecycle", () => {
api: "openai-codex-responses",
} as Model<any>,
modelRegistry: { complete },
ui: { notify: vi.fn() },
ui: { notify: vi.fn(), setWidget: vi.fn() },
} as unknown as ExtensionContext;
const harness = createFakePi();
@@ -571,7 +571,7 @@ describe("AI judge lifecycle", () => {
api: "openai-codex-responses",
} as Model<any>,
modelRegistry: { complete },
ui: { notify: vi.fn() },
ui: { notify: vi.fn(), setWidget: vi.fn() },
} as unknown as ExtensionContext;
const harness = createFakePi();
@@ -701,7 +701,7 @@ describe("AI judge Enforce authority seam (PIEXTENSIO-23, ADR 0008)", () => {
api: "openai-codex-responses",
} as Model<any>,
modelRegistry: { complete, find, hasConfiguredAuth },
ui: { notify },
ui: { notify, setWidget: vi.fn() },
} as unknown as ExtensionContext;
const harness = createFakePi();
@@ -936,7 +936,7 @@ describe("AI judge Enforce authority seam (PIEXTENSIO-23, ADR 0008)", () => {
api: "openai-codex-responses",
} as Model<any>,
modelRegistry: { complete, find: vi.fn(), hasConfiguredAuth: vi.fn(() => true) },
ui: { notify },
ui: { notify, setWidget: vi.fn() },
} as unknown as ExtensionContext;
const harness = createFakePi();