@fatagnus/convex-feedback 0.2.2 → 0.2.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fatagnus/convex-feedback",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Bug reports and feedback collection component for Convex applications with AI analysis and email notifications",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -19,32 +19,23 @@ import { components, internal } from "../_generated/api";
19
19
  import { internalAction, internalQuery } from "../_generated/server";
20
20
  import type { BugSeverity, Effort } from "../schema";
21
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
- });
22
+ // Helper to create OpenRouter provider dynamically
23
+ // This is created dynamically because Convex components don't inherit parent env vars
24
+ function createOpenRouterProvider(apiKey: string) {
25
+ return createOpenAICompatible({
26
+ name: "openrouter",
27
+ baseURL: "https://openrouter.ai/api/v1",
28
+ headers: {
29
+ Authorization: `Bearer ${apiKey}`,
30
+ },
31
+ });
32
+ }
30
33
 
31
34
  // Agent name constant
32
35
  const AGENT_NAME = "Bug Report Analyst";
33
36
 
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.
37
+ // Agent instructions (shared between dynamic agent instances)
38
+ const BUG_REPORT_AGENT_INSTRUCTIONS = `You are the Bug Report Analyst, an AI assistant that analyzes bug report submissions.
48
39
 
49
40
  When given a bug report, you will:
50
41
  1. **Summarize** the bug in 2-3 clear sentences
@@ -74,11 +65,7 @@ Always respond in the following JSON format:
74
65
  - Reproduction steps should be specific and actionable
75
66
  - Suggested fix should be practical and implementable
76
67
  - 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
- });
68
+ - Severity should reflect user impact and business criticality`;
82
69
 
83
70
  // ============================================================================
84
71
  // Internal Queries
@@ -142,7 +129,8 @@ export const processBugReport = internalAction({
142
129
  }),
143
130
  handler: async (ctx, args): Promise<{ success: boolean; error?: string }> => {
144
131
  // Check if OpenRouter API key is configured
145
- if (!process.env.OPENROUTER_API_KEY) {
132
+ const apiKey = process.env.OPENROUTER_API_KEY;
133
+ if (!apiKey) {
146
134
  console.log("OPENROUTER_API_KEY not configured, skipping AI analysis");
147
135
  // Still try to send notifications without AI analysis
148
136
  try {
@@ -170,6 +158,18 @@ export const processBugReport = internalAction({
170
158
  title: `Bug Report Analysis: ${bugReport.title}`,
171
159
  });
172
160
 
161
+ // Create OpenRouter provider dynamically with the API key
162
+ const openrouter = createOpenRouterProvider(apiKey);
163
+
164
+ // Create agent dynamically to ensure API key is available at runtime
165
+ const bugReportAgent = new Agent(components.agent, {
166
+ name: AGENT_NAME,
167
+ languageModel: openrouter.languageModel("anthropic/claude-sonnet-4"),
168
+ instructions: BUG_REPORT_AGENT_INSTRUCTIONS,
169
+ tools: {},
170
+ maxSteps: 3,
171
+ });
172
+
173
173
  // Build the prompt for analysis
174
174
  const consoleErrorsSection = bugReport.consoleErrors
175
175
  ? `**Console Errors:** ${bugReport.consoleErrors}`
@@ -18,32 +18,23 @@ import { components, internal } from "../_generated/api";
18
18
  import { internalAction, internalQuery } from "../_generated/server";
19
19
  import type { FeedbackPriority, Effort } from "../schema";
20
20
 
21
- // Create OpenRouter provider
22
- const openrouter = createOpenAICompatible({
23
- name: "openrouter",
24
- baseURL: "https://openrouter.ai/api/v1",
25
- headers: {
26
- Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
27
- },
28
- });
21
+ // Helper to create OpenRouter provider dynamically
22
+ // This is created dynamically because Convex components don't inherit parent env vars
23
+ function createOpenRouterProvider(apiKey: string) {
24
+ return createOpenAICompatible({
25
+ name: "openrouter",
26
+ baseURL: "https://openrouter.ai/api/v1",
27
+ headers: {
28
+ Authorization: `Bearer ${apiKey}`,
29
+ },
30
+ });
31
+ }
29
32
 
30
33
  // Agent name constant
31
34
  const AGENT_NAME = "Feedback Analyst";
32
35
 
33
- // ============================================================================
34
- // Agent Definition
35
- // ============================================================================
36
-
37
- /**
38
- * Feedback Analysis Agent
39
- *
40
- * Analyzes feedback submissions and generates comprehensive reports.
41
- */
42
- export const feedbackAgent = new Agent(components.agent, {
43
- name: AGENT_NAME,
44
- languageModel: openrouter.languageModel("anthropic/claude-sonnet-4"),
45
-
46
- instructions: `You are the Feedback Analyst, an AI assistant that analyzes user feedback submissions.
36
+ // Agent instructions (shared between dynamic agent instances)
37
+ const FEEDBACK_AGENT_INSTRUCTIONS = `You are the Feedback Analyst, an AI assistant that analyzes user feedback submissions.
47
38
 
48
39
  When given feedback, you will:
49
40
  1. **Summarize** the feedback in 2-3 clear sentences
@@ -70,11 +61,7 @@ Always respond in the following JSON format:
70
61
  - Consider both technical and business impact
71
62
  - Action items should be specific and actionable
72
63
  - Effort estimation should account for development, testing, and deployment
73
- - Priority should reflect urgency and user impact`,
74
-
75
- tools: {},
76
- maxSteps: 3,
77
- });
64
+ - Priority should reflect urgency and user impact`;
78
65
 
79
66
  // ============================================================================
80
67
  // Internal Queries
@@ -138,7 +125,8 @@ export const processFeedback = internalAction({
138
125
  }),
139
126
  handler: async (ctx, args): Promise<{ success: boolean; error?: string }> => {
140
127
  // Check if OpenRouter API key is configured
141
- if (!process.env.OPENROUTER_API_KEY) {
128
+ const apiKey = process.env.OPENROUTER_API_KEY;
129
+ if (!apiKey) {
142
130
  console.log("OPENROUTER_API_KEY not configured, skipping AI analysis");
143
131
  // Still try to send notifications without AI analysis
144
132
  try {
@@ -166,6 +154,18 @@ export const processFeedback = internalAction({
166
154
  title: `Feedback Analysis: ${feedback.title}`,
167
155
  });
168
156
 
157
+ // Create OpenRouter provider dynamically with the API key
158
+ const openrouter = createOpenRouterProvider(apiKey);
159
+
160
+ // Create agent dynamically to ensure API key is available at runtime
161
+ const feedbackAgent = new Agent(components.agent, {
162
+ name: AGENT_NAME,
163
+ languageModel: openrouter.languageModel("anthropic/claude-sonnet-4"),
164
+ instructions: FEEDBACK_AGENT_INSTRUCTIONS,
165
+ tools: {},
166
+ maxSteps: 3,
167
+ });
168
+
169
169
  // Build the prompt for analysis
170
170
  const analysisPrompt = `Please analyze the following feedback submission:
171
171
 
@@ -19,14 +19,17 @@ import { action, query, mutation, internalMutation } from "../_generated/server"
19
19
  import type { Id } from "../_generated/dataModel";
20
20
  import type { BugSeverity, FeedbackType, FeedbackPriority } from "../schema";
21
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
- });
22
+ // Helper to create OpenRouter provider with a specific API key
23
+ // This is created dynamically because Convex components don't inherit parent env vars
24
+ function createOpenRouterProvider(apiKey: string) {
25
+ return createOpenAICompatible({
26
+ name: "openrouter",
27
+ baseURL: "https://openrouter.ai/api/v1",
28
+ headers: {
29
+ Authorization: `Bearer ${apiKey}`,
30
+ },
31
+ });
32
+ }
30
33
 
31
34
  // Agent name constant
32
35
  const AGENT_NAME = "Feedback Interview Agent";
@@ -186,101 +189,6 @@ const generateFeedback = createTool({
186
189
  },
187
190
  });
188
191
 
189
- // ============================================================================
190
- // Agent Definition
191
- // ============================================================================
192
-
193
- /**
194
- * Bug Report Interview Agent
195
- */
196
- export const bugInterviewAgent = new Agent(components.agent, {
197
- name: "Bug Report Interview Agent",
198
- languageModel: openrouter.languageModel("anthropic/claude-sonnet-4"),
199
-
200
- instructions: `You are a helpful assistant that interviews users to gather detailed information about bugs they've encountered.
201
-
202
- Your goal is to understand the bug thoroughly and generate a high-quality bug report.
203
-
204
- ## Interview Flow
205
-
206
- 1. **Greet briefly** and ask what went wrong (the bug they encountered).
207
-
208
- 2. **Understand the bug**:
209
- - What happened? (the actual behavior)
210
- - What did they expect to happen? (expected behavior)
211
- - How often does this occur? (always, sometimes, once)
212
-
213
- 3. **Get reproduction info**:
214
- - What steps led to this bug?
215
- - Can they reliably reproduce it?
216
-
217
- 4. **Assess impact**:
218
- - How does this affect their work?
219
- - How urgent is this for them?
220
-
221
- 5. **Generate the report** using the generateBugReport tool when you have enough information.
222
-
223
- ## Guidelines
224
- - Be conversational but efficient - aim for 3-5 exchanges
225
- - Use choice inputs when there are clear options
226
- - Use text inputs for open-ended questions
227
- - Don't ask for technical details the user might not know
228
- - Focus on understanding the user experience
229
- - Generate the report as soon as you have enough info`,
230
-
231
- tools: {
232
- requestUserInput,
233
- generateBugReport,
234
- },
235
-
236
- maxSteps: 30,
237
- });
238
-
239
- /**
240
- * Feedback Interview Agent
241
- */
242
- export const feedbackInterviewAgent = new Agent(components.agent, {
243
- name: "Feedback Interview Agent",
244
- languageModel: openrouter.languageModel("anthropic/claude-sonnet-4"),
245
-
246
- instructions: `You are a helpful assistant that interviews users to gather detailed feedback and suggestions.
247
-
248
- Your goal is to understand their idea thoroughly and generate well-structured feedback.
249
-
250
- ## Interview Flow
251
-
252
- 1. **Greet briefly** and ask about their idea or suggestion.
253
-
254
- 2. **Understand the need**:
255
- - What problem does this solve?
256
- - What's their current workaround (if any)?
257
-
258
- 3. **Clarify the request**:
259
- - What would the ideal solution look like?
260
- - Is this a new feature, change to existing, or general feedback?
261
-
262
- 4. **Assess importance**:
263
- - How important is this to them?
264
- - Who else might benefit?
265
-
266
- 5. **Generate the feedback** using the generateFeedback tool when you have enough information.
267
-
268
- ## Guidelines
269
- - Be conversational but efficient - aim for 3-5 exchanges
270
- - Use choice inputs when there are clear options
271
- - Use text inputs for open-ended questions
272
- - Help users articulate their ideas clearly
273
- - Focus on understanding the value and impact
274
- - Generate the feedback as soon as you have enough info`,
275
-
276
- tools: {
277
- requestUserInput,
278
- generateFeedback,
279
- },
280
-
281
- maxSteps: 30,
282
- });
283
-
284
192
  // ============================================================================
285
193
  // Internal Mutations
286
194
  // ============================================================================
@@ -570,6 +478,7 @@ Your goal is to understand their idea thoroughly and generate well-structured fe
570
478
  */
571
479
  export const startBugInterview = action({
572
480
  args: {
481
+ openRouterApiKey: v.string(),
573
482
  reporterType: v.union(v.literal("staff"), v.literal("customer")),
574
483
  reporterId: v.string(),
575
484
  reporterEmail: v.string(),
@@ -605,10 +514,8 @@ export const startBugInterview = action({
605
514
  })),
606
515
  }),
607
516
  handler: async (ctx, args) => {
608
- // Check if OpenRouter API key is configured
609
- if (!process.env.OPENROUTER_API_KEY) {
610
- throw new Error("OPENROUTER_API_KEY not configured. Interview mode requires AI.");
611
- }
517
+ // Create OpenRouter provider with the passed API key
518
+ const openrouter = createOpenRouterProvider(args.openRouterApiKey);
612
519
 
613
520
  // Create a thread for the interview
614
521
  const threadId = await createThread(ctx, components.agent, {
@@ -702,6 +609,7 @@ export const startBugInterview = action({
702
609
  */
703
610
  export const startFeedbackInterview = action({
704
611
  args: {
612
+ openRouterApiKey: v.string(),
705
613
  reporterType: v.union(v.literal("staff"), v.literal("customer")),
706
614
  reporterId: v.string(),
707
615
  reporterEmail: v.string(),
@@ -737,10 +645,8 @@ export const startFeedbackInterview = action({
737
645
  })),
738
646
  }),
739
647
  handler: async (ctx, args) => {
740
- // Check if OpenRouter API key is configured
741
- if (!process.env.OPENROUTER_API_KEY) {
742
- throw new Error("OPENROUTER_API_KEY not configured. Interview mode requires AI.");
743
- }
648
+ // Create OpenRouter provider with the passed API key
649
+ const openrouter = createOpenRouterProvider(args.openRouterApiKey);
744
650
 
745
651
  // Create a thread for the interview
746
652
  const threadId = await createThread(ctx, components.agent, {
@@ -834,6 +740,7 @@ export const startFeedbackInterview = action({
834
740
  */
835
741
  export const continueInterview = action({
836
742
  args: {
743
+ openRouterApiKey: v.string(),
837
744
  threadId: v.string(),
838
745
  sessionId: v.string(),
839
746
  requestId: v.id("feedbackInputRequests"),
@@ -891,6 +798,9 @@ export const continueInterview = action({
891
798
  })),
892
799
  }),
893
800
  handler: async (ctx, args) => {
801
+ // Create OpenRouter provider with the passed API key
802
+ const openrouter = createOpenRouterProvider(args.openRouterApiKey);
803
+
894
804
  // Submit the response
895
805
  await ctx.runMutation(
896
806
  api.inputRequests.submitResponse,