@realtimex/email-automator 2.11.2 → 2.12.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 (47) hide show
  1. package/api/src/routes/index.ts +2 -0
  2. package/api/src/routes/rulePacks.ts +204 -0
  3. package/api/src/services/RulePackService.ts +354 -0
  4. package/api/src/services/SDKService.ts +18 -7
  5. package/api/src/services/gmail.ts +68 -0
  6. package/api/src/services/intelligence.ts +160 -22
  7. package/api/src/services/microsoft.ts +99 -0
  8. package/api/src/services/processor.ts +146 -12
  9. package/api/src/services/rulePacks/developer.ts +179 -0
  10. package/api/src/services/rulePacks/executive.ts +143 -0
  11. package/api/src/services/rulePacks/index.ts +63 -0
  12. package/api/src/services/rulePacks/operations.ts +183 -0
  13. package/api/src/services/rulePacks/sales.ts +160 -0
  14. package/api/src/services/rulePacks/types.ts +116 -0
  15. package/api/src/services/rulePacks/universal.ts +83 -0
  16. package/api/src/services/supabase.ts +40 -0
  17. package/dist/api/src/routes/index.js +2 -0
  18. package/dist/api/src/routes/rulePacks.js +179 -0
  19. package/dist/api/src/services/RulePackService.js +296 -0
  20. package/dist/api/src/services/SDKService.js +18 -7
  21. package/dist/api/src/services/gmail.js +56 -0
  22. package/dist/api/src/services/intelligence.js +153 -21
  23. package/dist/api/src/services/microsoft.js +79 -0
  24. package/dist/api/src/services/processor.js +133 -12
  25. package/dist/api/src/services/rulePacks/developer.js +176 -0
  26. package/dist/api/src/services/rulePacks/executive.js +140 -0
  27. package/dist/api/src/services/rulePacks/index.js +58 -0
  28. package/dist/api/src/services/rulePacks/operations.js +180 -0
  29. package/dist/api/src/services/rulePacks/sales.js +157 -0
  30. package/dist/api/src/services/rulePacks/types.js +7 -0
  31. package/dist/api/src/services/rulePacks/universal.js +80 -0
  32. package/dist/assets/index-B5rXh3y8.css +1 -0
  33. package/dist/assets/index-B62ViZum.js +105 -0
  34. package/dist/index.html +2 -2
  35. package/package.json +1 -1
  36. package/supabase/migrations/20260131000000_add_rule_packs.sql +176 -0
  37. package/supabase/migrations/20260131000001_set_zero_config_defaults.sql +51 -0
  38. package/supabase/migrations/20260131095000_backfill_user_settings.sql +36 -0
  39. package/supabase/migrations/20260131100000_rule_templates_table.sql +154 -0
  40. package/supabase/migrations/20260131110000_auto_init_user_data.sql +90 -0
  41. package/supabase/migrations/20260131120000_backfill_universal_pack.sql +84 -0
  42. package/supabase/migrations/20260131130000_simplify_rules_with_categories.sql +87 -0
  43. package/supabase/migrations/20260131140000_fix_action_constraint.sql +11 -0
  44. package/supabase/migrations/20260131150000_fix_trigger_error_handling.sql +71 -0
  45. package/supabase/migrations/20260131160000_enable_intelligent_rename_by_default.sql +14 -0
  46. package/dist/assets/index-BuWrl4UD.js +0 -105
  47. package/dist/assets/index-CtDzSy0n.css +0 -1
@@ -9,6 +9,7 @@ import { getStorageService } from './storage.js';
9
9
  import { generateEmailFilename } from '../utils/filename.js';
10
10
  import { EmailAccount, Email, Rule, ProcessingLog } from './supabase.js';
11
11
  import { EventLogger } from './eventLogger.js';
12
+ import { RulePackService } from './RulePackService.js';
12
13
 
13
14
  const logger = createLogger('Processor');
14
15
 
@@ -32,6 +33,18 @@ export class EmailProcessorService {
32
33
  async syncAccount(accountId: string, userId: string): Promise<ProcessingResult> {
33
34
  const result: ProcessingResult = { processed: 0, deleted: 0, drafted: 0, errors: 0 };
34
35
 
36
+ // Zero-Config UX: Auto-install Universal Pack for new users (self-healing)
37
+ try {
38
+ const rulePackService = new RulePackService(this.supabase);
39
+ const { installed } = await rulePackService.ensureUniversalPack(userId);
40
+ if (installed) {
41
+ logger.info(`Auto-installed Universal Pack for user ${userId}`);
42
+ }
43
+ } catch (error) {
44
+ // Don't fail sync if pack installation fails
45
+ logger.error('Failed to auto-install Universal Pack', error);
46
+ }
47
+
35
48
  // Create processing log
36
49
  const { data: log } = await this.supabase
37
50
  .from('processing_logs')
@@ -651,7 +664,11 @@ export class EmailProcessorService {
651
664
  },
652
665
  compiledContext || '', // Pre-compiled context (fast path)
653
666
  eventLogger || undefined,
654
- email.id
667
+ email.id,
668
+ {
669
+ llm_provider: settings?.llm_provider,
670
+ llm_model: settings?.llm_model
671
+ }
655
672
  );
656
673
 
657
674
  if (!analysis) {
@@ -789,7 +806,10 @@ export class EmailProcessorService {
789
806
  subject: email.subject || '',
790
807
  sender: email.sender || '',
791
808
  body: email.body_snippet || ''
792
- }, rule.instructions);
809
+ }, rule.instructions, {
810
+ llm_provider: settings?.llm_provider,
811
+ llm_model: settings?.llm_model
812
+ });
793
813
 
794
814
  if (customizedDraft) {
795
815
  draftContent = customizedDraft;
@@ -805,50 +825,92 @@ export class EmailProcessorService {
805
825
  }
806
826
  }
807
827
 
808
- private matchesCondition(email: Partial<Email>, analysis: EmailAnalysis, condition: Record<string, unknown>): boolean {
809
- if (!analysis) return false;
828
+ /**
829
+ * Enhanced condition matching with support for AI-powered rules
830
+ * Supports logical operators (AND/OR/NOT) and new condition types
831
+ */
832
+ private matchesCondition(email: Partial<Email>, analysis: EmailAnalysis | null, condition: Record<string, unknown>): boolean {
833
+ // Logical operators (evaluated recursively)
834
+ if ('and' in condition) {
835
+ const subConditions = condition.and as Record<string, unknown>[];
836
+ return subConditions.every(subCond => this.matchesCondition(email, analysis, subCond));
837
+ }
838
+
839
+ if ('or' in condition) {
840
+ const subConditions = condition.or as Record<string, unknown>[];
841
+ return subConditions.some(subCond => this.matchesCondition(email, analysis, subCond));
842
+ }
843
+
844
+ if ('not' in condition) {
845
+ const subCondition = condition.not as Record<string, unknown>;
846
+ return !this.matchesCondition(email, analysis, subCondition);
847
+ }
810
848
 
849
+ // Standard condition matching
811
850
  for (const [key, value] of Object.entries(condition)) {
812
851
  const val = value as string;
813
852
 
814
853
  switch (key) {
854
+ // === Legacy field-based matching ===
815
855
  case 'sender_email':
816
856
  if (email.sender?.toLowerCase() !== val.toLowerCase()) return false;
817
857
  break;
858
+
818
859
  case 'sender_domain':
819
- // Check if sender ends with domain (e.g. @gmail.com)
860
+ // Support both "gmail.com" and "@gmail.com" formats
820
861
  const domain = val.startsWith('@') ? val : `@${val}`;
821
862
  if (!email.sender?.toLowerCase().endsWith(domain.toLowerCase())) return false;
822
863
  break;
864
+
823
865
  case 'sender_contains':
824
866
  if (!email.sender?.toLowerCase().includes(val.toLowerCase())) return false;
825
867
  break;
868
+
826
869
  case 'subject_contains':
827
870
  if (!email.subject?.toLowerCase().includes(val.toLowerCase())) return false;
828
871
  break;
872
+
829
873
  case 'body_contains':
830
874
  if (!email.body_snippet?.toLowerCase().includes(val.toLowerCase())) return false;
831
875
  break;
876
+
832
877
  case 'older_than_days':
833
878
  if (!email.date) return false;
834
879
  const ageInMs = Date.now() - new Date(email.date).getTime();
835
880
  const ageInDays = ageInMs / (1000 * 60 * 60 * 24);
836
881
  if (ageInDays < (value as number)) return false;
837
882
  break;
883
+
884
+ // === AI-powered conditions ===
838
885
  case 'category':
839
- if (analysis.category !== value) return false;
886
+ if (!analysis || analysis.category !== value) return false;
840
887
  break;
888
+
841
889
  case 'priority':
842
- if (analysis.priority !== value) return false;
890
+ if (!analysis || analysis.priority !== value) return false;
843
891
  break;
892
+
893
+ case 'ai_priority': // Alias for priority
894
+ if (!analysis || analysis.priority !== value) return false;
895
+ break;
896
+
844
897
  case 'sentiment':
845
- if (analysis.sentiment !== value) return false;
898
+ if (!analysis || analysis.sentiment !== value) return false;
846
899
  break;
900
+
847
901
  case 'is_useless':
848
- if (analysis.is_useless !== value) return false;
902
+ if (!analysis || analysis.is_useless !== value) return false;
903
+ break;
904
+
905
+ case 'confidence_gt':
906
+ // Check if AI confidence is above threshold
907
+ // Note: We'd need to store confidence in analysis, for now skip
908
+ // TODO: Add confidence tracking to EmailAnalysis
849
909
  break;
910
+
850
911
  case 'suggested_actions':
851
- // Handle array membership check (e.g. if condition expects "reply" to be in actions)
912
+ if (!analysis) return false;
913
+ // Handle array membership check
852
914
  const requiredActions = Array.isArray(value) ? value : [value];
853
915
  const actualActions = analysis.suggested_actions || [];
854
916
  const hasAllActions = requiredActions.every(req =>
@@ -856,6 +918,61 @@ export class EmailProcessorService {
856
918
  );
857
919
  if (!hasAllActions) return false;
858
920
  break;
921
+
922
+ // === Content matching ===
923
+ case 'contains_keywords':
924
+ if (!Array.isArray(value)) return false;
925
+ const keywords = value as string[];
926
+ const searchText = `${email.subject || ''} ${email.body_snippet || ''}`.toLowerCase();
927
+ // Match if ANY keyword is found
928
+ const hasKeyword = keywords.some(kw => searchText.includes(kw.toLowerCase()));
929
+ if (!hasKeyword) return false;
930
+ break;
931
+
932
+ case 'matches_pattern':
933
+ if (typeof value !== 'string') return false;
934
+ try {
935
+ const regex = new RegExp(value, 'i');
936
+ const searchText = `${email.subject || ''} ${email.body_snippet || ''}`;
937
+ if (!regex.test(searchText)) return false;
938
+ } catch (e) {
939
+ // Invalid regex, treat as no match
940
+ return false;
941
+ }
942
+ break;
943
+
944
+ // === Recipient analysis ===
945
+ case 'recipient_type':
946
+ // Check if user is in to/cc/bcc
947
+ // Note: This requires parsing the recipient field
948
+ // For now, simple heuristic: if recipient field exists and matches pattern
949
+ // TODO: Implement proper recipient parsing
950
+ break;
951
+
952
+ case 'recipient_count_gt':
953
+ // Count number of recipients
954
+ // Note: This requires parsing recipient lists
955
+ // TODO: Implement recipient counting
956
+ break;
957
+
958
+ case 'is_first_contact':
959
+ // Check if no prior thread with sender
960
+ // Note: This requires thread tracking
961
+ // TODO: Implement thread tracking
962
+ break;
963
+
964
+ // === Sender analysis ===
965
+ case 'sender_is_vip':
966
+ // Check against VIP list
967
+ // Note: This requires VIP management feature
968
+ // TODO: Implement VIP list
969
+ break;
970
+
971
+ case 'sender_in_contacts':
972
+ // Check if sender is in contacts
973
+ // Note: This requires contact list integration
974
+ // TODO: Implement contact list
975
+ break;
859
976
  default:
860
977
  // Fallback for any other keys that might be in analysis
861
978
  if ((analysis as any)[key] !== value) return false;
@@ -918,7 +1035,7 @@ export class EmailProcessorService {
918
1035
  private async executeAction(
919
1036
  account: EmailAccount,
920
1037
  email: Email,
921
- action: 'delete' | 'archive' | 'draft' | 'read' | 'star',
1038
+ action: string, // Changed to string to support 'label:*' and other future actions
922
1039
  draftContent?: string,
923
1040
  eventLogger?: EventLogger | null,
924
1041
  reason?: string,
@@ -929,6 +1046,19 @@ export class EmailProcessorService {
929
1046
  await eventLogger.info('Acting', `Executing action: ${action}`, { reason, hasAttachments: !!attachments?.length }, email.id);
930
1047
  }
931
1048
 
1049
+ // Parse label actions (e.g., "label:Finance/Receipts")
1050
+ if (action.startsWith('label:')) {
1051
+ const labelName = action.substring(6); // Remove "label:" prefix
1052
+ if (account.provider === 'gmail') {
1053
+ await this.gmailService.applyLabelByName(account, email.external_id, labelName);
1054
+ } else if (account.provider === 'outlook') {
1055
+ await this.microsoftService.moveToFolderByPath(account, email.external_id, labelName);
1056
+ }
1057
+ logger.debug('Label/folder action executed', { emailId: email.id, labelName });
1058
+ return;
1059
+ }
1060
+
1061
+ // Standard actions
932
1062
  if (account.provider === 'gmail') {
933
1063
  if (action === 'delete') {
934
1064
  await this.gmailService.trashMessage(account, email.external_id);
@@ -946,6 +1076,10 @@ export class EmailProcessorService {
946
1076
  await this.gmailService.markAsRead(account, email.external_id);
947
1077
  } else if (action === 'star') {
948
1078
  await this.gmailService.starMessage(account, email.external_id);
1079
+ } else if (action === 'important') {
1080
+ await this.gmailService.addLabel(account, email.external_id, ['IMPORTANT']);
1081
+ } else if (action === 'unstar') {
1082
+ await this.gmailService.removeLabel(account, email.external_id, ['STARRED']);
949
1083
  }
950
1084
  } else if (account.provider === 'outlook') {
951
1085
  if (action === 'delete') {
@@ -959,7 +1093,7 @@ export class EmailProcessorService {
959
1093
  await this.microsoftService.createDraft(account, email.external_id, draftContent);
960
1094
  } else if (action === 'read') {
961
1095
  await this.microsoftService.markAsRead(account, email.external_id);
962
- } else if (action === 'star') {
1096
+ } else if (action === 'star' || action === 'important') {
963
1097
  await this.microsoftService.flagMessage(account, email.external_id);
964
1098
  }
965
1099
  }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Developer Pack - Engineering & Technical Workflows
3
+ *
4
+ * Designed for software engineers, DevOps, and technical teams who need to:
5
+ * - Surface critical system alerts
6
+ * - Filter project management noise
7
+ * - Organize meeting recordings and docs
8
+ * - Focus on code reviews and incidents
9
+ */
10
+
11
+ import { RulePack } from './types.js';
12
+
13
+ export const DEVELOPER_PACK: RulePack = {
14
+ id: 'developer',
15
+ name: 'Developer Pack',
16
+ description: 'Surface critical alerts and filter tool noise',
17
+ icon: '💻',
18
+ enabled_by_default: false,
19
+ target_roles: ['developer'],
20
+ rules: [
21
+ {
22
+ id: 'dev-system-alerts',
23
+ name: '🚨 System Alerts Prioritizer',
24
+ intent: 'Prioritize critical system alerts and incidents',
25
+ description: 'Stars critical alerts from AWS, Datadog, Sentry, PagerDuty while archiving info-level notifications',
26
+ condition: {
27
+ or: [
28
+ {
29
+ sender_domain: 'aws.amazon.com',
30
+ contains_keywords: ['critical', 'down', 'outage', 'incident']
31
+ },
32
+ {
33
+ sender_domain: 'datadoghq.com',
34
+ contains_keywords: ['alert', 'critical', 'error rate']
35
+ },
36
+ {
37
+ sender_domain: 'sentry.io',
38
+ contains_keywords: ['new issue', 'regression', 'error']
39
+ },
40
+ {
41
+ sender_domain: 'pagerduty.com',
42
+ contains_keywords: ['triggered', 'incident']
43
+ }
44
+ ]
45
+ },
46
+ actions: ['star', 'important', 'pin'],
47
+ priority: 100,
48
+ is_enabled_by_default: true
49
+ },
50
+ {
51
+ id: 'dev-system-info',
52
+ name: '⚠️ System Info Organizer',
53
+ intent: 'Archive non-critical system notifications',
54
+ description: 'Archives info and warning level alerts from monitoring tools',
55
+ condition: {
56
+ or: [
57
+ {
58
+ sender_domain: 'aws.amazon.com',
59
+ not: {
60
+ contains_keywords: ['critical', 'down', 'outage']
61
+ }
62
+ },
63
+ {
64
+ sender_domain: 'datadoghq.com',
65
+ contains_keywords: ['info', 'warning', 'recovered']
66
+ },
67
+ {
68
+ sender_domain: 'circleci.com'
69
+ },
70
+ {
71
+ sender_domain: 'github.com',
72
+ contains_keywords: ['build', 'workflow']
73
+ }
74
+ ]
75
+ },
76
+ actions: ['label:Logs', 'archive', 'read'],
77
+ priority: 20,
78
+ is_enabled_by_default: true
79
+ },
80
+ {
81
+ id: 'dev-project-management',
82
+ name: '🔨 Project Management Organizer',
83
+ intent: 'Organize Jira, GitHub, and project management notifications',
84
+ description: 'Archives project management notifications unless you\'re directly assigned',
85
+ condition: {
86
+ or: [
87
+ {
88
+ sender_domain: 'atlassian.net',
89
+ not: {
90
+ contains_keywords: ['assigned to you', 'mentioned you']
91
+ }
92
+ },
93
+ {
94
+ sender_domain: 'github.com',
95
+ not: {
96
+ contains_keywords: ['assigned', 'review requested', '@']
97
+ }
98
+ },
99
+ {
100
+ sender_domain: 'asana.com',
101
+ not: {
102
+ contains_keywords: ['assigned to you']
103
+ }
104
+ },
105
+ {
106
+ sender_domain: 'trello.com'
107
+ },
108
+ {
109
+ sender_domain: 'monday.com'
110
+ }
111
+ ]
112
+ },
113
+ actions: ['label:Tools', 'archive'],
114
+ priority: 15,
115
+ is_enabled_by_default: true
116
+ },
117
+ {
118
+ id: 'dev-code-reviews',
119
+ name: '👀 Code Review Highlighter',
120
+ intent: 'Highlight pull request reviews and assignments',
121
+ description: 'Stars GitHub/GitLab notifications where your review is requested',
122
+ condition: {
123
+ or: [
124
+ {
125
+ sender_domain: 'github.com',
126
+ contains_keywords: ['review requested', 'requested your review']
127
+ },
128
+ {
129
+ sender_domain: 'gitlab.com',
130
+ contains_keywords: ['review', 'merge request']
131
+ }
132
+ ]
133
+ },
134
+ actions: ['star'],
135
+ priority: 80,
136
+ is_enabled_by_default: true
137
+ },
138
+ {
139
+ id: 'dev-meeting-recordings',
140
+ name: '📹 Meeting Recording Archiver',
141
+ intent: 'Archive meeting recordings and transcripts',
142
+ description: 'Automatically archives Zoom, Teams, and AI note-taker recordings',
143
+ condition: {
144
+ or: [
145
+ {
146
+ sender_domain: 'zoom.us',
147
+ contains_keywords: ['recording', 'cloud recording']
148
+ },
149
+ {
150
+ sender_domain: 'microsoft.com',
151
+ contains_keywords: ['recording', 'teams recording']
152
+ },
153
+ {
154
+ sender_domain: 'fireflies.ai'
155
+ },
156
+ {
157
+ sender_domain: 'otter.ai'
158
+ }
159
+ ]
160
+ },
161
+ actions: ['label:Recordings', 'archive'],
162
+ priority: 10,
163
+ is_enabled_by_default: true
164
+ },
165
+ {
166
+ id: 'dev-drive-shares',
167
+ name: '📂 Drive Share Organizer',
168
+ intent: 'Archive Google Drive/Docs share notifications',
169
+ description: 'Archives notifications about shared documents (usually redundant)',
170
+ condition: {
171
+ sender_domain: 'google.com',
172
+ contains_keywords: ['shared', 'document', 'commented on', 'mentioned you in']
173
+ },
174
+ actions: ['label:Drive', 'archive'],
175
+ priority: 5,
176
+ is_enabled_by_default: true
177
+ }
178
+ ]
179
+ };
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Executive Pack - Leadership & Strategic Communications
3
+ *
4
+ * Designed for executives, C-suite, and leadership who need to:
5
+ * - Focus on VIP communications
6
+ * - Prioritize strategic decisions
7
+ * - Filter operational noise
8
+ * - Manage travel and legal documents
9
+ */
10
+
11
+ import { RulePack } from './types.js';
12
+
13
+ export const EXECUTIVE_PACK: RulePack = {
14
+ id: 'executive',
15
+ name: 'Executive Pack',
16
+ description: 'Focus on high-priority communications and strategic matters',
17
+ icon: '👔',
18
+ enabled_by_default: false,
19
+ target_roles: ['executive'],
20
+ rules: [
21
+ {
22
+ id: 'exec-vip-clients',
23
+ name: '⭐ VIP Client Prioritizer',
24
+ intent: 'Star and prioritize emails from key clients and partners',
25
+ description: 'Automatically highlights emails from VIP contacts and important clients',
26
+ condition: {
27
+ or: [
28
+ {
29
+ sender_is_vip: true // From VIP list (requires VIP management feature)
30
+ },
31
+ {
32
+ category: 'client',
33
+ ai_priority: 'High'
34
+ }
35
+ ]
36
+ },
37
+ actions: ['star', 'important'],
38
+ priority: 100,
39
+ is_enabled_by_default: true
40
+ },
41
+ {
42
+ id: 'exec-direct-questions',
43
+ name: '⚡ Direct Questions Highlighter',
44
+ intent: 'Highlight emails that require your direct input',
45
+ description: 'Identifies emails where you\'re the only recipient and a response is clearly needed',
46
+ condition: {
47
+ recipient_type: 'to',
48
+ recipient_count_gt: 0, // Only sent to you
49
+ contains_keywords: ['?', 'your input', 'your thoughts', 'need your', 'awaiting your'],
50
+ not: {
51
+ category: 'newsletter' // Exclude automated newsletters
52
+ }
53
+ },
54
+ actions: ['important'],
55
+ priority: 90,
56
+ is_enabled_by_default: true
57
+ },
58
+ {
59
+ id: 'exec-travel-itineraries',
60
+ name: '✈️ Travel Organizer',
61
+ intent: 'Organize flight confirmations, hotel bookings, and itineraries',
62
+ description: 'Labels travel-related emails for easy access before trips',
63
+ condition: {
64
+ or: [
65
+ {
66
+ sender_domain: 'delta.com'
67
+ },
68
+ {
69
+ sender_domain: 'united.com'
70
+ },
71
+ {
72
+ sender_domain: 'aa.com'
73
+ },
74
+ {
75
+ sender_domain: 'hilton.com'
76
+ },
77
+ {
78
+ sender_domain: 'marriott.com'
79
+ },
80
+ {
81
+ contains_keywords: ['itinerary', 'flight confirmation', 'booking confirmation', 'reservation confirmed']
82
+ }
83
+ ]
84
+ },
85
+ actions: ['label:Travel', 'star'],
86
+ priority: 70,
87
+ is_enabled_by_default: true
88
+ },
89
+ {
90
+ id: 'exec-legal-contracts',
91
+ name: '⚖️ Legal & Contracts Highlighter',
92
+ intent: 'Highlight important legal documents and contracts',
93
+ description: 'Stars emails containing contracts, NDAs, and legal documents requiring signature',
94
+ condition: {
95
+ or: [
96
+ {
97
+ sender_domain: 'docusign.com'
98
+ },
99
+ {
100
+ sender_domain: 'hellosign.com'
101
+ },
102
+ {
103
+ sender_domain: 'pandadoc.com'
104
+ },
105
+ {
106
+ contains_keywords: ['nda', 'agreement', 'contract', 'sign', 'signature required']
107
+ }
108
+ ]
109
+ },
110
+ actions: ['label:Legal', 'star'],
111
+ priority: 95,
112
+ is_enabled_by_default: true
113
+ },
114
+ {
115
+ id: 'exec-social-notifications',
116
+ name: '🔔 Social Media Archiver',
117
+ intent: 'Archive social media notifications',
118
+ description: 'Automatically archives LinkedIn, Twitter, and other social notifications',
119
+ condition: {
120
+ category: 'social',
121
+ confidence_gt: 0.8
122
+ },
123
+ actions: ['archive', 'read'],
124
+ priority: 5,
125
+ is_enabled_by_default: true
126
+ },
127
+ {
128
+ id: 'exec-calendar-responses',
129
+ name: '📅 Calendar Response Cleaner',
130
+ intent: 'Clean up calendar accept/decline notifications',
131
+ description: 'Deletes automatic calendar responses that don\'t contain custom messages',
132
+ condition: {
133
+ contains_keywords: ['accepted:', 'declined:', 'tentative:'],
134
+ not: {
135
+ recipient_type: 'to' // Don't delete if you're the only recipient
136
+ }
137
+ },
138
+ actions: ['delete'],
139
+ priority: 10,
140
+ is_enabled_by_default: false // Disabled by default - deletion is aggressive
141
+ }
142
+ ]
143
+ };
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Rule Packs - Zero-Configuration Email Automation
3
+ *
4
+ * This module provides pre-configured rule packs for different user roles.
5
+ * Rule packs enable zero-configuration UX by providing smart defaults that
6
+ * work out of the box.
7
+ */
8
+
9
+ export * from './types.js';
10
+ export { UNIVERSAL_PACK } from './universal.js';
11
+ export { EXECUTIVE_PACK } from './executive.js';
12
+ export { DEVELOPER_PACK } from './developer.js';
13
+ export { SALES_PACK } from './sales.js';
14
+ export { OPERATIONS_PACK } from './operations.js';
15
+
16
+ import { UNIVERSAL_PACK } from './universal.js';
17
+ import { EXECUTIVE_PACK } from './executive.js';
18
+ import { DEVELOPER_PACK } from './developer.js';
19
+ import { SALES_PACK } from './sales.js';
20
+ import { OPERATIONS_PACK } from './operations.js';
21
+ import { RulePack, UserRole } from './types.js';
22
+
23
+ /**
24
+ * All available rule packs
25
+ */
26
+ export const ALL_PACKS: Record<string, RulePack> = {
27
+ universal: UNIVERSAL_PACK,
28
+ executive: EXECUTIVE_PACK,
29
+ developer: DEVELOPER_PACK,
30
+ sales: SALES_PACK,
31
+ operations: OPERATIONS_PACK,
32
+ };
33
+
34
+ /**
35
+ * Get packs that should be installed for a specific user role
36
+ */
37
+ export function getPacksForRole(role?: UserRole | string): RulePack[] {
38
+ const packs: RulePack[] = [
39
+ UNIVERSAL_PACK // Always include universal pack
40
+ ];
41
+
42
+ // Add role-specific pack
43
+ if (role === 'executive') packs.push(EXECUTIVE_PACK);
44
+ if (role === 'developer') packs.push(DEVELOPER_PACK);
45
+ if (role === 'sales') packs.push(SALES_PACK);
46
+ if (role === 'operations') packs.push(OPERATIONS_PACK);
47
+
48
+ return packs;
49
+ }
50
+
51
+ /**
52
+ * Get a specific pack by ID
53
+ */
54
+ export function getPackById(packId: string): RulePack | undefined {
55
+ return ALL_PACKS[packId];
56
+ }
57
+
58
+ /**
59
+ * Get all available packs
60
+ */
61
+ export function getAllPacks(): RulePack[] {
62
+ return Object.values(ALL_PACKS);
63
+ }