create-principles-disciple 1.146.3 → 1.146.6

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 (311) hide show
  1. package/_release/product-identity.json +1 -1
  2. package/console/dist/web/assets/app.js +26 -26
  3. package/console/package.json +1 -1
  4. package/package.json +3 -3
  5. package/pd-cli/package.json +1 -1
  6. package/release-manager/package.json +3 -3
  7. package/console/dist/ui/App.d.ts +0 -1
  8. package/console/dist/ui/App.js +0 -172
  9. package/console/dist/ui/api.d.ts +0 -611
  10. package/console/dist/ui/api.js +0 -1094
  11. package/console/dist/ui/components/auth/login-form.d.ts +0 -4
  12. package/console/dist/ui/components/auth/login-form.js +0 -142
  13. package/console/dist/ui/components/auth/splash-screen.d.ts +0 -9
  14. package/console/dist/ui/components/auth/splash-screen.js +0 -22
  15. package/console/dist/ui/components/error-boundary.d.ts +0 -15
  16. package/console/dist/ui/components/error-boundary.js +0 -30
  17. package/console/dist/ui/components/focus/daily-thought-card.d.ts +0 -1
  18. package/console/dist/ui/components/focus/daily-thought-card.js +0 -41
  19. package/console/dist/ui/components/language-switcher.d.ts +0 -2
  20. package/console/dist/ui/components/language-switcher.js +0 -17
  21. package/console/dist/ui/components/layout/app-sidebar.d.ts +0 -27
  22. package/console/dist/ui/components/layout/app-sidebar.js +0 -128
  23. package/console/dist/ui/components/layout/page-loading.d.ts +0 -13
  24. package/console/dist/ui/components/layout/page-loading.js +0 -14
  25. package/console/dist/ui/components/layout/page-shell.d.ts +0 -12
  26. package/console/dist/ui/components/layout/page-shell.js +0 -11
  27. package/console/dist/ui/components/layout/section-title.d.ts +0 -10
  28. package/console/dist/ui/components/layout/section-title.js +0 -9
  29. package/console/dist/ui/components/notifications/NotificationProvider.d.ts +0 -14
  30. package/console/dist/ui/components/notifications/NotificationProvider.js +0 -136
  31. package/console/dist/ui/components/notifications/favicon-badge.d.ts +0 -3
  32. package/console/dist/ui/components/notifications/favicon-badge.js +0 -68
  33. package/console/dist/ui/components/notifications/notification-reducer.d.ts +0 -9
  34. package/console/dist/ui/components/notifications/notification-reducer.js +0 -9
  35. package/console/dist/ui/components/notifications/sound-storage.d.ts +0 -3
  36. package/console/dist/ui/components/notifications/sound-storage.js +0 -37
  37. package/console/dist/ui/components/notifications/useNotifications.d.ts +0 -1
  38. package/console/dist/ui/components/notifications/useNotifications.js +0 -9
  39. package/console/dist/ui/components/onboarding/CircuitDiagram.d.ts +0 -8
  40. package/console/dist/ui/components/onboarding/CircuitDiagram.js +0 -18
  41. package/console/dist/ui/components/onboarding/DemoResultView.d.ts +0 -22
  42. package/console/dist/ui/components/onboarding/DemoResultView.js +0 -33
  43. package/console/dist/ui/components/onboarding/SlashCommandsCard.d.ts +0 -16
  44. package/console/dist/ui/components/onboarding/SlashCommandsCard.js +0 -20
  45. package/console/dist/ui/components/onboarding/slashCommands.d.ts +0 -20
  46. package/console/dist/ui/components/onboarding/slashCommands.js +0 -22
  47. package/console/dist/ui/components/receipts/ReceiptCoverageDisclosure.d.ts +0 -6
  48. package/console/dist/ui/components/receipts/ReceiptCoverageDisclosure.js +0 -66
  49. package/console/dist/ui/components/theme-provider.d.ts +0 -14
  50. package/console/dist/ui/components/theme-provider.js +0 -40
  51. package/console/dist/ui/components/theme-toggle.d.ts +0 -2
  52. package/console/dist/ui/components/theme-toggle.js +0 -11
  53. package/console/dist/ui/components/ui/alert-dialog.d.ts +0 -20
  54. package/console/dist/ui/components/ui/alert-dialog.js +0 -26
  55. package/console/dist/ui/components/ui/badge.d.ts +0 -9
  56. package/console/dist/ui/components/ui/badge.js +0 -22
  57. package/console/dist/ui/components/ui/button.d.ts +0 -11
  58. package/console/dist/ui/components/ui/button.js +0 -34
  59. package/console/dist/ui/components/ui/card.d.ts +0 -8
  60. package/console/dist/ui/components/ui/card.js +0 -16
  61. package/console/dist/ui/components/ui/charts.d.ts +0 -51
  62. package/console/dist/ui/components/ui/charts.js +0 -72
  63. package/console/dist/ui/components/ui/dialog.d.ts +0 -19
  64. package/console/dist/ui/components/ui/dialog.js +0 -22
  65. package/console/dist/ui/components/ui/input.d.ts +0 -3
  66. package/console/dist/ui/components/ui/input.js +0 -8
  67. package/console/dist/ui/components/ui/markdown.d.ts +0 -24
  68. package/console/dist/ui/components/ui/markdown.js +0 -119
  69. package/console/dist/ui/components/ui/progress-bar.d.ts +0 -20
  70. package/console/dist/ui/components/ui/progress-bar.js +0 -38
  71. package/console/dist/ui/components/ui/select.d.ts +0 -13
  72. package/console/dist/ui/components/ui/select.js +0 -26
  73. package/console/dist/ui/components/ui/separator.d.ts +0 -3
  74. package/console/dist/ui/components/ui/separator.js +0 -6
  75. package/console/dist/ui/components/ui/sheet.d.ts +0 -25
  76. package/console/dist/ui/components/ui/sheet.js +0 -37
  77. package/console/dist/ui/components/ui/shiny-text.d.ts +0 -28
  78. package/console/dist/ui/components/ui/shiny-text.js +0 -46
  79. package/console/dist/ui/components/ui/skeleton.d.ts +0 -3
  80. package/console/dist/ui/components/ui/skeleton.js +0 -6
  81. package/console/dist/ui/components/ui/sonner.d.ts +0 -3
  82. package/console/dist/ui/components/ui/sonner.js +0 -13
  83. package/console/dist/ui/components/ui/tooltip.d.ts +0 -7
  84. package/console/dist/ui/components/ui/tooltip.js +0 -10
  85. package/console/dist/ui/data/daily-thoughts.d.ts +0 -15
  86. package/console/dist/ui/data/daily-thoughts.js +0 -164
  87. package/console/dist/ui/hooks/useNotificationSound.d.ts +0 -12
  88. package/console/dist/ui/hooks/useNotificationSound.js +0 -74
  89. package/console/dist/ui/i18n/en.json +0 -2399
  90. package/console/dist/ui/i18n/index.d.ts +0 -2
  91. package/console/dist/ui/i18n/index.js +0 -32
  92. package/console/dist/ui/i18n/zh-CN.json +0 -2399
  93. package/console/dist/ui/main.d.ts +0 -1
  94. package/console/dist/ui/main.js +0 -9
  95. package/console/dist/ui/pages/ReportProblemValidators.d.ts +0 -144
  96. package/console/dist/ui/pages/ReportProblemValidators.js +0 -418
  97. package/console/dist/ui/pages/activation/ActivationPage.d.ts +0 -1
  98. package/console/dist/ui/pages/activation/ActivationPage.js +0 -512
  99. package/console/dist/ui/pages/activation/ActivationValidators.d.ts +0 -20
  100. package/console/dist/ui/pages/activation/ActivationValidators.js +0 -160
  101. package/console/dist/ui/pages/control-center/AgentCard.d.ts +0 -31
  102. package/console/dist/ui/pages/control-center/AgentCard.js +0 -212
  103. package/console/dist/ui/pages/control-center/AgentGroup.d.ts +0 -26
  104. package/console/dist/ui/pages/control-center/AgentGroup.js +0 -38
  105. package/console/dist/ui/pages/control-center/ControlCenterPage.d.ts +0 -1
  106. package/console/dist/ui/pages/control-center/ControlCenterPage.js +0 -509
  107. package/console/dist/ui/pages/control-center/DesignGatePanel.d.ts +0 -1
  108. package/console/dist/ui/pages/control-center/DesignGatePanel.js +0 -137
  109. package/console/dist/ui/pages/control-center/RuntimeProfileManager.d.ts +0 -31
  110. package/console/dist/ui/pages/control-center/RuntimeProfileManager.js +0 -199
  111. package/console/dist/ui/pages/control-center/WorkflowDiagram.d.ts +0 -17
  112. package/console/dist/ui/pages/control-center/WorkflowDiagram.js +0 -55
  113. package/console/dist/ui/pages/debt/DebtPage.d.ts +0 -2
  114. package/console/dist/ui/pages/debt/DebtPage.js +0 -222
  115. package/console/dist/ui/pages/design-system/DesignSystemPage.d.ts +0 -2
  116. package/console/dist/ui/pages/design-system/DesignSystemPage.js +0 -30
  117. package/console/dist/ui/pages/examiner/ExaminerPage.d.ts +0 -15
  118. package/console/dist/ui/pages/examiner/ExaminerPage.js +0 -158
  119. package/console/dist/ui/pages/failed-tasks/FailedTasksPage.d.ts +0 -1
  120. package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +0 -421
  121. package/console/dist/ui/pages/focus/ApprovalSubjectReading.d.ts +0 -24
  122. package/console/dist/ui/pages/focus/ApprovalSubjectReading.js +0 -133
  123. package/console/dist/ui/pages/focus/FocusPage.d.ts +0 -123
  124. package/console/dist/ui/pages/focus/FocusPage.js +0 -567
  125. package/console/dist/ui/pages/focus/OwnerDecisionCard.d.ts +0 -9
  126. package/console/dist/ui/pages/focus/OwnerDecisionCard.js +0 -111
  127. package/console/dist/ui/pages/focus/OwnerWorkbench.d.ts +0 -19
  128. package/console/dist/ui/pages/focus/OwnerWorkbench.js +0 -75
  129. package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.d.ts +0 -34
  130. package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.js +0 -298
  131. package/console/dist/ui/pages/focus/focus-validation.d.ts +0 -40
  132. package/console/dist/ui/pages/focus/focus-validation.js +0 -211
  133. package/console/dist/ui/pages/intent/IntentOnboarding.d.ts +0 -67
  134. package/console/dist/ui/pages/intent/IntentOnboarding.js +0 -289
  135. package/console/dist/ui/pages/intent/IntentPage.d.ts +0 -1
  136. package/console/dist/ui/pages/intent/IntentPage.js +0 -252
  137. package/console/dist/ui/pages/pain/PainEvidenceValidators.d.ts +0 -59
  138. package/console/dist/ui/pages/pain/PainEvidenceValidators.js +0 -125
  139. package/console/dist/ui/pages/pain/PainPage.d.ts +0 -1
  140. package/console/dist/ui/pages/pain/PainPage.js +0 -420
  141. package/console/dist/ui/pages/pain/pain-card-helpers.d.ts +0 -175
  142. package/console/dist/ui/pages/pain/pain-card-helpers.js +0 -181
  143. package/console/dist/ui/pages/principles/CorePrinciplesPage.d.ts +0 -10
  144. package/console/dist/ui/pages/principles/CorePrinciplesPage.js +0 -59
  145. package/console/dist/ui/pages/principles/PrincipleDetailPage.d.ts +0 -60
  146. package/console/dist/ui/pages/principles/PrincipleDetailPage.js +0 -602
  147. package/console/dist/ui/pages/principles/PrinciplesPage.d.ts +0 -1
  148. package/console/dist/ui/pages/principles/PrinciplesPage.js +0 -327
  149. package/console/dist/ui/pages/principles/PrinciplesViewNav.d.ts +0 -8
  150. package/console/dist/ui/pages/principles/PrinciplesViewNav.js +0 -15
  151. package/console/dist/ui/pages/report-problem/ReportProblemPage.d.ts +0 -1
  152. package/console/dist/ui/pages/report-problem/ReportProblemPage.js +0 -467
  153. package/console/dist/ui/pages/settings/SettingsPage.d.ts +0 -4
  154. package/console/dist/ui/pages/settings/SettingsPage.js +0 -457
  155. package/console/dist/ui/pages/settings/UpdatePage.d.ts +0 -1
  156. package/console/dist/ui/pages/settings/UpdatePage.js +0 -215
  157. package/console/dist/ui/pages/welcome/WelcomePage.d.ts +0 -5
  158. package/console/dist/ui/pages/welcome/WelcomePage.js +0 -187
  159. package/console/dist/ui/utils/agent-metadata.d.ts +0 -71
  160. package/console/dist/ui/utils/agent-metadata.js +0 -284
  161. package/console/dist/ui/utils/approval-review.d.ts +0 -30
  162. package/console/dist/ui/utils/approval-review.js +0 -50
  163. package/console/dist/ui/utils/approval-warning-localization.d.ts +0 -34
  164. package/console/dist/ui/utils/approval-warning-localization.js +0 -148
  165. package/console/dist/ui/utils/attention-presentation.d.ts +0 -35
  166. package/console/dist/ui/utils/attention-presentation.js +0 -46
  167. package/console/dist/ui/utils/blocker-localization.d.ts +0 -23
  168. package/console/dist/ui/utils/blocker-localization.js +0 -36
  169. package/console/dist/ui/utils/control-center-helpers.d.ts +0 -79
  170. package/console/dist/ui/utils/control-center-helpers.js +0 -189
  171. package/console/dist/ui/utils/enum-labels.d.ts +0 -50
  172. package/console/dist/ui/utils/enum-labels.js +0 -171
  173. package/console/dist/ui/utils/format-date.d.ts +0 -11
  174. package/console/dist/ui/utils/format-date.js +0 -17
  175. package/console/dist/ui/utils/onboarding-state.d.ts +0 -25
  176. package/console/dist/ui/utils/onboarding-state.js +0 -77
  177. package/console/dist/ui/utils/reading-layout.d.ts +0 -20
  178. package/console/dist/ui/utils/reading-layout.js +0 -46
  179. package/console/dist/ui/utils/rule-binding-validators.d.ts +0 -13
  180. package/console/dist/ui/utils/rule-binding-validators.js +0 -188
  181. package/console/dist/ui/utils/runtime-mode.d.ts +0 -11
  182. package/console/dist/ui/utils/runtime-mode.js +0 -11
  183. package/console/dist/ui/utils/token-storage.d.ts +0 -22
  184. package/console/dist/ui/utils/token-storage.js +0 -47
  185. package/console/dist/ui/utils/validators.d.ts +0 -1178
  186. package/console/dist/ui/utils/validators.js +0 -3297
  187. package/release-manager/dist/bootstrap-entry.d.ts +0 -2
  188. package/release-manager/dist/bootstrap-entry.d.ts.map +0 -1
  189. package/release-manager/dist/bootstrap-entry.js.map +0 -1
  190. package/release-manager/dist/i18n.d.ts +0 -6
  191. package/release-manager/dist/i18n.d.ts.map +0 -1
  192. package/release-manager/dist/i18n.js.map +0 -1
  193. package/release-manager/dist/index.d.ts +0 -7
  194. package/release-manager/dist/index.d.ts.map +0 -1
  195. package/release-manager/dist/index.js +0 -656
  196. package/release-manager/dist/index.js.map +0 -1
  197. package/release-manager/dist/installer.d.ts +0 -629
  198. package/release-manager/dist/installer.d.ts.map +0 -1
  199. package/release-manager/dist/installer.js.map +0 -1
  200. package/release-manager/dist/installers/codex-host-installer.d.ts +0 -39
  201. package/release-manager/dist/installers/codex-host-installer.d.ts.map +0 -1
  202. package/release-manager/dist/installers/codex-host-installer.js.map +0 -1
  203. package/release-manager/dist/installers/index.d.ts +0 -51
  204. package/release-manager/dist/installers/index.d.ts.map +0 -1
  205. package/release-manager/dist/installers/index.js.map +0 -1
  206. package/release-manager/dist/installers/openclaw-host-installer.d.ts +0 -14
  207. package/release-manager/dist/installers/openclaw-host-installer.d.ts.map +0 -1
  208. package/release-manager/dist/installers/openclaw-host-installer.js.map +0 -1
  209. package/release-manager/dist/installers/workbuddy-host-installer.d.ts +0 -335
  210. package/release-manager/dist/installers/workbuddy-host-installer.d.ts.map +0 -1
  211. package/release-manager/dist/installers/workbuddy-host-installer.js.map +0 -1
  212. package/release-manager/dist/mvp-config.d.ts +0 -233
  213. package/release-manager/dist/mvp-config.d.ts.map +0 -1
  214. package/release-manager/dist/mvp-config.js.map +0 -1
  215. package/release-manager/dist/prompts.d.ts +0 -43
  216. package/release-manager/dist/prompts.d.ts.map +0 -1
  217. package/release-manager/dist/prompts.js +0 -268
  218. package/release-manager/dist/prompts.js.map +0 -1
  219. package/release-manager/dist/skill-language.d.ts +0 -32
  220. package/release-manager/dist/skill-language.d.ts.map +0 -1
  221. package/release-manager/dist/skill-language.js.map +0 -1
  222. package/release-manager/dist/uninstaller.d.ts +0 -68
  223. package/release-manager/dist/uninstaller.d.ts.map +0 -1
  224. package/release-manager/dist/uninstaller.js +0 -746
  225. package/release-manager/dist/uninstaller.js.map +0 -1
  226. package/release-manager/dist/update/apply-payload.d.ts +0 -143
  227. package/release-manager/dist/update/apply-payload.d.ts.map +0 -1
  228. package/release-manager/dist/update/apply-payload.js.map +0 -1
  229. package/release-manager/dist/update/atomic-file.d.ts +0 -35
  230. package/release-manager/dist/update/atomic-file.d.ts.map +0 -1
  231. package/release-manager/dist/update/atomic-file.js.map +0 -1
  232. package/release-manager/dist/update/bootstrap-executor.d.ts +0 -21
  233. package/release-manager/dist/update/bootstrap-executor.d.ts.map +0 -1
  234. package/release-manager/dist/update/bootstrap-executor.js.map +0 -1
  235. package/release-manager/dist/update/bootstrap-protocol.d.ts +0 -56
  236. package/release-manager/dist/update/bootstrap-protocol.d.ts.map +0 -1
  237. package/release-manager/dist/update/bootstrap-protocol.js.map +0 -1
  238. package/release-manager/dist/update/channel-metadata.d.ts +0 -28
  239. package/release-manager/dist/update/channel-metadata.d.ts.map +0 -1
  240. package/release-manager/dist/update/channel-metadata.js.map +0 -1
  241. package/release-manager/dist/update/channel-promotion.d.ts +0 -39
  242. package/release-manager/dist/update/channel-promotion.d.ts.map +0 -1
  243. package/release-manager/dist/update/channel-promotion.js +0 -55
  244. package/release-manager/dist/update/channel-promotion.js.map +0 -1
  245. package/release-manager/dist/update/console-surface.d.ts +0 -19
  246. package/release-manager/dist/update/console-surface.d.ts.map +0 -1
  247. package/release-manager/dist/update/console-surface.js.map +0 -1
  248. package/release-manager/dist/update/data-compatibility.d.ts +0 -35
  249. package/release-manager/dist/update/data-compatibility.d.ts.map +0 -1
  250. package/release-manager/dist/update/data-compatibility.js +0 -45
  251. package/release-manager/dist/update/data-compatibility.js.map +0 -1
  252. package/release-manager/dist/update/install-layout.d.ts +0 -105
  253. package/release-manager/dist/update/install-layout.d.ts.map +0 -1
  254. package/release-manager/dist/update/install-layout.js.map +0 -1
  255. package/release-manager/dist/update/product-identity.d.ts +0 -70
  256. package/release-manager/dist/update/product-identity.d.ts.map +0 -1
  257. package/release-manager/dist/update/product-identity.js.map +0 -1
  258. package/release-manager/dist/update/release-asset-manifest.d.ts +0 -31
  259. package/release-manager/dist/update/release-asset-manifest.d.ts.map +0 -1
  260. package/release-manager/dist/update/release-asset-manifest.js.map +0 -1
  261. package/release-manager/dist/update/release-identity.d.ts +0 -33
  262. package/release-manager/dist/update/release-identity.d.ts.map +0 -1
  263. package/release-manager/dist/update/release-identity.js.map +0 -1
  264. package/release-manager/dist/update/release-manager-authority.d.ts +0 -39
  265. package/release-manager/dist/update/release-manager-authority.d.ts.map +0 -1
  266. package/release-manager/dist/update/release-manager-authority.js.map +0 -1
  267. package/release-manager/dist/update/release-manager.d.ts +0 -156
  268. package/release-manager/dist/update/release-manager.d.ts.map +0 -1
  269. package/release-manager/dist/update/release-manager.js.map +0 -1
  270. package/release-manager/dist/update/release-metadata-publisher.d.ts +0 -199
  271. package/release-manager/dist/update/release-metadata-publisher.d.ts.map +0 -1
  272. package/release-manager/dist/update/release-metadata-publisher.js +0 -478
  273. package/release-manager/dist/update/release-metadata-publisher.js.map +0 -1
  274. package/release-manager/dist/update/release-metadata-source.d.ts +0 -85
  275. package/release-manager/dist/update/release-metadata-source.d.ts.map +0 -1
  276. package/release-manager/dist/update/release-metadata-source.js.map +0 -1
  277. package/release-manager/dist/update/release-metadata.d.ts +0 -60
  278. package/release-manager/dist/update/release-metadata.d.ts.map +0 -1
  279. package/release-manager/dist/update/release-metadata.js.map +0 -1
  280. package/release-manager/dist/update/release-policy.d.ts +0 -51
  281. package/release-manager/dist/update/release-policy.d.ts.map +0 -1
  282. package/release-manager/dist/update/release-policy.js.map +0 -1
  283. package/release-manager/dist/update/rollback-policy.d.ts +0 -56
  284. package/release-manager/dist/update/rollback-policy.d.ts.map +0 -1
  285. package/release-manager/dist/update/rollback-policy.js +0 -61
  286. package/release-manager/dist/update/rollback-policy.js.map +0 -1
  287. package/release-manager/dist/update/transaction-journal.d.ts +0 -212
  288. package/release-manager/dist/update/transaction-journal.d.ts.map +0 -1
  289. package/release-manager/dist/update/transaction-journal.js.map +0 -1
  290. package/release-manager/dist/update/trust-metadata.d.ts +0 -55
  291. package/release-manager/dist/update/trust-metadata.d.ts.map +0 -1
  292. package/release-manager/dist/update/trust-metadata.js.map +0 -1
  293. package/release-manager/dist/update/trust-root-provisioning.d.ts +0 -99
  294. package/release-manager/dist/update/trust-root-provisioning.d.ts.map +0 -1
  295. package/release-manager/dist/update/trust-root-provisioning.js.map +0 -1
  296. package/release-manager/dist/update/update-history.d.ts +0 -46
  297. package/release-manager/dist/update/update-history.d.ts.map +0 -1
  298. package/release-manager/dist/update/update-history.js +0 -123
  299. package/release-manager/dist/update/update-history.js.map +0 -1
  300. package/release-manager/dist/utils/config-file-io.d.ts +0 -34
  301. package/release-manager/dist/utils/config-file-io.d.ts.map +0 -1
  302. package/release-manager/dist/utils/config-file-io.js.map +0 -1
  303. package/release-manager/dist/utils/env.d.ts +0 -94
  304. package/release-manager/dist/utils/env.d.ts.map +0 -1
  305. package/release-manager/dist/utils/env.js.map +0 -1
  306. package/release-manager/dist/utils/logger.d.ts +0 -15
  307. package/release-manager/dist/utils/logger.d.ts.map +0 -1
  308. package/release-manager/dist/utils/logger.js.map +0 -1
  309. package/release-manager/dist/utils/workspace-divergence.d.ts +0 -36
  310. package/release-manager/dist/utils/workspace-divergence.d.ts.map +0 -1
  311. package/release-manager/dist/utils/workspace-divergence.js.map +0 -1
@@ -1,421 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * FailedTasksPage — Pipeline observability page showing failed or
4
- * needs_human_review tasks, grouped by taskKind.
5
- *
6
- * Part of the PD feedback-pipeline observability spec (Task 10).
7
- * Gated by the `failed_tasks_observability` feature flag. When the flag is
8
- * disabled, the backend returns 403 and this page shows a "disabled" state
9
- * with guidance on how to enable the flag.
10
- *
11
- * Each row exposes a "Create Feedback Draft" button that deep-links into the
12
- * Report Problem page with the task/pain context pre-filled.
13
- *
14
- * Governance Recovery Actions v1: when the `failed_task_recovery_console`
15
- * feature flag is enabled, each row also exposes a "Resume Evolution" button.
16
- * Recovery is an Owner governance action (never auto-triggered on load): it
17
- * always goes through an explicit confirmation dialog before POSTing
18
- * /api/v1/failed-tasks/:id/recover. The success state means "Recovery
19
- * Accepted" — the task is pending again; execution is asynchronous.
20
- *
21
- * PRI-747 F22 / PRI-749: each row can be expanded into its detail — the full
22
- * task record plus run history — fetched lazily from the pre-existing
23
- * GET /api/v1/failed-tasks/:id endpoint (server route +
24
- * SqliteTaskStore.getFailedTaskDetail already existed; the UI simply never
25
- * consumed them).
26
- *
27
- * Runtime Contract (rc-*):
28
- * - rc-1: API response is treated as `unknown` and validated before use
29
- * - rc-2: no `as` type assertions — runtime type guards only
30
- * - rc-5: `Object.hasOwn()` for untrusted object keys
31
- * - rc-9: every error/disabled path surfaces a reason + next action
32
- */
33
- import { useState, useEffect, useCallback, useRef, Fragment } from "react";
34
- import { useNavigate, useSearchParams } from "react-router-dom";
35
- import { useTranslation } from "react-i18next";
36
- import { toast } from "sonner";
37
- import { PageShell } from "../../components/layout/page-shell.js";
38
- import { PageLoading } from "../../components/layout/page-loading.js";
39
- import { Badge } from "../../components/ui/badge.js";
40
- import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card.js";
41
- import { Button } from "../../components/ui/button.js";
42
- import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, } from "../../components/ui/alert-dialog.js";
43
- import { ShinyText } from "../../components/ui/shiny-text.js";
44
- import { formatDate } from "../../utils/format-date.js";
45
- import i18n from "../../i18n/index.js";
46
- import { recoverFailedTask, fetchConfigSummary, fetchFailedTaskDetail, request } from "../../api.js";
47
- // Same component set the former utils/format.js rendered, now served by the
48
- // defensive single-owner formatter: an invalid date string returns the raw
49
- // input instead of Intl throwing RangeError mid-render (review round of
50
- // PRI-747 F22 — utils/format.js was retired with this migration).
51
- const DATETIME_FORMAT_OPTS = {
52
- year: "numeric",
53
- month: "2-digit",
54
- day: "2-digit",
55
- hour: "2-digit",
56
- minute: "2-digit",
57
- };
58
- function formatDateTime(iso) {
59
- return formatDate(iso, i18n.language, DATETIME_FORMAT_OPTS);
60
- }
61
- // ── Runtime validators (rc-1, rc-2, rc-5) ───────────────────────────────────
62
- //
63
- // The API response is `unknown` until these guards run. No `as` casts.
64
- function isObject(v) {
65
- return typeof v === "object" && v !== null && !Array.isArray(v);
66
- }
67
- function isString(v) {
68
- return typeof v === "string";
69
- }
70
- function isNumber(v) {
71
- return typeof v === "number" && !Number.isNaN(v);
72
- }
73
- function readString(v, key) {
74
- if (!Object.hasOwn(v, key))
75
- return null;
76
- return isString(v[key]) ? v[key] : null;
77
- }
78
- function readNullableString(v, key) {
79
- if (!Object.hasOwn(v, key))
80
- return null;
81
- const val = v[key];
82
- if (val === null)
83
- return null;
84
- return isString(val) ? val : null;
85
- }
86
- function readNumber(v, key) {
87
- if (!Object.hasOwn(v, key))
88
- return null;
89
- return isNumber(v[key]) ? v[key] : null;
90
- }
91
- function validateTask(v) {
92
- if (!isObject(v))
93
- return null;
94
- const taskId = readString(v, "taskId");
95
- const taskKind = readString(v, "taskKind");
96
- const status = readString(v, "status");
97
- const attemptCount = readNumber(v, "attemptCount");
98
- const maxAttempts = readNumber(v, "maxAttempts");
99
- const createdAt = readString(v, "createdAt");
100
- // Required fields — fail loud when missing or wrong type (rc-3)
101
- if (taskId === null ||
102
- taskKind === null ||
103
- status === null ||
104
- attemptCount === null ||
105
- maxAttempts === null ||
106
- createdAt === null) {
107
- return null;
108
- }
109
- const painId = readNullableString(v, "painId");
110
- const lastError = readNullableString(v, "lastError");
111
- const lastAttemptAt = readNullableString(v, "lastAttemptAt");
112
- const ownerDecisionRequired = Object.hasOwn(v, "ownerDecisionRequired")
113
- ? v.ownerDecisionRequired === true
114
- : undefined;
115
- return {
116
- taskId,
117
- taskKind,
118
- painId,
119
- status,
120
- lastError,
121
- attemptCount,
122
- maxAttempts,
123
- createdAt,
124
- lastAttemptAt,
125
- ...(ownerDecisionRequired !== undefined ? { ownerDecisionRequired } : {}),
126
- };
127
- }
128
- function validateFailedTasksData(v) {
129
- if (!isObject(v))
130
- return null;
131
- if (!Object.hasOwn(v, "tasks"))
132
- return null;
133
- const tasksRaw = v.tasks;
134
- if (!Array.isArray(tasksRaw))
135
- return null;
136
- const tasks = [];
137
- for (const item of tasksRaw) {
138
- const task = validateTask(item);
139
- if (task === null)
140
- return null;
141
- tasks.push(task);
142
- }
143
- const total = readNumber(v, "total");
144
- if (total === null)
145
- return null;
146
- const nextAction = readNullableString(v, "nextAction");
147
- // PRI-955 (F11): optional pipeline health block — an invalid block is
148
- // omitted (never fails the whole payload), the page then renders nothing.
149
- let pipeline;
150
- if (Object.hasOwn(v, "pipeline") && isObject(v.pipeline)) {
151
- const p = v.pipeline;
152
- const status = readString(p, "status");
153
- const note = readString(p, "note");
154
- if (status === "healthy" || status === "backlogged" || status === "stalled" || status === "disabled" || status === "unknown") {
155
- if (note !== null) {
156
- pipeline = {
157
- status,
158
- pendingCount: readNumber(p, "pendingCount"),
159
- oldestPendingTaskKind: readNullableString(p, "oldestPendingTaskKind"),
160
- oldestPendingAgeMinutes: readNumber(p, "oldestPendingAgeMinutes"),
161
- lastProgressAt: readNullableString(p, "lastProgressAt"),
162
- note,
163
- nextAction: readNullableString(p, "nextAction") ?? undefined,
164
- };
165
- }
166
- }
167
- }
168
- return { tasks, total, nextAction: nextAction ?? undefined, ...(pipeline ? { pipeline } : {}) };
169
- }
170
- // ── Helpers ──────────────────────────────────────────────────────────────────
171
- function truncateId(id) {
172
- return id.length > 12 ? id.slice(0, 12) + "…" : id;
173
- }
174
- function statusToVariant(status) {
175
- if (status === "failed")
176
- return "destructive";
177
- if (status === "needs_human_review")
178
- return "amber";
179
- return "default";
180
- }
181
- /** A failed task whose retry budget is spent — only a force recovery can requeue it. */
182
- function isAttemptBudgetExhausted(task) {
183
- return task.status === "failed" && task.attemptCount >= task.maxAttempts;
184
- }
185
- function groupTasksByKind(tasks) {
186
- const grouped = new Map();
187
- for (const task of tasks) {
188
- const existing = grouped.get(task.taskKind) ?? [];
189
- existing.push(task);
190
- grouped.set(task.taskKind, existing);
191
- }
192
- return grouped;
193
- }
194
- // ── Component ────────────────────────────────────────────────────────────────
195
- export function FailedTasksPage() {
196
- const { t } = useTranslation();
197
- const navigate = useNavigate();
198
- const [searchParams] = useSearchParams();
199
- // ?taskId= deep link (治理焦点「查看恢复详情」直达): initial value only —
200
- // the row auto-expands once; the Owner can still collapse/switch freely.
201
- const deepLinkTaskId = searchParams.get("taskId") ?? undefined;
202
- const [state, setState] = useState({ status: "loading" });
203
- // Governance Recovery Actions v1 — flag-gated recovery UI (fail-closed:
204
- // absent/false → no recovery entry, Console stays read-only).
205
- const [recoveryEnabled, setRecoveryEnabled] = useState(false);
206
- const [recoverTarget, setRecoverTarget] = useState(null);
207
- const [recoverLoading, setRecoverLoading] = useState(false);
208
- // Exhausted targets recover with force: the dialog warns first and the
209
- // action button becomes 强制恢复 (server refuses force=false for these).
210
- const recoverExhausted = recoverTarget !== null && isAttemptBudgetExhausted(recoverTarget);
211
- let recoverConfirmLabel = t("pages.failedTasks.recoverConfirmButton");
212
- if (recoverLoading) {
213
- recoverConfirmLabel = t("common.loading");
214
- }
215
- else if (recoverExhausted) {
216
- recoverConfirmLabel = t("pages.failedTasks.recoverConfirmForceButton");
217
- }
218
- useEffect(() => {
219
- let cancelled = false;
220
- fetchConfigSummary()
221
- .then((result) => {
222
- if (cancelled)
223
- return;
224
- // Fail-closed: only an explicitly enabled flag shows the action
225
- if (result.success && result.data) {
226
- const flag = result.data.features.find((f) => f.id === "failed_task_recovery_console");
227
- setRecoveryEnabled(flag?.enabled === true);
228
- }
229
- })
230
- .catch(() => {
231
- // rc-9: flag probe failure keeps recovery hidden (fail-closed)
232
- });
233
- return () => {
234
- cancelled = true;
235
- };
236
- }, []);
237
- const loadData = useCallback(async () => {
238
- setState({ status: "loading" });
239
- // Shared request(): same auth header and global 401 session-expiry
240
- // handling as every other page (PRI-643 — a manual fetch here bypassed
241
- // session-expiry routing entirely).
242
- const result = await request("/api/v1/failed-tasks", undefined, validateFailedTasksData);
243
- if (result.success) {
244
- setState({ status: "loaded", data: result.data });
245
- return;
246
- }
247
- // 403 = feature flag disabled — show disabled state (rc-9: surface reason)
248
- if (result.status === 403) {
249
- setState({ status: "disabled" });
250
- return;
251
- }
252
- setState({ status: "error", message: result.error, nextAction: result.nextAction });
253
- }, []);
254
- useEffect(() => {
255
- loadData();
256
- }, [loadData]);
257
- const handleCreateDraft = (task) => {
258
- const params = new URLSearchParams({
259
- source: "failed_tasks_page",
260
- taskId: task.taskId,
261
- });
262
- if (task.painId) {
263
- params.set("painId", task.painId);
264
- }
265
- navigate(`/report-problem?${params.toString()}`);
266
- };
267
- // Owner-confirmed recovery (SPEC §6.3: explicit confirmation only — this
268
- // handler runs exclusively from the dialog's action button, never on load).
269
- const handleRecoverConfirmed = async () => {
270
- if (!recoverTarget || recoverLoading)
271
- return;
272
- setRecoverLoading(true);
273
- try {
274
- const result = await recoverFailedTask(recoverTarget.taskId, undefined, recoverExhausted);
275
- if (result.success) {
276
- toast.success(t("pages.failedTasks.recoverSuccess"));
277
- setRecoverTarget(null);
278
- await loadData();
279
- }
280
- else {
281
- // rc-9: server-provided reason + next action accompany the failure
282
- const detail = result.nextAction
283
- ? `${result.error} — ${result.nextAction}`
284
- : result.error;
285
- toast.error(`${t("pages.failedTasks.recoverFailed")}: ${detail}`);
286
- }
287
- }
288
- catch (err) {
289
- toast.error(`${t("pages.failedTasks.recoverFailed")}: ${err instanceof Error ? err.message : "Network error"}`);
290
- }
291
- finally {
292
- setRecoverLoading(false);
293
- }
294
- };
295
- return (_jsxs(PageShell, { children: [_jsxs("div", { className: "mb-8", children: [_jsx("div", { className: "font-mono text-[11px] uppercase tracking-[0.14em] text-ink-3", children: t("pages.failedTasks.title") }), _jsx(ShinyText, { as: "h1", className: "text-[29px] font-semibold tracking-tight text-ink mt-3 mb-2", duration: 4.5, brightness: 0.5, disabled: state.status !== "loaded" || state.data.tasks.length === 0, children: t("pages.failedTasks.title") }), _jsx("p", { className: "text-ink-3 text-sm leading-relaxed max-w-[712px]", children: t("pages.failedTasks.subtitle") })] }), state.status === "loading" && (_jsx(PageLoading, { cardCount: 3, label: t("common.loading") })), state.status === "disabled" && (_jsxs("div", { className: "py-8", children: [_jsx("div", { className: "p-5 bg-panel border border-amber/20 rounded-[var(--radius-md)] text-amber text-[13px] leading-relaxed", children: t("pages.failedTasks.disabled") }), _jsx("div", { className: "mt-4", children: _jsx(Button, { variant: "outline", size: "sm", onClick: loadData, children: t("common.refresh") }) })] })), state.status === "error" && (_jsxs("div", { className: "py-8", children: [_jsxs("div", { className: "text-danger text-sm mb-2", children: [t("pages.failedTasks.error"), ": ", state.message] }), state.nextAction && (_jsx("div", { className: "text-ink-3 text-[13px] mb-3", children: state.nextAction })), _jsx(Button, { variant: "outline", size: "sm", onClick: loadData, children: t("common.refresh") })] })), state.status === "loaded" && (_jsx("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: _jsx(LoadedContent, { data: state.data, onCreateDraft: handleCreateDraft, onRecover: recoveryEnabled ? (task) => setRecoverTarget(task) : undefined, initialExpandedTaskId: deepLinkTaskId, t: t }) })), _jsx(AlertDialog, { open: recoverTarget !== null, onOpenChange: (open) => {
296
- if (!open && !recoverLoading) {
297
- setRecoverTarget(null);
298
- }
299
- }, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: t("pages.failedTasks.recoverConfirmTitle") }), _jsx(AlertDialogDescription, { asChild: true, children: _jsxs("div", { className: "space-y-2 text-[13px] leading-relaxed", children: [_jsxs("div", { children: [_jsxs("span", { className: "font-medium", children: [t("pages.failedTasks.recoverConfirmStatus"), ":"] }), " ", _jsx("span", { className: "font-mono", children: recoverTarget?.status ?? "—" })] }), recoverExhausted && (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsxs("span", { className: "font-medium", children: [t("pages.failedTasks.recoverConfirmAttempts"), ":"] }), " ", _jsxs("span", { className: "font-mono", children: [recoverTarget?.attemptCount, " / ", recoverTarget?.maxAttempts] })] }), _jsx("div", { className: "text-danger", children: t("pages.failedTasks.recoverExhaustedWarning", {
300
- attempt: recoverTarget?.attemptCount ?? 0,
301
- max: recoverTarget?.maxAttempts ?? 0,
302
- }) })] })), _jsxs("div", { children: [_jsxs("span", { className: "font-medium", children: [t("pages.failedTasks.recoverConfirmActionLabel"), ":"] }), " ", t("pages.failedTasks.recoverConfirmActionDesc")] }), _jsxs("div", { children: [_jsxs("span", { className: "font-medium", children: [t("pages.failedTasks.recoverConfirmImpactLabel"), ":"] }), " ", recoverExhausted
303
- ? t("pages.failedTasks.recoverConfirmForceImpactDesc")
304
- : t("pages.failedTasks.recoverConfirmImpactDesc")] })] }) })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: recoverLoading, children: t("common.cancel") }), _jsx(AlertDialogAction, { disabled: recoverLoading, onClick: (event) => {
305
- // Prevent Radix from auto-closing before the async POST
306
- // resolves; handleRecoverConfirmed closes on success.
307
- event.preventDefault();
308
- void handleRecoverConfirmed();
309
- }, children: recoverConfirmLabel })] })] }) })] }));
310
- }
311
- /** PRI-955 (F11): derived learning-queue health block — never claims health
312
- * from the failed-task count alone. Facts come from the server projection. */
313
- function PipelineHealthCard({ pipeline, t }) {
314
- const statusLabel = pipeline.status === "healthy" ? t("pages.failedTasks.pipeline.statusHealthy")
315
- : pipeline.status === "backlogged" ? t("pages.failedTasks.pipeline.statusBacklogged")
316
- : pipeline.status === "stalled" ? t("pages.failedTasks.pipeline.statusStalled")
317
- : t("pages.failedTasks.pipeline.statusUnknown");
318
- const variant = pipeline.status === "healthy" ? "default"
319
- : pipeline.status === "stalled" ? "destructive"
320
- : pipeline.status === "backlogged" ? "amber"
321
- : "default";
322
- return (_jsxs("div", { className: "p-5 bg-panel border border-line rounded-[var(--radius-md)] mb-4", children: [_jsxs("div", { className: "flex items-center gap-3 mb-2", children: [_jsx("span", { className: "text-[15px] font-semibold text-ink", children: t("pages.failedTasks.pipeline.title") }), _jsx("span", { className: `text-xs px-2 py-0.5 rounded-full border ${variant === "destructive" ? "border-red-300 text-red-700 bg-red-50"
323
- : variant === "amber" ? "border-amber-300 text-amber-700 bg-amber-50"
324
- : "border-line text-ink-3 bg-canvas"}`, children: statusLabel })] }), _jsx("p", { className: "text-ink-3 text-sm leading-relaxed", children: pipeline.note }), pipeline.nextAction && (_jsxs("p", { className: "text-ink-3 text-sm leading-relaxed mt-1", children: [t("pages.failedTasks.pipeline.nextActionPrefix"), pipeline.nextAction] }))] }));
325
- }
326
- function LoadedContent({ data, onCreateDraft, onRecover, initialExpandedTaskId, t }) {
327
- const hasTasks = data.tasks.length > 0;
328
- // Empty state — surface nextAction from the API (rc-9: no silent fallback).
329
- // PRI-955: the empty copy no longer claims the pipeline is healthy; the
330
- // derived queue-health block carries that judgment with facts.
331
- if (!hasTasks) {
332
- return (_jsxs("div", { className: "py-8", children: [_jsxs("div", { className: "p-5 bg-panel border border-line rounded-[var(--radius-md)]", children: [_jsx("h3", { className: "text-[17px] font-semibold text-ink mb-2", children: t("pages.failedTasks.empty") }), data.nextAction && (_jsx("p", { className: "text-ink-3 text-sm leading-relaxed", children: data.nextAction }))] }), data.pipeline && _jsx(PipelineHealthCard, { pipeline: data.pipeline, t: t })] }));
333
- }
334
- const grouped = groupTasksByKind(data.tasks);
335
- const sortedKinds = Array.from(grouped.keys()).sort();
336
- return (_jsxs(_Fragment, { children: [data.pipeline && _jsx(PipelineHealthCard, { pipeline: data.pipeline, t: t }), _jsx("div", { className: "space-y-6", children: sortedKinds.map((kind) => {
337
- const tasks = grouped.get(kind);
338
- if (!tasks || tasks.length === 0)
339
- return null;
340
- return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Badge, { variant: "outline", children: t("pages.failedTasks.kind") }), _jsxs(CardTitle, { className: "text-[15px] font-mono", children: [kind, " (", tasks.length, ")"] })] }) }), _jsx(CardContent, { children: _jsx(TaskTable, { tasks: tasks, onCreateDraft: onCreateDraft, onRecover: onRecover, initialExpandedTaskId: initialExpandedTaskId, t: t }) })] }, kind));
341
- }) }), _jsxs("div", { className: "mt-8 font-mono text-[11px] text-ink-4 space-y-1", children: [_jsxs("div", { children: [t("common.total"), ": ", data.total] }), data.total > data.tasks.length && (_jsx("div", { className: "text-amber/80 leading-relaxed", children: t("pages.failedTasks.partialResults")
342
- .replace("{shown}", String(data.tasks.length))
343
- .replace("{total}", String(data.total)) }))] })] }));
344
- }
345
- function TaskTable({ tasks, onCreateDraft, onRecover, initialExpandedTaskId, t }) {
346
- // PRI-747 F22: one row at a time is expanded into its detail panel.
347
- // ?taskId= deep link seeds the initial expansion (only when this table
348
- // actually contains the task); after mount it behaves like a normal toggle.
349
- const deepLinkTask = initialExpandedTaskId !== undefined
350
- ? tasks.find((task) => task.taskId === initialExpandedTaskId)
351
- : undefined;
352
- const [expandedId, setExpandedId] = useState(deepLinkTask?.taskId ?? null);
353
- const [detail, setDetail] = useState({ status: "idle" });
354
- // Drop responses from a superseded expand/collapse (rc-7: only the newest
355
- // request's result may become current state).
356
- const detailRequestIdRef = useRef(0);
357
- const deepLinkFetchedRef = useRef(false);
358
- // Fetch (or refetch) one task's detail; a stale response from a superseded
359
- // request is dropped (rc-7: only the newest request's result may land).
360
- const requestDetail = useCallback(async (taskId) => {
361
- const requestId = ++detailRequestIdRef.current;
362
- setDetail({ status: "loading", taskId });
363
- // Lazy fetch on expand: the list view stays cheap; the run history is
364
- // only loaded when the Owner actually asks for this task's detail.
365
- const result = await fetchFailedTaskDetail(taskId);
366
- if (requestId !== detailRequestIdRef.current)
367
- return;
368
- if (result.success) {
369
- setDetail({ status: "loaded", taskId, data: result.data });
370
- }
371
- else {
372
- // rc-9: surface the server-provided reason + next action (404 = the
373
- // task recovered/changed state after the list was loaded).
374
- setDetail({ status: "error", taskId, message: result.error, nextAction: result.nextAction });
375
- }
376
- }, []);
377
- // Deep link (?taskId=): fetch the linked task's detail and scroll its row
378
- // into view once. The table mounts only after the task list has loaded, so
379
- // the row is present at first effect run. The URL param is untrusted
380
- // (rc-1): CSS.escape (unquoted attribute selector) keeps a crafted id from
381
- // breaking the selector.
382
- useEffect(() => {
383
- if (deepLinkTask === undefined || deepLinkFetchedRef.current)
384
- return;
385
- deepLinkFetchedRef.current = true;
386
- void requestDetail(deepLinkTask.taskId);
387
- const row = document.querySelector(`[data-testid=${CSS.escape(`detail-toggle-${deepLinkTask.taskId}`)}]`);
388
- row?.scrollIntoView({ block: "center" });
389
- }, [deepLinkTask, requestDetail]);
390
- const toggleDetail = useCallback((taskId) => {
391
- if (expandedId === taskId) {
392
- // Invalidate any in-flight response, then collapse.
393
- detailRequestIdRef.current += 1;
394
- setExpandedId(null);
395
- setDetail({ status: "idle" });
396
- return;
397
- }
398
- setExpandedId(taskId);
399
- void requestDetail(taskId);
400
- }, [expandedId, requestDetail]);
401
- return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto_auto] gap-3 pb-2 border-b border-line font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: [_jsx("span", { "aria-hidden": "true" }), _jsx("span", { children: t("pages.failedTasks.taskId") }), _jsx("span", { children: t("pages.failedTasks.painId") }), _jsx("span", { children: t("pages.failedTasks.status") }), _jsx("span", { children: t("pages.failedTasks.lastError") }), _jsx("span", { children: t("pages.failedTasks.attempts") }), _jsx("span", { children: t("pages.failedTasks.lastAttempt") }), _jsx("span", { "aria-hidden": "true" }), _jsx("span", { "aria-hidden": "true" })] }), tasks.map((task) => {
402
- const isExpanded = expandedId === task.taskId;
403
- return (_jsxs(Fragment, { children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto_auto] gap-3 py-2 items-center text-[13px] border-b border-line/50 last:border-b-0", children: [_jsx(Button, { variant: "quiet", size: "icon", className: "h-6 w-6 shrink-0 text-[12px]", onClick: () => toggleDetail(task.taskId), "aria-label": t("pages.failedTasks.detailToggle"), "aria-expanded": isExpanded, "data-testid": `detail-toggle-${task.taskId}`, children: isExpanded ? "▾" : "▸" }), _jsx("span", { className: "font-mono text-ink-2 truncate", title: task.taskId, children: truncateId(task.taskId) }), _jsx("span", { className: "font-mono text-ink-3 truncate", title: task.painId ?? "", children: task.painId ? truncateId(task.painId) : t("pages.failedTasks.noPainId") }), _jsx(Badge, { variant: statusToVariant(task.status), children: task.status }), _jsx("span", { className: "text-ink-3 truncate", title: task.lastError ?? "", children: task.lastError ?? "—" }), _jsx("span", { className: "font-mono text-ink-2 text-center", children: task.attemptCount }), _jsx("span", { className: "font-mono text-ink-3 text-[12px]", children: task.lastAttemptAt ? formatDateTime(task.lastAttemptAt) : "—" }), task.ownerDecisionRequired === true ? (_jsxs("span", { className: "whitespace-nowrap text-[12px]", children: [_jsx("span", { className: "text-amber", children: t("pages.failedTasks.awaitingOwnerDecision") }), " ", _jsx("a", { href: "#/focus", className: "text-gov underline underline-offset-2 hover:text-gov/80", "data-testid": `go-focus-${task.taskId}`, children: t("pages.failedTasks.goGovernanceFocus") })] })) : onRecover ? (_jsx(Button, { variant: "default", size: "sm", onClick: () => onRecover(task), className: "whitespace-nowrap", children: t("pages.failedTasks.recover") })) : null, _jsx(Button, { variant: "outline", size: "sm", onClick: () => onCreateDraft(task), className: "whitespace-nowrap", children: t("pages.failedTasks.createDraft") })] }), isExpanded && (_jsx("div", { className: "border border-line/60 rounded-[var(--radius-md)] bg-panel/60 p-4 mb-2", "data-testid": `detail-panel-${task.taskId}`, children: _jsx(TaskDetailPanel, { state: detail.status === "idle" ? { status: "loading", taskId: task.taskId } : detail, taskId: task.taskId, onRetry: requestDetail, t: t }) }))] }, task.taskId));
404
- })] }));
405
- }
406
- function TaskDetailPanel({ state, taskId, onRetry, t }) {
407
- if (state.status === "loading") {
408
- return (_jsx("div", { className: "text-ink-3 text-[13px]", "data-testid": "detail-loading", children: t("common.loading") }));
409
- }
410
- if (state.status === "error") {
411
- // rc-9: reason + next action + an explicit retry affordance
412
- return (_jsxs("div", { className: "text-[13px] space-y-2", "data-testid": "detail-error", children: [_jsxs("div", { className: "text-danger", children: [t("pages.failedTasks.detailError"), ": ", state.message] }), state.nextAction && _jsx("div", { className: "text-ink-3", children: state.nextAction }), _jsx("div", { children: _jsx(Button, { variant: "outline", size: "sm", onClick: () => onRetry(taskId), children: t("common.refresh") }) })] }));
413
- }
414
- // The idle state only occurs if the panel renders for a task whose fetch was
415
- // superseded mid-flight; treat it as loading for the row just expanded.
416
- if (state.status === "idle" || state.taskId !== taskId) {
417
- return (_jsx("div", { className: "text-ink-3 text-[13px]", "data-testid": "detail-loading", children: t("common.loading") }));
418
- }
419
- const { data } = state;
420
- return (_jsxs("div", { className: "space-y-4 text-[13px]", children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)] gap-x-4 gap-y-1 font-mono text-[12px]", children: [_jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.taskId") }), _jsx("span", { className: "text-ink-2 break-all", children: data.taskId }), _jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.kind") }), _jsx("span", { className: "text-ink-2", children: data.taskKind }), _jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.status") }), _jsx("span", { className: "text-ink-2", children: data.status }), _jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.createdAt") }), _jsx("span", { className: "text-ink-3", children: formatDateTime(data.createdAt) }), data.updatedAt && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.updatedAt") }), _jsx("span", { className: "text-ink-3", children: formatDateTime(data.updatedAt) })] })), _jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.attempts") }), _jsxs("span", { className: "text-ink-2", children: [data.attemptCount, " / ", data.maxAttempts] }), _jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.lastError") }), _jsx("span", { className: data.lastError ? "text-danger" : "text-ink-3", children: data.lastError ?? "—" })] }), _jsxs("div", { children: [_jsxs("div", { className: "font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4 mb-2", children: [t("pages.failedTasks.runsTitle"), " (", data.runs.length, ")"] }), data.runs.length === 0 ? (_jsx("div", { className: "text-ink-3", children: t("pages.failedTasks.noRuns") })) : (_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "grid grid-cols-[auto_auto_minmax(0,1fr)_minmax(0,1fr)] gap-3 pb-1 border-b border-line/50 font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: [_jsx("span", { children: t("pages.failedTasks.runAttempt") }), _jsx("span", { children: t("pages.failedTasks.status") }), _jsx("span", { children: t("pages.failedTasks.runStarted") }), _jsx("span", { children: t("pages.failedTasks.runReason") })] }), data.runs.map((run) => (_jsxs("div", { className: "grid grid-cols-[auto_auto_minmax(0,1fr)_minmax(0,1fr)] gap-3 py-1 text-[12px] border-b border-line/30 last:border-b-0", title: `${t("pages.failedTasks.runEnded")}: ${run.endedAt ? formatDateTime(run.endedAt) : "—"}${run.errorCategory ? ` · ${t("pages.failedTasks.runErrorCategory")}: ${run.errorCategory}` : ""}`, children: [_jsxs("span", { className: "font-mono text-ink-2", children: ["#", run.attemptNumber] }), _jsx("span", { className: "font-mono text-ink-3", children: run.executionStatus }), _jsx("span", { className: "font-mono text-ink-3", children: formatDateTime(run.startedAt) }), _jsx("span", { className: "text-ink-3 break-words", title: run.reason ?? undefined, children: run.reason ?? (run.errorCategory ?? "—") })] }, run.runId)))] }))] })] }));
421
- }
@@ -1,24 +0,0 @@
1
- import type { ApprovalsGroupedData } from '../../api.js';
2
- export declare function ApprovalSubjectReading({ principleId, approvalId, artifactId, channel, lockedReason, targetHost, onResolved, revisionReview, revisionReviewUnavailable, allowedActions, budgetNotice, promptInjection, sourceUnlinked, showPrincipleLink }: {
3
- principleId: string;
4
- approvalId: string;
5
- artifactId: string;
6
- channel: string;
7
- lockedReason?: string;
8
- targetHost?: 'openclaw' | 'codex' | 'workbuddy';
9
- onResolved?: (message: string) => void;
10
- revisionReview?: {
11
- artifactId?: string;
12
- artifactDigest?: string;
13
- oldStatement: string;
14
- newStatement: string;
15
- intentFields: Record<string, string>;
16
- };
17
- revisionReviewUnavailable?: boolean;
18
- /** Backend-assessed actions for THIS approval; absent means no per-action gate (the guarded submit still applies). */
19
- allowedActions?: readonly ('approve' | 'reject' | 'edit_approval')[];
20
- budgetNotice?: string;
21
- promptInjection?: ApprovalsGroupedData['promptInjection'];
22
- sourceUnlinked?: boolean;
23
- showPrincipleLink?: boolean;
24
- }): import("react").JSX.Element;
@@ -1,133 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useRef, useState } from 'react';
3
- import { Link, useLocation } from 'react-router-dom';
4
- import { useTranslation } from 'react-i18next';
5
- import { fetchApprovalReview, fetchOwnerDecisionView, approveApproval, rejectApproval, editApproval, fetchAllActivations } from '../../api.js';
6
- import { localizeApprovalWarning } from '../../utils/approval-warning-localization.js';
7
- import { truncateByCodePoints } from '../../utils/reading-layout.js';
8
- export function ApprovalSubjectReading({ principleId, approvalId, artifactId, channel, lockedReason, targetHost, onResolved, revisionReview, revisionReviewUnavailable, allowedActions, budgetNotice, promptInjection, sourceUnlinked, showPrincipleLink = true }) {
9
- const { t } = useTranslation();
10
- const location = useLocation();
11
- const [review, setReview] = useState(null);
12
- const [view, setView] = useState(null);
13
- const [viewSettled, setViewSettled] = useState(false);
14
- const [error, setError] = useState('');
15
- const [resultText, setResultText] = useState('');
16
- const [loading, setLoading] = useState(true);
17
- const [busy, setBusy] = useState(false);
18
- const [mode, setMode] = useState(null);
19
- const [reason, setReason] = useState('');
20
- const [newArtifact, setNewArtifact] = useState('');
21
- const [intentReviewed, setIntentReviewed] = useState(false);
22
- const [approveNote, setApproveNote] = useState('');
23
- const epoch = useRef(0);
24
- const read = useCallback(async () => {
25
- const current = ++epoch.current;
26
- setReview(null);
27
- setLoading(true);
28
- setIntentReviewed(false);
29
- setError('');
30
- const result = await fetchApprovalReview(approvalId);
31
- if (current !== epoch.current)
32
- return null;
33
- setLoading(false);
34
- if (result.success) {
35
- setReview(result.data);
36
- return result.data;
37
- }
38
- setError([result.error, result.nextAction].filter(Boolean).join(' '));
39
- return null;
40
- }, [approvalId]);
41
- useEffect(() => {
42
- setError('');
43
- setResultText('');
44
- setView(null);
45
- setViewSettled(false);
46
- setMode(null);
47
- setReason('');
48
- setNewArtifact('');
49
- setApproveNote('');
50
- let current = true;
51
- void read();
52
- void fetchOwnerDecisionView(principleId).then(result => { if (current) {
53
- setView(result.success ? result.data : null);
54
- setViewSettled(true);
55
- } });
56
- return () => { current = false; epoch.current++; };
57
- }, [principleId, approvalId, artifactId, channel, read]);
58
- const narrative = (field) => field.status === 'known' ? field.value.map(item => item.text).join('\n\n') : field.reason.ownerText;
59
- const subject = view?.decisionSubjects.find(item => item.key === approvalId && item.channel === channel
60
- && item.revisionArtifactId.status === 'known' && item.revisionArtifactId.value === review?.expectedReview.artifactId);
61
- const materialBlocks = subject?.materialGates.find(gate => gate.actionSemantic === 'approve')?.requiredItems.filter(item => item.status !== 'satisfied') ?? [];
62
- const revisionMatches = !revisionReview || (review?.expectedReview.artifactState === 'present' && review.expectedReview.artifactId === artifactId
63
- && revisionReview.artifactId === review.expectedReview.artifactId && revisionReview.artifactDigest === review.expectedReview.artifactDigest);
64
- const approveReady = review?.approval.status === 'pending' && subject !== undefined && review.expectedReview.artifactState === 'present' && review.material.exactBody !== null && review.material.availability === 'available' && revisionMatches && !revisionReviewUnavailable && materialBlocks.length === 0;
65
- const may = (semantic) => allowedActions === undefined || allowedActions.includes(semantic);
66
- async function submit(action) {
67
- if (!review || busy || lockedReason || review.approval.status !== 'pending')
68
- return;
69
- if (!may(action === 'approve' ? 'approve' : action === 'reject' ? 'reject' : 'edit_approval'))
70
- return;
71
- if (action === 'approve' && (!approveReady || (revisionReview && !intentReviewed)))
72
- return;
73
- if (action !== 'approve' && !reason.trim())
74
- return;
75
- if (action === 'edit' && !newArtifact.trim())
76
- return;
77
- const snapshot = review;
78
- const current = epoch.current;
79
- setBusy(true);
80
- setError('');
81
- setResultText('');
82
- const result = action === 'approve' ? await approveApproval(approvalId, approveNote.trim() || undefined, { expectedReview: snapshot.expectedReview, targetHost,
83
- ...(revisionReview ? { intentReviewed, reviewedArtifactId: snapshot.expectedReview.artifactId } : {}) })
84
- : action === 'reject' ? await rejectApproval(approvalId, reason.trim(), snapshot.expectedReview)
85
- : await editApproval(approvalId, newArtifact.trim(), { reason: reason.trim(), expectedReview: snapshot.expectedReview });
86
- if (current !== epoch.current)
87
- return;
88
- const fresh = await read();
89
- setBusy(false);
90
- if (!fresh) {
91
- setError(t('pages.focus.workbench.resultUnknown'));
92
- return;
93
- }
94
- if (fresh.approval.status === 'approved' || fresh.approval.status === 'rejected') {
95
- const state = t(fresh.approval.status === 'approved' ? 'pages.focus.workbench.currentApproved' : 'pages.focus.workbench.currentRejected');
96
- const ownOutcome = result.success && ((action === 'approve' && fresh.approval.status === 'approved') || (action === 'reject' && fresh.approval.status === 'rejected'));
97
- const refused = !result.success && result.status !== undefined && result.status >= 400 && result.status < 500;
98
- let message = ownOutcome ? t(fresh.approval.status === 'approved' ? 'pages.focus.workbench.approved' : 'pages.focus.workbench.rejected')
99
- : refused ? t('pages.focus.workbench.refusedCurrentState', { state }) + ' ' + [result.error, result.nextAction].filter(Boolean).join(' ')
100
- : t('pages.focus.workbench.currentStateConfirmed', { state });
101
- if (fresh.approval.status === 'approved' && !result.success && !refused) {
102
- const activations = await fetchAllActivations(targetHost);
103
- if (epoch.current !== current + 1)
104
- return;
105
- const active = activations.success && activations.data.activations.some(item => item.artifactId === fresh.approval.artifactId && item.status === 'active');
106
- message += ' ' + t(active ? 'pages.focus.workbench.runtimeCheckLink' : result.reason === 'activation_failed' ? 'pages.focus.workbench.activationFailed' : 'pages.focus.workbench.approvedRuntimeUnknown');
107
- }
108
- if (result.success && result.data.warning) {
109
- const warning = localizeApprovalWarning(result.data.warning, t);
110
- message += ` ${warning.title} ${warning.body ?? warning.detail}`;
111
- }
112
- setResultText(message);
113
- onResolved?.(message);
114
- return;
115
- }
116
- if (!result.success)
117
- setError(t(result.status === undefined ? 'pages.focus.workbench.resultUnknown' : 'pages.focus.workbench.notRecorded') + ' ' + [result.error, result.nextAction].filter(Boolean).join(' '));
118
- else if (action === 'edit') {
119
- setResultText(t('pages.focus.workbench.replaced'));
120
- setMode(null);
121
- setReason('');
122
- setNewArtifact('');
123
- setApproveNote('');
124
- }
125
- }
126
- const body = review?.material.exactBody;
127
- const bodyText = body?.text ?? review?.material.statement ?? '';
128
- return _jsxs("div", { className: "space-y-7", children: [_jsxs("section", { children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.why') }), sourceUnlinked && _jsx("p", { role: "alert", className: "mb-3 text-sm leading-relaxed text-amber", children: t('pages.focus.workbench.sourceUnlinked') }), _jsx("p", { className: "whitespace-pre-wrap text-[14px] leading-relaxed text-ink-2", children: view ? narrative(view.incidentSummary) : t('pages.focus.workbench.materialUnavailable') })] }), _jsxs("section", { children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.proposal') }), loading ? _jsx("p", { role: "status", className: "text-sm text-ink-3", children: t('pages.focus.workbench.loading') }) : _jsxs(_Fragment, { children: [bodyText ? _jsxs(_Fragment, { children: [_jsx("p", { className: "mb-2 text-[12px] text-ink-4", children: t(body?.kind === 'rule_code' ? 'pages.focus.workbench.codePreview' : 'pages.focus.workbench.textPreview') }), _jsx("p", { className: "whitespace-pre-wrap break-words text-[15px] leading-[1.85] text-ink", children: truncateByCodePoints(bodyText, 240) }), _jsxs("details", { className: "mt-4 rounded-[4px] border border-line px-4 py-3 text-[13px]", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t(body?.kind === 'rule_code' ? 'pages.focus.workbench.fullCode' : 'pages.focus.workbench.fullText') }), _jsx("div", { className: "mt-4 whitespace-pre-wrap break-words text-[15px] leading-[1.85] text-ink-2", children: bodyText })] })] }) : _jsx("p", { className: "text-sm text-amber", children: t('pages.focus.workbench.exactMaterialMissing') }), review?.material.scope.length ? _jsxs("div", { className: "mt-4", children: [_jsx("h4", { className: "text-sm font-medium text-ink", children: t('pages.focus.workbench.scope') }), _jsx("ul", { className: "mt-2 list-disc space-y-1 pl-5 text-[13px] leading-relaxed text-ink-2", children: review.material.scope.map((scope, index) => _jsx("li", { children: scope }, index)) })] }) : null, review?.material.rationale && _jsxs("details", { className: "mt-4 text-[13px] text-ink-3", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.reasonAndScope') }), _jsx("p", { className: "mt-3 whitespace-pre-wrap leading-relaxed", children: review.material.rationale })] }), revisionReview && _jsxs("details", { className: "mt-4 text-[13px] leading-relaxed text-ink-2", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.compare') }), _jsx("p", { className: "mt-3 whitespace-pre-wrap", children: revisionReview.oldStatement }), _jsx("p", { className: "mt-3 whitespace-pre-wrap", children: revisionReview.newStatement }), _jsx("dl", { className: "mt-3", children: Object.entries(revisionReview.intentFields).map(([key, value]) => _jsxs("div", { children: [_jsx("dt", { className: "font-medium", children: t(`pages.focus.intentField.${key}`, { defaultValue: key }) }), _jsx("dd", { children: value })] }, key)) })] })] })] }), _jsxs("section", { className: "border-t border-line pt-5", "data-testid": "approval-review-limits", children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.beforeDecision') }), viewSettled && !subject && _jsx("p", { className: "text-sm leading-relaxed text-amber", children: t('pages.focus.workbench.subjectUnconfirmed') }), review && _jsx("div", { className: "mb-3 text-sm leading-relaxed text-ink-2", "data-testid": "approval-exact-risks", children: review.material.risks?.length ? _jsx("ul", { className: "list-disc space-y-1 pl-5", children: review.material.risks.map((risk, index) => _jsx("li", { children: risk }, index)) }) : _jsx("p", { children: t(review.material.risks === null ? 'pages.focus.workbench.risksUnknown' : 'pages.focus.workbench.risksEmpty') }) }), materialBlocks.map(item => _jsx("p", { className: "mb-2 text-sm leading-relaxed text-amber", children: item.ownerText }, item.key)), !revisionMatches && _jsx("p", { role: "alert", className: "mb-2 text-sm text-amber", children: t('pages.focus.workbench.revisionChanged') }), revisionReviewUnavailable && _jsx("p", { role: "alert", className: "mb-2 text-sm text-amber", children: t('pages.focus.revisionReviewUnavailable') }), budgetNotice && channel === 'prompt' && _jsx("p", { role: "alert", className: "mb-2 text-sm leading-relaxed text-amber", "data-testid": `approve-queue-badge-${principleId}`, children: budgetNotice }), promptInjection && channel === 'prompt' && _jsxs("details", { className: "mb-3 text-[12px] leading-relaxed text-ink-3", "data-testid": `approve-queue-badge-scope-${principleId}`, children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.budgetBasis') }), _jsxs("p", { className: "mt-2", children: [promptInjection.route === 'shared_render' ? t('pages.focus.promptScopeShared') : t('pages.focus.promptScopeList', { full: promptInjection.fullRenderChars !== undefined ? t('pages.focus.promptScopeFullRender', { count: promptInjection.fullRenderChars }) : '' }), promptInjection.hostKind ? ` · ${promptInjection.hostKind}` : ''] }), _jsx("p", { className: "mt-2", children: promptInjection.budgetCapacityStatus === 'confirmed' && promptInjection.budgetContextWindowTokens !== undefined
129
- ? promptInjection.budgetObservedAt !== undefined ? t('pages.focus.promptBudgetObservedNote', { source: t(promptInjection.budgetCapacitySource === 'pi_catalog_exact' ? 'pages.focus.budgetCapacitySourcePiCatalog' : promptInjection.budgetCapacitySource === 'operator_declared' ? 'pages.focus.budgetCapacitySourceOperatorDeclared' : 'pages.focus.budgetCapacitySourceHostWindow'), window: promptInjection.budgetContextWindowTokens, observedAt: promptInjection.budgetObservedAt })
130
- : t('pages.focus.promptBudgetModelAwareNote', { window: promptInjection.budgetContextWindowTokens, sourceSuffix: promptInjection.budgetCapacitySource === 'operator_declared' ? t('pages.focus.budgetCapacitySourceOperatorDeclaredSuffix') : '' })
131
- : promptInjection.budgetObservedAt !== undefined ? t('pages.focus.promptBudgetFallbackObservedNote', { reason: t(`pages.focus.budgetFallbackReason.${promptInjection.budgetFallbackReason}`, { defaultValue: promptInjection.budgetFallbackReason ?? 'unknown' }), observedAt: promptInjection.budgetObservedAt })
132
- : t('pages.focus.promptBudgetFallbackNote', { reason: promptInjection.budgetFallbackReason ? t('pages.focus.promptBudgetFallbackReasonSuffix', { code: t(`pages.focus.budgetFallbackReason.${promptInjection.budgetFallbackReason}`, { defaultValue: promptInjection.budgetFallbackReason }) }) : '' }) })] }), review?.material.targetBehavior && _jsxs("div", { className: "mb-3 text-[14px] leading-relaxed text-ink-2", "data-testid": "approval-exact-target-behavior", children: [_jsx("h4", { className: "mb-1 font-medium", children: t('pages.focus.workbench.proposedBehavior') }), _jsx("p", { className: "whitespace-pre-wrap break-words", children: review.material.targetBehavior })] }), subject && _jsx("p", { className: "text-sm leading-relaxed text-ink-2", children: narrative(subject.decisionMaterial.consequence) }), _jsx("p", { className: "mt-2 text-[13px] leading-relaxed text-ink-3", children: t('pages.focus.workbench.scopeUnknown') }), view && _jsxs("details", { className: "mt-3 text-[13px] leading-relaxed text-ink-3", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.principleLimits') }), _jsx("p", { className: "mt-3", children: view.nonApplicabilityOrUnknown.status === 'known' ? view.nonApplicabilityOrUnknown.value.map(item => item.text).join(' ') : view.nonApplicabilityOrUnknown.reason.ownerText }), _jsx("p", { className: "mt-2", children: view.risk.status === 'known' ? view.risk.value?.items.map(item => item.text).join(' ') || t('pages.principles.detail.ownerDecision.riskNotAssessed') : view.risk.reason?.ownerText }), _jsx("p", { className: "mt-2", children: view.uncertainty.status === 'known' ? view.uncertainty.value.map(item => item.text).join(' ') : view.uncertainty.reason.ownerText })] })] }), _jsxs("section", { className: "border-t border-line pt-5", children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.decision') }), lockedReason && _jsx("p", { role: "alert", className: "mb-3 text-[13px] leading-relaxed text-amber", children: lockedReason }), error && _jsx("p", { role: "alert", className: "mb-3 text-[13px] leading-relaxed text-amber", children: error }), resultText && _jsx("p", { role: "status", className: "mb-3 text-sm leading-relaxed text-ink", children: resultText }), !review && !loading && _jsx("button", { type: "button", className: "text-sm text-gov underline", onClick: () => { void read(); }, children: t('pages.focus.workbench.retry') }), review?.approval.status === 'pending' && _jsxs(_Fragment, { children: [_jsxs("details", { className: "mb-4 text-sm text-ink-3", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.approvalNote') }), _jsxs("label", { className: "mt-3 block", children: [t('pages.focus.workbench.approvalNote'), _jsx("textarea", { value: approveNote, maxLength: 2000, onChange: event => setApproveNote(event.target.value), className: "mt-2 block w-full rounded border border-line bg-panel px-3 py-2" })] })] }), revisionReview && _jsxs("label", { className: "mb-4 block text-sm text-ink-2", children: [_jsx("input", { type: "checkbox", checked: intentReviewed, onChange: event => setIntentReviewed(event.target.checked), className: "mr-2" }), t('pages.focus.workbench.intentReviewed')] }), _jsx("p", { className: "mb-4 text-[12.5px] leading-relaxed text-ink-3", children: t(channel === 'code_tool_hook' ? 'pages.focus.workbench.ruleConsequence' : channel === 'defer_archive' ? 'pages.focus.workbench.archiveConsequence' : 'pages.focus.workbench.promptConsequence') }), _jsxs("div", { className: "flex flex-wrap gap-3", children: [_jsx("button", { type: "button", disabled: !approveReady || busy || Boolean(lockedReason) || !may('approve') || Boolean(revisionReview && !intentReviewed), "data-testid": `approve-btn-${principleId}`, onClick: () => { void submit('approve'); }, className: "rounded-[4px] border border-gov bg-gov px-4 py-2.5 text-[13px] text-paper disabled:opacity-50", children: t(channel === 'defer_archive' ? 'pages.focus.workbench.approveArchive' : 'pages.focus.workbench.approveOne') }), _jsx("button", { type: "button", disabled: busy || Boolean(lockedReason) || !may('reject'), onClick: () => setMode('reject'), className: "rounded-[4px] border border-line px-4 py-2.5 text-[13px] text-ink disabled:opacity-50", children: t('pages.focus.workbench.rejectOne') }), _jsx("button", { type: "button", "data-testid": `edit-btn-${principleId}`, disabled: busy || Boolean(lockedReason) || !may('edit_approval') || review.expectedReview.artifactState === 'missing', onClick: () => setMode('edit'), className: "text-[13px] text-gov underline disabled:opacity-50", children: t('pages.focus.workbench.replaceOne') })] }), mode && _jsxs("form", { className: "mt-4 space-y-3", onSubmit: event => { event.preventDefault(); void submit(mode); }, children: [mode === 'edit' && _jsxs("label", { className: "block text-sm text-ink-2", children: [t('pages.focus.workbench.replacementId'), _jsx("input", { value: newArtifact, onChange: event => setNewArtifact(event.target.value), className: "mt-2 block w-full rounded border border-line bg-panel px-3 py-2" })] }), _jsxs("label", { className: "block text-sm text-ink-2", children: [t('pages.focus.workbench.reason'), _jsx("textarea", { value: reason, maxLength: 2000, onChange: event => setReason(event.target.value), className: "mt-2 block w-full rounded border border-line bg-panel px-3 py-2" })] }), _jsx("button", { type: "submit", disabled: busy || !reason.trim() || (mode === 'edit' && !newArtifact.trim()), className: "rounded border border-line px-4 py-2 text-sm disabled:opacity-50", children: t('pages.focus.workbench.confirm') }), _jsx("button", { type: "button", onClick: () => setMode(null), className: "ml-3 text-sm text-ink-3", children: t('pages.focus.workbench.cancel') })] })] }), showPrincipleLink && _jsx(Link, { className: "mt-5 inline-block text-[13px] text-gov underline underline-offset-4", state: { returnTo: `/focus${location.search}` }, to: `/principles/${encodeURIComponent(principleId)}`, children: t('pages.focus.workbench.openPrinciple') }), _jsxs("details", { className: "mt-4 text-[12px] text-ink-4", children: [_jsx("summary", { className: "cursor-pointer", children: t('pages.focus.workbench.subjectDetails') }), _jsxs("dl", { className: "mt-2 break-all font-mono", children: [_jsxs("div", { children: ["approval: ", approvalId] }), _jsxs("div", { children: ["artifact: ", review?.expectedReview.artifactId ?? artifactId] }), _jsxs("div", { children: ["channel: ", review?.expectedReview.channel ?? channel] })] })] })] })] });
133
- }