33namespace App \Services \Support \Agents ;
44
55use App \Models \Support \SupportCase ;
6+ use App \Services \Support \SupportEmailChangeRequestParser ;
67use App \Services \Support \SupportProfileRequestParser ;
78use App \Services \Support \SupportRoleRequestParser ;
89use Illuminate \Support \Str ;
@@ -11,6 +12,7 @@ class TriageAgentService
1112{
1213 public function __construct (
1314 private readonly SupportProfileRequestParser $ profileParser ,
15+ private readonly SupportEmailChangeRequestParser $ emailChangeParser ,
1416 private readonly CursorCliTriageProvider $ aiProvider ,
1517 private readonly SupportRoleRequestParser $ roleParser ,
1618 ) {
@@ -58,17 +60,29 @@ private function heuristicTriage(SupportCase $case): array
5860 $ rawText = (string ) ($ case ->normalized_message ?? $ case ->raw_message ?? '' );
5961 $ text = Str::lower ($ rawText );
6062 $ profile = $ this ->profileParser ->parse ($ rawText );
63+ $ emailChange = $ this ->emailChangeParser ->parse ($ rawText );
6164 $ roleRequest = $ this ->roleParser ->parse ($ rawText );
62- $ hasRoleRequest = $ roleRequest ['role ' ] !== null
65+ $ hasEmailChangeRequest = $ emailChange ['from_email ' ] !== null && $ emailChange ['to_email ' ] !== null ;
66+ $ hasRoleAddRequest = $ roleRequest ['role ' ] !== null
6367 && $ roleRequest ['operation ' ] === 'add '
6468 && $ roleRequest ['emails ' ] !== [];
69+ $ hasRoleRemoveRequest = $ roleRequest ['role ' ] !== null
70+ && $ roleRequest ['operation ' ] === 'remove '
71+ && $ roleRequest ['emails ' ] !== [];
72+ $ hasRoleRequest = $ hasRoleAddRequest || $ hasRoleRemoveRequest ;
6573
6674 // V1 heuristic placeholder (replace with LLM later, keep output schema stable).
6775 $ caseType = 'unknown ' ;
6876 $ runbook = 'unknown ' ;
69- if ($ hasRoleRequest ) {
77+ if ($ hasRoleRemoveRequest ) {
78+ $ caseType = 'role_remove ' ;
79+ $ runbook = 'remove_user_role ' ;
80+ } elseif ($ hasRoleAddRequest ) {
7081 $ caseType = 'role_add ' ;
7182 $ runbook = 'add_user_role ' ;
83+ } elseif ($ hasEmailChangeRequest ) {
84+ $ caseType = 'email_change ' ;
85+ $ runbook = 'update_user_email ' ;
7286 } elseif (Str::contains ($ text , ['soft-deleted ' , 'deleted ' , 'restore account ' , 'account missing ' ])) {
7387 $ caseType = 'account_restore ' ;
7488 $ runbook = 'restore_deleted_account ' ;
@@ -100,6 +114,9 @@ private function heuristicTriage(SupportCase $case): array
100114 if ($ hasRoleRequest ) {
101115 $ targetEmail = $ roleRequest ['emails ' ][0 ];
102116 $ secondary = array_values (array_slice ($ roleRequest ['emails ' ], 1 ));
117+ } elseif ($ hasEmailChangeRequest ) {
118+ $ targetEmail = $ emailChange ['from_email ' ];
119+ $ secondary = [$ emailChange ['to_email ' ]];
103120 } else {
104121 $ targetEmail = $ profile ['email ' ] ?? $ this ->extractFirstEmail ($ text );
105122 $ secondary = $ this ->extractAllEmails ($ text );
@@ -110,6 +127,9 @@ private function heuristicTriage(SupportCase $case): array
110127 if ($ caseType === 'profile_update ' ) {
111128 $ risk = 'low ' ;
112129 }
130+ if ($ caseType === 'email_change ' ) {
131+ $ risk = 'medium ' ;
132+ }
113133 if ($ hasRoleRequest && $ this ->roleLooksPrivileged ((string ) $ roleRequest ['role ' ])) {
114134 $ risk = 'high ' ;
115135 }
@@ -120,6 +140,8 @@ private function heuristicTriage(SupportCase $case): array
120140 $ requestedAction = match ($ caseType ) {
121141 'profile_update ' => 'user_profile_update ' ,
122142 'role_add ' => 'user_role_add ' ,
143+ 'role_remove ' => 'user_role_remove ' ,
144+ 'email_change ' => 'user_email_update ' ,
123145 default => null ,
124146 };
125147
@@ -134,6 +156,8 @@ private function heuristicTriage(SupportCase $case): array
134156 'profile_lastname ' => $ profile ['lastname ' ],
135157 'role_name ' => $ hasRoleRequest ? $ roleRequest ['role ' ] : null ,
136158 'role_operation ' => $ hasRoleRequest ? $ roleRequest ['operation ' ] : null ,
159+ 'current_email ' => $ hasEmailChangeRequest ? $ emailChange ['from_email ' ] : null ,
160+ 'new_email ' => $ hasEmailChangeRequest ? $ emailChange ['to_email ' ] : null ,
137161 'risk_level ' => $ risk ,
138162 'recommended_runbook ' => $ runbook ,
139163 'needs_human_review ' => $ needsHuman ,
0 commit comments