@fatagnus/convex-feedback 0.1.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.
- package/LICENSE +177 -0
- package/README.md +382 -0
- package/dist/convex/agents/bugReportAgent.d.ts +30 -0
- package/dist/convex/agents/bugReportAgent.d.ts.map +1 -0
- package/dist/convex/agents/bugReportAgent.js +243 -0
- package/dist/convex/agents/bugReportAgent.js.map +1 -0
- package/dist/convex/agents/feedbackAgent.d.ts +29 -0
- package/dist/convex/agents/feedbackAgent.d.ts.map +1 -0
- package/dist/convex/agents/feedbackAgent.js +232 -0
- package/dist/convex/agents/feedbackAgent.js.map +1 -0
- package/dist/convex/bugReports.d.ts +49 -0
- package/dist/convex/bugReports.d.ts.map +1 -0
- package/dist/convex/bugReports.js +321 -0
- package/dist/convex/bugReports.js.map +1 -0
- package/dist/convex/convex.config.d.ts +3 -0
- package/dist/convex/convex.config.d.ts.map +1 -0
- package/dist/convex/convex.config.js +6 -0
- package/dist/convex/convex.config.js.map +1 -0
- package/dist/convex/emails/bugReportEmails.d.ts +16 -0
- package/dist/convex/emails/bugReportEmails.d.ts.map +1 -0
- package/dist/convex/emails/bugReportEmails.js +403 -0
- package/dist/convex/emails/bugReportEmails.js.map +1 -0
- package/dist/convex/emails/feedbackEmails.d.ts +16 -0
- package/dist/convex/emails/feedbackEmails.d.ts.map +1 -0
- package/dist/convex/emails/feedbackEmails.js +389 -0
- package/dist/convex/emails/feedbackEmails.js.map +1 -0
- package/dist/convex/feedback.d.ts +49 -0
- package/dist/convex/feedback.d.ts.map +1 -0
- package/dist/convex/feedback.js +327 -0
- package/dist/convex/feedback.js.map +1 -0
- package/dist/convex/index.d.ts +10 -0
- package/dist/convex/index.d.ts.map +1 -0
- package/dist/convex/index.js +12 -0
- package/dist/convex/index.js.map +1 -0
- package/dist/convex/schema.d.ts +200 -0
- package/dist/convex/schema.d.ts.map +1 -0
- package/dist/convex/schema.js +150 -0
- package/dist/convex/schema.js.map +1 -0
- package/dist/convex/supportTeams.d.ts +29 -0
- package/dist/convex/supportTeams.d.ts.map +1 -0
- package/dist/convex/supportTeams.js +159 -0
- package/dist/convex/supportTeams.js.map +1 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/react/BugReportButton.d.ts +70 -0
- package/dist/react/BugReportButton.d.ts.map +1 -0
- package/dist/react/BugReportButton.js +371 -0
- package/dist/react/BugReportButton.js.map +1 -0
- package/dist/react/BugReportContext.d.ts +59 -0
- package/dist/react/BugReportContext.d.ts.map +1 -0
- package/dist/react/BugReportContext.js +107 -0
- package/dist/react/BugReportContext.js.map +1 -0
- package/dist/react/index.d.ts +36 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +36 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types.d.ts +89 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +101 -0
- package/src/convex/agents/bugReportAgent.ts +277 -0
- package/src/convex/agents/feedbackAgent.ts +264 -0
- package/src/convex/bugReports.ts +350 -0
- package/src/convex/convex.config.ts +7 -0
- package/src/convex/emails/bugReportEmails.ts +479 -0
- package/src/convex/emails/feedbackEmails.ts +465 -0
- package/src/convex/feedback.ts +356 -0
- package/src/convex/index.ts +28 -0
- package/src/convex/schema.ts +207 -0
- package/src/convex/supportTeams.ts +179 -0
- package/src/index.ts +77 -0
- package/src/react/BugReportButton.tsx +755 -0
- package/src/react/BugReportContext.tsx +146 -0
- package/src/react/index.ts +46 -0
- package/src/types.ts +93 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @convex-dev/feedback - React Components
|
|
3
|
+
*
|
|
4
|
+
* React components for bug reports and feedback collection.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { BugReportProvider, BugReportButton } from '@convex-dev/feedback/react';
|
|
9
|
+
* import { api } from './convex/_generated/api';
|
|
10
|
+
*
|
|
11
|
+
* function App() {
|
|
12
|
+
* return (
|
|
13
|
+
* <BugReportProvider>
|
|
14
|
+
* <YourApp />
|
|
15
|
+
* <BugReportButton
|
|
16
|
+
* reporterType="staff"
|
|
17
|
+
* reporterId={user.id}
|
|
18
|
+
* reporterEmail={user.email}
|
|
19
|
+
* reporterName={user.name}
|
|
20
|
+
* bugReportApi={{
|
|
21
|
+
* create: api.feedback.bugReports.create,
|
|
22
|
+
* generateUploadUrl: api.feedback.bugReports.generateUploadUrl,
|
|
23
|
+
* }}
|
|
24
|
+
* feedbackApi={{
|
|
25
|
+
* create: api.feedback.feedback.create,
|
|
26
|
+
* generateUploadUrl: api.feedback.feedback.generateUploadUrl,
|
|
27
|
+
* }}
|
|
28
|
+
* />
|
|
29
|
+
* </BugReportProvider>
|
|
30
|
+
* );
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export { BugReportProvider, useBugReportContext, } from './BugReportContext';
|
|
35
|
+
export { BugReportButton, } from './BugReportButton';
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GAIpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,GAEhB,MAAM,mBAAmB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for @convex-dev/feedback
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Bug report document type
|
|
6
|
+
* Note: _id is typed as string for portability. Use your app's Id type for full typing.
|
|
7
|
+
*/
|
|
8
|
+
export interface BugReport {
|
|
9
|
+
_id: string;
|
|
10
|
+
_creationTime: number;
|
|
11
|
+
title: string;
|
|
12
|
+
description: string;
|
|
13
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
14
|
+
status: 'open' | 'in-progress' | 'resolved' | 'closed';
|
|
15
|
+
isArchived?: boolean;
|
|
16
|
+
reporterType: 'staff' | 'customer';
|
|
17
|
+
reporterId: string;
|
|
18
|
+
reporterEmail: string;
|
|
19
|
+
reporterName: string;
|
|
20
|
+
url: string;
|
|
21
|
+
route?: string;
|
|
22
|
+
browserInfo: string;
|
|
23
|
+
consoleErrors?: string;
|
|
24
|
+
screenshotStorageId?: string;
|
|
25
|
+
viewportWidth: number;
|
|
26
|
+
viewportHeight: number;
|
|
27
|
+
networkState: string;
|
|
28
|
+
aiSummary?: string;
|
|
29
|
+
aiRootCauseAnalysis?: string;
|
|
30
|
+
aiReproductionSteps?: string[];
|
|
31
|
+
aiSuggestedFix?: string;
|
|
32
|
+
aiEstimatedEffort?: 'low' | 'medium' | 'high';
|
|
33
|
+
aiSuggestedSeverity?: 'low' | 'medium' | 'high' | 'critical';
|
|
34
|
+
aiAnalyzedAt?: number;
|
|
35
|
+
aiThreadId?: string;
|
|
36
|
+
notificationsSentAt?: number;
|
|
37
|
+
createdAt: number;
|
|
38
|
+
updatedAt: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Feedback document type
|
|
42
|
+
*/
|
|
43
|
+
export interface Feedback {
|
|
44
|
+
_id: string;
|
|
45
|
+
_creationTime: number;
|
|
46
|
+
type: 'feature_request' | 'change_request' | 'general';
|
|
47
|
+
title: string;
|
|
48
|
+
description: string;
|
|
49
|
+
priority: 'nice_to_have' | 'important' | 'critical';
|
|
50
|
+
status: 'open' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
|
|
51
|
+
isArchived?: boolean;
|
|
52
|
+
reporterType: 'staff' | 'customer';
|
|
53
|
+
reporterId: string;
|
|
54
|
+
reporterEmail: string;
|
|
55
|
+
reporterName: string;
|
|
56
|
+
url: string;
|
|
57
|
+
route?: string;
|
|
58
|
+
browserInfo: string;
|
|
59
|
+
consoleErrors?: string;
|
|
60
|
+
screenshotStorageId?: string;
|
|
61
|
+
viewportWidth: number;
|
|
62
|
+
viewportHeight: number;
|
|
63
|
+
networkState: string;
|
|
64
|
+
aiSummary?: string;
|
|
65
|
+
aiImpactAnalysis?: string;
|
|
66
|
+
aiActionItems?: string[];
|
|
67
|
+
aiEstimatedEffort?: 'low' | 'medium' | 'high';
|
|
68
|
+
aiSuggestedPriority?: 'nice_to_have' | 'important' | 'critical';
|
|
69
|
+
aiAnalyzedAt?: number;
|
|
70
|
+
aiThreadId?: string;
|
|
71
|
+
notificationsSentAt?: number;
|
|
72
|
+
createdAt: number;
|
|
73
|
+
updatedAt: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Support team document type
|
|
77
|
+
*/
|
|
78
|
+
export interface SupportTeam {
|
|
79
|
+
_id: string;
|
|
80
|
+
_creationTime: number;
|
|
81
|
+
teamName: string;
|
|
82
|
+
memberEmails: string[];
|
|
83
|
+
feedbackTypes: ('feature_request' | 'change_request' | 'general')[];
|
|
84
|
+
bugReportSeverities: ('low' | 'medium' | 'high' | 'critical')[];
|
|
85
|
+
isActive: boolean;
|
|
86
|
+
createdAt: number;
|
|
87
|
+
updatedAt: number;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fatagnus/convex-feedback",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Bug reports and feedback collection component for Convex applications with AI analysis and email notifications",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/ozanturksever/convex-feedback"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/ozanturksever/convex-feedback#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/ozanturksever/convex-feedback/issues"
|
|
13
|
+
},
|
|
14
|
+
"author": "Ozan Turksever <ozan.turksever@gmail.com>",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"convex",
|
|
20
|
+
"feedback",
|
|
21
|
+
"bug-reports",
|
|
22
|
+
"ai-analysis",
|
|
23
|
+
"email-notifications",
|
|
24
|
+
"react"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "./dist/index.js",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"import": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./react": {
|
|
36
|
+
"types": "./dist/react/index.d.ts",
|
|
37
|
+
"import": "./dist/react/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./convex.config": "./src/convex/convex.config.ts",
|
|
40
|
+
"./convex/*": "./src/convex/*"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"src",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc",
|
|
50
|
+
"dev": "tsc --watch",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"clean": "rm -rf dist"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@ai-sdk/openai-compatible": ">=0.1.0",
|
|
56
|
+
"@convex-dev/agent": ">=0.1.0",
|
|
57
|
+
"@mantine/core": ">=7.0.0",
|
|
58
|
+
"@mantine/form": ">=7.0.0",
|
|
59
|
+
"@mantine/hooks": ">=7.0.0",
|
|
60
|
+
"@mantine/notifications": ">=7.0.0",
|
|
61
|
+
"@tabler/icons-react": ">=3.0.0",
|
|
62
|
+
"convex": ">=1.17.0",
|
|
63
|
+
"html2canvas": ">=1.4.0",
|
|
64
|
+
"react": ">=18.0.0",
|
|
65
|
+
"resend": ">=4.0.0"
|
|
66
|
+
},
|
|
67
|
+
"peerDependenciesMeta": {
|
|
68
|
+
"@convex-dev/agent": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"@ai-sdk/openai-compatible": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"resend": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"@mantine/core": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"@mantine/form": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"@mantine/hooks": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"@mantine/notifications": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"@tabler/icons-react": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"html2canvas": {
|
|
93
|
+
"optional": true
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"devDependencies": {
|
|
97
|
+
"@types/node": "^20.0.0",
|
|
98
|
+
"@types/react": "^18.0.0",
|
|
99
|
+
"typescript": "^5.0.0"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug Report AI Agent
|
|
3
|
+
*
|
|
4
|
+
* Automatically analyzes bug report submissions and generates:
|
|
5
|
+
* - Summary of the bug
|
|
6
|
+
* - Root cause analysis
|
|
7
|
+
* - Reproduction steps
|
|
8
|
+
* - Suggested fix
|
|
9
|
+
* - Estimated effort (low/medium/high)
|
|
10
|
+
* - Suggested severity (low/medium/high/critical)
|
|
11
|
+
*
|
|
12
|
+
* Triggers email notifications to the reporter and assigned team.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { v } from "convex/values";
|
|
16
|
+
import { Agent, createThread } from "@convex-dev/agent";
|
|
17
|
+
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
18
|
+
import { components, internal } from "../_generated/api";
|
|
19
|
+
import { internalAction, internalQuery } from "../_generated/server";
|
|
20
|
+
import type { BugSeverity, Effort } from "../schema";
|
|
21
|
+
|
|
22
|
+
// Create OpenRouter provider
|
|
23
|
+
const openrouter = createOpenAICompatible({
|
|
24
|
+
name: "openrouter",
|
|
25
|
+
baseURL: "https://openrouter.ai/api/v1",
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Agent name constant
|
|
32
|
+
const AGENT_NAME = "Bug Report Analyst";
|
|
33
|
+
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Agent Definition
|
|
36
|
+
// ============================================================================
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Bug Report Analysis Agent
|
|
40
|
+
*
|
|
41
|
+
* Analyzes bug report submissions and generates comprehensive analysis.
|
|
42
|
+
*/
|
|
43
|
+
export const bugReportAgent = new Agent(components.agent, {
|
|
44
|
+
name: AGENT_NAME,
|
|
45
|
+
languageModel: openrouter.languageModel("anthropic/claude-sonnet-4"),
|
|
46
|
+
|
|
47
|
+
instructions: `You are the Bug Report Analyst, an AI assistant that analyzes bug report submissions.
|
|
48
|
+
|
|
49
|
+
When given a bug report, you will:
|
|
50
|
+
1. **Summarize** the bug in 2-3 clear sentences
|
|
51
|
+
2. **Analyze Root Cause** - identify potential underlying causes based on the symptoms described
|
|
52
|
+
3. **Extract Reproduction Steps** - create clear, numbered steps to reproduce the bug
|
|
53
|
+
4. **Suggest Fix** - provide a recommended approach to fix the bug
|
|
54
|
+
5. **Estimate Effort** - rate as "low" (< 1 day), "medium" (1-3 days), or "high" (> 3 days)
|
|
55
|
+
6. **Suggest Severity** - rate as "low" (minor inconvenience), "medium" (affects workflow), "high" (blocks work), or "critical" (data loss/security)
|
|
56
|
+
|
|
57
|
+
## Response Format
|
|
58
|
+
Always respond in the following JSON format:
|
|
59
|
+
\`\`\`json
|
|
60
|
+
{
|
|
61
|
+
"summary": "A 2-3 sentence summary of the bug",
|
|
62
|
+
"rootCauseAnalysis": "Analysis of potential root cause",
|
|
63
|
+
"reproductionSteps": ["Step 1", "Step 2", "Step 3"],
|
|
64
|
+
"suggestedFix": "Recommended approach to fix the bug",
|
|
65
|
+
"estimatedEffort": "low" | "medium" | "high",
|
|
66
|
+
"suggestedSeverity": "low" | "medium" | "high" | "critical",
|
|
67
|
+
"reasoning": "Brief explanation for estimates"
|
|
68
|
+
}
|
|
69
|
+
\`\`\`
|
|
70
|
+
|
|
71
|
+
## Guidelines
|
|
72
|
+
- Be precise and technical in your analysis
|
|
73
|
+
- Consider edge cases and related issues
|
|
74
|
+
- Reproduction steps should be specific and actionable
|
|
75
|
+
- Suggested fix should be practical and implementable
|
|
76
|
+
- Effort estimation should account for investigation, fix, testing, and deployment
|
|
77
|
+
- Severity should reflect user impact and business criticality`,
|
|
78
|
+
|
|
79
|
+
tools: {},
|
|
80
|
+
maxSteps: 3,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Internal Queries
|
|
85
|
+
// ============================================================================
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get bug report by ID for analysis
|
|
89
|
+
*/
|
|
90
|
+
export const getBugReportForAnalysis = internalQuery({
|
|
91
|
+
args: {
|
|
92
|
+
bugReportId: v.id("bugReports"),
|
|
93
|
+
},
|
|
94
|
+
returns: v.union(
|
|
95
|
+
v.object({
|
|
96
|
+
_id: v.id("bugReports"),
|
|
97
|
+
title: v.string(),
|
|
98
|
+
description: v.string(),
|
|
99
|
+
severity: v.string(),
|
|
100
|
+
reporterName: v.string(),
|
|
101
|
+
reporterEmail: v.string(),
|
|
102
|
+
url: v.string(),
|
|
103
|
+
browserInfo: v.string(),
|
|
104
|
+
consoleErrors: v.optional(v.string()),
|
|
105
|
+
}),
|
|
106
|
+
v.null()
|
|
107
|
+
),
|
|
108
|
+
handler: async (ctx, args) => {
|
|
109
|
+
const bugReport = await ctx.db.get(args.bugReportId);
|
|
110
|
+
if (!bugReport) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
_id: bugReport._id,
|
|
115
|
+
title: bugReport.title,
|
|
116
|
+
description: bugReport.description,
|
|
117
|
+
severity: bugReport.severity,
|
|
118
|
+
reporterName: bugReport.reporterName,
|
|
119
|
+
reporterEmail: bugReport.reporterEmail,
|
|
120
|
+
url: bugReport.url,
|
|
121
|
+
browserInfo: bugReport.browserInfo,
|
|
122
|
+
consoleErrors: bugReport.consoleErrors,
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// Main Processing Action
|
|
129
|
+
// ============================================================================
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Analyze bug report using AI and send notifications.
|
|
133
|
+
* This is the main entry point triggered after bug report creation.
|
|
134
|
+
*/
|
|
135
|
+
export const processBugReport = internalAction({
|
|
136
|
+
args: {
|
|
137
|
+
bugReportId: v.id("bugReports"),
|
|
138
|
+
},
|
|
139
|
+
returns: v.object({
|
|
140
|
+
success: v.boolean(),
|
|
141
|
+
error: v.optional(v.string()),
|
|
142
|
+
}),
|
|
143
|
+
handler: async (ctx, args): Promise<{ success: boolean; error?: string }> => {
|
|
144
|
+
// Check if OpenRouter API key is configured
|
|
145
|
+
if (!process.env.OPENROUTER_API_KEY) {
|
|
146
|
+
console.log("OPENROUTER_API_KEY not configured, skipping AI analysis");
|
|
147
|
+
// Still try to send notifications without AI analysis
|
|
148
|
+
try {
|
|
149
|
+
await ctx.runAction(internal.emails.bugReportEmails.sendBugReportNotifications, {
|
|
150
|
+
reportId: args.bugReportId,
|
|
151
|
+
analysis: null,
|
|
152
|
+
});
|
|
153
|
+
} catch {
|
|
154
|
+
console.log("Email notifications not configured");
|
|
155
|
+
}
|
|
156
|
+
return { success: true };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Get the bug report
|
|
160
|
+
const bugReport = await ctx.runQuery(internal.agents.bugReportAgent.getBugReportForAnalysis, {
|
|
161
|
+
bugReportId: args.bugReportId,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (!bugReport) {
|
|
165
|
+
return { success: false, error: "Bug report not found" };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Create a thread for this analysis
|
|
169
|
+
const threadId = await createThread(ctx, components.agent, {
|
|
170
|
+
title: `Bug Report Analysis: ${bugReport.title}`,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Build the prompt for analysis
|
|
174
|
+
const consoleErrorsSection = bugReport.consoleErrors
|
|
175
|
+
? `**Console Errors:** ${bugReport.consoleErrors}`
|
|
176
|
+
: "";
|
|
177
|
+
|
|
178
|
+
const analysisPrompt = `Please analyze the following bug report:
|
|
179
|
+
|
|
180
|
+
**Title:** ${bugReport.title}
|
|
181
|
+
**Description:** ${bugReport.description}
|
|
182
|
+
**Reporter Severity:** ${bugReport.severity}
|
|
183
|
+
**Submitted From:** ${bugReport.url}
|
|
184
|
+
**Browser Info:** ${bugReport.browserInfo}
|
|
185
|
+
${consoleErrorsSection}
|
|
186
|
+
|
|
187
|
+
Provide your analysis in the JSON format specified.`;
|
|
188
|
+
|
|
189
|
+
// Generate analysis using the agent
|
|
190
|
+
let result: { text: string };
|
|
191
|
+
try {
|
|
192
|
+
result = await bugReportAgent.generateText(
|
|
193
|
+
ctx,
|
|
194
|
+
{ threadId },
|
|
195
|
+
{ prompt: analysisPrompt }
|
|
196
|
+
);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error("AI analysis failed:", error);
|
|
199
|
+
return { success: false, error: `AI analysis failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Parse the AI response
|
|
203
|
+
let analysis: {
|
|
204
|
+
summary: string;
|
|
205
|
+
rootCauseAnalysis: string;
|
|
206
|
+
reproductionSteps: string[];
|
|
207
|
+
suggestedFix: string;
|
|
208
|
+
estimatedEffort: Effort;
|
|
209
|
+
suggestedSeverity: BugSeverity;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
// Extract JSON from the response (handle markdown code blocks)
|
|
214
|
+
const jsonMatch = result.text.match(/```json\s*([\s\S]*?)\s*```/);
|
|
215
|
+
const jsonStr = jsonMatch ? jsonMatch[1] : result.text;
|
|
216
|
+
const parsed = JSON.parse(jsonStr);
|
|
217
|
+
|
|
218
|
+
analysis = {
|
|
219
|
+
summary: parsed.summary || "Unable to generate summary",
|
|
220
|
+
rootCauseAnalysis: parsed.rootCauseAnalysis || "Unable to generate root cause analysis",
|
|
221
|
+
reproductionSteps: Array.isArray(parsed.reproductionSteps) ? parsed.reproductionSteps : [],
|
|
222
|
+
suggestedFix: parsed.suggestedFix || "Unable to generate suggested fix",
|
|
223
|
+
estimatedEffort: ["low", "medium", "high"].includes(parsed.estimatedEffort)
|
|
224
|
+
? parsed.estimatedEffort
|
|
225
|
+
: "medium",
|
|
226
|
+
suggestedSeverity: ["low", "medium", "high", "critical"].includes(parsed.suggestedSeverity)
|
|
227
|
+
? parsed.suggestedSeverity
|
|
228
|
+
: bugReport.severity as BugSeverity,
|
|
229
|
+
};
|
|
230
|
+
} catch {
|
|
231
|
+
// If parsing fails, create a basic analysis from the text
|
|
232
|
+
analysis = {
|
|
233
|
+
summary: result.text.substring(0, 500),
|
|
234
|
+
rootCauseAnalysis: "AI analysis parsing failed. Please review manually.",
|
|
235
|
+
reproductionSteps: ["Review bug report manually", "Contact reporter for clarification"],
|
|
236
|
+
suggestedFix: "Manual investigation required.",
|
|
237
|
+
estimatedEffort: "medium",
|
|
238
|
+
suggestedSeverity: bugReport.severity as BugSeverity,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Update bug report with analysis
|
|
243
|
+
await ctx.runMutation(internal.bugReports.updateWithAnalysis, {
|
|
244
|
+
bugReportId: args.bugReportId,
|
|
245
|
+
aiSummary: analysis.summary,
|
|
246
|
+
aiRootCauseAnalysis: analysis.rootCauseAnalysis,
|
|
247
|
+
aiReproductionSteps: analysis.reproductionSteps,
|
|
248
|
+
aiSuggestedFix: analysis.suggestedFix,
|
|
249
|
+
aiEstimatedEffort: analysis.estimatedEffort,
|
|
250
|
+
aiSuggestedSeverity: analysis.suggestedSeverity,
|
|
251
|
+
aiThreadId: threadId,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Send email notifications
|
|
255
|
+
try {
|
|
256
|
+
await ctx.runAction(internal.emails.bugReportEmails.sendBugReportNotifications, {
|
|
257
|
+
reportId: args.bugReportId,
|
|
258
|
+
analysis: {
|
|
259
|
+
summary: analysis.summary,
|
|
260
|
+
rootCauseAnalysis: analysis.rootCauseAnalysis,
|
|
261
|
+
reproductionSteps: analysis.reproductionSteps,
|
|
262
|
+
suggestedFix: analysis.suggestedFix,
|
|
263
|
+
estimatedEffort: analysis.estimatedEffort,
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Mark notifications as sent
|
|
268
|
+
await ctx.runMutation(internal.bugReports.markNotificationsSent, {
|
|
269
|
+
bugReportId: args.bugReportId,
|
|
270
|
+
});
|
|
271
|
+
} catch {
|
|
272
|
+
console.log("Email notifications not configured or failed");
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return { success: true };
|
|
276
|
+
},
|
|
277
|
+
});
|