@fatagnus/convex-feedback 0.2.0 → 0.2.2

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.0",
3
+ "version": "0.2.2",
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": {
@@ -642,14 +642,30 @@ export const startBugInterview = action({
642
642
  });
643
643
 
644
644
  // Start the interview
645
- const result = await dynamicAgent.generateText(
646
- ctx,
647
- {
648
- threadId,
649
- customCtx: { sessionId },
650
- },
651
- { prompt: "Start the bug report interview. Greet the user briefly and ask what bug or issue they encountered." }
652
- );
645
+ let result;
646
+ try {
647
+ result = await dynamicAgent.generateText(
648
+ ctx,
649
+ {
650
+ threadId,
651
+ customCtx: { sessionId },
652
+ },
653
+ { prompt: "Start the bug report interview. Greet the user briefly and ask what bug or issue they encountered." }
654
+ );
655
+ } catch (error) {
656
+ // Provide more descriptive error messages based on the error type
657
+ const errorMessage = error instanceof Error ? error.message : String(error);
658
+ if (errorMessage.includes("401") || errorMessage.includes("unauthorized")) {
659
+ throw new Error("AI service authentication failed. Please check your OPENROUTER_API_KEY.");
660
+ } else if (errorMessage.includes("429") || errorMessage.includes("rate limit")) {
661
+ throw new Error("AI service rate limited. Please try again in a few moments.");
662
+ } else if (errorMessage.includes("503") || errorMessage.includes("unavailable")) {
663
+ throw new Error("AI service temporarily unavailable. Please try again later.");
664
+ } else if (errorMessage.includes("timeout") || errorMessage.includes("ETIMEDOUT")) {
665
+ throw new Error("AI service request timed out. Please try again.");
666
+ }
667
+ throw new Error(`Failed to start interview: ${errorMessage}`);
668
+ }
653
669
 
654
670
  // Check for pending input request
655
671
  const pendingRequest = await ctx.runQuery(
@@ -758,14 +774,30 @@ export const startFeedbackInterview = action({
758
774
  });
759
775
 
760
776
  // Start the interview
761
- const result = await dynamicAgent.generateText(
762
- ctx,
763
- {
764
- threadId,
765
- customCtx: { sessionId },
766
- },
767
- { prompt: "Start the feedback interview. Greet the user briefly and ask about their idea or suggestion." }
768
- );
777
+ let result;
778
+ try {
779
+ result = await dynamicAgent.generateText(
780
+ ctx,
781
+ {
782
+ threadId,
783
+ customCtx: { sessionId },
784
+ },
785
+ { prompt: "Start the feedback interview. Greet the user briefly and ask about their idea or suggestion." }
786
+ );
787
+ } catch (error) {
788
+ // Provide more descriptive error messages based on the error type
789
+ const errorMessage = error instanceof Error ? error.message : String(error);
790
+ if (errorMessage.includes("401") || errorMessage.includes("unauthorized")) {
791
+ throw new Error("AI service authentication failed. Please check your OPENROUTER_API_KEY.");
792
+ } else if (errorMessage.includes("429") || errorMessage.includes("rate limit")) {
793
+ throw new Error("AI service rate limited. Please try again in a few moments.");
794
+ } else if (errorMessage.includes("503") || errorMessage.includes("unavailable")) {
795
+ throw new Error("AI service temporarily unavailable. Please try again later.");
796
+ } else if (errorMessage.includes("timeout") || errorMessage.includes("ETIMEDOUT")) {
797
+ throw new Error("AI service request timed out. Please try again.");
798
+ }
799
+ throw new Error(`Failed to start interview: ${errorMessage}`);
800
+ }
769
801
 
770
802
  // Check for pending input request
771
803
  const pendingRequest = await ctx.runQuery(
@@ -887,18 +919,34 @@ export const continueInterview = action({
887
919
  });
888
920
 
889
921
  // Continue the agent
890
- const result = await dynamicAgent.generateText(
891
- ctx,
892
- {
893
- threadId: args.threadId,
894
- customCtx: { sessionId: args.sessionId },
895
- },
896
- {
897
- prompt: `The user responded: ${args.response}
922
+ let result;
923
+ try {
924
+ result = await dynamicAgent.generateText(
925
+ ctx,
926
+ {
927
+ threadId: args.threadId,
928
+ customCtx: { sessionId: args.sessionId },
929
+ },
930
+ {
931
+ prompt: `The user responded: ${args.response}
898
932
 
899
933
  Please continue the interview based on their response.`,
934
+ }
935
+ );
936
+ } catch (error) {
937
+ // Provide more descriptive error messages based on the error type
938
+ const errorMessage = error instanceof Error ? error.message : String(error);
939
+ if (errorMessage.includes("401") || errorMessage.includes("unauthorized")) {
940
+ throw new Error("AI service authentication failed. Please check your OPENROUTER_API_KEY.");
941
+ } else if (errorMessage.includes("429") || errorMessage.includes("rate limit")) {
942
+ throw new Error("AI service rate limited. Please try again in a few moments.");
943
+ } else if (errorMessage.includes("503") || errorMessage.includes("unavailable")) {
944
+ throw new Error("AI service temporarily unavailable. Please try again later.");
945
+ } else if (errorMessage.includes("timeout") || errorMessage.includes("ETIMEDOUT")) {
946
+ throw new Error("AI service request timed out. Please try again.");
900
947
  }
901
- );
948
+ throw new Error(`Failed to continue interview: ${errorMessage}`);
949
+ }
902
950
 
903
951
  // Check if interview is complete (session has generated report)
904
952
  const session = await ctx.runQuery(api.agents.feedbackInterviewAgent.getSessionByThread, {
@@ -8,6 +8,8 @@
8
8
  export * as bugReports from './bugReports';
9
9
  export * as feedback from './feedback';
10
10
  export * as supportTeams from './supportTeams';
11
+ export * as inputRequests from './inputRequests';
12
+ export * as agents from './agents';
11
13
 
12
14
  // Re-export schema types and validators
13
15
  export {
@@ -752,11 +752,12 @@ export function BugReportButton({
752
752
  };
753
753
 
754
754
  // Auto-start interview when interview mode is selected and no interview is active
755
+ // Important: Check for error state to prevent infinite retry loop on failure
755
756
  useEffect(() => {
756
- if (opened && inputMode === 'interview' && enableInterview && !interviewState.threadId && !interviewState.isThinking) {
757
+ if (opened && inputMode === 'interview' && enableInterview && !interviewState.threadId && !interviewState.isThinking && !interviewState.error) {
757
758
  handleStartInterview();
758
759
  }
759
- }, [opened, inputMode, enableInterview, interviewState.threadId, interviewState.isThinking, handleStartInterview]);
760
+ }, [opened, inputMode, enableInterview, interviewState.threadId, interviewState.isThinking, interviewState.error, handleStartInterview]);
760
761
 
761
762
  return (
762
763
  <>
@@ -844,9 +845,21 @@ export function BugReportButton({
844
845
  {interviewState.error && (
845
846
  <Alert icon={<IconAlertCircle size={16} />} color="red" title="Interview Error">
846
847
  {interviewState.error}
847
- <Button variant="subtle" size="xs" mt="xs" onClick={() => setInputMode('form')}>
848
- Switch to Form
849
- </Button>
848
+ <Group gap="xs" mt="xs">
849
+ <Button
850
+ variant="subtle"
851
+ size="xs"
852
+ onClick={() => {
853
+ // Clear error and retry
854
+ setInterviewState(prev => ({ ...prev, error: null }));
855
+ }}
856
+ >
857
+ Try Again
858
+ </Button>
859
+ <Button variant="subtle" size="xs" onClick={() => setInputMode('form')}>
860
+ Switch to Form
861
+ </Button>
862
+ </Group>
850
863
  </Alert>
851
864
  )}
852
865