patchwarden 1.5.0 → 1.5.1

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.
Files changed (217) hide show
  1. package/README.en.md +43 -2
  2. package/README.md +43 -2
  3. package/dist/assessments/agentAssessor.d.ts +1 -1
  4. package/dist/assessments/agentAssessor.js +3 -3
  5. package/dist/assessments/assessmentStore.d.ts +1 -1
  6. package/dist/assessments/assessmentStore.js +2 -2
  7. package/dist/assessments/confirmCli.js +5 -4
  8. package/dist/config.d.ts +7 -0
  9. package/dist/config.js +34 -0
  10. package/dist/control/middleware/auth.d.ts +10 -0
  11. package/dist/control/middleware/auth.js +8 -0
  12. package/dist/control/middleware/static.d.ts +3 -0
  13. package/dist/control/middleware/static.js +65 -0
  14. package/dist/control/routes/audit.d.ts +15 -0
  15. package/dist/control/routes/audit.js +277 -0
  16. package/dist/control/routes/evidence.d.ts +4 -0
  17. package/dist/control/routes/evidence.js +96 -0
  18. package/dist/control/routes/lineage.d.ts +3 -0
  19. package/dist/control/routes/lineage.js +71 -0
  20. package/dist/control/routes/policy.d.ts +3 -0
  21. package/dist/control/routes/policy.js +81 -0
  22. package/dist/control/routes/process.d.ts +5 -0
  23. package/dist/control/routes/process.js +200 -0
  24. package/dist/control/routes/sessions.d.ts +22 -0
  25. package/dist/control/routes/sessions.js +224 -0
  26. package/dist/control/routes/status.d.ts +6 -0
  27. package/dist/control/routes/status.js +250 -0
  28. package/dist/control/routes/taskActions.d.ts +21 -0
  29. package/dist/control/routes/taskActions.js +233 -0
  30. package/dist/control/routes/tasks.d.ts +20 -0
  31. package/dist/control/routes/tasks.js +310 -0
  32. package/dist/control/routes/workspace.d.ts +15 -0
  33. package/dist/control/routes/workspace.js +193 -0
  34. package/dist/control/runtime.d.ts +91 -0
  35. package/dist/control/runtime.js +392 -0
  36. package/dist/control/server.d.ts +13 -0
  37. package/dist/control/server.js +479 -0
  38. package/dist/control/shared.d.ts +35 -0
  39. package/dist/control/shared.js +288 -0
  40. package/dist/controlCenter.d.ts +6 -0
  41. package/dist/controlCenter.js +7 -2197
  42. package/dist/direct/directGuards.js +30 -8
  43. package/dist/doctor.d.ts +18 -1
  44. package/dist/doctor.js +579 -348
  45. package/dist/goal/goalReport.d.ts +54 -0
  46. package/dist/goal/goalReport.js +204 -0
  47. package/dist/goal/goalStatus.d.ts +6 -0
  48. package/dist/goal/specKitImport.d.ts +63 -0
  49. package/dist/goal/specKitImport.js +220 -0
  50. package/dist/goal/subgoalSync.js +2 -1
  51. package/dist/httpServer.js +15 -12
  52. package/dist/index.js +7 -4
  53. package/dist/logging.d.ts +7 -1
  54. package/dist/logging.js +8 -0
  55. package/dist/runner/changeCapture.d.ts +3 -3
  56. package/dist/runner/changeCapture.js +63 -39
  57. package/dist/runner/cli.js +7 -6
  58. package/dist/runner/postTaskCleanup.js +26 -2
  59. package/dist/runner/runTask.js +245 -221
  60. package/dist/runner/simpleProcess.js +4 -4
  61. package/dist/runner/watch.js +17 -14
  62. package/dist/security/contentRedaction.d.ts +6 -0
  63. package/dist/security/contentRedaction.js +22 -0
  64. package/dist/smoke-test.js +257 -251
  65. package/dist/test/unit/apply-patch.test.d.ts +1 -0
  66. package/dist/test/unit/apply-patch.test.js +225 -0
  67. package/dist/test/unit/create-task.test.d.ts +1 -0
  68. package/dist/test/unit/create-task.test.js +197 -0
  69. package/dist/test/unit/direct-guards.test.js +124 -9
  70. package/dist/test/unit/evidence-pack.test.js +95 -1
  71. package/dist/test/unit/get-task-status.test.d.ts +1 -0
  72. package/dist/test/unit/get-task-status.test.js +174 -0
  73. package/dist/test/unit/get-task-summary.test.d.ts +1 -0
  74. package/dist/test/unit/get-task-summary.test.js +146 -0
  75. package/dist/test/unit/goal-report.test.d.ts +1 -0
  76. package/dist/test/unit/goal-report.test.js +159 -0
  77. package/dist/test/unit/goal-subgoal-task.test.js +6 -6
  78. package/dist/test/unit/goal-tools-registry.test.js +6 -4
  79. package/dist/test/unit/path-guard.test.js +24 -0
  80. package/dist/test/unit/spec-kit-import.test.d.ts +1 -0
  81. package/dist/test/unit/spec-kit-import.test.js +341 -0
  82. package/dist/test/unit/wait-for-task.test.d.ts +1 -0
  83. package/dist/test/unit/wait-for-task.test.js +144 -0
  84. package/dist/tools/auditTask.d.ts +8 -63
  85. package/dist/tools/auditTask.js +12 -8
  86. package/dist/tools/createDirectSession.d.ts +1 -1
  87. package/dist/tools/createDirectSession.js +2 -2
  88. package/dist/tools/createTask.d.ts +2 -2
  89. package/dist/tools/createTask.js +4 -4
  90. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  91. package/dist/tools/dispatch/coreDispatch.js +282 -0
  92. package/dist/tools/dispatch/diagnosticDispatch.d.ts +14 -0
  93. package/dist/tools/dispatch/diagnosticDispatch.js +78 -0
  94. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  95. package/dist/tools/dispatch/directDispatch.js +115 -0
  96. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  97. package/dist/tools/dispatch/goalDispatch.js +91 -0
  98. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  99. package/dist/tools/dispatch/releaseDispatch.js +45 -0
  100. package/dist/tools/dispatch/types.d.ts +23 -0
  101. package/dist/tools/dispatch/types.js +15 -0
  102. package/dist/tools/evidencePack.d.ts +6 -0
  103. package/dist/tools/evidencePack.js +213 -6
  104. package/dist/tools/finalizeDirectSession.d.ts +1 -1
  105. package/dist/tools/finalizeDirectSession.js +3 -3
  106. package/dist/tools/goalSubgoalTask.d.ts +1 -1
  107. package/dist/tools/goalSubgoalTask.js +2 -2
  108. package/dist/tools/healthCheck.js +3 -3
  109. package/dist/tools/registry.d.ts +3 -3
  110. package/dist/tools/registry.js +60 -503
  111. package/dist/tools/retryTask.d.ts +2 -2
  112. package/dist/tools/retryTask.js +2 -2
  113. package/dist/tools/runTaskLoop.js +4 -4
  114. package/dist/tools/safeViews.d.ts +2 -2
  115. package/dist/tools/safeViews.js +2 -2
  116. package/dist/tools/toolRegistry.js +22 -0
  117. package/dist/version.d.ts +1 -1
  118. package/dist/version.js +1 -1
  119. package/docs/CODE_WIKI.md +983 -0
  120. package/docs/agentseal-integration.md +150 -0
  121. package/docs/architecture.md +63 -0
  122. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  123. package/docs/dashboard-overview.md +86 -0
  124. package/docs/demo.md +8 -0
  125. package/docs/direct-session-workflow.md +98 -0
  126. package/docs/evidence-pack-schema.md +215 -0
  127. package/docs/execution-plan-2026-07-09.md +315 -0
  128. package/docs/lineage-evidence-pack-workflow.md +127 -0
  129. package/docs/mcp-inspector-testing.md +200 -0
  130. package/docs/open-source-application.md +162 -0
  131. package/docs/opencode-worker.md +151 -0
  132. package/docs/openhands-worker.md +181 -0
  133. package/docs/release-evidence.md +72 -0
  134. package/docs/roadmap-execution-and-acceptance.md +365 -0
  135. package/docs/spec-kit-integration.md +131 -0
  136. package/docs/task-safe-review-workflow.md +98 -0
  137. package/docs/threat-model.md +79 -0
  138. package/docs/user-feedback.md +40 -0
  139. package/docs/why-patchwarden.md +110 -0
  140. package/package.json +2 -2
  141. package/scripts/checks/control-center-smoke.js +356 -0
  142. package/scripts/checks/lifecycle-smoke.js +29 -23
  143. package/scripts/checks/mcp-smoke.js +3 -1
  144. package/scripts/generate-demo-gif.py +320 -0
  145. package/src/assessments/agentAssessor.ts +3 -3
  146. package/src/assessments/assessmentStore.ts +2 -2
  147. package/src/assessments/confirmCli.ts +5 -4
  148. package/src/config.ts +37 -0
  149. package/src/control/middleware/auth.ts +17 -0
  150. package/src/control/middleware/static.ts +71 -0
  151. package/src/control/routes/audit.ts +321 -0
  152. package/src/control/routes/evidence.ts +107 -0
  153. package/src/control/routes/lineage.ts +92 -0
  154. package/src/control/routes/policy.ts +81 -0
  155. package/src/control/routes/process.ts +204 -0
  156. package/src/control/routes/sessions.ts +251 -0
  157. package/src/control/routes/status.ts +325 -0
  158. package/src/control/routes/taskActions.ts +248 -0
  159. package/src/control/routes/tasks.ts +323 -0
  160. package/src/control/routes/workspace.ts +203 -0
  161. package/src/control/runtime.ts +472 -0
  162. package/src/control/server.ts +471 -0
  163. package/src/control/shared.ts +294 -0
  164. package/src/controlCenter.ts +7 -2347
  165. package/src/direct/directGuards.ts +28 -7
  166. package/src/doctor.ts +741 -481
  167. package/src/goal/goalReport.ts +271 -0
  168. package/src/goal/goalStatus.ts +6 -0
  169. package/src/goal/specKitImport.ts +355 -0
  170. package/src/goal/subgoalSync.ts +4 -2
  171. package/src/httpServer.ts +17 -14
  172. package/src/index.ts +7 -4
  173. package/src/logging.ts +10 -1
  174. package/src/runner/changeCapture.ts +70 -42
  175. package/src/runner/cli.ts +7 -6
  176. package/src/runner/postTaskCleanup.ts +26 -2
  177. package/src/runner/runTask.ts +325 -223
  178. package/src/runner/simpleProcess.ts +4 -4
  179. package/src/runner/watch.ts +17 -14
  180. package/src/security/contentRedaction.ts +29 -0
  181. package/src/smoke-test.ts +252 -250
  182. package/src/test/unit/apply-patch.test.ts +293 -0
  183. package/src/test/unit/create-task.test.ts +255 -0
  184. package/src/test/unit/direct-guards.test.ts +178 -8
  185. package/src/test/unit/evidence-pack.test.ts +110 -1
  186. package/src/test/unit/get-task-status.test.ts +203 -0
  187. package/src/test/unit/get-task-summary.test.ts +173 -0
  188. package/src/test/unit/goal-report.test.ts +189 -0
  189. package/src/test/unit/goal-subgoal-task.test.ts +6 -6
  190. package/src/test/unit/goal-tools-registry.test.ts +7 -5
  191. package/src/test/unit/path-guard.test.ts +30 -0
  192. package/src/test/unit/spec-kit-import.test.ts +429 -0
  193. package/src/test/unit/wait-for-task.test.ts +176 -0
  194. package/src/tools/auditTask.ts +99 -59
  195. package/src/tools/createDirectSession.ts +3 -3
  196. package/src/tools/createTask.ts +7 -7
  197. package/src/tools/dispatch/coreDispatch.ts +374 -0
  198. package/src/tools/dispatch/diagnosticDispatch.ts +101 -0
  199. package/src/tools/dispatch/directDispatch.ts +167 -0
  200. package/src/tools/dispatch/goalDispatch.ts +127 -0
  201. package/src/tools/dispatch/releaseDispatch.ts +65 -0
  202. package/src/tools/dispatch/types.ts +24 -0
  203. package/src/tools/evidencePack.ts +291 -6
  204. package/src/tools/finalizeDirectSession.ts +4 -4
  205. package/src/tools/goalSubgoalTask.ts +2 -2
  206. package/src/tools/healthCheck.ts +3 -3
  207. package/src/tools/registry.ts +68 -628
  208. package/src/tools/retryTask.ts +2 -2
  209. package/src/tools/runTaskLoop.ts +4 -4
  210. package/src/tools/safeViews.ts +2 -2
  211. package/src/tools/toolRegistry.ts +22 -0
  212. package/src/version.ts +1 -1
  213. package/ui/pages/audit.html +192 -3
  214. package/ui/pages/dashboard.html +959 -44
  215. package/ui/pages/direct-sessions.html +505 -53
  216. package/ui/pages/task-detail.html +456 -438
  217. package/ui/pages/tasks.html +598 -61
@@ -4,7 +4,7 @@ import { getTasksDir, getConfig } from "../config.js";
4
4
  import { guardReadPath } from "../security/pathGuard.js";
5
5
  import { createTask } from "./createTask.js";
6
6
 
7
- export function retryTask(taskId: string) {
7
+ export async function retryTask(taskId: string) {
8
8
  const config = getConfig();
9
9
  const tasksDir = getTasksDir(config);
10
10
  const taskDir = join(tasksDir, taskId);
@@ -23,7 +23,7 @@ export function retryTask(taskId: string) {
23
23
  }
24
24
 
25
25
  // Create a new task with the same parameters
26
- const newTask = createTask({
26
+ const newTask = await createTask({
27
27
  plan_id: data.plan_id,
28
28
  agent: data.agent,
29
29
  repo_path: data.repo_path,
@@ -202,7 +202,7 @@ export async function runTaskLoopWithDeps(
202
202
  timeout_seconds: normalized.task_timeout_seconds,
203
203
  execution_mode: "assess_only",
204
204
  };
205
- const assessment = deps.createTask(assessmentInput as CreateTaskInput & { execution_mode: "assess_only" }) as any;
205
+ const assessment = await deps.createTask(assessmentInput as CreateTaskInput & { execution_mode: "assess_only" }) as any;
206
206
  if (assessment.decision === "blocked") {
207
207
  return finalize("blocked", "high_risk_blocked", "Risk assessment blocked task execution.", assessment.reason_codes?.join(", "));
208
208
  }
@@ -210,7 +210,7 @@ export async function runTaskLoopWithDeps(
210
210
  return finalize("blocked", "user_confirmation_required", "Ask the user to confirm the assessment before executing the loop.");
211
211
  }
212
212
 
213
- const created = deps.createTask({
213
+ const created = await deps.createTask({
214
214
  execution_mode: "execute",
215
215
  assessment_id: String(assessment.assessment_id || ""),
216
216
  }) as any;
@@ -403,7 +403,7 @@ async function runDirectVerification(
403
403
 
404
404
  let sessionId = "";
405
405
  try {
406
- const session = deps.createDirectSession({
406
+ const session = await deps.createDirectSession({
407
407
  repo_path: repoPath,
408
408
  title: `Direct verification for ${lineageId}`,
409
409
  });
@@ -413,7 +413,7 @@ async function runDirectVerification(
413
413
  commands: normalized.direct_verify_commands,
414
414
  timeout_seconds: normalized.direct_verify_timeout_seconds,
415
415
  });
416
- const finalized = deps.safeFinalizeDirectSession(sessionId, { max_items: 8 }) as any;
416
+ const finalized = await deps.safeFinalizeDirectSession(sessionId, { max_items: 8 }) as any;
417
417
  const audit = deps.safeAuditDirectSession(sessionId, { max_items: 8 }) as any;
418
418
  const evidence: TaskLineageDirectSession = {
419
419
  session_id: sessionId,
@@ -103,9 +103,9 @@ export function safeDirectSummary(sessionId: string, options: SafeViewOptions =
103
103
  return directSessionToSafe(session, maxItems, "summary");
104
104
  }
105
105
 
106
- export function safeFinalizeDirectSession(sessionId: string, options: SafeViewOptions = {}) {
106
+ export async function safeFinalizeDirectSession(sessionId: string, options: SafeViewOptions = {}) {
107
107
  const maxItems = normalizeMaxItems(options.max_items);
108
- const finalized = finalizeDirectSession({ session_id: sessionId });
108
+ const finalized = await finalizeDirectSession({ session_id: sessionId });
109
109
  const session = readDirectSession(sessionId);
110
110
  return redact({
111
111
  ...directSessionToSafe(session, maxItems, "finalize"),
@@ -728,6 +728,28 @@ const STATIC_TOOL_META: Record<string, StaticToolMeta> = {
728
728
  requiresConfirmation: false,
729
729
  relatedTools: ["summarize_goal_progress", "read_goal"],
730
730
  },
731
+ export_goal_report: {
732
+ title: "Export Goal Report",
733
+ summary: "Export a structured final report for a Goal session",
734
+ profiles: ["full"],
735
+ modes: ["delegate", "audit"],
736
+ tags: ["goal", "report", "export", "summary", "final"],
737
+ aliases: ["goal_report", "report"],
738
+ risk: "workspace_write",
739
+ requiresConfirmation: false,
740
+ relatedTools: ["summarize_goal_progress", "export_handoff"],
741
+ },
742
+ import_speckit_tasks: {
743
+ title: "Import Spec Kit Tasks",
744
+ summary: "Import Spec Kit tasks into a Goal session as subgoals",
745
+ profiles: ["full"],
746
+ modes: ["delegate", "audit"],
747
+ tags: ["goal", "speckit", "import", "tasks", "subgoal"],
748
+ aliases: ["speckit_import", "import_tasks"],
749
+ risk: "workspace_write",
750
+ requiresConfirmation: false,
751
+ relatedTools: ["create_subgoal_task", "export_goal_report"],
752
+ },
731
753
  invoke_discovered_tool: {
732
754
  title: "Invoke Discovered Tool",
733
755
  summary: "v0.8.1: 通过 token 调用 discover_tools 发现的动态工具",
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const PATCHWARDEN_VERSION = "1.5.0";
1
+ export const PATCHWARDEN_VERSION = "1.5.1";
2
2
  export const TOOL_SCHEMA_EPOCH = "2026-07-05-v13";
@@ -293,6 +293,55 @@ html.dark {
293
293
  </div>
294
294
  </section>
295
295
 
296
+ <!-- ===== 1b. Warnings Aggregation ===== -->
297
+ <section class="rounded-lg overflow-hidden mb-6" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border); border-left: 3px solid var(--pw-state-warning);">
298
+ <div class="flex items-center justify-between px-5 py-4" style="border-bottom: 1px solid var(--pw-border-subtle);">
299
+ <div class="flex items-center gap-2 flex-wrap">
300
+ <i data-lucide="alert-triangle" class="w-4 h-4 shrink-0" style="color: var(--pw-state-warning);"></i>
301
+ <h2 class="text-sm font-semibold" style="color: var(--pw-text-primary);" data-zh="警告聚合" data-en="Warnings Aggregation">警告聚合</h2>
302
+ <span id="warnings-count" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="font-family: var(--pw-font-mono); color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover);">—</span>
303
+ </div>
304
+ </div>
305
+
306
+ <!-- Warnings Loading State -->
307
+ <div id="warningsLoadingState" class="py-10 text-center">
308
+ <svg class="pw-spin inline-block mb-2" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-tertiary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
309
+ <line x1="12" y1="2" x2="12" y2="6"/>
310
+ <line x1="12" y1="18" x2="12" y2="22"/>
311
+ <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/>
312
+ <line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/>
313
+ <line x1="2" y1="12" x2="6" y2="12"/>
314
+ <line x1="18" y1="12" x2="22" y2="12"/>
315
+ <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/>
316
+ <line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/>
317
+ </svg>
318
+ <p class="text-xs" style="color: var(--pw-text-tertiary);" data-zh="加载警告中..." data-en="Loading warnings...">加载警告中...</p>
319
+ </div>
320
+
321
+ <!-- Warnings Error State -->
322
+ <div id="warningsErrorState" class="hidden py-10 text-center">
323
+ <svg class="inline-block mb-2" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--pw-state-error)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
324
+ <circle cx="12" cy="12" r="10"/>
325
+ <line x1="12" y1="8" x2="12" y2="12"/>
326
+ <line x1="12" y1="16" x2="12.01" y2="16"/>
327
+ </svg>
328
+ <p class="text-xs font-medium" style="color: var(--pw-state-error);" data-zh="警告加载失败" data-en="Warnings Load Failed">警告加载失败</p>
329
+ <p id="warningsErrorMessage" class="text-xs mt-2 px-6 break-words" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-mono);"></p>
330
+ </div>
331
+
332
+ <!-- Warnings Empty State -->
333
+ <div id="warningsEmptyState" class="hidden py-10 text-center">
334
+ <svg class="inline-block mb-2" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--pw-state-success)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
335
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
336
+ <polyline points="22 4 12 14.01 9 11.01"/>
337
+ </svg>
338
+ <p class="text-xs" style="color: var(--pw-text-tertiary);" data-zh="暂无警告" data-en="No Warnings">暂无警告</p>
339
+ </div>
340
+
341
+ <!-- Warnings Cards Container -->
342
+ <div id="warningsBody" class="hidden px-5 py-4 space-y-3"></div>
343
+ </section>
344
+
296
345
  <!-- ===== 2. Audit Log Table ===== -->
297
346
  <section class="rounded-lg overflow-hidden mb-6" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border); border-left: 3px solid var(--pw-accent);">
298
347
 
@@ -438,8 +487,10 @@ html.dark {
438
487
  'use strict';
439
488
 
440
489
  var API_URL = '/api/audit';
490
+ var WARNINGS_URL = '/api/warnings';
441
491
 
442
492
  var allAudits = [];
493
+ var allWarnings = [];
443
494
  var selectedTaskId = null;
444
495
 
445
496
  // ---- DOM helpers ----
@@ -724,16 +775,154 @@ html.dark {
724
775
  });
725
776
  }
726
777
 
778
+ // ---- Warnings aggregation ----
779
+ function showWarningsLoading() {
780
+ show($('warningsLoadingState'));
781
+ hide($('warningsErrorState'));
782
+ hide($('warningsEmptyState'));
783
+ hide($('warningsBody'));
784
+ }
785
+
786
+ function showWarningsError(msg) {
787
+ hide($('warningsLoadingState'));
788
+ show($('warningsErrorState'));
789
+ hide($('warningsEmptyState'));
790
+ hide($('warningsBody'));
791
+ var errEl = $('warningsErrorMessage');
792
+ if (errEl) errEl.textContent = msg || '未知错误';
793
+ }
794
+
795
+ function showWarningsEmpty() {
796
+ hide($('warningsLoadingState'));
797
+ hide($('warningsErrorState'));
798
+ show($('warningsEmptyState'));
799
+ hide($('warningsBody'));
800
+ }
801
+
802
+ function showWarningsBody() {
803
+ hide($('warningsLoadingState'));
804
+ hide($('warningsErrorState'));
805
+ hide($('warningsEmptyState'));
806
+ show($('warningsBody'));
807
+ }
808
+
809
+ // Map a warning type to a display label + icon + accent color.
810
+ function warningTypeMeta(type) {
811
+ var map = {
812
+ failed_verification: { label: 'Failed verification', color: 'var(--pw-state-error)' },
813
+ stale_task: { label: 'Stale task', color: 'var(--pw-state-warning)' },
814
+ manual_verification_required: { label: 'Manual verification required', color: 'var(--pw-state-warning)' },
815
+ unrecorded_command_execution: { label: 'Unrecorded command execution', color: 'var(--pw-state-info)' },
816
+ release_publish_claim: { label: 'Release/publish claim', color: 'var(--pw-state-error)' },
817
+ artifact_hygiene: { label: 'Artifact hygiene', color: 'var(--pw-state-info)' },
818
+ scope_changes: { label: 'Scope changes', color: 'var(--pw-state-info)' }
819
+ };
820
+ return map[type] || { label: type || 'Unknown', color: 'var(--pw-text-tertiary)' };
821
+ }
822
+
823
+ function severityBadge(severity) {
824
+ var v = String(severity || '').toLowerCase();
825
+ var bg, color;
826
+ if (v === 'error') {
827
+ bg = 'var(--pw-state-error-bg)';
828
+ color = 'var(--pw-state-error)';
829
+ } else if (v === 'warning') {
830
+ bg = 'var(--pw-state-warning-bg)';
831
+ color = 'var(--pw-state-warning)';
832
+ } else {
833
+ bg = 'var(--pw-state-info-bg)';
834
+ color = 'var(--pw-state-info)';
835
+ }
836
+ return '<span class="inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium whitespace-nowrap" style="background-color: ' + bg + '; color: ' + color + ';">' + escapeHtml(v || 'info') + '</span>';
837
+ }
838
+
839
+ function flagLabel(active, label, activeColor) {
840
+ if (!active) return '';
841
+ return '<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs whitespace-nowrap" style="background-color: ' + activeColor + '_bg; color: ' + activeColor + '; border-left: 2px solid ' + activeColor + ';">' + escapeHtml(label) + '</span>';
842
+ }
843
+
844
+ function renderWarnings() {
845
+ var countEl = $('warnings-count');
846
+ if (allWarnings.length === 0) {
847
+ if (countEl) countEl.textContent = '0';
848
+ showWarningsEmpty();
849
+ return;
850
+ }
851
+ if (countEl) countEl.textContent = String(allWarnings.length);
852
+ var html = '';
853
+ for (var i = 0; i < allWarnings.length; i++) {
854
+ var w = allWarnings[i];
855
+ var meta = warningTypeMeta(w.type);
856
+ var tasks = Array.isArray(w.affected_tasks) ? w.affected_tasks : [];
857
+ var taskLinksHtml = '';
858
+ for (var j = 0; j < tasks.length; j++) {
859
+ var tid = tasks[j];
860
+ var href = '/pages/task-detail.html?id=' + encodeURIComponent(tid);
861
+ taskLinksHtml += '<a href="' + href + '" class="inline-block hover:underline px-1.5 py-0.5 rounded text-xs" style="font-family: var(--pw-font-mono); color: var(--pw-text-accent); background-color: var(--pw-accent-subtle);" title="' + escapeAttr(tid) + '">' + escapeHtml(truncate(tid, 18)) + '</a>';
862
+ }
863
+ var flagsHtml = ''
864
+ + flagLabel(w.likely_false_positive === true, 'likely false positive', 'var(--pw-state-warning)')
865
+ + flagLabel(w.needs_fix === true, 'needs fix', 'var(--pw-state-error)')
866
+ + flagLabel(w.blocked === true, 'blocked', 'var(--pw-state-error)');
867
+ html += ''
868
+ + '<div class="rounded-md p-3" style="background-color: var(--pw-bg-hover); border-left: 3px solid ' + meta.color + ';">'
869
+ + '<div class="flex items-center gap-2 mb-2 flex-wrap">'
870
+ + '<span class="text-sm font-semibold truncate" style="color: var(--pw-text-primary);">' + escapeHtml(meta.label) + '</span>'
871
+ + severityBadge(w.severity)
872
+ + '<span class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-mono);">' + escapeHtml(String(w.affected_tasks_count || 0)) + ' task' + (w.affected_tasks_count === 1 ? '' : 's') + '</span>'
873
+ + (flagsHtml ? '<span class="flex items-center gap-1 flex-wrap">' + flagsHtml + '</span>' : '')
874
+ + '</div>'
875
+ + (w.recommended_action ? '<div class="text-xs mb-2" style="color: var(--pw-text-secondary);">' + escapeHtml(w.recommended_action) + '</div>' : '')
876
+ + (taskLinksHtml ? '<div class="flex flex-wrap gap-1.5">' + taskLinksHtml + '</div>' : '')
877
+ + '</div>';
878
+ }
879
+ $('warningsBody').innerHTML = html;
880
+ showWarningsBody();
881
+ }
882
+
883
+ function fetchWarnings() {
884
+ showWarningsLoading();
885
+ fetch(WARNINGS_URL, {
886
+ method: 'GET',
887
+ headers: { 'Accept': 'application/json' },
888
+ cache: 'no-store'
889
+ })
890
+ .then(function (res) {
891
+ if (!res.ok) {
892
+ return res.text().then(function (body) {
893
+ throw new Error('HTTP ' + res.status + (body ? (': ' + body.slice(0, 200)) : ''));
894
+ });
895
+ }
896
+ return res.json();
897
+ })
898
+ .then(function (data) {
899
+ allWarnings = (data && Array.isArray(data.warnings)) ? data.warnings : [];
900
+ renderWarnings();
901
+ })
902
+ .catch(function (err) {
903
+ var msg = (err && err.message) ? err.message : '未知错误';
904
+ if (err && err.name === 'TypeError' && /fetch/i.test(msg)) {
905
+ msg = '无法连接到 ' + WARNINGS_URL + '(请确认 PatchWarden 服务正在运行)';
906
+ }
907
+ showWarningsError(msg);
908
+ });
909
+ }
910
+
911
+ function refreshAll() {
912
+ fetchWarnings();
913
+ fetchAudits();
914
+ }
915
+
727
916
  // ---- Event wiring ----
728
- $('refreshBtn').addEventListener('click', fetchAudits);
917
+ $('refreshBtn').addEventListener('click', refreshAll);
729
918
 
730
919
  var retryBtn = $('retryBtn');
731
920
  if (retryBtn) {
732
- retryBtn.addEventListener('click', fetchAudits);
921
+ retryBtn.addEventListener('click', refreshAll);
733
922
  }
734
923
 
735
924
  // Initial load
736
- fetchAudits();
925
+ refreshAll();
737
926
  })();
738
927
  </script>
739
928