@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.
- package/api/src/routes/index.ts +2 -0
- package/api/src/routes/rulePacks.ts +204 -0
- package/api/src/services/RulePackService.ts +354 -0
- package/api/src/services/SDKService.ts +18 -7
- package/api/src/services/gmail.ts +68 -0
- package/api/src/services/intelligence.ts +160 -22
- package/api/src/services/microsoft.ts +99 -0
- package/api/src/services/processor.ts +146 -12
- package/api/src/services/rulePacks/developer.ts +179 -0
- package/api/src/services/rulePacks/executive.ts +143 -0
- package/api/src/services/rulePacks/index.ts +63 -0
- package/api/src/services/rulePacks/operations.ts +183 -0
- package/api/src/services/rulePacks/sales.ts +160 -0
- package/api/src/services/rulePacks/types.ts +116 -0
- package/api/src/services/rulePacks/universal.ts +83 -0
- package/api/src/services/supabase.ts +40 -0
- package/dist/api/src/routes/index.js +2 -0
- package/dist/api/src/routes/rulePacks.js +179 -0
- package/dist/api/src/services/RulePackService.js +296 -0
- package/dist/api/src/services/SDKService.js +18 -7
- package/dist/api/src/services/gmail.js +56 -0
- package/dist/api/src/services/intelligence.js +153 -21
- package/dist/api/src/services/microsoft.js +79 -0
- package/dist/api/src/services/processor.js +133 -12
- package/dist/api/src/services/rulePacks/developer.js +176 -0
- package/dist/api/src/services/rulePacks/executive.js +140 -0
- package/dist/api/src/services/rulePacks/index.js +58 -0
- package/dist/api/src/services/rulePacks/operations.js +180 -0
- package/dist/api/src/services/rulePacks/sales.js +157 -0
- package/dist/api/src/services/rulePacks/types.js +7 -0
- package/dist/api/src/services/rulePacks/universal.js +80 -0
- package/dist/assets/index-B5rXh3y8.css +1 -0
- package/dist/assets/index-B62ViZum.js +105 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/supabase/migrations/20260131000000_add_rule_packs.sql +176 -0
- package/supabase/migrations/20260131000001_set_zero_config_defaults.sql +51 -0
- package/supabase/migrations/20260131095000_backfill_user_settings.sql +36 -0
- package/supabase/migrations/20260131100000_rule_templates_table.sql +154 -0
- package/supabase/migrations/20260131110000_auto_init_user_data.sql +90 -0
- package/supabase/migrations/20260131120000_backfill_universal_pack.sql +84 -0
- package/supabase/migrations/20260131130000_simplify_rules_with_categories.sql +87 -0
- package/supabase/migrations/20260131140000_fix_action_constraint.sql +11 -0
- package/supabase/migrations/20260131150000_fix_trigger_error_handling.sql +71 -0
- package/supabase/migrations/20260131160000_enable_intelligent_rename_by_default.sql +14 -0
- package/dist/assets/index-BuWrl4UD.js +0 -105
- 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
|
-
|
|
809
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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:
|
|
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
|
+
}
|