patchwarden 1.5.0 → 1.6.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 (597) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +194 -26
  3. package/README.md +160 -22
  4. package/dist/assessments/agentAssessor.d.ts +1 -1
  5. package/dist/assessments/agentAssessor.js +24 -15
  6. package/dist/assessments/assessmentStore.d.ts +1 -1
  7. package/dist/assessments/assessmentStore.js +44 -36
  8. package/dist/assessments/confirmCli.js +5 -4
  9. package/dist/config.d.ts +22 -0
  10. package/dist/config.js +98 -11
  11. package/dist/control/fileManager.d.ts +2 -0
  12. package/dist/control/fileManager.js +39 -0
  13. package/dist/control/middleware/auth.d.ts +16 -0
  14. package/dist/control/middleware/auth.js +17 -0
  15. package/dist/control/middleware/static.d.ts +3 -0
  16. package/dist/control/middleware/static.js +78 -0
  17. package/dist/control/routeTable.d.ts +24 -0
  18. package/dist/control/routeTable.js +263 -0
  19. package/dist/control/routes/audit.d.ts +15 -0
  20. package/dist/control/routes/audit.js +287 -0
  21. package/dist/control/routes/evidence.d.ts +4 -0
  22. package/dist/control/routes/evidence.js +96 -0
  23. package/dist/control/routes/lineage.d.ts +3 -0
  24. package/dist/control/routes/lineage.js +71 -0
  25. package/dist/control/routes/policy.d.ts +3 -0
  26. package/dist/control/routes/policy.js +81 -0
  27. package/dist/control/routes/process.d.ts +12 -0
  28. package/dist/control/routes/process.js +487 -0
  29. package/dist/control/routes/sessions.d.ts +30 -0
  30. package/dist/control/routes/sessions.js +287 -0
  31. package/dist/control/routes/status.d.ts +51 -0
  32. package/dist/control/routes/status.js +307 -0
  33. package/dist/control/routes/taskActions.d.ts +21 -0
  34. package/dist/control/routes/taskActions.js +197 -0
  35. package/dist/control/routes/tasks.d.ts +24 -0
  36. package/dist/control/routes/tasks.js +309 -0
  37. package/dist/control/routes/workspace.d.ts +15 -0
  38. package/dist/control/routes/workspace.js +220 -0
  39. package/dist/control/runtime.d.ts +92 -0
  40. package/dist/control/runtime.js +425 -0
  41. package/dist/control/server.d.ts +13 -0
  42. package/dist/control/server.js +150 -0
  43. package/dist/control/shared.d.ts +59 -0
  44. package/dist/control/shared.js +341 -0
  45. package/dist/controlCenter.d.ts +6 -0
  46. package/dist/controlCenter.js +7 -2197
  47. package/dist/direct/directAudit.js +234 -225
  48. package/dist/direct/directGuards.d.ts +2 -1
  49. package/dist/direct/directGuards.js +72 -37
  50. package/dist/direct/directPatch.d.ts +6 -1
  51. package/dist/direct/directPatch.js +55 -8
  52. package/dist/direct/directSessionStore.d.ts +16 -6
  53. package/dist/direct/directSessionStore.js +127 -35
  54. package/dist/direct/directVerification.js +8 -16
  55. package/dist/doctor.d.ts +18 -1
  56. package/dist/doctor.js +614 -373
  57. package/dist/goal/acceptanceEngine.d.ts +1 -1
  58. package/dist/goal/goalProgress.js +75 -69
  59. package/dist/goal/goalReport.d.ts +54 -0
  60. package/dist/goal/goalReport.js +197 -0
  61. package/dist/goal/goalStatus.d.ts +8 -0
  62. package/dist/goal/goalStatus.js +10 -3
  63. package/dist/goal/goalStore.d.ts +9 -2
  64. package/dist/goal/goalStore.js +152 -45
  65. package/dist/goal/handoffExport.js +6 -6
  66. package/dist/goal/specKitImport.d.ts +63 -0
  67. package/dist/goal/specKitImport.js +221 -0
  68. package/dist/goal/subgoalSync.js +13 -10
  69. package/dist/goal/worktreeManager.d.ts +2 -0
  70. package/dist/goal/worktreeManager.js +209 -136
  71. package/dist/httpServer.js +126 -44
  72. package/dist/index.js +7 -4
  73. package/dist/logging.d.ts +7 -1
  74. package/dist/logging.js +13 -3
  75. package/dist/policy/projectPolicy.js +5 -2
  76. package/dist/release/releaseGate.d.ts +1 -1
  77. package/dist/release/releaseGate.js +78 -25
  78. package/dist/runner/agentInvocation.d.ts +14 -0
  79. package/dist/runner/agentInvocation.js +96 -4
  80. package/dist/runner/changeCapture.d.ts +14 -6
  81. package/dist/runner/changeCapture.js +287 -81
  82. package/dist/runner/cli.js +7 -6
  83. package/dist/runner/postTaskCleanup.js +60 -7
  84. package/dist/runner/processSecurity.d.ts +56 -0
  85. package/dist/runner/processSecurity.js +373 -0
  86. package/dist/runner/runTask.d.ts +1 -1
  87. package/dist/runner/runTask.js +339 -287
  88. package/dist/runner/simpleProcess.d.ts +3 -0
  89. package/dist/runner/simpleProcess.js +52 -38
  90. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  91. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  92. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
  93. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  94. package/dist/runner/taskStatusStore.d.ts +11 -0
  95. package/dist/runner/taskStatusStore.js +24 -0
  96. package/dist/runner/watch.d.ts +14 -1
  97. package/dist/runner/watch.js +265 -54
  98. package/dist/security/contentRedaction.d.ts +6 -0
  99. package/dist/security/contentRedaction.js +24 -2
  100. package/dist/security/discoveryTokenStore.d.ts +3 -2
  101. package/dist/security/discoveryTokenStore.js +17 -2
  102. package/dist/security/loopbackHost.d.ts +2 -0
  103. package/dist/security/loopbackHost.js +16 -0
  104. package/dist/security/pathGuard.js +32 -17
  105. package/dist/security/planGuard.js +2 -1
  106. package/dist/security/runtimeGuard.js +9 -6
  107. package/dist/security/secretComparison.d.ts +3 -0
  108. package/dist/security/secretComparison.js +11 -0
  109. package/dist/security/sensitiveGuard.d.ts +5 -0
  110. package/dist/security/sensitiveGuard.js +27 -8
  111. package/dist/security/toolInvocationGuard.d.ts +2 -2
  112. package/dist/security/toolInvocationGuard.js +1 -1
  113. package/dist/security/workspaceRootGuard.d.ts +8 -0
  114. package/dist/security/workspaceRootGuard.js +47 -0
  115. package/dist/smoke-test.js +280 -273
  116. package/dist/tools/catalog/index.d.ts +4 -0
  117. package/dist/tools/catalog/index.js +4 -0
  118. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  119. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  120. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +28 -15
  121. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  122. package/dist/tools/definitions/toolDefs.js +1177 -0
  123. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  124. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  125. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +8 -63
  126. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +111 -37
  127. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
  128. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +10 -10
  129. package/dist/tools/diagnostics/index.d.ts +6 -0
  130. package/dist/tools/diagnostics/index.js +6 -0
  131. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  132. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  133. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +14 -10
  134. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +25 -15
  135. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  136. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  137. package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +1 -1
  138. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +8 -8
  139. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +2 -2
  140. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  141. package/dist/tools/direct/index.d.ts +3 -0
  142. package/dist/tools/direct/index.js +3 -0
  143. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  144. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  145. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  146. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  147. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  148. package/dist/tools/discovery/index.d.ts +3 -0
  149. package/dist/tools/discovery/index.js +3 -0
  150. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  151. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  152. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  153. package/dist/tools/dispatch/coreDispatch.js +283 -0
  154. package/dist/tools/dispatch/diagnosticDispatch.d.ts +12 -0
  155. package/dist/tools/dispatch/diagnosticDispatch.js +80 -0
  156. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  157. package/dist/tools/dispatch/directDispatch.js +116 -0
  158. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  159. package/dist/tools/dispatch/goalDispatch.js +92 -0
  160. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  161. package/dist/tools/dispatch/releaseDispatch.js +46 -0
  162. package/dist/tools/dispatch/types.d.ts +23 -0
  163. package/dist/tools/dispatch/types.js +15 -0
  164. package/dist/tools/dispatch/validation.d.ts +7 -0
  165. package/dist/tools/dispatch/validation.js +71 -0
  166. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  167. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +2 -2
  168. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  169. package/dist/tools/goals/index.d.ts +3 -0
  170. package/dist/tools/goals/index.js +3 -0
  171. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  172. package/dist/tools/registry.d.ts +10 -14
  173. package/dist/tools/registry.js +47 -1651
  174. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  175. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  176. package/dist/tools/release/index.d.ts +2 -0
  177. package/dist/tools/release/index.js +2 -0
  178. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  179. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  180. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  181. package/dist/tools/tasks/cancelTask.js +85 -0
  182. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  183. package/dist/tools/{createTask.js → tasks/createTask.js} +46 -31
  184. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  185. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
  186. package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +6 -0
  187. package/dist/tools/tasks/evidencePack.js +387 -0
  188. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  189. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  190. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
  191. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  192. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
  193. package/dist/tools/tasks/index.d.ts +18 -0
  194. package/dist/tools/tasks/index.js +18 -0
  195. package/dist/tools/tasks/killTask.d.ts +1 -0
  196. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  197. package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
  198. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
  199. package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
  200. package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +2 -2
  201. package/dist/tools/tasks/retryTask.js +60 -0
  202. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  203. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +59 -31
  204. package/dist/tools/tasks/runVerification.js +38 -0
  205. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  206. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  207. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  208. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  209. package/dist/tools/workspace/applyPatch.js +53 -0
  210. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  211. package/dist/tools/workspace/index.d.ts +10 -0
  212. package/dist/tools/workspace/index.js +10 -0
  213. package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
  214. package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
  215. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  216. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  217. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  218. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  219. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  220. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  221. package/dist/tools/workspace/syncFile.js +134 -0
  222. package/dist/utils/atomicFile.d.ts +7 -0
  223. package/dist/utils/atomicFile.js +48 -0
  224. package/dist/utils/boundedFile.d.ts +8 -0
  225. package/dist/utils/boundedFile.js +93 -0
  226. package/dist/utils/lockedJsonFile.d.ts +15 -0
  227. package/dist/utils/lockedJsonFile.js +224 -0
  228. package/dist/utils/platform.d.ts +11 -0
  229. package/dist/utils/platform.js +26 -0
  230. package/dist/utils/stableJson.d.ts +2 -0
  231. package/dist/utils/stableJson.js +12 -0
  232. package/dist/version.d.ts +3 -2
  233. package/dist/version.js +3 -2
  234. package/dist/watcherStatus.d.ts +2 -2
  235. package/dist/watcherStatus.js +18 -0
  236. package/docs/CODE_WIKI.md +1648 -0
  237. package/docs/agentseal-integration.md +150 -0
  238. package/docs/architecture.md +23 -0
  239. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  240. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  241. package/docs/control-center/README.md +4 -4
  242. package/docs/dashboard-overview.md +86 -0
  243. package/docs/demo.md +8 -0
  244. package/docs/desktop-app.md +175 -0
  245. package/docs/direct-session-workflow.md +98 -0
  246. package/docs/evidence-pack-schema.md +215 -0
  247. package/docs/lineage-evidence-pack-workflow.md +127 -0
  248. package/docs/mcp-inspector-testing.md +200 -0
  249. package/docs/open-source-application.md +168 -0
  250. package/docs/opencode-worker.md +151 -0
  251. package/docs/openhands-worker.md +181 -0
  252. package/docs/release-checklist.md +1 -1
  253. package/docs/release-evidence.md +82 -0
  254. package/docs/spec-kit-integration.md +131 -0
  255. package/docs/task-safe-review-workflow.md +98 -0
  256. package/docs/threat-model.md +97 -0
  257. package/docs/user-feedback.md +40 -0
  258. package/docs/why-patchwarden.md +110 -0
  259. package/examples/config.example.json +4 -2
  260. package/package.json +28 -11
  261. package/scripts/README.md +15 -6
  262. package/scripts/build.js +11 -0
  263. package/scripts/checks/brand-check.js +3 -0
  264. package/scripts/checks/build-output-check.js +37 -0
  265. package/scripts/checks/control-center-smoke.js +447 -6
  266. package/scripts/checks/control-smoke.js +19 -8
  267. package/scripts/checks/http-mcp-smoke.js +82 -4
  268. package/scripts/checks/lifecycle-smoke.js +39 -33
  269. package/scripts/checks/mcp-manifest-check.js +1 -1
  270. package/scripts/checks/mcp-smoke.js +30 -4
  271. package/scripts/checks/package-manifest-check.js +20 -7
  272. package/scripts/checks/unit-tests.js +3 -5
  273. package/scripts/checks/watcher-supervisor-smoke.js +9 -0
  274. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  275. package/scripts/control/manage-patchwarden.ps1 +69 -4
  276. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  277. package/scripts/control/run-background-supervisor.ps1 +41 -0
  278. package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
  279. package/scripts/generate-demo-gif.py +320 -0
  280. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  281. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  282. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  283. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  284. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  285. package/scripts/lib/clean-generated-output.js +11 -0
  286. package/scripts/lib/file-discovery.js +12 -0
  287. package/scripts/release/desktop-preflight.js +211 -0
  288. package/scripts/release/pack-clean.js +59 -14
  289. package/src/assessments/agentAssessor.ts +29 -15
  290. package/src/assessments/assessmentStore.ts +55 -43
  291. package/src/assessments/confirmCli.ts +5 -4
  292. package/src/config.ts +105 -11
  293. package/src/control/fileManager.ts +40 -0
  294. package/src/control/middleware/auth.ts +35 -0
  295. package/src/control/middleware/static.ts +84 -0
  296. package/src/control/routeTable.ts +301 -0
  297. package/src/control/routes/audit.ts +329 -0
  298. package/src/control/routes/evidence.ts +107 -0
  299. package/src/control/routes/lineage.ts +92 -0
  300. package/src/control/routes/policy.ts +81 -0
  301. package/src/control/routes/process.ts +505 -0
  302. package/src/control/routes/sessions.ts +314 -0
  303. package/src/control/routes/status.ts +405 -0
  304. package/src/control/routes/taskActions.ts +214 -0
  305. package/src/control/routes/tasks.ts +331 -0
  306. package/src/control/routes/workspace.ts +234 -0
  307. package/src/control/runtime.ts +508 -0
  308. package/src/control/server.ts +165 -0
  309. package/src/control/shared.ts +360 -0
  310. package/src/controlCenter.ts +7 -2347
  311. package/src/direct/directAudit.ts +17 -9
  312. package/src/direct/directGuards.ts +100 -51
  313. package/src/direct/directPatch.ts +95 -8
  314. package/src/direct/directSessionStore.ts +211 -55
  315. package/src/direct/directVerification.ts +9 -23
  316. package/src/doctor.ts +766 -492
  317. package/src/goal/acceptanceEngine.ts +1 -1
  318. package/src/goal/goalProgress.ts +17 -13
  319. package/src/goal/goalReport.ts +263 -0
  320. package/src/goal/goalStatus.ts +25 -3
  321. package/src/goal/goalStore.ts +241 -54
  322. package/src/goal/handoffExport.ts +6 -6
  323. package/src/goal/specKitImport.ts +355 -0
  324. package/src/goal/subgoalSync.ts +15 -10
  325. package/src/goal/worktreeManager.ts +139 -22
  326. package/src/httpServer.ts +130 -44
  327. package/src/index.ts +7 -4
  328. package/src/logging.ts +20 -4
  329. package/src/policy/projectPolicy.ts +5 -2
  330. package/src/release/releaseGate.ts +74 -27
  331. package/src/runner/agentInvocation.ts +126 -4
  332. package/src/runner/changeCapture.ts +342 -80
  333. package/src/runner/cli.ts +7 -6
  334. package/src/runner/postTaskCleanup.ts +55 -7
  335. package/src/runner/processSecurity.ts +433 -0
  336. package/src/runner/runTask.ts +441 -292
  337. package/src/runner/simpleProcess.ts +68 -33
  338. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  339. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
  340. package/src/runner/taskStatusStore.ts +54 -0
  341. package/src/runner/watch.ts +290 -57
  342. package/src/security/contentRedaction.ts +31 -2
  343. package/src/security/discoveryTokenStore.ts +18 -4
  344. package/src/security/loopbackHost.ts +19 -0
  345. package/src/security/pathGuard.ts +35 -16
  346. package/src/security/planGuard.ts +2 -1
  347. package/src/security/runtimeGuard.ts +9 -6
  348. package/src/security/secretComparison.ts +13 -0
  349. package/src/security/sensitiveGuard.ts +27 -8
  350. package/src/security/toolInvocationGuard.ts +2 -2
  351. package/src/security/workspaceRootGuard.ts +53 -0
  352. package/src/smoke-test.ts +276 -273
  353. package/src/tools/catalog/index.ts +4 -0
  354. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  355. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +29 -16
  356. package/src/tools/definitions/toolDefs.ts +1264 -0
  357. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  358. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +196 -89
  359. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +11 -11
  360. package/src/tools/diagnostics/index.ts +6 -0
  361. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  362. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +29 -19
  363. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  364. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +9 -9
  365. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +11 -9
  366. package/src/tools/direct/index.ts +3 -0
  367. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  368. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  369. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  370. package/src/tools/discovery/index.ts +3 -0
  371. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  372. package/src/tools/dispatch/coreDispatch.ts +379 -0
  373. package/src/tools/dispatch/diagnosticDispatch.ts +106 -0
  374. package/src/tools/dispatch/directDispatch.ts +168 -0
  375. package/src/tools/dispatch/goalDispatch.ts +128 -0
  376. package/src/tools/dispatch/releaseDispatch.ts +66 -0
  377. package/src/tools/dispatch/types.ts +24 -0
  378. package/src/tools/dispatch/validation.ts +78 -0
  379. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  380. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +43 -28
  381. package/src/tools/goals/index.ts +3 -0
  382. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  383. package/src/tools/registry.ts +54 -1858
  384. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  385. package/src/tools/release/index.ts +2 -0
  386. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  387. package/src/tools/tasks/cancelTask.ts +109 -0
  388. package/src/tools/{createTask.ts → tasks/createTask.ts} +56 -33
  389. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
  390. package/src/tools/tasks/evidencePack.ts +504 -0
  391. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  392. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
  393. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  394. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
  395. package/src/tools/tasks/index.ts +18 -0
  396. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
  397. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
  398. package/src/tools/tasks/retryTask.ts +69 -0
  399. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +70 -35
  400. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  401. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  402. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  403. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  404. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  405. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  406. package/src/tools/workspace/index.ts +10 -0
  407. package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
  408. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  409. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  410. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  411. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  412. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  413. package/src/tools/workspace/syncFile.ts +292 -0
  414. package/src/utils/atomicFile.ts +58 -0
  415. package/src/utils/boundedFile.ts +116 -0
  416. package/src/utils/lockedJsonFile.ts +251 -0
  417. package/src/utils/platform.ts +31 -0
  418. package/src/utils/stableJson.ts +12 -0
  419. package/src/version.ts +3 -2
  420. package/src/watcherStatus.ts +19 -1
  421. package/ui/desktop-bootstrap.js +11 -0
  422. package/ui/desktop-bridge.js +36 -0
  423. package/ui/desktop.css +160 -0
  424. package/ui/getting-started.js +93 -0
  425. package/ui/i18n.js +484 -0
  426. package/ui/log-parser.js +30 -0
  427. package/ui/pages/audit.html +204 -3
  428. package/ui/pages/dashboard.html +937 -79
  429. package/ui/pages/direct-sessions.html +609 -60
  430. package/ui/pages/getting-started.html +43 -0
  431. package/ui/pages/logs.html +105 -22
  432. package/ui/pages/settings.html +101 -0
  433. package/ui/pages/task-detail.html +468 -438
  434. package/ui/pages/tasks.html +648 -65
  435. package/ui/pages/workspace.html +38 -1
  436. package/ui/settings.js +245 -0
  437. package/PatchWarden-Control.cmd +0 -6
  438. package/Restart-PatchWarden-Control.cmd +0 -6
  439. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  440. package/dist/test/unit/acceptance-engine.test.js +0 -228
  441. package/dist/test/unit/agent-router.test.d.ts +0 -1
  442. package/dist/test/unit/agent-router.test.js +0 -287
  443. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  444. package/dist/test/unit/android-doctor.test.js +0 -118
  445. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  446. package/dist/test/unit/audit-checks.test.js +0 -350
  447. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  448. package/dist/test/unit/chinese-path.test.js +0 -91
  449. package/dist/test/unit/command-guard.test.d.ts +0 -1
  450. package/dist/test/unit/command-guard.test.js +0 -160
  451. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  452. package/dist/test/unit/diagnose-task.test.js +0 -457
  453. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  454. package/dist/test/unit/direct-guards.test.js +0 -213
  455. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  456. package/dist/test/unit/discovery-token-store.test.js +0 -139
  457. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  458. package/dist/test/unit/evidence-pack.test.js +0 -130
  459. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  460. package/dist/test/unit/goal-graph.test.js +0 -298
  461. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  462. package/dist/test/unit/goal-progress.test.js +0 -381
  463. package/dist/test/unit/goal-status.test.d.ts +0 -1
  464. package/dist/test/unit/goal-status.test.js +0 -215
  465. package/dist/test/unit/goal-store.test.d.ts +0 -1
  466. package/dist/test/unit/goal-store.test.js +0 -253
  467. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  468. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  469. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  470. package/dist/test/unit/goal-tools-registry.test.js +0 -190
  471. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  472. package/dist/test/unit/handoff-export.test.js +0 -263
  473. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  474. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  475. package/dist/test/unit/logging.test.d.ts +0 -1
  476. package/dist/test/unit/logging.test.js +0 -397
  477. package/dist/test/unit/path-guard.test.d.ts +0 -1
  478. package/dist/test/unit/path-guard.test.js +0 -109
  479. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  480. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  481. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  482. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  483. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  484. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  485. package/dist/test/unit/release-gate.test.d.ts +0 -1
  486. package/dist/test/unit/release-gate.test.js +0 -242
  487. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  488. package/dist/test/unit/run-task-loop.test.js +0 -380
  489. package/dist/test/unit/safe-status.test.d.ts +0 -1
  490. package/dist/test/unit/safe-status.test.js +0 -165
  491. package/dist/test/unit/safe-views.test.d.ts +0 -1
  492. package/dist/test/unit/safe-views.test.js +0 -171
  493. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  494. package/dist/test/unit/schema-drift-check.test.js +0 -176
  495. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  496. package/dist/test/unit/sensitive-guard.test.js +0 -104
  497. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  498. package/dist/test/unit/subgoal-sync.test.js +0 -183
  499. package/dist/test/unit/sync-file.test.d.ts +0 -1
  500. package/dist/test/unit/sync-file.test.js +0 -154
  501. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  502. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  503. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  504. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  505. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  506. package/dist/test/unit/toolSearch.test.js +0 -571
  507. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  508. package/dist/test/unit/watcher-status.test.js +0 -169
  509. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  510. package/dist/test/unit/worktree-manager.test.js +0 -176
  511. package/dist/tools/applyPatch.js +0 -41
  512. package/dist/tools/cancelTask.d.ts +0 -30
  513. package/dist/tools/cancelTask.js +0 -64
  514. package/dist/tools/evidencePack.js +0 -168
  515. package/dist/tools/finalizeDirectSession.js +0 -84
  516. package/dist/tools/goalSubgoalTask.js +0 -110
  517. package/dist/tools/killTask.d.ts +0 -15
  518. package/dist/tools/retryTask.js +0 -45
  519. package/dist/tools/runVerification.js +0 -36
  520. package/dist/tools/syncFile.js +0 -65
  521. package/docs/control-center/control-center-daily-driver.md +0 -211
  522. package/docs/control-center/control-center-mvp.md +0 -205
  523. package/docs/control-center/control-center-phase2.md +0 -159
  524. package/docs/release-v0.6.0.md +0 -71
  525. package/docs/release-v0.6.1.md +0 -75
  526. package/docs/release-v0.6.4.md +0 -45
  527. package/src/test/unit/acceptance-engine.test.ts +0 -261
  528. package/src/test/unit/agent-router.test.ts +0 -342
  529. package/src/test/unit/android-doctor.test.ts +0 -158
  530. package/src/test/unit/audit-checks.test.ts +0 -567
  531. package/src/test/unit/chinese-path.test.ts +0 -106
  532. package/src/test/unit/command-guard.test.ts +0 -221
  533. package/src/test/unit/diagnose-task.test.ts +0 -544
  534. package/src/test/unit/direct-guards.test.ts +0 -297
  535. package/src/test/unit/discovery-token-store.test.ts +0 -181
  536. package/src/test/unit/evidence-pack.test.ts +0 -142
  537. package/src/test/unit/goal-graph.test.ts +0 -347
  538. package/src/test/unit/goal-progress.test.ts +0 -538
  539. package/src/test/unit/goal-status.test.ts +0 -270
  540. package/src/test/unit/goal-store.test.ts +0 -318
  541. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  542. package/src/test/unit/goal-tools-registry.test.ts +0 -243
  543. package/src/test/unit/handoff-export.test.ts +0 -295
  544. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  545. package/src/test/unit/logging.test.ts +0 -497
  546. package/src/test/unit/path-guard.test.ts +0 -150
  547. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  548. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  549. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  550. package/src/test/unit/release-gate.test.ts +0 -314
  551. package/src/test/unit/run-task-loop.test.ts +0 -425
  552. package/src/test/unit/safe-status.test.ts +0 -187
  553. package/src/test/unit/safe-views.test.ts +0 -184
  554. package/src/test/unit/schema-drift-check.test.ts +0 -260
  555. package/src/test/unit/sensitive-guard.test.ts +0 -124
  556. package/src/test/unit/subgoal-sync.test.ts +0 -236
  557. package/src/test/unit/sync-file.test.ts +0 -231
  558. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  559. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  560. package/src/test/unit/toolSearch.test.ts +0 -756
  561. package/src/test/unit/watcher-status.test.ts +0 -190
  562. package/src/test/unit/worktree-manager.test.ts +0 -247
  563. package/src/tools/cancelTask.ts +0 -70
  564. package/src/tools/evidencePack.ts +0 -205
  565. package/src/tools/retryTask.ts +0 -53
  566. package/src/tools/syncFile.ts +0 -122
  567. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  568. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  569. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  570. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  571. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  572. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  573. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  574. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  575. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  576. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  577. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  578. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  579. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  580. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  581. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  582. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  583. /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
  584. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  585. /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
  586. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  587. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  588. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  589. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  590. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  591. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  592. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  593. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  594. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  595. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  596. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
  597. /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
@@ -135,6 +135,8 @@ html.dark {
135
135
  .pw-spin { animation: pw-spin 1s linear infinite; }
136
136
  @keyframes pw-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
137
137
  </style>
138
+ <script src="/desktop-bootstrap.js"></script>
139
+ <link rel="stylesheet" href="/desktop.css">
138
140
  </head>
139
141
  <body class="flex h-screen overflow-hidden">
140
142
 
@@ -152,6 +154,10 @@ html.dark {
152
154
 
153
155
  <!-- Navigation -->
154
156
  <nav class="flex-1 flex flex-col gap-1 px-3 pt-4 overflow-y-auto">
157
+ <a href="/pages/getting-started.html" data-nav-key="getting-started" class="desktop-only-nav flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
158
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="shrink-0"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.87 12.87 0 0 1 22 2c0 2.72-.78 7.5-6.05 11a22.35 22.35 0 0 1-3.95 2z"/></svg>
159
+ <span class="truncate" data-i18n="nav.gettingStarted">开始使用</span>
160
+ </a>
155
161
 
156
162
  <a href="/pages/dashboard.html" data-nav-key="dashboard" data-dom-id="nav-dashboard" class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
157
163
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0">
@@ -207,6 +213,10 @@ html.dark {
207
213
  <span class="truncate" data-zh="日志" data-en="Logs">日志</span>
208
214
  </a>
209
215
 
216
+ <a href="/pages/settings.html" data-nav-key="settings" class="desktop-only-nav flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
217
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="shrink-0"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1A1.7 1.7 0 0 0 9 4.6 1.7 1.7 0 0 0 10 3V2.8h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9A1.7 1.7 0 0 0 21 10h.2v4H21a1.7 1.7 0 0 0-1.6 1z"/></svg>
218
+ <span class="truncate" data-i18n="nav.settings">设置</span>
219
+ </a>
210
220
  </nav>
211
221
 
212
222
  <!-- Bottom Section -->
@@ -372,176 +382,192 @@ html.dark {
372
382
  </div>
373
383
  </section>
374
384
 
375
- <!-- Two-Column Layout -->
376
- <div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
377
-
378
- <!-- Left Column (~58%) -->
379
- <div class="lg:col-span-7 flex flex-col gap-6">
380
-
381
- <!-- Execution Status Card -->
382
- <section class="rounded-lg p-5" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
383
- <h2 class="text-base font-semibold mb-4" data-zh="执行状态" data-en="Execution Status" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">执行状态</h2>
384
- <div class="grid grid-cols-1 sm:grid-cols-3 gap-x-6 gap-y-3 text-sm">
385
- <div class="flex flex-col gap-0.5 min-w-0">
386
- <span class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary);" data-zh="运行阶段" data-en="Runtime Phase">运行阶段</span>
387
- <span id="rtPhase" class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</span>
388
- </div>
389
- <div class="flex flex-col gap-0.5 min-w-0">
390
- <span class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary);" data-zh="最近心跳" data-en="Last Heartbeat">最近心跳</span>
391
- <span id="rtHeartbeat" class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
392
- </div>
393
- <div class="flex flex-col gap-0.5 min-w-0">
394
- <span class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary);" data-zh="当前命令" data-en="Current Command">当前命令</span>
395
- <span id="rtCurrentCmd" class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
396
- </div>
397
- </div>
398
- </section>
385
+ <!-- ===== Safe Summary Section (DEFAULT visible, loaded from 4 safe endpoints) ===== -->
386
+ <section class="rounded-lg p-5" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
387
+ <div class="flex items-center gap-2 mb-1">
388
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-accent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
389
+ <h2 class="text-base font-semibold" data-zh="安全摘要" data-en="Safe Summary" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">安全摘要</h2>
390
+ <span class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-accent); background-color: var(--pw-accent-subtle); font-family: var(--pw-font-mono);">safe-first</span>
391
+ </div>
392
+ <p class="text-xs mb-5" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="默认仅展示脱敏后的安全摘要。完整结果、diff、测试日志请见下方「高级」区域。" data-en="Shows redacted safe summaries by default. Full result/diff/test log are in the Advanced section below.">默认仅展示脱敏后的安全摘要。完整结果、diff、测试日志请见下方「高级」区域。</p>
393
+
394
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-5">
399
395
 
400
- <!-- Diff Preview Card -->
401
- <section class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
402
- <div class="flex items-center gap-2 px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
403
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-secondary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>
404
- <h2 class="text-base font-semibold" data-zh="变更预览" data-en="Diff Preview" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">变更预览</h2>
396
+ <!-- Safe Result Overview -->
397
+ <div class="rounded-lg p-4 flex flex-col gap-3" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle);">
398
+ <div class="flex items-center justify-between">
399
+ <h3 class="text-sm font-semibold" data-zh="结果概览" data-en="Safe Result" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">结果概览 (safe_result)</h3>
400
+ <span id="srStatusBadge" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
405
401
  </div>
406
- <div id="diffEmpty" class="px-4 py-8 text-center text-sm" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="暂无 diff 内容" data-en="No diff available">暂无 diff 内容</div>
407
- <pre id="diffContent" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 480px; overflow-y: auto;"></pre>
408
- </section>
409
-
410
- <!-- Changed Files Card -->
411
- <section class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
412
- <div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
413
- <div class="flex items-center gap-2">
414
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-secondary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
415
- <h2 class="text-base font-semibold" data-zh="变更文件" data-en="Changed Files" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">变更文件</h2>
416
- </div>
417
- <span id="cfCountBadge" class="hidden text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
402
+ <dl class="grid grid-cols-2 gap-x-4 gap-y-2 text-xs">
403
+ <div class="flex flex-col gap-0.5 min-w-0"><dt style="color: var(--pw-text-tertiary);">terminal</dt><dd id="srTerminal" class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
404
+ <div class="flex flex-col gap-0.5 min-w-0"><dt style="color: var(--pw-text-tertiary);">acceptance_status</dt><dd id="srAcceptance" class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
405
+ <div class="flex flex-col gap-0.5 min-w-0"><dt style="color: var(--pw-text-tertiary);">changed_files_total</dt><dd id="srChangedTotal" class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
406
+ <div class="flex flex-col gap-0.5 min-w-0"><dt style="color: var(--pw-text-tertiary);">out_of_scope_changes_total</dt><dd id="srOosTotal" class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
407
+ </dl>
408
+ <div class="flex flex-col gap-0.5 min-w-0">
409
+ <span class="text-xs" style="color: var(--pw-text-tertiary);">verification.headline</span>
410
+ <span id="srVerifyHeadline" class="text-xs break-words" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
418
411
  </div>
419
- <div id="cfEmpty" class="px-4 py-8 text-center text-sm" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="暂无变更文件记录" data-en="No changed-files record">暂无变更文件记录</div>
420
- <div id="cfBody" class="hidden px-4 py-3 flex flex-col gap-3">
421
- <!-- file_stats summary row -->
422
- <div id="cfStatsRow" class="hidden grid grid-cols-2 sm:grid-cols-4 gap-2 text-xs"></div>
423
- <!-- changed files list (truncated) -->
424
- <div id="cfList" class="rounded-md p-2 text-xs leading-relaxed overflow-auto" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 320px;"></div>
412
+ <div id="srFailureBlock" class="hidden flex flex-col gap-1">
413
+ <div class="flex flex-col gap-0.5 min-w-0"><span class="text-xs" style="color: var(--pw-state-error);">failure_reason</span><span id="srFailureReason" class="text-xs break-words" style="color: var(--pw-state-error); font-family: var(--pw-font-mono);">—</span></div>
414
+ <div class="flex flex-col gap-0.5 min-w-0"><span class="text-xs" style="color: var(--pw-state-error);">failed_command</span><span id="srFailedCommand" class="text-xs break-all" style="color: var(--pw-state-error); font-family: var(--pw-font-mono);">—</span></div>
425
415
  </div>
426
- </section>
427
-
428
- <!-- Verify Log Card -->
429
- <section class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
430
- <div class="flex items-center gap-2 px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
431
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-secondary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
432
- <h2 class="text-base font-semibold" data-zh="验证日志" data-en="Verify Log" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">验证日志</h2>
416
+ <div class="flex flex-col gap-0.5 min-w-0">
417
+ <span class="text-xs" style="color: var(--pw-text-tertiary);">next_action</span>
418
+ <span id="srNextAction" class="text-xs break-words" style="color: var(--pw-accent); font-family: var(--pw-font-mono);">—</span>
419
+ </div>
420
+ <div id="srBlockedBlock" class="hidden flex flex-col gap-0.5 min-w-0">
421
+ <span class="text-xs" style="color: var(--pw-state-warning);">execution_blocked / pending_reason</span>
422
+ <span id="srPendingReason" class="text-xs break-words" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">—</span>
433
423
  </div>
434
- <div id="verifyLogEmpty" class="px-4 py-8 text-center text-sm" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="暂无验证日志" data-en="No verify log">暂无验证日志</div>
435
- <pre id="verifyLogContent" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 320px; overflow-y: auto;"></pre>
436
- </section>
424
+ <div id="srWarningsBlock" class="hidden flex flex-col gap-1 min-w-0">
425
+ <span class="text-xs font-medium" style="color: var(--pw-state-warning);">warnings</span>
426
+ <pre id="srWarnings" class="rounded-md p-2 text-xs leading-relaxed whitespace-pre-wrap break-all" style="background-color: var(--pw-state-warning-bg); color: var(--pw-state-warning); font-family: var(--pw-font-mono); max-height: 160px; overflow-y: auto;"></pre>
427
+ </div>
428
+ </div>
437
429
 
438
- </div>
430
+ <!-- Safe Test Summary -->
431
+ <div class="rounded-lg p-4 flex flex-col gap-3" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle);">
432
+ <div class="flex items-center justify-between">
433
+ <h3 class="text-sm font-semibold" data-zh="测试摘要" data-en="Safe Test Summary" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">测试摘要 (safe_test_summary)</h3>
434
+ <span id="stStatusBadge" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
435
+ </div>
436
+ <dl class="grid grid-cols-4 gap-x-3 gap-y-2 text-xs">
437
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">commands</dt><dd id="stCmdCount" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
438
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">passed</dt><dd id="stPassed" style="color: var(--pw-state-success); font-family: var(--pw-font-mono);">—</dd></div>
439
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">failed</dt><dd id="stFailed" style="color: var(--pw-state-error); font-family: var(--pw-font-mono);">—</dd></div>
440
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">skipped</dt><dd id="stSkipped" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</dd></div>
441
+ </dl>
442
+ <div class="flex flex-col gap-0.5 min-w-0">
443
+ <span class="text-xs" style="color: var(--pw-text-tertiary);">headline</span>
444
+ <span id="stHeadline" class="text-xs break-words" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
445
+ </div>
446
+ <div class="flex flex-col gap-1 min-w-0">
447
+ <span class="text-xs" style="color: var(--pw-text-tertiary);">commands</span>
448
+ <div id="stCommandsBody" class="rounded-md p-2 text-xs leading-relaxed overflow-auto" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 220px;"></div>
449
+ </div>
450
+ </div>
439
451
 
440
- <!-- Right Column (~42%) -->
441
- <div class="lg:col-span-5 flex flex-col gap-6">
442
-
443
- <!-- Result Summary Card -->
444
- <section class="rounded-lg p-5" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
445
- <h2 class="text-base font-semibold mb-4" data-zh="结果摘要" data-en="Result Summary" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">结果摘要</h2>
446
- <div class="flex flex-col gap-3">
447
- <div class="flex items-center justify-between text-sm">
448
- <span style="color: var(--pw-text-secondary);" data-zh="任务状态" data-en="Status">任务状态</span>
449
- <span id="rsStatus" class="font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</span>
450
- </div>
451
- <div class="flex items-center justify-between text-sm">
452
- <span style="color: var(--pw-text-secondary);" data-zh="验证状态" data-en="Verify Status">验证状态</span>
453
- <span id="rsVerifyStatus" class="font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</span>
454
- </div>
455
- <div id="rsSummaryBlock" class="hidden rounded-md p-3 text-xs leading-relaxed" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle); color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
456
- <span id="rsSummary" class="break-all whitespace-pre-wrap"></span>
457
- </div>
452
+ <!-- Safe Diff Summary -->
453
+ <div class="rounded-lg p-4 flex flex-col gap-3" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle);">
454
+ <div class="flex items-center justify-between">
455
+ <h3 class="text-sm font-semibold" data-zh="变更摘要" data-en="Safe Diff Summary" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">变更摘要 (safe_diff_summary)</h3>
456
+ <span id="sdPatchMode" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
457
+ </div>
458
+ <dl class="grid grid-cols-4 gap-x-3 gap-y-2 text-xs">
459
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">files</dt><dd id="sdChangedTotal" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
460
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">additions</dt><dd id="sdAdditions" style="color: var(--pw-state-success); font-family: var(--pw-font-mono);">—</dd></div>
461
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">deletions</dt><dd id="sdDeletions" style="color: var(--pw-state-error); font-family: var(--pw-font-mono);">—</dd></div>
462
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">diff_available</dt><dd id="sdDiffAvailable" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</dd></div>
463
+ </dl>
464
+ <div id="sdTruncatedBlock" class="hidden text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠ diff_truncated / large_diff_omitted</div>
465
+ <div class="flex flex-col gap-1 min-w-0">
466
+ <span class="text-xs" style="color: var(--pw-text-tertiary);">files (path · change · kind)</span>
467
+ <div id="sdFilesBody" class="rounded-md p-2 text-xs leading-relaxed overflow-auto" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 220px;"></div>
468
+ </div>
469
+ <div id="sdHygieneBlock" class="hidden flex flex-col gap-0.5 min-w-0">
470
+ <span class="text-xs" style="color: var(--pw-text-tertiary);">artifact_hygiene_counts</span>
471
+ <pre id="sdHygiene" class="text-xs whitespace-pre-wrap break-all" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);"></pre>
458
472
  </div>
459
- </section>
473
+ </div>
460
474
 
461
- <!-- Audit Results Card -->
462
- <section class="rounded-lg p-5" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
463
- <div class="flex items-center justify-between mb-4">
464
- <h2 class="text-base font-semibold" data-zh="审计结果" data-en="Audit Result" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">审计结果</h2>
465
- <span id="auditVerdictBadge" class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium whitespace-nowrap" style="background-color: var(--pw-bg-hover); color: var(--pw-text-tertiary); border: 1px solid var(--pw-border-subtle);">—</span>
475
+ <!-- Safe Audit -->
476
+ <div class="rounded-lg p-4 flex flex-col gap-3" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle);">
477
+ <div class="flex items-center justify-between">
478
+ <h3 class="text-sm font-semibold" data-zh="审计结论" data-en="Safe Audit" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">审计结论 (safe_audit)</h3>
479
+ <span id="saVerdictBadge" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
480
+ </div>
481
+ <dl class="grid grid-cols-3 gap-x-3 gap-y-2 text-xs">
482
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">pass</dt><dd id="saCountsPass" style="color: var(--pw-state-success); font-family: var(--pw-font-mono);">—</dd></div>
483
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">warn</dt><dd id="saCountsWarn" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">—</dd></div>
484
+ <div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">fail</dt><dd id="saCountsFail" style="color: var(--pw-state-error); font-family: var(--pw-font-mono);">—</dd></div>
485
+ </dl>
486
+ <div class="flex flex-col gap-0.5 min-w-0">
487
+ <span class="text-xs" style="color: var(--pw-text-tertiary);">acceptance.verdict / status</span>
488
+ <span id="saAccVerdict" class="text-xs break-words" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
489
+ </div>
490
+ <div class="flex flex-col gap-0.5 min-w-0">
491
+ <span class="text-xs" style="color: var(--pw-text-tertiary);">acceptance.reason</span>
492
+ <span id="saAccReason" class="text-xs break-words" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
466
493
  </div>
467
- <div class="flex flex-col gap-3">
468
- <div class="flex items-center justify-between text-sm">
469
- <span style="color: var(--pw-text-secondary);" data-zh="范围变更" data-en="Scope Changes">范围变更</span>
470
- <span id="auditScope" class="font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</span>
471
- </div>
472
- <div class="flex items-center justify-between text-sm">
473
- <span style="color: var(--pw-text-secondary);" data-zh="检查时间" data-en="Checked At">检查时间</span>
474
- <span id="auditCheckedAt" class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
475
- </div>
476
- <div id="auditEmptyHint" class="text-xs" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="暂无审计记录" data-en="No audit record yet">暂无审计记录</div>
494
+ <div class="flex flex-col gap-0.5 min-w-0">
495
+ <span class="text-xs" style="color: var(--pw-text-tertiary);">next_suggested_task</span>
496
+ <span id="saAccNextTask" class="text-xs break-words" style="color: var(--pw-accent); font-family: var(--pw-font-mono);">—</span>
477
497
  </div>
478
- </section>
479
-
480
- <!-- Independent Review Card (audit_task artifact) -->
481
- <section class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
482
- <div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
483
- <div class="flex items-center gap-2">
484
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-secondary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
485
- <h2 class="text-base font-semibold" data-zh="独立审计报告" data-en="Independent Review" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">独立审计报告</h2>
486
- </div>
487
- <span id="reviewVerdictBadge" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
498
+ <div id="saFailChecksBlock" class="hidden flex flex-col gap-0.5 min-w-0">
499
+ <span class="text-xs" style="color: var(--pw-state-error);">fail_checks</span>
500
+ <pre id="saFailChecks" class="rounded-md p-2 text-xs whitespace-pre-wrap break-all" style="background-color: var(--pw-state-error-bg); color: var(--pw-state-error); font-family: var(--pw-font-mono); max-height: 120px; overflow-y: auto;"></pre>
488
501
  </div>
489
- <div id="reviewEmpty" class="px-4 py-8 text-center text-sm" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="尚未运行 audit_task" data-en="audit_task not run yet">尚未运行 audit_task</div>
490
- <pre id="reviewContent" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 320px; overflow-y: auto;"></pre>
491
- </section>
492
-
493
- <!-- Verification Summary Card -->
494
- <section class="rounded-lg p-5" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
495
- <h2 class="text-base font-semibold mb-4" data-zh="验证摘要" data-en="Verification Summary" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">验证摘要</h2>
496
- <div class="flex flex-col gap-3">
497
- <div class="flex items-center justify-between text-sm">
498
- <span style="color: var(--pw-text-secondary);" data-zh="验证状态" data-en="Status">验证状态</span>
499
- <span id="vsStatus" class="font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</span>
500
- </div>
501
- <div class="flex items-center justify-between text-sm">
502
- <span style="color: var(--pw-text-secondary);" data-zh="检查时间" data-en="Checked At">检查时间</span>
503
- <span id="vsCheckedAt" class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
504
- </div>
505
- <div id="vsCommandsBlock" class="hidden rounded-md p-2 text-xs leading-relaxed" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle); color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">
506
- <div class="text-xs mb-1" style="color: var(--pw-text-tertiary);" data-zh="验证命令" data-en="Commands">验证命令</div>
507
- <pre id="vsCommands" class="whitespace-pre-wrap break-all"></pre>
508
- </div>
509
- <div id="vsEmptyHint" class="text-xs" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="暂无验证摘要" data-en="No verification summary">暂无验证摘要</div>
502
+ <div id="saManualBlock" class="hidden text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠ manual_verification_required</div>
503
+ <div id="saActionsBlock" class="hidden flex flex-col gap-0.5 min-w-0">
504
+ <span class="text-xs" style="color: var(--pw-text-tertiary);">recommended_next_actions</span>
505
+ <pre id="saActions" class="text-xs whitespace-pre-wrap break-all" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);"></pre>
510
506
  </div>
511
- </section>
507
+ </div>
508
+
509
+ </div>
510
+ </section>
512
511
 
513
- <!-- Warnings / Errors Card -->
514
- <section class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
515
- <div class="flex items-center gap-2 px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
516
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-secondary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
517
- <h2 class="text-base font-semibold" data-zh="警告 / 错误" data-en="Warnings / Errors" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">警告 / 错误</h2>
512
+ <!-- ===== Advanced Section (collapsed by default; full content loaded ON DEMAND) ===== -->
513
+ <details class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
514
+ <summary class="flex items-center gap-2 px-5 py-4 cursor-pointer select-none" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">
515
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-secondary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
516
+ <span class="text-base font-semibold" data-zh="高级" data-en="Advanced">高级</span>
517
+ <span class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-state-warning); background-color: var(--pw-state-warning-bg); font-family: var(--pw-font-mono);">⚠️ may contain large output</span>
518
+ </summary>
519
+ <div class="flex flex-col gap-4 px-5 pb-5">
520
+ <p class="text-xs" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="以下内容仅在点击时按需加载,可能包含完整 stdout/stderr/diff 等较大输出。" data-en="The following content is loaded on demand only and may contain full stdout/stderr/diff.">以下内容仅在点击时按需加载,可能包含完整 stdout/stderr/diff 等较大输出。</p>
521
+
522
+ <!-- Advanced: full result -->
523
+ <div class="flex flex-col gap-2">
524
+ <div class="flex items-center gap-2 flex-wrap">
525
+ <button id="advBtnResult" class="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors" style="background-color: var(--pw-bg-hover); color: var(--pw-text-primary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">
526
+ <svg id="advSpinnerResult" class="hidden pw-spin" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/></svg>
527
+ <span data-zh="加载完整结果 (get_result)" data-en="Load full result (get_result)">加载完整结果 (get_result)</span>
528
+ </button>
529
+ <span class="text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠️ Advanced view: may contain large output</span>
518
530
  </div>
519
- <div id="weEmpty" class="px-4 py-6 text-center text-sm" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="无警告或错误" data-en="No warnings or errors">无警告或错误</div>
520
- <div id="weBody" class="hidden px-4 py-3 flex flex-col gap-3">
521
- <div id="weWarningsBlock" class="hidden">
522
- <div class="text-xs mb-1.5 font-medium" style="color: var(--pw-state-warning);" data-zh="警告" data-en="Warnings">警告</div>
523
- <div id="weWarnings" class="rounded-md p-2 text-xs leading-relaxed" style="background-color: var(--pw-state-warning-bg); color: var(--pw-state-warning); font-family: var(--pw-font-mono);"></div>
524
- </div>
525
- <div id="weErrorsBlock" class="hidden">
526
- <div class="text-xs mb-1.5 font-medium" style="color: var(--pw-state-error);" data-zh="错误" data-en="Errors">错误</div>
527
- <div id="weErrors" class="rounded-md p-2 text-xs leading-relaxed" style="background-color: var(--pw-state-error-bg); color: var(--pw-state-error); font-family: var(--pw-font-mono);"></div>
528
- </div>
531
+ <pre id="advPreResult" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 520px; overflow-y: auto;"></pre>
532
+ </div>
533
+
534
+ <!-- Advanced: full test log -->
535
+ <div class="flex flex-col gap-2">
536
+ <div class="flex items-center gap-2 flex-wrap">
537
+ <button id="advBtnTestLog" class="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors" style="background-color: var(--pw-bg-hover); color: var(--pw-text-primary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">
538
+ <svg id="advSpinnerTestLog" class="hidden pw-spin" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/></svg>
539
+ <span data-zh="加载完整测试日志 (get_test_log)" data-en="Load full test log (get_test_log)">加载完整测试日志 (get_test_log)</span>
540
+ </button>
541
+ <span class="text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠️ Advanced view: may contain large output</span>
529
542
  </div>
530
- </section>
543
+ <pre id="advPreTestLog" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 520px; overflow-y: auto;"></pre>
544
+ </div>
531
545
 
532
- <!-- Test Log Card -->
533
- <section class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
534
- <div class="flex items-center gap-2 px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
535
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-secondary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
536
- <h2 class="text-base font-semibold" data-zh="测试日志" data-en="Test Log" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">测试日志</h2>
546
+ <!-- Advanced: full diff -->
547
+ <div class="flex flex-col gap-2">
548
+ <div class="flex items-center gap-2 flex-wrap">
549
+ <button id="advBtnDiff" class="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors" style="background-color: var(--pw-bg-hover); color: var(--pw-text-primary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">
550
+ <svg id="advSpinnerDiff" class="hidden pw-spin" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/></svg>
551
+ <span data-zh="加载完整 diff (get_diff)" data-en="Load full diff (get_diff)">加载完整 diff (get_diff)</span>
552
+ </button>
553
+ <span class="text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠️ Advanced view: may contain large output</span>
537
554
  </div>
538
- <div id="logEmpty" class="px-4 py-8 text-center text-sm" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="暂无测试日志" data-en="No test log available">暂无测试日志</div>
539
- <pre id="logContent" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 480px; overflow-y: auto;"></pre>
540
- </section>
555
+ <pre id="advPreDiff" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 520px; overflow-y: auto;"></pre>
556
+ </div>
541
557
 
558
+ <!-- Advanced: full audit_task review -->
559
+ <div class="flex flex-col gap-2">
560
+ <div class="flex items-center gap-2 flex-wrap">
561
+ <button id="advBtnAudit" class="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors" style="background-color: var(--pw-accent-muted); color: var(--pw-accent); border: 1px solid rgba(45, 212, 168, 0.3); font-family: var(--pw-font-sans);">
562
+ <svg id="advSpinnerAudit" class="hidden pw-spin" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/></svg>
563
+ <span data-zh="运行 audit_task 完整审计" data-en="Run audit_task full review">运行 audit_task 完整审计</span>
564
+ </button>
565
+ <span class="text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠️ Advanced view: may contain large output</span>
566
+ </div>
567
+ <pre id="advPreAudit" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 520px; overflow-y: auto;"></pre>
568
+ </div>
542
569
  </div>
543
-
544
- </div>
570
+ </details>
545
571
 
546
572
  </div>
547
573
 
@@ -705,311 +731,171 @@ html.dark {
705
731
  show($('detailContent'));
706
732
  }
707
733
 
708
- // ---- Render task detail ----
709
- function renderDetail(data) {
710
- var taskId = data.task_id || '—';
734
+ // ---- Render Safe Summary (from 4 safe endpoints) ----
735
+ // renderHeader populates the Task Header Card from safe_result
736
+ function renderHeader(taskId, safeResult) {
711
737
  text($('breadcrumbId'), taskId);
712
738
  text($('hdrTaskId'), taskId);
713
739
 
714
- // Status
715
- var status = (data.status && data.status.status) || data.status_status || null;
740
+ var status = safeResult.status || null;
716
741
  var statusMeta = getStatusMeta(status);
717
742
  text($('hdrStatusText'), statusMeta.text);
718
743
  applyBadgeColors($('hdrStatusBadge'), statusMeta.color);
719
744
 
720
- // Phase
721
- var phase = (data.status && data.status.phase) || null;
722
- text($('hdrPhase'), getPhaseLabel(phase));
723
-
724
- // Agent
725
- var agent = (data.status && data.status.agent) || null;
726
- text($('hdrAgent'), agent || '—');
727
-
728
- // Times
729
- var createdAt = (data.status && data.status.created_at) || null;
730
- var updatedAt = (data.status && data.status.updated_at) || null;
731
- text($('hdrCreatedAt'), formatTime(createdAt));
732
- text($('hdrUpdatedAt'), formatTime(updatedAt));
745
+ text($('hdrPhase'), getPhaseLabel(safeResult.phase));
733
746
 
734
- // Test command + timeout
735
- var testCommand = (data.status && data.status.test_command) || null;
736
- text($('hdrTestCommand'), testCommand || '—');
737
- var timeoutSeconds = (data.status && data.status.timeout_seconds);
738
- text($('hdrTimeout'), (timeoutSeconds === null || timeoutSeconds === undefined) ? '—' : String(timeoutSeconds));
747
+ // repo_path from safe_result
748
+ text($('hdrRepoPath'), safeResult.repo_path || '—');
739
749
 
740
- // Code metadata
741
- var repoPath = (data.status && data.status.repo_path) || null;
742
- text($('hdrRepoPath'), repoPath || '—');
750
+ // created_at/updated_at not available from safe endpoints; keep placeholder
751
+ text($('hdrCreatedAt'), '—');
752
+ text($('hdrUpdatedAt'), '—');
743
753
 
744
- var verifyCommands = (data.status && data.status.verify_commands) || null;
745
- text($('hdrVerifyCommands'), renderListOrJoin(verifyCommands));
754
+ text($('hdrTestCommand'), '—');
755
+ text($('hdrTimeout'), '—');
756
+ text($('hdrAgent'), '—');
757
+ text($('hdrVerifyCommands'), '—');
758
+ text($('hdrPlanId'), '—');
746
759
 
747
- var planId = (data.status && data.status.plan_id) || null;
748
- text($('hdrPlanId'), planId || '');
749
-
750
- // Error block
751
- var statusError = (data.status && data.status.error) || null;
752
- if (statusError) {
753
- text($('hdrErrorText'), escapeText(statusError));
760
+ if (safeResult.errors && safeResult.errors.length > 0) {
761
+ text($('hdrErrorText'), safeResult.errors.join('\n'));
762
+ show($('hdrErrorBlock'));
763
+ } else if (safeResult.failure_reason) {
764
+ text($('hdrErrorText'), safeResult.failure_reason);
754
765
  show($('hdrErrorBlock'));
755
766
  } else {
756
767
  hide($('hdrErrorBlock'));
757
768
  }
769
+ }
758
770
 
759
- // ---- Execution status (runtime) ----
760
- var runtime = data.runtime || null;
761
- var rtPhase = runtime ? (runtime.phase || null) : null;
762
- text($('rtPhase'), getPhaseLabel(rtPhase));
763
- var rtHeartbeat = runtime ? (runtime.last_heartbeat_at || null) : null;
764
- text($('rtHeartbeat'), formatTime(rtHeartbeat));
765
- var rtCmd = runtime ? (runtime.current_command || null) : null;
766
- text($('rtCurrentCmd'), rtCmd || '—');
767
-
768
- // ---- Result summary ----
769
- var result = data.result || null;
770
- var rsStatus = result ? (result.status || null) : null;
771
- text($('rsStatus'), rsStatus || '—');
772
- var rsVerify = result ? (result.verify_status || null) : null;
773
- text($('rsVerifyStatus'), rsVerify || '—');
774
- var rsSummary = result ? (result.summary || null) : null;
775
- if (rsSummary) {
776
- text($('rsSummary'), escapeText(rsSummary));
777
- show($('rsSummaryBlock'));
778
- } else {
779
- hide($('rsSummaryBlock'));
780
- }
771
+ // Render safe_result card
772
+ function renderSafeResult(sr) {
773
+ text($('srStatusBadge'), sr.status || '—');
774
+ applyBadgeColors($('srStatusBadge'), getStatusMeta(sr.status).color);
781
775
 
782
- // ---- Audit ----
783
- var audit = data.audit || null;
784
- var verdict = audit ? (audit.verdict || audit.decision || null) : null;
785
- applyVerdictBadge($('auditVerdictBadge'), verdict);
786
- var scopeChanges = audit ? (audit.scope_changes || null) : null;
787
- text($('auditScope'), scopeChanges || '—');
788
- var checkedAt = audit ? (audit.checked_at || null) : null;
789
- text($('auditCheckedAt'), formatTime(checkedAt));
790
- if (audit) {
791
- hide($('auditEmptyHint'));
792
- } else {
793
- show($('auditEmptyHint'));
794
- }
776
+ text($('srTerminal'), sr.terminal ? 'true' : 'false');
777
+ text($('srAcceptance'), sr.acceptance_status || '—');
778
+ text($('srChangedTotal'), sr.changed_files_total != null ? String(sr.changed_files_total) : '—');
779
+ text($('srOosTotal'), sr.out_of_scope_changes_total != null ? String(sr.out_of_scope_changes_total) : '—');
795
780
 
796
- // ---- Diff preview ----
797
- // API returns diff_patch (snake_case); also accept diff/testLog/test_log defensively.
798
- var diff = data.diff_patch || data.diff || data.diffPatch || null;
799
- var diffEl = $('diffContent');
800
- var diffEmpty = $('diffEmpty');
801
- if (diff) {
802
- text(diffEl, escapeText(diff));
803
- show(diffEl);
804
- hide(diffEmpty);
805
- } else {
806
- hide(diffEl);
807
- show(diffEmpty);
808
- }
781
+ var verifyHeadline = (sr.verification && sr.verification.headline) || null;
782
+ text($('srVerifyHeadline'), verifyHeadline || '—');
809
783
 
810
- // ---- Test log ----
811
- var testLog = data.test_log || data.testLog || null;
812
- var logEl = $('logContent');
813
- var logEmpty = $('logEmpty');
814
- if (testLog) {
815
- text(logEl, escapeText(testLog));
816
- show(logEl);
817
- hide(logEmpty);
784
+ if (sr.failure_reason || sr.failed_command) {
785
+ text($('srFailureReason'), sr.failure_reason || '—');
786
+ text($('srFailedCommand'), sr.failed_command || '');
787
+ show($('srFailureBlock'));
818
788
  } else {
819
- hide(logEl);
820
- show(logEmpty);
789
+ hide($('srFailureBlock'));
821
790
  }
822
791
 
823
- // ---- Changed files ----
824
- renderChangedFiles(data.changed_files, data.file_stats);
825
-
826
- // ---- Verify log ----
827
- var verifyLog = data.verify_log || null;
828
- var vlEl = $('verifyLogContent');
829
- var vlEmpty = $('verifyLogEmpty');
830
- if (verifyLog) {
831
- text(vlEl, escapeText(verifyLog));
832
- show(vlEl);
833
- hide(vlEmpty);
792
+ text($('srNextAction'), sr.next_action || '—');
793
+
794
+ if (sr.execution_blocked || sr.pending_reason) {
795
+ text($('srPendingReason'), sr.pending_reason || (sr.execution_blocked ? 'blocked' : '—'));
796
+ show($('srBlockedBlock'));
834
797
  } else {
835
- hide(vlEl);
836
- show(vlEmpty);
798
+ hide($('srBlockedBlock'));
837
799
  }
838
800
 
839
- // ---- Independent review (audit_task artifact) ----
840
- var review = data.independent_review || null;
841
- var rvContent = review ? (review.content || null) : null;
842
- var rvVerdict = review ? (review.verdict || null) : null;
843
- var rvEl = $('reviewContent');
844
- var rvEmpty = $('reviewEmpty');
845
- var rvBadge = $('reviewVerdictBadge');
846
- if (rvContent) {
847
- text(rvEl, escapeText(rvContent));
848
- show(rvEl);
849
- hide(rvEmpty);
801
+ if (Array.isArray(sr.warnings) && sr.warnings.length > 0) {
802
+ $('srWarnings').textContent = sr.warnings.join('\n');
803
+ show($('srWarningsBlock'));
850
804
  } else {
851
- hide(rvEl);
852
- show(rvEmpty);
805
+ hide($('srWarningsBlock'));
853
806
  }
854
- applyVerdictBadge(rvBadge, rvVerdict);
855
-
856
- // ---- Verification summary ----
857
- renderVerificationSummary(data.verification_summary);
807
+ }
858
808
 
859
- // ---- Warnings / Errors ----
860
- renderWarningsErrors(data.warnings, data.errors);
809
+ // Render safe_test_summary card
810
+ function renderSafeTestSummary(sts) {
811
+ text($('stStatusBadge'), sts.status || '—');
812
+ applyBadgeColors($('stStatusBadge'), getStatusMeta(sts.status).color);
861
813
 
862
- // ---- Stale / Reconcile banner ----
863
- renderStaleBanner(data.stale, data.reconcile);
814
+ text($('stCmdCount'), String(sts.command_count || 0));
815
+ text($('stPassed'), String(sts.passed_commands || 0));
816
+ text($('stFailed'), String(sts.failed_commands || 0));
817
+ text($('stSkipped'), String(sts.skipped_commands || 0));
864
818
 
865
- showDetail();
866
- }
819
+ text($('stHeadline'), sts.headline || '—');
867
820
 
868
- // ---- Render: changed files ----
869
- function renderChangedFiles(changedFiles, fileStats) {
870
- var emptyEl = $('cfEmpty');
871
- var bodyEl = $('cfBody');
872
- var listEl = $('cfList');
873
- var statsEl = $('cfStatsRow');
874
- var badgeEl = $('cfCountBadge');
875
-
876
- if (!changedFiles) {
877
- hide(bodyEl);
878
- hide(badgeEl);
879
- show(emptyEl);
880
- return;
821
+ var cmds = Array.isArray(sts.commands) ? sts.commands : [];
822
+ var cmdsHtml = '';
823
+ for (var i = 0; i < cmds.length; i++) {
824
+ var cmd = cmds[i];
825
+ var statusColor = cmd.status === 'passed' ? 'var(--pw-state-success)' : (cmd.status === 'failed' || cmd.status === 'timed_out') ? 'var(--pw-state-error)' : 'var(--pw-text-secondary)';
826
+ cmdsHtml += '<div style="margin-bottom:4px;"><span style="color:' + statusColor + ';">' + escapeHtml(cmd.status || 'unknown') + '</span> <span style="color:var(--pw-text-tertiary);">exit=' + (cmd.exit_code !== null ? cmd.exit_code : '—') + '</span> <span style="color:var(--pw-text-primary);">' + escapeHtml(cmd.command || '') + '</span></div>';
881
827
  }
828
+ if (!cmdsHtml) cmdsHtml = '<div style="color:var(--pw-text-tertiary);">—</div>';
829
+ $('stCommandsBody').innerHTML = cmdsHtml;
830
+ }
882
831
 
883
- // Build a list of paths to display.
884
- var paths = [];
885
- if (Array.isArray(changedFiles.files)) {
886
- for (var i = 0; i < changedFiles.files.length; i++) {
887
- var f = changedFiles.files[i];
888
- if (typeof f === 'string') paths.push(f);
889
- else if (f && typeof f === 'object' && typeof f.path === 'string') paths.push(f.path);
890
- }
891
- }
892
- if (paths.length === 0 && Array.isArray(changedFiles.changed_files)) {
893
- for (var j = 0; j < changedFiles.changed_files.length; j++) {
894
- var cf = changedFiles.changed_files[j];
895
- if (typeof cf === 'string') paths.push(cf);
896
- else if (cf && typeof cf === 'object' && typeof cf.path === 'string') paths.push(cf.path);
897
- }
898
- }
832
+ // Render safe_diff_summary card
833
+ function renderSafeDiffSummary(sds) {
834
+ text($('sdPatchMode'), sds.patch_mode || '—');
835
+ text($('sdChangedTotal'), String(sds.changed_files_total || 0));
836
+ text($('sdAdditions'), String(sds.additions || 0));
837
+ text($('sdDeletions'), String(sds.deletions || 0));
838
+ text($('sdDiffAvailable'), sds.diff_available ? 'true' : 'false');
899
839
 
900
- // file_stats summary
901
- var statsHtml = '';
902
- if (fileStats && typeof fileStats === 'object') {
903
- var counts = fileStats.counts || fileStats;
904
- var statItems = [
905
- { label: 'modified', value: counts.modified != null ? counts.modified : null },
906
- { label: 'added', value: counts.added != null ? counts.added : null },
907
- { label: 'deleted', value: counts.deleted != null ? counts.deleted : null },
908
- { label: 'untracked', value: counts.untracked != null ? counts.untracked : null }
909
- ];
910
- var hasAny = false;
911
- statsHtml = statItems.map(function (it) {
912
- if (it.value === null || it.value === undefined) return '';
913
- hasAny = true;
914
- return '<div class="rounded-md px-2 py-1.5" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle);">'
915
- + '<div class="text-xs" style="color: var(--pw-text-tertiary);">' + escapeHtml(it.label) + '</div>'
916
- + '<div class="text-sm font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">' + escapeHtml(String(it.value)) + '</div>'
917
- + '</div>';
918
- }).join('');
919
- if (hasAny) {
920
- show(statsEl);
921
- statsEl.innerHTML = statsHtml;
922
- } else {
923
- hide(statsEl);
924
- }
840
+ if (sds.diff_truncated || sds.large_diff_omitted) {
841
+ show($('sdTruncatedBlock'));
925
842
  } else {
926
- hide(statsEl);
927
- }
928
-
929
- if (paths.length === 0) {
930
- hide(bodyEl);
931
- hide(badgeEl);
932
- show(emptyEl);
933
- return;
843
+ hide($('sdTruncatedBlock'));
934
844
  }
935
845
 
936
- // Render path list (cap at 500 to avoid rendering huge DOM).
937
- var displayCount = Math.min(paths.length, 500);
938
- var listHtml = '';
939
- for (var k = 0; k < displayCount; k++) {
940
- listHtml += escapeHtml(paths[k]) + '\n';
846
+ var files = Array.isArray(sds.files) ? sds.files : [];
847
+ var filesHtml = '';
848
+ for (var j = 0; j < files.length; j++) {
849
+ var f = files[j];
850
+ var changeColor = (f.change === 'added' || f.change === 'A') ? 'var(--pw-state-success)' : (f.change === 'deleted' || f.change === 'D') ? 'var(--pw-state-error)' : 'var(--pw-accent)';
851
+ filesHtml += '<div style="margin-bottom:2px;"><span style="color:' + changeColor + ';">' + escapeHtml(f.change || '?') + '</span> <span style="color:var(--pw-text-tertiary);">' + escapeHtml(f.kind || 'file') + '</span> <span style="color:var(--pw-text-primary);">' + escapeHtml(f.path || '') + '</span></div>';
941
852
  }
942
- if (paths.length > displayCount) {
943
- listHtml += '\n... (' + (paths.length - displayCount) + ' more truncated)';
944
- }
945
- listEl.textContent = listHtml;
946
-
947
- // Badge
948
- text(badgeEl, paths.length + ' 个文件');
949
- show(badgeEl);
950
-
951
- hide(emptyEl);
952
- show(bodyEl);
953
- }
853
+ if (!filesHtml) filesHtml = '<div style="color:var(--pw-text-tertiary);">—</div>';
854
+ $('sdFilesBody').innerHTML = filesHtml;
954
855
 
955
- // ---- Render: verification summary ----
956
- function renderVerificationSummary(vs) {
957
- var statusEl = $('vsStatus');
958
- var checkedEl = $('vsCheckedAt');
959
- var cmdsBlock = $('vsCommandsBlock');
960
- var cmdsEl = $('vsCommands');
961
- var emptyHint = $('vsEmptyHint');
962
-
963
- if (!vs) {
964
- text(statusEl, '—');
965
- text(checkedEl, '—');
966
- hide(cmdsBlock);
967
- show(emptyHint);
968
- return;
969
- }
970
- hide(emptyHint);
971
- text(statusEl, vs.status || '—');
972
- text(checkedEl, formatTime(vs.checked_at));
973
- if (Array.isArray(vs.commands) && vs.commands.length > 0) {
974
- text(cmdsEl, vs.commands.map(function (c) { return JSON.stringify(c); }).join('\n'));
975
- show(cmdsBlock);
856
+ if (sds.artifact_hygiene_counts && Object.keys(sds.artifact_hygiene_counts).length > 0) {
857
+ $('sdHygiene').textContent = JSON.stringify(sds.artifact_hygiene_counts, null, 2);
858
+ show($('sdHygieneBlock'));
976
859
  } else {
977
- hide(cmdsBlock);
860
+ hide($('sdHygieneBlock'));
978
861
  }
979
862
  }
980
863
 
981
- // ---- Render: warnings / errors ----
982
- function renderWarningsErrors(warnings, errors) {
983
- var emptyEl = $('weEmpty');
984
- var bodyEl = $('weBody');
985
- var warnBlock = $('weWarningsBlock');
986
- var errBlock = $('weErrorsBlock');
987
- var warnEl = $('weWarnings');
988
- var errEl = $('weErrors');
989
-
990
- var wList = Array.isArray(warnings) ? warnings : [];
991
- var eList = Array.isArray(errors) ? errors : [];
992
-
993
- if (wList.length === 0 && eList.length === 0) {
994
- show(emptyEl);
995
- hide(bodyEl);
996
- return;
864
+ // Render safe_audit card
865
+ function renderSafeAudit(sa) {
866
+ text($('saVerdictBadge'), sa.verdict || '');
867
+ applyVerdictBadge($('saVerdictBadge'), sa.verdict);
868
+
869
+ var cc = sa.check_counts || {};
870
+ text($('saCountsPass'), String(cc.pass || 0));
871
+ text($('saCountsWarn'), String(cc.warn || 0));
872
+ text($('saCountsFail'), String(cc.fail || 0));
873
+
874
+ var acc = sa.acceptance || {};
875
+ text($('saAccVerdict'), (acc.verdict || '—') + ' / ' + (acc.status || '—'));
876
+ text($('saAccReason'), acc.reason || '—');
877
+ text($('saAccNextTask'), acc.next_suggested_task || '—');
878
+
879
+ var failChecks = Array.isArray(sa.fail_checks) ? sa.fail_checks : [];
880
+ if (failChecks.length > 0) {
881
+ $('saFailChecks').textContent = failChecks.map(function (c) { return c.name || String(c); }).join('\n');
882
+ show($('saFailChecksBlock'));
883
+ } else {
884
+ hide($('saFailChecksBlock'));
997
885
  }
998
- hide(emptyEl);
999
- show(bodyEl);
1000
886
 
1001
- if (wList.length > 0) {
1002
- warnEl.textContent = wList.map(function (w) { return String(w); }).join('\n---\n');
1003
- show(warnBlock);
887
+ if (sa.manual_verification_required) {
888
+ show($('saManualBlock'));
1004
889
  } else {
1005
- hide(warnBlock);
890
+ hide($('saManualBlock'));
1006
891
  }
1007
892
 
1008
- if (eList.length > 0) {
1009
- errEl.textContent = eList.map(function (e) { return String(e); }).join('\n---\n');
1010
- show(errBlock);
893
+ var actions = Array.isArray(sa.recommended_next_actions) ? sa.recommended_next_actions : [];
894
+ if (actions.length > 0) {
895
+ $('saActions').textContent = actions.join('\n');
896
+ show($('saActionsBlock'));
1011
897
  } else {
1012
- hide(errBlock);
898
+ hide($('saActionsBlock'));
1013
899
  }
1014
900
  }
1015
901
 
@@ -1055,7 +941,7 @@ html.dark {
1055
941
  alert('运行审计失败: ' + msg);
1056
942
  } else {
1057
943
  alert('审计完成。verdict=' + ((r.data && r.data.audit && r.data.audit.verdict) || '—'));
1058
- loadTask(taskId);
944
+ loadSafeSummary(taskId);
1059
945
  }
1060
946
  })
1061
947
  .catch(function (err) {
@@ -1089,7 +975,7 @@ html.dark {
1089
975
  } else {
1090
976
  var decision = (r.data && r.data.decision) || '—';
1091
977
  alert('Reconcile 完成: ' + decision);
1092
- loadTask(taskId);
978
+ loadSafeSummary(taskId);
1093
979
  }
1094
980
  })
1095
981
  .catch(function (err) {
@@ -1146,45 +1032,167 @@ html.dark {
1146
1032
  } catch (e) { /* ignore */ }
1147
1033
  }
1148
1034
 
1149
- // ---- API fetch ----
1150
- function loadTask(taskId) {
1035
+ // ---- Safe summary fetch (4 safe endpoints, parallel; default on init) ----
1036
+ function safeGet(url) {
1037
+ return fetch(url, { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
1038
+ .then(function (res) {
1039
+ return res.json().then(function (data) {
1040
+ return { ok: res.ok, status: res.status, data: data };
1041
+ });
1042
+ });
1043
+ }
1044
+
1045
+ function loadSafeSummary(taskId) {
1151
1046
  showLoading();
1152
- var url = '/api/tasks/' + encodeURIComponent(taskId);
1153
- fetch(url, {
1154
- method: 'GET',
1155
- headers: { 'Accept': 'application/json' },
1156
- cache: 'no-store'
1157
- })
1047
+ var base = '/api/tasks/' + encodeURIComponent(taskId);
1048
+ Promise.all([
1049
+ safeGet(base + '/safe-result'),
1050
+ safeGet(base + '/safe-test-summary'),
1051
+ safeGet(base + '/safe-diff-summary'),
1052
+ safeGet(base + '/safe-audit')
1053
+ ]).then(function (results) {
1054
+ var safeResult = results[0].data || {};
1055
+ var safeTest = results[1].data || {};
1056
+ var safeDiff = results[2].data || {};
1057
+ var safeAudit = results[3].data || {};
1058
+
1059
+ // If safe-result returned an explicit error and no status, treat as not found.
1060
+ if (safeResult.error && safeResult.status === undefined) {
1061
+ showError('任务不存在', safeResult.error || ('未找到任务 ID: ' + taskId));
1062
+ return;
1063
+ }
1064
+
1065
+ renderHeader(taskId, safeResult);
1066
+ renderSafeResult(safeResult);
1067
+ renderSafeTestSummary(safeTest);
1068
+ renderSafeDiffSummary(safeDiff);
1069
+ renderSafeAudit(safeAudit);
1070
+ showDetail();
1071
+ }).catch(function (err) {
1072
+ var msg = (err && err.message) ? err.message : '未知错误';
1073
+ if (err && err.name === 'TypeError' && /fetch/i.test(msg)) {
1074
+ showError('无法连接服务', '请确认 PatchWarden Control Center 正在运行 (127.0.0.1:8090)');
1075
+ } else {
1076
+ showError('加载失败', msg);
1077
+ }
1078
+ });
1079
+ }
1080
+
1081
+ // ---- Advanced on-demand loaders (full content; only fetched when clicked) ----
1082
+ // HTML id pattern: advBtn<Suffix>, advSpinner<Suffix>, advPre<Suffix>
1083
+ function setAdvLoading(suffix, loading) {
1084
+ var spinner = $('advSpinner' + suffix);
1085
+ var btn = $('advBtn' + suffix);
1086
+ if (spinner) { if (loading) show(spinner); else hide(spinner); }
1087
+ if (btn) { btn.disabled = loading; btn.style.opacity = loading ? '0.6' : ''; }
1088
+ }
1089
+
1090
+ function showAdvPre(suffix, content) {
1091
+ var pre = $('advPre' + suffix);
1092
+ if (!pre) return;
1093
+ pre.textContent = content;
1094
+ show(pre);
1095
+ }
1096
+
1097
+ // Load full result (GET /api/tasks/:taskId) — NOT auto-loaded
1098
+ function loadFullResult(taskId) {
1099
+ setAdvLoading('Result', true);
1100
+ fetch('/api/tasks/' + encodeURIComponent(taskId), { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
1158
1101
  .then(function (res) {
1159
- if (res.status === 404) {
1160
- // Task not found
1161
- return res.text().then(function () {
1162
- throw new Error('NOT_FOUND');
1163
- });
1102
+ if (res.status === 404) throw new Error('NOT_FOUND');
1103
+ if (!res.ok) throw new Error('HTTP ' + res.status);
1104
+ return res.json();
1105
+ })
1106
+ .then(function (data) {
1107
+ // Refresh header metadata that is only available from the full endpoint.
1108
+ if (data && data.status) {
1109
+ var s = data.status;
1110
+ text($('hdrAgent'), s.agent || '—');
1111
+ text($('hdrCreatedAt'), formatTime(s.created_at));
1112
+ text($('hdrUpdatedAt'), formatTime(s.updated_at));
1113
+ text($('hdrTestCommand'), s.test_command || '—');
1114
+ text($('hdrTimeout'), (s.timeout_seconds === null || s.timeout_seconds === undefined) ? '—' : String(s.timeout_seconds));
1115
+ text($('hdrVerifyCommands'), renderListOrJoin(s.verify_commands));
1116
+ text($('hdrPlanId'), s.plan_id || '—');
1117
+ if (s.error) {
1118
+ text($('hdrErrorText'), escapeText(s.error));
1119
+ show($('hdrErrorBlock'));
1120
+ }
1164
1121
  }
1165
- if (!res.ok) {
1166
- return res.text().then(function (body) {
1167
- throw new Error('HTTP ' + res.status + (body ? (': ' + body.slice(0, 200)) : ''));
1168
- });
1122
+ if (data && (data.stale || data.reconcile)) {
1123
+ renderStaleBanner(data.stale, data.reconcile);
1169
1124
  }
1125
+ showAdvPre('Result', JSON.stringify(data, null, 2));
1126
+ })
1127
+ .catch(function (err) {
1128
+ var msg = (err && err.message) === 'NOT_FOUND' ? '任务不存在' : ((err && err.message) || '未知错误');
1129
+ showAdvPre('Result', '⚠ ' + msg);
1130
+ })
1131
+ .finally(function () { setAdvLoading('Result', false); });
1132
+ }
1133
+
1134
+ // Load full test log portion (GET /api/tasks/:taskId, extract test_log)
1135
+ function loadFullTestLog(taskId) {
1136
+ setAdvLoading('TestLog', true);
1137
+ fetch('/api/tasks/' + encodeURIComponent(taskId), { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
1138
+ .then(function (res) {
1139
+ if (res.status === 404) throw new Error('NOT_FOUND');
1140
+ if (!res.ok) throw new Error('HTTP ' + res.status);
1170
1141
  return res.json();
1171
1142
  })
1172
1143
  .then(function (data) {
1173
- if (!data || data.error) {
1174
- throw new Error((data && data.error) ? data.error : '响应缺少任务数据');
1175
- }
1176
- renderDetail(data);
1144
+ var testLog = (data && (data.test_log || data.testLog)) || null;
1145
+ showAdvPre('TestLog', testLog ? escapeText(testLog) : '⚠ No test_log available for this task.');
1146
+ })
1147
+ .catch(function (err) {
1148
+ var msg = (err && err.message) === 'NOT_FOUND' ? '任务不存在' : ((err && err.message) || '未知错误');
1149
+ showAdvPre('TestLog', '⚠ ' + msg);
1150
+ })
1151
+ .finally(function () { setAdvLoading('TestLog', false); });
1152
+ }
1153
+
1154
+ // Load full diff portion (GET /api/tasks/:taskId, extract diff_patch/diff)
1155
+ function loadFullDiff(taskId) {
1156
+ setAdvLoading('Diff', true);
1157
+ fetch('/api/tasks/' + encodeURIComponent(taskId), { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
1158
+ .then(function (res) {
1159
+ if (res.status === 404) throw new Error('NOT_FOUND');
1160
+ if (!res.ok) throw new Error('HTTP ' + res.status);
1161
+ return res.json();
1162
+ })
1163
+ .then(function (data) {
1164
+ var diff = (data && (data.diff_patch || data.diff || data.diffPatch)) || null;
1165
+ showAdvPre('Diff', diff ? escapeText(diff) : '⚠ No diff available for this task.');
1177
1166
  })
1178
1167
  .catch(function (err) {
1179
- var msg = (err && err.message) ? err.message : '未知错误';
1180
- if (msg === 'NOT_FOUND') {
1181
- showError('任务不存在', '未找到任务 ID: ' + taskId);
1182
- } else if (err && err.name === 'TypeError' && /fetch/i.test(msg)) {
1183
- showError('无法连接服务', '请确认 PatchWarden Control Center 正在运行 (127.0.0.1:8090)');
1168
+ var msg = (err && err.message) === 'NOT_FOUND' ? '任务不存在' : ((err && err.message) || '未知错误');
1169
+ showAdvPre('Diff', '⚠ ' + msg);
1170
+ })
1171
+ .finally(function () { setAdvLoading('Diff', false); });
1172
+ }
1173
+
1174
+ // Run audit_task full review (POST /api/tasks/:taskId/audit with control token)
1175
+ function runFullAuditReview(taskId) {
1176
+ setAdvLoading('Audit', true);
1177
+ postAction('/api/tasks/' + encodeURIComponent(taskId) + '/audit')
1178
+ .then(function (res) {
1179
+ return res.json().then(function (data) { return { ok: res.ok, status: res.status, data: data }; });
1180
+ })
1181
+ .then(function (r) {
1182
+ if (!r.ok) {
1183
+ var msg = (r.data && r.data.error) || ('HTTP ' + r.status);
1184
+ showAdvPre('Audit', '⚠ ' + msg);
1184
1185
  } else {
1185
- showError('加载失败', msg);
1186
+ showAdvPre('Audit', JSON.stringify(r.data, null, 2));
1187
+ // Refresh the safe-audit card so conclusions stay in sync.
1188
+ safeGet('/api/tasks/' + encodeURIComponent(taskId) + '/safe-audit')
1189
+ .then(function (res2) { if (res2.data && !res2.data.error) renderSafeAudit(res2.data); });
1186
1190
  }
1187
- });
1191
+ })
1192
+ .catch(function (err) {
1193
+ showAdvPre('Audit', '⚠ ' + ((err && err.message) || '未知错误'));
1194
+ })
1195
+ .finally(function () { setAdvLoading('Audit', false); });
1188
1196
  }
1189
1197
 
1190
1198
  // ---- Entry point ----
@@ -1193,13 +1201,13 @@ html.dark {
1193
1201
  if (!taskId) {
1194
1202
  showError('缺少 task id', '请在 URL 中提供 ?id=<task_id>');
1195
1203
  } else {
1196
- loadTask(taskId);
1204
+ loadSafeSummary(taskId);
1197
1205
  }
1198
1206
 
1199
1207
  // ---- Action button wiring ----
1200
1208
  var btnRefresh = $('btnRefresh');
1201
1209
  if (btnRefresh) btnRefresh.addEventListener('click', function () {
1202
- if (taskId) loadTask(taskId);
1210
+ if (taskId) loadSafeSummary(taskId);
1203
1211
  });
1204
1212
  var btnCopyId = $('btnCopyId');
1205
1213
  if (btnCopyId) btnCopyId.addEventListener('click', function () {
@@ -1221,9 +1229,31 @@ html.dark {
1221
1229
  if (!confirm('确认 reconcile 该任务?将写入 reconcile 记录(不会删除任务)。')) return;
1222
1230
  runReconcile(taskId);
1223
1231
  });
1232
+
1233
+ // ---- Advanced section on-demand button wiring ----
1234
+ var advBtnResult = $('advBtnResult');
1235
+ if (advBtnResult) advBtnResult.addEventListener('click', function () {
1236
+ if (taskId) loadFullResult(taskId);
1237
+ });
1238
+ var advBtnTestLog = $('advBtnTestLog');
1239
+ if (advBtnTestLog) advBtnTestLog.addEventListener('click', function () {
1240
+ if (taskId) loadFullTestLog(taskId);
1241
+ });
1242
+ var advBtnDiff = $('advBtnDiff');
1243
+ if (advBtnDiff) advBtnDiff.addEventListener('click', function () {
1244
+ if (taskId) loadFullDiff(taskId);
1245
+ });
1246
+ var advBtnAudit = $('advBtnAudit');
1247
+ if (advBtnAudit) advBtnAudit.addEventListener('click', function () {
1248
+ if (!taskId) return;
1249
+ if (!confirm('确认运行 audit_task 完整审计?')) return;
1250
+ runFullAuditReview(taskId);
1251
+ });
1224
1252
  })();
1225
1253
  </script>
1226
1254
 
1227
1255
  <script>lucide.createIcons();</script>
1256
+ <script src="/i18n.js"></script>
1257
+ <script src="/desktop-bridge.js"></script>
1228
1258
  </body>
1229
1259
  </html>