create-principles-disciple 1.76.0 → 1.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/console/dist/server/config/feature-flags.d.ts +25 -0
  2. package/console/dist/server/config/feature-flags.js +101 -0
  3. package/console/dist/server/index.js +16 -0
  4. package/console/dist/server/models/FeedbackReportConsoleModel.d.ts +49 -0
  5. package/console/dist/server/models/FeedbackReportConsoleModel.js +242 -0
  6. package/console/dist/server/routes/feedback-reports.d.ts +18 -0
  7. package/console/dist/server/routes/feedback-reports.js +165 -0
  8. package/console/dist/ui/App.js +2 -1
  9. package/console/dist/ui/api.d.ts +26 -2
  10. package/console/dist/ui/api.js +18 -1
  11. package/console/dist/ui/components/app-sidebar.js +2 -1
  12. package/console/dist/ui/components/error-boundary.js +9 -2
  13. package/console/dist/ui/i18n/en.json +67 -1
  14. package/console/dist/ui/i18n/zh-CN.json +67 -1
  15. package/console/dist/ui/pages/ReportProblemPage.d.ts +1 -0
  16. package/console/dist/ui/pages/ReportProblemPage.js +186 -0
  17. package/console/dist/ui/pages/ReportProblemValidators.d.ts +51 -0
  18. package/console/dist/ui/pages/ReportProblemValidators.js +114 -0
  19. package/console/dist/web/assets/app.css +18 -0
  20. package/console/dist/web/assets/app.js +1047 -125
  21. package/console/package.json +1 -0
  22. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +24 -1
  23. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  24. package/core/dist/runtime-v2/__tests__/feedback/create-report.test.d.ts +2 -0
  25. package/core/dist/runtime-v2/__tests__/feedback/create-report.test.d.ts.map +1 -0
  26. package/core/dist/runtime-v2/__tests__/feedback/create-report.test.js +402 -0
  27. package/core/dist/runtime-v2/__tests__/feedback/create-report.test.js.map +1 -0
  28. package/core/dist/runtime-v2/__tests__/feedback/privacy-preview.test.d.ts +2 -0
  29. package/core/dist/runtime-v2/__tests__/feedback/privacy-preview.test.d.ts.map +1 -0
  30. package/core/dist/runtime-v2/__tests__/feedback/privacy-preview.test.js +128 -0
  31. package/core/dist/runtime-v2/__tests__/feedback/privacy-preview.test.js.map +1 -0
  32. package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.d.ts +2 -0
  33. package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.d.ts.map +1 -0
  34. package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.js +231 -0
  35. package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.js.map +1 -0
  36. package/core/dist/runtime-v2/__tests__/feedback/render-github-url.test.d.ts +2 -0
  37. package/core/dist/runtime-v2/__tests__/feedback/render-github-url.test.d.ts.map +1 -0
  38. package/core/dist/runtime-v2/__tests__/feedback/render-github-url.test.js +102 -0
  39. package/core/dist/runtime-v2/__tests__/feedback/render-github-url.test.js.map +1 -0
  40. package/core/dist/runtime-v2/__tests__/feedback/render-markdown.test.d.ts +2 -0
  41. package/core/dist/runtime-v2/__tests__/feedback/render-markdown.test.d.ts.map +1 -0
  42. package/core/dist/runtime-v2/__tests__/feedback/render-markdown.test.js +130 -0
  43. package/core/dist/runtime-v2/__tests__/feedback/render-markdown.test.js.map +1 -0
  44. package/core/dist/runtime-v2/__tests__/workspace-guidance-migration.test.d.ts +2 -0
  45. package/core/dist/runtime-v2/__tests__/workspace-guidance-migration.test.d.ts.map +1 -0
  46. package/core/dist/runtime-v2/__tests__/workspace-guidance-migration.test.js +328 -0
  47. package/core/dist/runtime-v2/__tests__/workspace-guidance-migration.test.js.map +1 -0
  48. package/core/dist/runtime-v2/activation/__tests__/prompt-activation-reader-contract.test.d.ts +2 -0
  49. package/core/dist/runtime-v2/activation/__tests__/prompt-activation-reader-contract.test.d.ts.map +1 -0
  50. package/core/dist/runtime-v2/activation/__tests__/prompt-activation-reader-contract.test.js +343 -0
  51. package/core/dist/runtime-v2/activation/__tests__/prompt-activation-reader-contract.test.js.map +1 -0
  52. package/core/dist/runtime-v2/activation/index.d.ts +2 -0
  53. package/core/dist/runtime-v2/activation/index.d.ts.map +1 -1
  54. package/core/dist/runtime-v2/activation/index.js +1 -0
  55. package/core/dist/runtime-v2/activation/index.js.map +1 -1
  56. package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.d.ts +34 -0
  57. package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.d.ts.map +1 -0
  58. package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.js +109 -0
  59. package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.js.map +1 -0
  60. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +13 -0
  61. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
  62. package/core/dist/runtime-v2/feature-flags/__tests__/plugin-surface-registry.test.d.ts +2 -0
  63. package/core/dist/runtime-v2/feature-flags/__tests__/plugin-surface-registry.test.d.ts.map +1 -0
  64. package/core/dist/runtime-v2/feature-flags/__tests__/plugin-surface-registry.test.js +141 -0
  65. package/core/dist/runtime-v2/feature-flags/__tests__/plugin-surface-registry.test.js.map +1 -0
  66. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  67. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +4 -1
  68. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  69. package/core/dist/runtime-v2/feature-flags/index.d.ts +2 -0
  70. package/core/dist/runtime-v2/feature-flags/index.d.ts.map +1 -1
  71. package/core/dist/runtime-v2/feature-flags/index.js +1 -0
  72. package/core/dist/runtime-v2/feature-flags/index.js.map +1 -1
  73. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.d.ts +25 -0
  74. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.d.ts.map +1 -0
  75. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js +233 -0
  76. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js.map +1 -0
  77. package/core/dist/runtime-v2/feedback/create-report.d.ts +14 -0
  78. package/core/dist/runtime-v2/feedback/create-report.d.ts.map +1 -0
  79. package/core/dist/runtime-v2/feedback/create-report.js +257 -0
  80. package/core/dist/runtime-v2/feedback/create-report.js.map +1 -0
  81. package/core/dist/runtime-v2/feedback/feedback-types.d.ts +120 -0
  82. package/core/dist/runtime-v2/feedback/feedback-types.d.ts.map +1 -0
  83. package/core/dist/runtime-v2/feedback/feedback-types.js +243 -0
  84. package/core/dist/runtime-v2/feedback/feedback-types.js.map +1 -0
  85. package/core/dist/runtime-v2/feedback/index.d.ts +9 -0
  86. package/core/dist/runtime-v2/feedback/index.d.ts.map +1 -0
  87. package/core/dist/runtime-v2/feedback/index.js +11 -0
  88. package/core/dist/runtime-v2/feedback/index.js.map +1 -0
  89. package/core/dist/runtime-v2/feedback/internal-guards.d.ts +2 -0
  90. package/core/dist/runtime-v2/feedback/internal-guards.d.ts.map +1 -0
  91. package/core/dist/runtime-v2/feedback/internal-guards.js +6 -0
  92. package/core/dist/runtime-v2/feedback/internal-guards.js.map +1 -0
  93. package/core/dist/runtime-v2/feedback/privacy-preview.d.ts +14 -0
  94. package/core/dist/runtime-v2/feedback/privacy-preview.d.ts.map +1 -0
  95. package/core/dist/runtime-v2/feedback/privacy-preview.js +128 -0
  96. package/core/dist/runtime-v2/feedback/privacy-preview.js.map +1 -0
  97. package/core/dist/runtime-v2/feedback/redact-sensitive.d.ts +37 -0
  98. package/core/dist/runtime-v2/feedback/redact-sensitive.d.ts.map +1 -0
  99. package/core/dist/runtime-v2/feedback/redact-sensitive.js +272 -0
  100. package/core/dist/runtime-v2/feedback/redact-sensitive.js.map +1 -0
  101. package/core/dist/runtime-v2/feedback/render-github-url.d.ts +21 -0
  102. package/core/dist/runtime-v2/feedback/render-github-url.d.ts.map +1 -0
  103. package/core/dist/runtime-v2/feedback/render-github-url.js +57 -0
  104. package/core/dist/runtime-v2/feedback/render-github-url.js.map +1 -0
  105. package/core/dist/runtime-v2/feedback/render-markdown.d.ts +12 -0
  106. package/core/dist/runtime-v2/feedback/render-markdown.d.ts.map +1 -0
  107. package/core/dist/runtime-v2/feedback/render-markdown.js +166 -0
  108. package/core/dist/runtime-v2/feedback/render-markdown.js.map +1 -0
  109. package/core/dist/runtime-v2/feedback/safe-stringify.d.ts +6 -0
  110. package/core/dist/runtime-v2/feedback/safe-stringify.d.ts.map +1 -0
  111. package/core/dist/runtime-v2/feedback/safe-stringify.js +115 -0
  112. package/core/dist/runtime-v2/feedback/safe-stringify.js.map +1 -0
  113. package/core/dist/runtime-v2/index.d.ts +8 -1
  114. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  115. package/core/dist/runtime-v2/index.js +12 -1
  116. package/core/dist/runtime-v2/index.js.map +1 -1
  117. package/core/dist/runtime-v2/workspace-guidance-migration.d.ts +12 -0
  118. package/core/dist/runtime-v2/workspace-guidance-migration.d.ts.map +1 -0
  119. package/core/dist/runtime-v2/workspace-guidance-migration.js +193 -0
  120. package/core/dist/runtime-v2/workspace-guidance-migration.js.map +1 -0
  121. package/core/package.json +4 -0
  122. package/package.json +1 -1
  123. package/plugin/dist/commands/context.js +5 -5
  124. package/plugin/dist/commands/strategy.d.ts +0 -1
  125. package/plugin/dist/commands/strategy.js +0 -14
  126. package/plugin/dist/core/runtime-v2-prompt-activation-reader.d.ts +4 -13
  127. package/plugin/dist/core/runtime-v2-prompt-activation-reader.js +36 -74
  128. package/plugin/dist/core/surface-guard.d.ts +27 -0
  129. package/plugin/dist/core/surface-guard.js +82 -0
  130. package/plugin/dist/core/workspace-guidance-migrator.d.ts +12 -0
  131. package/plugin/dist/core/workspace-guidance-migrator.js +141 -0
  132. package/plugin/dist/hooks/prompt.js +20 -143
  133. package/plugin/dist/i18n/commands.js +2 -14
  134. package/plugin/dist/index.d.ts +27 -0
  135. package/plugin/dist/index.js +180 -65
  136. package/plugin/dist/service/correction-observer-service.d.ts +11 -0
  137. package/plugin/dist/service/correction-observer-service.js +172 -0
  138. package/plugin/dist/service/evolution-worker.js +1 -110
  139. package/plugin/dist/types.d.ts +2 -2
  140. package/plugin/dist/types.js +3 -3
  141. package/plugin/templates/langs/en/core/BOOTSTRAP.md +5 -19
  142. package/plugin/templates/langs/en/principles/THINKING_OS.md +1 -1
  143. package/plugin/templates/langs/en/skills/admin/SKILL.md +2 -2
  144. package/plugin/templates/langs/en/skills/bootstrap-tools/SKILL.md +1 -1
  145. package/plugin/templates/langs/en/skills/init-strategy/SKILL.md +1 -1
  146. package/plugin/templates/langs/en/skills/pd-mentor/SKILL.md +8 -23
  147. package/plugin/templates/langs/en/skills/reflection/SKILL.md +2 -2
  148. package/plugin/templates/langs/en/skills/report/SKILL.md +1 -1
  149. package/plugin/templates/langs/zh/core/BOOTSTRAP.md +3 -16
  150. package/plugin/templates/langs/zh/principles/THINKING_OS.md +1 -1
  151. package/plugin/templates/langs/zh/skills/admin/SKILL.md +2 -2
  152. package/plugin/templates/langs/zh/skills/bootstrap-tools/SKILL.md +1 -1
  153. package/plugin/templates/langs/zh/skills/init-strategy/SKILL.md +1 -1
  154. package/plugin/templates/langs/zh/skills/pd-mentor/SKILL.md +7 -22
  155. package/plugin/templates/langs/zh/skills/reflection/SKILL.md +2 -2
  156. package/plugin/templates/langs/zh/skills/report/SKILL.md +1 -1
  157. package/plugin/templates/workspace/.principles/THINKING_OS.md +1 -1
  158. package/templates/langs/en/core/BOOTSTRAP.md +1 -1
  159. package/templates/langs/en/principles/THINKING_OS.md +1 -1
  160. package/templates/langs/en/skills/admin/SKILL.md +2 -2
  161. package/templates/langs/en/skills/pd-grooming/SKILL.md +1 -1
  162. package/templates/langs/en/skills/reflection/SKILL.md +2 -2
  163. package/templates/langs/en/skills/report/SKILL.md +1 -1
  164. package/templates/langs/zh/core/BOOTSTRAP.md +1 -1
  165. package/templates/langs/zh/principles/THINKING_OS.md +1 -1
  166. package/templates/langs/zh/skills/admin/SKILL.md +2 -2
  167. package/templates/langs/zh/skills/pd-grooming/SKILL.md +1 -1
  168. package/templates/langs/zh/skills/reflection/SKILL.md +2 -2
  169. package/templates/langs/zh/skills/report/SKILL.md +1 -1
  170. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/EXAMPLES.md +0 -63
  171. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/REFERENCE.md +0 -136
  172. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/SKILL.md +0 -67
  173. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +0 -143
  174. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +0 -107
  175. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +0 -107
  176. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +0 -95
  177. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +0 -98
  178. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +0 -58
  179. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +0 -190
  180. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +0 -310
  181. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +0 -683
  182. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +0 -604
  183. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +0 -32
  184. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +0 -707
  185. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +0 -3455
  186. package/plugin/templates/langs/en/skills/evolve-system/SKILL.md +0 -46
  187. package/plugin/templates/langs/en/skills/manage-okr/SKILL.md +0 -96
  188. package/plugin/templates/langs/en/skills/pd-daily/SKILL.md +0 -199
  189. package/plugin/templates/langs/en/skills/pd-grooming/SKILL.md +0 -46
  190. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/EXAMPLES.md +0 -63
  191. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/REFERENCE.md +0 -136
  192. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/SKILL.md +0 -67
  193. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +0 -143
  194. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +0 -107
  195. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +0 -107
  196. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +0 -111
  197. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +0 -95
  198. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +0 -98
  199. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +0 -58
  200. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +0 -190
  201. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +0 -2
  202. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +0 -310
  203. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +0 -683
  204. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +0 -604
  205. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +0 -32
  206. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +0 -707
  207. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +0 -3455
  208. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/test/archive.test.mjs +0 -230
  209. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/test/contract-enforcement.test.mjs +0 -672
  210. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/test/decision.test.mjs +0 -1321
  211. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +0 -1435
  212. package/plugin/templates/langs/zh/skills/evolve-system/SKILL.md +0 -46
  213. package/plugin/templates/langs/zh/skills/manage-okr/SKILL.md +0 -109
  214. package/plugin/templates/langs/zh/skills/pd-daily/SKILL.md +0 -283
  215. package/plugin/templates/langs/zh/skills/pd-grooming/SKILL.md +0 -46
  216. package/plugin/templates/workspace/PLAN.md +0 -2
  217. package/templates/workspace/PLAN.md +0 -2
@@ -494,5 +494,29 @@ declare function fetchApprovals(params?: {
494
494
  declare function fetchApprovalDetail(approvalId: string): Promise<ApiResponse<ApprovalRecord>>;
495
495
  declare function approveApproval(approvalId: string, note?: string): Promise<ApiResponse<ApprovalRecord>>;
496
496
  declare function rejectApproval(approvalId: string, reason: string): Promise<ApiResponse<ApprovalRecord>>;
497
- export { getToken, setToken, clearToken, checkAuth, request, fetchTasks, fetchTaskEvidence, approveTask, rejectTask, cleanupTask, fetchStatus, fetchActivity, fetchOverview, fetchOverviewHealth, fetchGateStats, fetchGateBlocks, fetchFeedbackGfi, fetchEmpathyEvents, fetchFeedbackGateBlocks, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchCentralOverview, fetchCentralHealth, fetchSamples, fetchSampleDetail, reviewSample, fetchEvolutionStats, fetchEvolutionTasks, fetchEvolutionPrinciples, fetchPrinciples, fetchPrincipleDetail, fetchEvolutionQueue, fetchThinkingModels, fetchSystemHealth, fetchPipelineStats, fetchEvents, fetchEventsGrouped, fetchRelatedEvents, fetchAgents, fetchAgentDetail, fetchApprovals, fetchApprovalDetail, approveApproval, rejectApproval, };
498
- export type { OverviewData, OverviewHealth, GateStats, FeedbackGfi, EmpathyEvent, GateBlockItem, WorkspaceEntry, CentralOverview, CentralHealth, SampleListItem, SampleDetail, SamplesData, EvolutionStats, EvolutionTaskItem, EvolutionTasksData, EvolutionPrinciplesData, PrincipleListItem, RuleItem, PrincipleDetail, PrinciplesListData, PrincipleDetailData, QueueHealthData, ThinkingModelOverview, HealthCheckItem, PipelineTimestamps, SystemHealthStatus, PipelineStage, Bottleneck, PipelineStats, EventLogEntry, EventsResponse, RelatedEventsResponse, AgentInfo, AgentDetail, ApprovalRecord, ApprovalListResult, };
497
+ type FeedbackDraftSummary = {
498
+ id: string;
499
+ createdAt: string;
500
+ type: string;
501
+ title: string;
502
+ };
503
+ type FeedbackReportEnvelope = {
504
+ id: string;
505
+ createdAt: string;
506
+ report: Record<string, unknown>;
507
+ };
508
+ type FeedbackDraftsListEnvelope = {
509
+ drafts: FeedbackDraftSummary[];
510
+ };
511
+ type FeedbackDraftEnvelope = {
512
+ report: Record<string, unknown>;
513
+ };
514
+ type FeedbackDeleteEnvelope = {
515
+ deleted: boolean;
516
+ };
517
+ declare function createFeedbackReport(input: unknown, diagnostics: unknown): Promise<ApiResponse<FeedbackReportEnvelope>>;
518
+ declare function listFeedbackReports(): Promise<ApiResponse<FeedbackDraftsListEnvelope>>;
519
+ declare function getFeedbackReport(id: string): Promise<ApiResponse<FeedbackDraftEnvelope>>;
520
+ declare function deleteFeedbackReport(id: string): Promise<ApiResponse<FeedbackDeleteEnvelope>>;
521
+ export { getToken, setToken, clearToken, checkAuth, request, fetchTasks, fetchTaskEvidence, approveTask, rejectTask, cleanupTask, fetchStatus, fetchActivity, fetchOverview, fetchOverviewHealth, fetchGateStats, fetchGateBlocks, fetchFeedbackGfi, fetchEmpathyEvents, fetchFeedbackGateBlocks, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchCentralOverview, fetchCentralHealth, fetchSamples, fetchSampleDetail, reviewSample, fetchEvolutionStats, fetchEvolutionTasks, fetchEvolutionPrinciples, fetchPrinciples, fetchPrincipleDetail, fetchEvolutionQueue, fetchThinkingModels, fetchSystemHealth, fetchPipelineStats, fetchEvents, fetchEventsGrouped, fetchRelatedEvents, fetchAgents, fetchAgentDetail, fetchApprovals, fetchApprovalDetail, approveApproval, rejectApproval, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, };
522
+ export type { OverviewData, OverviewHealth, ApiResponse, FeedbackDraftSummary, FeedbackReportEnvelope, FeedbackDraftsListEnvelope, FeedbackDraftEnvelope, FeedbackDeleteEnvelope, GateStats, FeedbackGfi, EmpathyEvent, GateBlockItem, WorkspaceEntry, CentralOverview, CentralHealth, SampleListItem, SampleDetail, SamplesData, EvolutionStats, EvolutionTaskItem, EvolutionTasksData, EvolutionPrinciplesData, PrincipleListItem, RuleItem, PrincipleDetail, PrinciplesListData, PrincipleDetailData, QueueHealthData, ThinkingModelOverview, HealthCheckItem, PipelineTimestamps, SystemHealthStatus, PipelineStage, Bottleneck, PipelineStats, EventLogEntry, EventsResponse, RelatedEventsResponse, AgentInfo, AgentDetail, ApprovalRecord, ApprovalListResult, };
@@ -240,4 +240,21 @@ async function rejectApproval(approvalId, reason) {
240
240
  body: JSON.stringify({ reason }),
241
241
  });
242
242
  }
243
- export { getToken, setToken, clearToken, checkAuth, request, fetchTasks, fetchTaskEvidence, approveTask, rejectTask, cleanupTask, fetchStatus, fetchActivity, fetchOverview, fetchOverviewHealth, fetchGateStats, fetchGateBlocks, fetchFeedbackGfi, fetchEmpathyEvents, fetchFeedbackGateBlocks, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchCentralOverview, fetchCentralHealth, fetchSamples, fetchSampleDetail, reviewSample, fetchEvolutionStats, fetchEvolutionTasks, fetchEvolutionPrinciples, fetchPrinciples, fetchPrincipleDetail, fetchEvolutionQueue, fetchThinkingModels, fetchSystemHealth, fetchPipelineStats, fetchEvents, fetchEventsGrouped, fetchRelatedEvents, fetchAgents, fetchAgentDetail, fetchApprovals, fetchApprovalDetail, approveApproval, rejectApproval, };
243
+ async function createFeedbackReport(input, diagnostics) {
244
+ return request('/api/feedback/reports', {
245
+ method: 'POST',
246
+ body: JSON.stringify({ input, diagnostics }),
247
+ });
248
+ }
249
+ async function listFeedbackReports() {
250
+ return request('/api/feedback/reports');
251
+ }
252
+ async function getFeedbackReport(id) {
253
+ return request('/api/feedback/reports/' + encodeURIComponent(id));
254
+ }
255
+ async function deleteFeedbackReport(id) {
256
+ return request('/api/feedback/reports/' + encodeURIComponent(id), {
257
+ method: 'DELETE',
258
+ });
259
+ }
260
+ export { getToken, setToken, clearToken, checkAuth, request, fetchTasks, fetchTaskEvidence, approveTask, rejectTask, cleanupTask, fetchStatus, fetchActivity, fetchOverview, fetchOverviewHealth, fetchGateStats, fetchGateBlocks, fetchFeedbackGfi, fetchEmpathyEvents, fetchFeedbackGateBlocks, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchCentralOverview, fetchCentralHealth, fetchSamples, fetchSampleDetail, reviewSample, fetchEvolutionStats, fetchEvolutionTasks, fetchEvolutionPrinciples, fetchPrinciples, fetchPrincipleDetail, fetchEvolutionQueue, fetchThinkingModels, fetchSystemHealth, fetchPipelineStats, fetchEvents, fetchEventsGrouped, fetchRelatedEvents, fetchAgents, fetchAgentDetail, fetchApprovals, fetchApprovalDetail, approveApproval, rejectApproval, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, };
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState, useEffect } from "react";
3
3
  import { Link, useLocation } from "react-router-dom";
4
- import { Flame, ScrollText, ShieldCheck, Settings, ChevronLeft, ChevronRight, ChevronDown, ChevronUp, AlertTriangle, Activity, FileText, Dna, Wrench, } from "lucide-react";
4
+ import { Flame, ScrollText, ShieldCheck, Settings, ChevronLeft, ChevronRight, ChevronDown, ChevronUp, AlertTriangle, Activity, FileText, Dna, Wrench, MessageSquare, } from "lucide-react";
5
5
  import { cn } from "../../lib/utils.js";
6
6
  import { isNavActive } from "../utils/navigation.js";
7
7
  import { Button } from "./ui/button.js";
@@ -11,6 +11,7 @@ const mvpNavItems = [
11
11
  { id: "pain", label: "Pain", icon: Flame, href: "/pain", alsoActive: ["/"] },
12
12
  { id: "principles", label: "Principle", icon: ScrollText, href: "/principles" },
13
13
  { id: "approvals", label: "Approval", icon: ShieldCheck, href: "/approvals" },
14
+ { id: "report-problem", label: "Report Problem", icon: MessageSquare, href: "/report-problem" },
14
15
  ];
15
16
  const diagnosticNavItems = [
16
17
  { id: "overview", label: "Overview", icon: Activity, href: "/overview" },
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Component } from "react";
3
+ import { useLocation, useNavigate } from "react-router-dom";
3
4
  import i18n from "../i18n/index.js";
4
5
  import { Card, CardContent } from "./ui/card.js";
5
6
  import { Button } from "./ui/button.js";
6
- import { AlertCircle } from "lucide-react";
7
+ import { AlertCircle, MessageSquare } from "lucide-react";
7
8
  export class ErrorBoundary extends Component {
8
9
  constructor(props) {
9
10
  super(props);
@@ -16,8 +17,14 @@ export class ErrorBoundary extends Component {
16
17
  if (this.state.hasError) {
17
18
  if (this.props.fallback)
18
19
  return this.props.fallback;
19
- return (_jsx(Card, { className: "border-destructive/50", children: _jsxs(CardContent, { className: "p-6", children: [_jsxs("div", { className: "flex items-center gap-3 mb-4", children: [_jsx(AlertCircle, { className: "h-5 w-5 text-destructive" }), _jsx("h3", { className: "font-semibold text-destructive", children: i18n.t("components:errorBoundary.title") })] }), _jsx("p", { className: "text-sm text-muted-foreground mb-4", children: this.state.error?.message ?? i18n.t("components:errorBoundary.description") }), _jsx(Button, { variant: "outline", onClick: () => this.setState({ hasError: false, error: null }), children: i18n.t("components:errorBoundary.retry") })] }) }));
20
+ return (_jsx(ErrorBoundaryCard, { error: this.state.error, onRetry: () => this.setState({ hasError: false, error: null }) }));
20
21
  }
21
22
  return this.props.children;
22
23
  }
23
24
  }
25
+ function ErrorBoundaryCard({ error, onRetry }) {
26
+ const navigate = useNavigate();
27
+ const location = useLocation();
28
+ const reportHref = `/report-problem?source=${encodeURIComponent("error")}&message=${encodeURIComponent(error?.message ?? "")}&from=${encodeURIComponent(location.pathname + location.search)}`;
29
+ return (_jsx(Card, { className: "border-destructive/50", children: _jsxs(CardContent, { className: "p-6", children: [_jsxs("div", { className: "flex items-center gap-3 mb-4", children: [_jsx(AlertCircle, { className: "h-5 w-5 text-destructive" }), _jsx("h3", { className: "font-semibold text-destructive", children: i18n.t("components:errorBoundary.title") })] }), _jsx("p", { className: "text-sm text-muted-foreground mb-4", children: error?.message ?? i18n.t("components:errorBoundary.description") }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { variant: "outline", onClick: onRetry, children: i18n.t("components:errorBoundary.retry") }), _jsxs(Button, { variant: "ghost", onClick: () => navigate(reportHref), children: [_jsx(MessageSquare, { className: "h-4 w-4 mr-2" }), i18n.t("components:errorBoundary.reportAction")] })] })] }) }));
30
+ }
@@ -154,6 +154,71 @@
154
154
  "origin": "Origin:",
155
155
  "gfiAfter": "GFI after:"
156
156
  },
157
+ "reportProblem": {
158
+ "title": "Report a Problem",
159
+ "description": "Generate a privacy-preserving local feedback draft. The Console never auto-uploads — you choose what to share, how to share it, and when.",
160
+ "deepLinked": "Deep-linked from {{source}}.",
161
+ "form": {
162
+ "title": "Describe what happened",
163
+ "description": "Only fields below are stored. Raw prompts, chat history, file contents, paths, env vars, and tokens are excluded by default.",
164
+ "type": "Type",
165
+ "severity": "Severity",
166
+ "severityNotSet": "Not set",
167
+ "severityLow": "Low",
168
+ "severityMedium": "Medium",
169
+ "severityHigh": "High",
170
+ "titleLabel": "Title",
171
+ "descriptionLabel": "Description",
172
+ "stepsLabel": "Steps to reproduce (optional)",
173
+ "expectedLabel": "Expected behavior (optional)",
174
+ "actualLabel": "Actual behavior (optional)",
175
+ "submit": "Save local draft",
176
+ "submitting": "Saving…",
177
+ "hidePreview": "Hide markdown preview",
178
+ "showPreview": "Show markdown preview",
179
+ "types": {
180
+ "bug": "Bug",
181
+ "confusing": "Confusing",
182
+ "privacy_concern": "Privacy concern",
183
+ "feature_request": "Feature request",
184
+ "other": "Other"
185
+ }
186
+ },
187
+ "privacy": {
188
+ "title": "Privacy boundary",
189
+ "description": "What this report will and will not include.",
190
+ "included": "Included",
191
+ "excluded": "Excluded by default",
192
+ "notes": "Redaction notes",
193
+ "submitFirst": "Save a draft to see exactly which sections are included.",
194
+ "guarantee": "No automatic upload. Drafts are stored at <workspace>/.pd/feedback/drafts/. You decide what to do with them."
195
+ },
196
+ "draft": {
197
+ "title": "Draft saved locally",
198
+ "savedAt": "id: {{id}} · saved at {{at}}",
199
+ "copyMarkdown": "Copy Markdown",
200
+ "copyEmail": "Copy email text",
201
+ "openGithub": "Open GitHub issue URL",
202
+ "delete": "Delete draft"
203
+ },
204
+ "saved": {
205
+ "title": "Saved drafts",
206
+ "load": "Load"
207
+ },
208
+ "toast": {
209
+ "draftCreated": "Draft saved locally",
210
+ "draftDeleted": "Draft deleted",
211
+ "markdownCopied": "Markdown copied to clipboard",
212
+ "emailCopied": "Email text copied to clipboard",
213
+ "copyFailed": "Copy failed",
214
+ "loadFailed": "Failed to load draft",
215
+ "deleteFailed": "Failed to delete draft"
216
+ },
217
+ "errors": {
218
+ "missingRequired": "Title and description are required.",
219
+ "createFailed": "Failed to create draft."
220
+ }
221
+ },
157
222
  "gates": {
158
223
  "title": "Gates",
159
224
  "description": "Manage Security Gates",
@@ -422,7 +487,8 @@
422
487
  "errorBoundary": {
423
488
  "title": "An Error Occurred",
424
489
  "description": "An error occurred while loading the page",
425
- "retry": "Retry"
490
+ "retry": "Retry",
491
+ "reportAction": "Report this problem"
426
492
  },
427
493
  "zoneSection": {
428
494
  "empty": "No data",
@@ -154,6 +154,71 @@
154
154
  "origin": "来源:",
155
155
  "gfiAfter": "GFI 之后:"
156
156
  },
157
+ "reportProblem": {
158
+ "title": "报告问题",
159
+ "description": "生成本地隐私可控的反馈草稿。Console 不会自动上传——你决定分享什么、如何分享、何时分享。",
160
+ "deepLinked": "从 {{source}} 跳转而来。",
161
+ "form": {
162
+ "title": "描述发生了什么",
163
+ "description": "仅存储以下字段。原始 prompt、聊天记录、文件内容、路径、环境变量和令牌默认排除。",
164
+ "type": "类型",
165
+ "severity": "严重程度",
166
+ "severityNotSet": "未设置",
167
+ "severityLow": "低",
168
+ "severityMedium": "中",
169
+ "severityHigh": "高",
170
+ "titleLabel": "标题",
171
+ "descriptionLabel": "描述",
172
+ "stepsLabel": "复现步骤(可选)",
173
+ "expectedLabel": "预期行为(可选)",
174
+ "actualLabel": "实际行为(可选)",
175
+ "submit": "保存本地草稿",
176
+ "submitting": "保存中…",
177
+ "hidePreview": "隐藏 Markdown 预览",
178
+ "showPreview": "显示 Markdown 预览",
179
+ "types": {
180
+ "bug": "缺陷",
181
+ "confusing": "体验困惑",
182
+ "privacy_concern": "隐私问题",
183
+ "feature_request": "功能请求",
184
+ "other": "其他"
185
+ }
186
+ },
187
+ "privacy": {
188
+ "title": "隐私边界",
189
+ "description": "本报告将包含什么、不包含什么。",
190
+ "included": "已包含",
191
+ "excluded": "默认排除",
192
+ "notes": "脱敏说明",
193
+ "submitFirst": "保存一份草稿后可看到具体包含哪些部分。",
194
+ "guarantee": "不会自动上传。草稿保存在 <workspace>/.pd/feedback/drafts/。你可以自行决定如何使用。"
195
+ },
196
+ "draft": {
197
+ "title": "草稿已本地保存",
198
+ "savedAt": "id:{{id}} · 保存于 {{at}}",
199
+ "copyMarkdown": "复制 Markdown",
200
+ "copyEmail": "复制邮件文本",
201
+ "openGithub": "打开 GitHub issue 链接",
202
+ "delete": "删除草稿"
203
+ },
204
+ "saved": {
205
+ "title": "已保存的草稿",
206
+ "load": "加载"
207
+ },
208
+ "toast": {
209
+ "draftCreated": "草稿已本地保存",
210
+ "draftDeleted": "草稿已删除",
211
+ "markdownCopied": "Markdown 已复制到剪贴板",
212
+ "emailCopied": "邮件文本已复制到剪贴板",
213
+ "copyFailed": "复制失败",
214
+ "loadFailed": "加载草稿失败",
215
+ "deleteFailed": "删除草稿失败"
216
+ },
217
+ "errors": {
218
+ "missingRequired": "标题和描述为必填项。",
219
+ "createFailed": "创建草稿失败。"
220
+ }
221
+ },
157
222
  "gates": {
158
223
  "title": "门控",
159
224
  "description": "管理安全门控",
@@ -422,7 +487,8 @@
422
487
  "errorBoundary": {
423
488
  "title": "发生错误",
424
489
  "description": "页面加载时发生错误",
425
- "retry": "重试"
490
+ "retry": "重试",
491
+ "reportAction": "报告此问题"
426
492
  },
427
493
  "zoneSection": {
428
494
  "empty": "暂无数据",
@@ -0,0 +1 @@
1
+ export declare function ReportProblemPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,186 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ // ReportProblemPage.tsx
3
+ // Console-first MVP seed feedback report generator (PRI-285, Scheme B).
4
+ //
5
+ // This page NEVER uploads the report anywhere automatically. The user fills
6
+ // in the form, the browser calls POST /api/feedback/reports, and the server
7
+ // writes the draft to <workspace>/.pd/feedback/drafts/<id>.json. The page
8
+ // then offers three local-only export actions:
9
+ // 1. Copy Markdown to clipboard
10
+ // 2. Copy email-ready plain text to clipboard
11
+ // 3. Open a pre-filled GitHub issue URL (the user still has to click Submit)
12
+ //
13
+ // The page can be deep-linked via `?source=<tag>&message=<text>` so the error
14
+ // boundary can route here with a pre-filled description.
15
+ import { useEffect, useMemo, useState } from "react";
16
+ import { useSearchParams } from "react-router-dom";
17
+ import { useTranslation } from "react-i18next";
18
+ import { toast } from "sonner";
19
+ import { Copy, ExternalLink, Save, Trash2, Eye, EyeOff } from "lucide-react";
20
+ import { PageHeader } from "../components/page-header.js";
21
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/ui/card.js";
22
+ import { Button } from "../components/ui/button.js";
23
+ import { Input } from "../components/ui/input.js";
24
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../components/ui/select.js";
25
+ import { createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport } from "../api.js";
26
+ import { parseDraftRecord, parseDraftSummary, parseEnvelopeReport, getErrorMessage, } from "./ReportProblemValidators.js";
27
+ const FEEDBACK_TYPES = ["bug", "confusing", "privacy_concern", "feature_request", "other"];
28
+ async function tryCopyToClipboard(text) {
29
+ if (typeof navigator === "undefined" || !navigator.clipboard)
30
+ return false;
31
+ try {
32
+ await navigator.clipboard.writeText(text);
33
+ return true;
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ export function ReportProblemPage() {
40
+ const { t } = useTranslation();
41
+ const [searchParams] = useSearchParams();
42
+ // Form state
43
+ const initialMessage = searchParams.get("message") ?? "";
44
+ const sourceFromUrl = searchParams.get("source") ?? "";
45
+ const [type, setType] = useState("bug");
46
+ const [title, setTitle] = useState("");
47
+ const [description, setDescription] = useState(initialMessage);
48
+ const [stepsToReproduce, setStepsToReproduce] = useState("");
49
+ const [expectedBehavior, setExpectedBehavior] = useState("");
50
+ const [actualBehavior, setActualBehavior] = useState("");
51
+ const [userSeverity, setUserSeverity] = useState("");
52
+ const [showPreview, setShowPreview] = useState(true);
53
+ const [submitting, setSubmitting] = useState(false);
54
+ // Result state
55
+ const [draft, setDraft] = useState(null);
56
+ const [drafts, setDrafts] = useState([]);
57
+ const [error, setError] = useState(null);
58
+ // Refresh the draft list on mount
59
+ useEffect(() => {
60
+ void refreshDrafts();
61
+ }, []);
62
+ const refreshDrafts = async () => {
63
+ try {
64
+ const result = await listFeedbackReports();
65
+ if (result.success && result.data) {
66
+ const rawDrafts = result.data.drafts;
67
+ const items = Array.isArray(rawDrafts) ? rawDrafts : [];
68
+ const parsed = [];
69
+ for (const item of items) {
70
+ const p = parseDraftSummary(item);
71
+ if (p)
72
+ parsed.push(p);
73
+ }
74
+ setDrafts(parsed);
75
+ }
76
+ }
77
+ catch {
78
+ // Swallow — draft loading failure must not block the form.
79
+ }
80
+ };
81
+ const onSubmit = async (e) => {
82
+ e.preventDefault();
83
+ setError(null);
84
+ if (!title.trim() || !description.trim()) {
85
+ setError(t("pages:reportProblem.errors.missingRequired"));
86
+ return;
87
+ }
88
+ setSubmitting(true);
89
+ try {
90
+ const input = {
91
+ type,
92
+ title: title.trim(),
93
+ description: description.trim(),
94
+ };
95
+ if (stepsToReproduce.trim())
96
+ input.stepsToReproduce = stepsToReproduce.trim();
97
+ if (expectedBehavior.trim())
98
+ input.expectedBehavior = expectedBehavior.trim();
99
+ if (actualBehavior.trim())
100
+ input.actualBehavior = actualBehavior.trim();
101
+ if (userSeverity)
102
+ input.userSeverity = userSeverity;
103
+ input.context = { source: 'console', page: sourceFromUrl ? `/report-problem?source=${sourceFromUrl}` : '/report-problem' };
104
+ const result = await createFeedbackReport(input, {});
105
+ if (result.success !== true || !result.data) {
106
+ setError(getErrorMessage(result, t("pages:reportProblem.errors.createFailed")));
107
+ return;
108
+ }
109
+ const parsedDraft = parseDraftRecord(result.data.report);
110
+ if (!parsedDraft) {
111
+ setError(t("pages:reportProblem.errors.createFailed"));
112
+ return;
113
+ }
114
+ setDraft(parsedDraft);
115
+ toast.success(t("pages:reportProblem.toast.draftCreated"));
116
+ await refreshDrafts();
117
+ }
118
+ catch (err) {
119
+ setError(err instanceof Error ? err.message : String(err));
120
+ }
121
+ finally {
122
+ setSubmitting(false);
123
+ }
124
+ };
125
+ const onCopyMarkdown = async () => {
126
+ if (!draft)
127
+ return;
128
+ const ok = await tryCopyToClipboard(draft.outputs.markdown);
129
+ if (ok)
130
+ toast.success(t("pages:reportProblem.toast.markdownCopied"));
131
+ else
132
+ toast.error(t("pages:reportProblem.toast.copyFailed"));
133
+ };
134
+ const onCopyEmail = async () => {
135
+ if (!draft)
136
+ return;
137
+ const ok = await tryCopyToClipboard(draft.outputs.emailText);
138
+ if (ok)
139
+ toast.success(t("pages:reportProblem.toast.emailCopied"));
140
+ else
141
+ toast.error(t("pages:reportProblem.toast.copyFailed"));
142
+ };
143
+ const onOpenGithub = () => {
144
+ if (!draft)
145
+ return;
146
+ window.open(draft.outputs.githubIssueUrl, "_blank", "noopener,noreferrer");
147
+ };
148
+ const onLoadDraft = async (id) => {
149
+ const result = await getFeedbackReport(id);
150
+ if (result.success === true && result.data) {
151
+ const parsed = parseEnvelopeReport(result.data);
152
+ if (!parsed) {
153
+ toast.error(t("pages:reportProblem.toast.loadFailed"));
154
+ return;
155
+ }
156
+ setDraft(parsed);
157
+ setTitle(parsed.title);
158
+ setType(parsed.type);
159
+ const ut = parsed.userText;
160
+ setDescription(ut.description);
161
+ setStepsToReproduce(ut.stepsToReproduce ?? "");
162
+ setExpectedBehavior(ut.expectedBehavior ?? "");
163
+ setActualBehavior(ut.actualBehavior ?? "");
164
+ setUserSeverity(ut.userSeverity ?? "");
165
+ }
166
+ else {
167
+ toast.error(getErrorMessage(result, t("pages:reportProblem.toast.loadFailed")));
168
+ }
169
+ };
170
+ const onDeleteDraft = async (id) => {
171
+ const result = await deleteFeedbackReport(id);
172
+ if (result.success === true) {
173
+ toast.success(t("pages:reportProblem.toast.draftDeleted"));
174
+ if (draft?.id === id)
175
+ setDraft(null);
176
+ await refreshDrafts();
177
+ }
178
+ else {
179
+ toast.error(getErrorMessage(result, t("pages:reportProblem.toast.deleteFailed")));
180
+ }
181
+ };
182
+ const includedSections = useMemo(() => draft?.privacy.includedSections ?? [], [draft]);
183
+ const excludedByDefault = useMemo(() => draft?.privacy.excludedByDefault ?? [], [draft]);
184
+ const redactionNotes = useMemo(() => draft?.privacy.redactionNotes ?? [], [draft]);
185
+ return (_jsxs("div", { className: "space-y-6", children: [_jsx(PageHeader, { title: t("pages:reportProblem.title"), description: t("pages:reportProblem.description") }), sourceFromUrl ? (_jsx("div", { className: "text-xs text-muted-foreground", children: t("pages:reportProblem.deepLinked", { source: sourceFromUrl }) })) : null, _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-6", children: [_jsxs(Card, { className: "lg:col-span-2", children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: t("pages:reportProblem.form.title") }), _jsx(CardDescription, { children: t("pages:reportProblem.form.description") })] }), _jsx(CardContent, { children: _jsxs("form", { onSubmit: onSubmit, className: "space-y-4", "aria-label": "feedback-form", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "feedback-type", className: "text-sm font-medium", children: t("pages:reportProblem.form.type") }), _jsxs(Select, { value: type, onValueChange: (v) => setType(v), children: [_jsx(SelectTrigger, { id: "feedback-type", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: FEEDBACK_TYPES.map((tt) => (_jsx(SelectItem, { value: tt, children: t(`pages:reportProblem.form.types.${tt}`) }, tt))) })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "feedback-severity", className: "text-sm font-medium", children: t("pages:reportProblem.form.severity") }), _jsxs(Select, { value: userSeverity || "none", onValueChange: (v) => setUserSeverity(v === "none" ? "" : v), children: [_jsx(SelectTrigger, { id: "feedback-severity", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "none", children: t("pages:reportProblem.form.severityNotSet") }), _jsx(SelectItem, { value: "low", children: t("pages:reportProblem.form.severityLow") }), _jsx(SelectItem, { value: "medium", children: t("pages:reportProblem.form.severityMedium") }), _jsx(SelectItem, { value: "high", children: t("pages:reportProblem.form.severityHigh") })] })] })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "feedback-title", className: "text-sm font-medium", children: t("pages:reportProblem.form.titleLabel") }), _jsx(Input, { id: "feedback-title", value: title, onChange: (e) => setTitle(e.target.value), maxLength: 200, required: true })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "feedback-description", className: "text-sm font-medium", children: t("pages:reportProblem.form.descriptionLabel") }), _jsx("textarea", { id: "feedback-description", value: description, onChange: (e) => setDescription(e.target.value), rows: 4, className: "flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", required: true })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "feedback-steps", className: "text-sm font-medium", children: t("pages:reportProblem.form.stepsLabel") }), _jsx("textarea", { id: "feedback-steps", value: stepsToReproduce, onChange: (e) => setStepsToReproduce(e.target.value), rows: 3, className: "flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "feedback-expected", className: "text-sm font-medium", children: t("pages:reportProblem.form.expectedLabel") }), _jsx("textarea", { id: "feedback-expected", value: expectedBehavior, onChange: (e) => setExpectedBehavior(e.target.value), rows: 2, className: "flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "feedback-actual", className: "text-sm font-medium", children: t("pages:reportProblem.form.actualLabel") }), _jsx("textarea", { id: "feedback-actual", value: actualBehavior, onChange: (e) => setActualBehavior(e.target.value), rows: 2, className: "flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" })] })] }), error ? (_jsx("div", { role: "alert", className: "text-sm text-destructive", children: error })) : null, _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Button, { type: "submit", disabled: submitting, children: [_jsx(Save, { className: "h-4 w-4 mr-2" }), submitting ? t("pages:reportProblem.form.submitting") : t("pages:reportProblem.form.submit")] }), _jsx(Button, { type: "button", variant: "ghost", onClick: () => setShowPreview((v) => !v), children: showPreview ? (_jsxs(_Fragment, { children: [_jsx(EyeOff, { className: "h-4 w-4 mr-2" }), t("pages:reportProblem.form.hidePreview")] })) : (_jsxs(_Fragment, { children: [_jsx(Eye, { className: "h-4 w-4 mr-2" }), t("pages:reportProblem.form.showPreview")] })) })] })] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: t("pages:reportProblem.privacy.title") }), _jsx(CardDescription, { children: t("pages:reportProblem.privacy.description") })] }), _jsxs(CardContent, { className: "space-y-4 text-sm", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-medium mb-1", children: t("pages:reportProblem.privacy.included") }), _jsxs("ul", { className: "list-disc list-inside text-muted-foreground space-y-1", children: [includedSections.map((s) => (_jsx("li", { children: s }, `i-${s}`))), includedSections.length === 0 ? (_jsx("li", { className: "text-xs italic", children: t("pages:reportProblem.privacy.submitFirst") })) : null] })] }), _jsxs("div", { children: [_jsx("h4", { className: "font-medium mb-1", children: t("pages:reportProblem.privacy.excluded") }), _jsx("ul", { className: "list-disc list-inside text-muted-foreground space-y-1", children: excludedByDefault.map((s) => (_jsx("li", { children: s }, `e-${s}`))) })] }), redactionNotes.length > 0 ? (_jsxs("div", { children: [_jsx("h4", { className: "font-medium mb-1", children: t("pages:reportProblem.privacy.notes") }), _jsx("ul", { className: "list-disc list-inside text-muted-foreground space-y-1", children: redactionNotes.map((n, i) => (_jsx("li", { children: n }, `n-${i}`))) })] })) : null, _jsx("p", { className: "text-xs text-muted-foreground", children: t("pages:reportProblem.privacy.guarantee") })] })] })] }), draft ? (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: t("pages:reportProblem.draft.title") }), _jsx(CardDescription, { children: t("pages:reportProblem.draft.savedAt", { id: draft.id, at: draft.createdAt }) })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Button, { onClick: onCopyMarkdown, variant: "default", children: [_jsx(Copy, { className: "h-4 w-4 mr-2" }), t("pages:reportProblem.draft.copyMarkdown")] }), _jsxs(Button, { onClick: onCopyEmail, variant: "outline", children: [_jsx(Copy, { className: "h-4 w-4 mr-2" }), t("pages:reportProblem.draft.copyEmail")] }), _jsxs(Button, { onClick: onOpenGithub, variant: "outline", children: [_jsx(ExternalLink, { className: "h-4 w-4 mr-2" }), t("pages:reportProblem.draft.openGithub")] }), _jsxs(Button, { onClick: () => void onDeleteDraft(draft.id), variant: "ghost", children: [_jsx(Trash2, { className: "h-4 w-4 mr-2" }), t("pages:reportProblem.draft.delete")] })] }), showPreview ? (_jsx("pre", { className: "rounded-md border border-border bg-muted/40 p-4 text-xs whitespace-pre-wrap break-words max-h-[480px] overflow-y-auto", children: draft.outputs.markdown })) : null] })] })) : null, drafts.length > 0 ? (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: t("pages:reportProblem.saved.title") }) }), _jsx(CardContent, { children: _jsx("ul", { className: "divide-y divide-border", children: drafts.map((d) => (_jsxs("li", { className: "flex items-center justify-between py-2", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: d.title }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [d.type, " \u00B7 ", d.createdAt] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => void onLoadDraft(d.id), children: t("pages:reportProblem.saved.load") }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => void onDeleteDraft(d.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] })] }, d.id))) }) })] })) : null] }));
186
+ }
@@ -0,0 +1,51 @@
1
+ export type FeedbackType = 'bug' | 'confusing' | 'privacy_concern' | 'feature_request' | 'other';
2
+ export type UserSeverity = 'low' | 'medium' | 'high';
3
+ export type DraftRecord = {
4
+ id: string;
5
+ createdAt: string;
6
+ type: FeedbackType;
7
+ title: string;
8
+ userText: {
9
+ description: string;
10
+ stepsToReproduce?: string;
11
+ expectedBehavior?: string;
12
+ actualBehavior?: string;
13
+ userSeverity?: UserSeverity;
14
+ };
15
+ diagnosticSummary: {
16
+ versions: Record<string, unknown>;
17
+ platform: Record<string, unknown>;
18
+ featureFlags: Record<string, unknown>;
19
+ canary: {
20
+ status: 'available' | 'unavailable';
21
+ summary?: string;
22
+ unavailableReason?: string;
23
+ };
24
+ recentEvents: {
25
+ type: string;
26
+ at: string;
27
+ severity?: string;
28
+ summary: string;
29
+ }[];
30
+ };
31
+ privacy: {
32
+ includedSections: string[];
33
+ excludedByDefault: string[];
34
+ redactionNotes: string[];
35
+ };
36
+ outputs: {
37
+ markdown: string;
38
+ emailText: string;
39
+ githubIssueUrl: string;
40
+ };
41
+ };
42
+ export type FeedbackDraftSummary = {
43
+ id: string;
44
+ createdAt: string;
45
+ type: string;
46
+ title: string;
47
+ };
48
+ export declare function parseDraftRecord(value: unknown): DraftRecord | null;
49
+ export declare function parseDraftSummary(value: unknown): FeedbackDraftSummary | null;
50
+ export declare function parseEnvelopeReport(value: unknown): DraftRecord | null;
51
+ export declare function getErrorMessage(result: unknown, fallback: string): string;
@@ -0,0 +1,114 @@
1
+ // ReportProblemValidators.ts
2
+ // Runtime validators for feedback draft payloads received from the server.
3
+ // Extracted from ReportProblemPage.tsx so they can be unit-tested without
4
+ // pulling in React/JSX (vitest in this package runs in `node` environment).
5
+ //
6
+ // The server returns JSON parsed via the standard fetch path. Per ERR-001
7
+ // and ERR-005, every field must be runtime-validated before use; no `as`
8
+ // casts are allowed to bypass validation.
9
+ function isString(value) {
10
+ return typeof value === 'string';
11
+ }
12
+ function isRecord(value) {
13
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
14
+ }
15
+ function isStringArray(value) {
16
+ return Array.isArray(value) && value.every(isString);
17
+ }
18
+ function asRecord(v) {
19
+ return isRecord(v) ? v : {};
20
+ }
21
+ export function parseDraftRecord(value) {
22
+ if (!isRecord(value))
23
+ return null;
24
+ if (!isString(value.id) || !isString(value.createdAt) || !isString(value.title))
25
+ return null;
26
+ if (value.type !== 'bug' &&
27
+ value.type !== 'confusing' &&
28
+ value.type !== 'privacy_concern' &&
29
+ value.type !== 'feature_request' &&
30
+ value.type !== 'other') {
31
+ return null;
32
+ }
33
+ const userText = asRecord(value.userText);
34
+ if (!isString(userText.description))
35
+ return null;
36
+ const outputs = asRecord(value.outputs);
37
+ if (!isString(outputs.markdown) ||
38
+ !isString(outputs.emailText) ||
39
+ !isString(outputs.githubIssueUrl)) {
40
+ return null;
41
+ }
42
+ const privacy = asRecord(value.privacy);
43
+ if (!isStringArray(privacy.includedSections) ||
44
+ !isStringArray(privacy.excludedByDefault) ||
45
+ !isStringArray(privacy.redactionNotes)) {
46
+ return null;
47
+ }
48
+ const diagnostic = asRecord(value.diagnosticSummary);
49
+ const rawCanary = diagnostic.canary;
50
+ const canaryRecord = isRecord(rawCanary) ? rawCanary : null;
51
+ let canaryStatus = null;
52
+ if (canaryRecord && typeof canaryRecord.status === 'string' && (canaryRecord.status === 'available' || canaryRecord.status === 'unavailable')) {
53
+ canaryStatus = canaryRecord.status;
54
+ }
55
+ const validCanary = canaryStatus
56
+ ? { status: canaryStatus, summary: typeof canaryRecord?.summary === 'string' ? canaryRecord.summary : undefined, unavailableReason: typeof canaryRecord?.unavailableReason === 'string' ? canaryRecord.unavailableReason : undefined }
57
+ : null;
58
+ return {
59
+ id: value.id,
60
+ createdAt: value.createdAt,
61
+ type: value.type,
62
+ title: value.title,
63
+ userText: {
64
+ description: userText.description,
65
+ stepsToReproduce: isString(userText.stepsToReproduce) ? userText.stepsToReproduce : undefined,
66
+ expectedBehavior: isString(userText.expectedBehavior) ? userText.expectedBehavior : undefined,
67
+ actualBehavior: isString(userText.actualBehavior) ? userText.actualBehavior : undefined,
68
+ userSeverity: userText.userSeverity === 'low' || userText.userSeverity === 'medium' || userText.userSeverity === 'high'
69
+ ? userText.userSeverity
70
+ : undefined,
71
+ },
72
+ diagnosticSummary: {
73
+ versions: asRecord(diagnostic.versions),
74
+ platform: asRecord(diagnostic.platform),
75
+ featureFlags: asRecord(diagnostic.featureFlags),
76
+ canary: validCanary ?? { status: 'unavailable', unavailableReason: 'diagnostic summary unavailable' },
77
+ recentEvents: Array.isArray(diagnostic.recentEvents) ? diagnostic.recentEvents : [],
78
+ },
79
+ privacy: {
80
+ includedSections: privacy.includedSections,
81
+ excludedByDefault: privacy.excludedByDefault,
82
+ redactionNotes: privacy.redactionNotes,
83
+ },
84
+ outputs: {
85
+ markdown: outputs.markdown,
86
+ emailText: outputs.emailText,
87
+ githubIssueUrl: outputs.githubIssueUrl,
88
+ },
89
+ };
90
+ }
91
+ export function parseDraftSummary(value) {
92
+ if (!isRecord(value))
93
+ return null;
94
+ if (!isString(value.id) || !isString(value.createdAt) || !isString(value.type) || !isString(value.title)) {
95
+ return null;
96
+ }
97
+ return {
98
+ id: value.id,
99
+ createdAt: value.createdAt,
100
+ type: value.type,
101
+ title: value.title,
102
+ };
103
+ }
104
+ export function parseEnvelopeReport(value) {
105
+ if (!isRecord(value))
106
+ return null;
107
+ return parseDraftRecord(value.report);
108
+ }
109
+ export function getErrorMessage(result, fallback) {
110
+ if (isRecord(result) && result.success === false && isString(result.error)) {
111
+ return result.error;
112
+ }
113
+ return fallback;
114
+ }