@troykelly/openclaw-projects 0.0.11 → 0.0.13

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 (97) hide show
  1. package/dist/config.d.ts +16 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +11 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/hooks.d.ts.map +1 -1
  6. package/dist/hooks.js +22 -3
  7. package/dist/hooks.js.map +1 -1
  8. package/dist/register-openclaw.d.ts +3 -1
  9. package/dist/register-openclaw.d.ts.map +1 -1
  10. package/dist/register-openclaw.js +640 -55
  11. package/dist/register-openclaw.js.map +1 -1
  12. package/dist/tools/context-search.d.ts +79 -0
  13. package/dist/tools/context-search.d.ts.map +1 -0
  14. package/dist/tools/context-search.js +267 -0
  15. package/dist/tools/context-search.js.map +1 -0
  16. package/dist/tools/email-send.d.ts.map +1 -1
  17. package/dist/tools/email-send.js +1 -14
  18. package/dist/tools/email-send.js.map +1 -1
  19. package/dist/tools/entity-links.d.ts +117 -0
  20. package/dist/tools/entity-links.d.ts.map +1 -0
  21. package/dist/tools/entity-links.js +446 -0
  22. package/dist/tools/entity-links.js.map +1 -0
  23. package/dist/tools/index.d.ts +4 -0
  24. package/dist/tools/index.d.ts.map +1 -1
  25. package/dist/tools/index.js +8 -0
  26. package/dist/tools/index.js.map +1 -1
  27. package/dist/tools/memory-recall.d.ts +28 -0
  28. package/dist/tools/memory-recall.d.ts.map +1 -1
  29. package/dist/tools/memory-recall.js +42 -3
  30. package/dist/tools/memory-recall.js.map +1 -1
  31. package/dist/tools/memory-store.d.ts +57 -0
  32. package/dist/tools/memory-store.d.ts.map +1 -1
  33. package/dist/tools/memory-store.js +29 -2
  34. package/dist/tools/memory-store.js.map +1 -1
  35. package/dist/tools/message-search.d.ts +2 -2
  36. package/dist/tools/message-search.d.ts.map +1 -1
  37. package/dist/tools/message-search.js +36 -3
  38. package/dist/tools/message-search.js.map +1 -1
  39. package/dist/tools/notes.d.ts +2 -2
  40. package/dist/tools/project-search.d.ts +92 -0
  41. package/dist/tools/project-search.d.ts.map +1 -0
  42. package/dist/tools/project-search.js +160 -0
  43. package/dist/tools/project-search.js.map +1 -0
  44. package/dist/tools/skill-store.d.ts +6 -6
  45. package/dist/tools/threads.d.ts +3 -3
  46. package/dist/tools/threads.d.ts.map +1 -1
  47. package/dist/tools/threads.js +45 -7
  48. package/dist/tools/threads.js.map +1 -1
  49. package/dist/tools/todo-search.d.ts +95 -0
  50. package/dist/tools/todo-search.d.ts.map +1 -0
  51. package/dist/tools/todo-search.js +164 -0
  52. package/dist/tools/todo-search.js.map +1 -0
  53. package/dist/types/openclaw-api.d.ts +15 -0
  54. package/dist/types/openclaw-api.d.ts.map +1 -1
  55. package/dist/utils/auto-linker.d.ts +66 -0
  56. package/dist/utils/auto-linker.d.ts.map +1 -0
  57. package/dist/utils/auto-linker.js +354 -0
  58. package/dist/utils/auto-linker.js.map +1 -0
  59. package/dist/utils/geo.d.ts +24 -0
  60. package/dist/utils/geo.d.ts.map +1 -0
  61. package/dist/utils/geo.js +38 -0
  62. package/dist/utils/geo.js.map +1 -0
  63. package/dist/utils/inbound-gate.d.ts +85 -0
  64. package/dist/utils/inbound-gate.d.ts.map +1 -0
  65. package/dist/utils/inbound-gate.js +133 -0
  66. package/dist/utils/inbound-gate.js.map +1 -0
  67. package/dist/utils/injection-log-rate-limiter.d.ts +62 -0
  68. package/dist/utils/injection-log-rate-limiter.d.ts.map +1 -0
  69. package/dist/utils/injection-log-rate-limiter.js +106 -0
  70. package/dist/utils/injection-log-rate-limiter.js.map +1 -0
  71. package/dist/utils/injection-protection.d.ts +133 -0
  72. package/dist/utils/injection-protection.d.ts.map +1 -0
  73. package/dist/utils/injection-protection.js +252 -0
  74. package/dist/utils/injection-protection.js.map +1 -0
  75. package/dist/utils/nominatim.d.ts +18 -0
  76. package/dist/utils/nominatim.d.ts.map +1 -0
  77. package/dist/utils/nominatim.js +56 -0
  78. package/dist/utils/nominatim.js.map +1 -0
  79. package/dist/utils/prompt-guard-client.d.ts +59 -0
  80. package/dist/utils/prompt-guard-client.d.ts.map +1 -0
  81. package/dist/utils/prompt-guard-client.js +99 -0
  82. package/dist/utils/prompt-guard-client.js.map +1 -0
  83. package/dist/utils/rate-limiter.d.ts +81 -0
  84. package/dist/utils/rate-limiter.d.ts.map +1 -0
  85. package/dist/utils/rate-limiter.js +188 -0
  86. package/dist/utils/rate-limiter.js.map +1 -0
  87. package/dist/utils/spam-filter.d.ts +79 -0
  88. package/dist/utils/spam-filter.d.ts.map +1 -0
  89. package/dist/utils/spam-filter.js +237 -0
  90. package/dist/utils/spam-filter.js.map +1 -0
  91. package/dist/utils/token-budget.d.ts +68 -0
  92. package/dist/utils/token-budget.d.ts.map +1 -0
  93. package/dist/utils/token-budget.js +142 -0
  94. package/dist/utils/token-budget.js.map +1 -0
  95. package/openclaw.plugin.json +25 -3
  96. package/package.json +12 -11
  97. package/LICENSE +0 -21
@@ -0,0 +1,164 @@
1
+ /**
2
+ * todo_search tool implementation.
3
+ * Provides semantic search through work items (todos and projects).
4
+ * Uses the unified search API with work_item type filter.
5
+ * Geo-contextual ranking added in Issue #1218.
6
+ *
7
+ * Part of Issue #1216.
8
+ */
9
+ import { z } from 'zod';
10
+ import { sanitizeErrorMessage } from '../utils/sanitize.js';
11
+ import { reverseGeocode } from '../utils/nominatim.js';
12
+ /** Parameters for todo_search tool */
13
+ export const TodoSearchParamsSchema = z.object({
14
+ query: z.string().min(1, 'Query cannot be empty').max(1000, 'Query must be 1000 characters or less'),
15
+ limit: z.number().int().min(1).max(50).optional(),
16
+ kind: z.enum(['task', 'project', 'initiative', 'epic', 'issue']).optional(),
17
+ status: z.string().max(50).optional(),
18
+ location: z.object({
19
+ lat: z.number().min(-90).max(90),
20
+ lng: z.number().min(-180).max(180),
21
+ }).optional(),
22
+ });
23
+ /**
24
+ * Sanitize query input to prevent injection and remove control characters.
25
+ */
26
+ function sanitizeQuery(query) {
27
+ return query.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '').trim();
28
+ }
29
+ /**
30
+ * Creates the todo_search tool.
31
+ */
32
+ export function createTodoSearchTool(options) {
33
+ const { client, logger, config, userId } = options;
34
+ return {
35
+ name: 'todo_search',
36
+ description: 'Search todos and work items by natural language query. Uses semantic and text search to find relevant items. ' +
37
+ 'Optionally filter by kind (task, project, initiative, epic, issue) or status. ' +
38
+ 'Provide location (lat/lng) to boost results relevant to the current location.',
39
+ parameters: TodoSearchParamsSchema,
40
+ async execute(params) {
41
+ const parseResult = TodoSearchParamsSchema.safeParse(params);
42
+ if (!parseResult.success) {
43
+ const errorMessage = parseResult.error.errors.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
44
+ return { success: false, error: errorMessage };
45
+ }
46
+ const { query, limit = 10, kind, status, location } = parseResult.data;
47
+ const sanitizedQuery = sanitizeQuery(query);
48
+ if (sanitizedQuery.length === 0) {
49
+ return { success: false, error: 'Query cannot be empty after sanitization' };
50
+ }
51
+ logger.info('todo_search invoked', {
52
+ userId,
53
+ queryLength: sanitizedQuery.length,
54
+ limit,
55
+ kind: kind ?? 'all',
56
+ status: status ?? 'all',
57
+ hasLocation: !!location,
58
+ });
59
+ try {
60
+ // Augment query with location context for geo-contextual ranking (Issue #1218)
61
+ let searchQuery = sanitizedQuery;
62
+ if (location && config.nominatimUrl) {
63
+ try {
64
+ const geo = await reverseGeocode(location.lat, location.lng, config.nominatimUrl);
65
+ if (geo?.placeLabel) {
66
+ const augmented = `${sanitizedQuery} near ${geo.placeLabel}`;
67
+ if (augmented.length <= 1000) {
68
+ searchQuery = augmented;
69
+ }
70
+ }
71
+ }
72
+ catch {
73
+ logger.warn('Reverse geocode failed, proceeding without location augmentation', { userId });
74
+ }
75
+ }
76
+ // Over-fetch by 3x to compensate for client-side kind/status filtering (Issue #1216 review fix)
77
+ const fetchLimit = (kind || status || location) ? Math.min(limit * 3, 50) : limit;
78
+ const queryParams = new URLSearchParams({
79
+ q: searchQuery,
80
+ types: 'work_item',
81
+ limit: String(fetchLimit),
82
+ semantic: 'true',
83
+ user_email: userId, // Issue #1216: scope results to current user
84
+ });
85
+ const response = await client.get(`/api/search?${queryParams.toString()}`, { userId });
86
+ if (!response.success) {
87
+ logger.error('todo_search API error', {
88
+ userId,
89
+ status: response.error.status,
90
+ code: response.error.code,
91
+ });
92
+ return {
93
+ success: false,
94
+ error: response.error.message || 'Failed to search work items',
95
+ };
96
+ }
97
+ let results = response.data.results ?? [];
98
+ // Client-side filtering by kind and status if specified, then truncate to requested limit
99
+ if (kind) {
100
+ results = results.filter((r) => r.metadata?.kind === kind);
101
+ }
102
+ if (status) {
103
+ results = results.filter((r) => r.metadata?.status === status);
104
+ }
105
+ results = results.slice(0, limit);
106
+ const items = results.map((r) => ({
107
+ id: r.id,
108
+ title: r.title,
109
+ snippet: r.snippet,
110
+ score: r.score,
111
+ kind: r.metadata?.kind,
112
+ status: r.metadata?.status,
113
+ }));
114
+ if (items.length === 0) {
115
+ return {
116
+ success: true,
117
+ data: {
118
+ content: 'No matching work items found.',
119
+ details: {
120
+ count: 0,
121
+ results: [],
122
+ searchType: response.data.search_type,
123
+ userId,
124
+ },
125
+ },
126
+ };
127
+ }
128
+ const content = items
129
+ .map((item) => {
130
+ const kindStr = item.kind ? `[${item.kind}]` : '';
131
+ const statusStr = item.status ? ` (${item.status})` : '';
132
+ const snippetStr = item.snippet ? ` - ${item.snippet}` : '';
133
+ return `- ${kindStr} **${item.title}**${statusStr}${snippetStr}`;
134
+ })
135
+ .join('\n');
136
+ logger.debug('todo_search completed', {
137
+ userId,
138
+ resultCount: items.length,
139
+ searchType: response.data.search_type,
140
+ });
141
+ return {
142
+ success: true,
143
+ data: {
144
+ content,
145
+ details: {
146
+ count: items.length,
147
+ results: items,
148
+ searchType: response.data.search_type,
149
+ userId,
150
+ },
151
+ },
152
+ };
153
+ }
154
+ catch (error) {
155
+ logger.error('todo_search failed', {
156
+ userId,
157
+ error: error instanceof Error ? error.message : String(error),
158
+ });
159
+ return { success: false, error: sanitizeErrorMessage(error) };
160
+ }
161
+ },
162
+ };
163
+ }
164
+ //# sourceMappingURL=todo-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"todo-search.js","sourceRoot":"","sources":["../../src/tools/todo-search.ts"],"names":[],"mappings":"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"}
@@ -125,6 +125,21 @@ export interface PluginHookBeforeAgentStartResult {
125
125
  /** Prepend to conversation context */
126
126
  prependContext?: string;
127
127
  }
128
+ /** Event payload for message_received hook (Issue #1223) */
129
+ export interface PluginHookMessageReceivedEvent {
130
+ /** Thread ID for the message */
131
+ threadId?: string;
132
+ /** Sender identifier (email or phone) */
133
+ sender?: string;
134
+ /** Sender email address */
135
+ senderEmail?: string;
136
+ /** Sender phone number */
137
+ senderPhone?: string;
138
+ /** Message body content */
139
+ content?: string;
140
+ /** Channel the message was received on (sms, email, etc.) */
141
+ channel?: string;
142
+ }
128
143
  /** Event payload for agent_end hook */
129
144
  export interface PluginHookAgentEndEvent {
130
145
  /** Full conversation messages */
@@ -1 +1 @@
1
- {"version":3,"file":"openclaw-api.d.ts","sourceRoot":"","sources":["../../src/types/openclaw-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openclaw-api.d.ts","sourceRoot":"","sources":["../../src/types/openclaw-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Auto-linker utility for inbound messages (Issue #1223).
3
+ *
4
+ * Automatically creates entity links when inbound SMS/email arrives:
5
+ * 1. Sender -> Contact matching: matches sender email/phone to existing contacts
6
+ * 2. Content -> Project/Todo matching: semantic search for related work items
7
+ * (only runs when sender is a known contact — safety decision to prevent
8
+ * untrusted senders from creating spurious links)
9
+ *
10
+ * Links are created via the skill_store API using the same pattern as
11
+ * entity-links.ts tools. All operations are failure-isolated so that
12
+ * auto-linking never crashes inbound message processing.
13
+ */
14
+ import type { ApiClient } from '../api-client.js';
15
+ import type { Logger } from '../logger.js';
16
+ /** Default similarity threshold for content matching */
17
+ export declare const DEFAULT_SIMILARITY_THRESHOLD = 0.75;
18
+ /** Inbound message data for auto-linking */
19
+ export interface AutoLinkMessage {
20
+ /** Thread ID (UUID) for the inbound message thread */
21
+ threadId: string;
22
+ /** Sender email address, if known */
23
+ senderEmail?: string;
24
+ /** Sender phone number, if known */
25
+ senderPhone?: string;
26
+ /** Message body content */
27
+ content: string;
28
+ }
29
+ /** Options for the auto-link function */
30
+ export interface AutoLinkOptions {
31
+ /** API client for backend calls */
32
+ client: ApiClient;
33
+ /** Logger instance */
34
+ logger: Logger;
35
+ /** User ID for scoping API calls */
36
+ userId: string;
37
+ /** Inbound message data */
38
+ message: AutoLinkMessage;
39
+ /** Similarity threshold for content matching (default: 0.75) */
40
+ similarityThreshold?: number;
41
+ }
42
+ /** Result of auto-linking */
43
+ export interface AutoLinkResult {
44
+ /** Number of entity links created */
45
+ linksCreated: number;
46
+ /** IDs of matched entities by type */
47
+ matches: {
48
+ contacts: string[];
49
+ projects: string[];
50
+ todos: string[];
51
+ };
52
+ }
53
+ /**
54
+ * Auto-link an inbound message to related entities.
55
+ *
56
+ * Performs sender matching first. Content matching only runs when at least
57
+ * one contact matched — this prevents untrusted/unknown senders from
58
+ * creating spurious links to projects and todos.
59
+ *
60
+ * Never throws - all errors are caught and logged.
61
+ *
62
+ * @param options - Auto-link configuration and message data
63
+ * @returns Summary of links created and entities matched
64
+ */
65
+ export declare function autoLinkInboundMessage(options: AutoLinkOptions): Promise<AutoLinkResult>;
66
+ //# sourceMappingURL=auto-linker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-linker.d.ts","sourceRoot":"","sources":["../../src/utils/auto-linker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAM3C,wDAAwD;AACxD,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAmBjD,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,yCAAyC;AACzC,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,MAAM,EAAE,SAAS,CAAC;IAClB,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,OAAO,EAAE,eAAe,CAAC;IACzB,gEAAgE;IAChE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,6BAA6B;AAC7B,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,EAAE,MAAM,EAAE,CAAC;KACjB,CAAC;CACH;AA+XD;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA6F9F"}
@@ -0,0 +1,354 @@
1
+ /**
2
+ * Auto-linker utility for inbound messages (Issue #1223).
3
+ *
4
+ * Automatically creates entity links when inbound SMS/email arrives:
5
+ * 1. Sender -> Contact matching: matches sender email/phone to existing contacts
6
+ * 2. Content -> Project/Todo matching: semantic search for related work items
7
+ * (only runs when sender is a known contact — safety decision to prevent
8
+ * untrusted senders from creating spurious links)
9
+ *
10
+ * Links are created via the skill_store API using the same pattern as
11
+ * entity-links.ts tools. All operations are failure-isolated so that
12
+ * auto-linking never crashes inbound message processing.
13
+ */
14
+ import { sanitizeExternalMessage } from './injection-protection.js';
15
+ import { sanitizeErrorMessage } from './sanitize.js';
16
+ // ==================== Constants ====================
17
+ /** Default similarity threshold for content matching */
18
+ export const DEFAULT_SIMILARITY_THRESHOLD = 0.75;
19
+ /** Skill ID for entity link storage (matches entity-links.ts) */
20
+ const SKILL_ID = 'entity-links';
21
+ /** Collection name for entity links (matches entity-links.ts) */
22
+ const COLLECTION = 'entity_links';
23
+ /** Maximum content length to send to search API */
24
+ const MAX_SEARCH_QUERY_LENGTH = 500;
25
+ /** Work item kinds that map to "project" in results */
26
+ const PROJECT_KINDS = new Set(['project']);
27
+ /** Work item kinds that map to "todo" in results */
28
+ const TODO_KINDS = new Set(['task', 'issue', 'epic', 'initiative']);
29
+ // ==================== Helpers ====================
30
+ /**
31
+ * Build a URI-style thread reference for use as target_ref.
32
+ * Uses 'url' entity type since the entity-links schema has a fixed enum
33
+ * and does not yet include a 'message_thread' type.
34
+ * TODO(#1223): Add a proper 'message_thread' entity type to entity-links schema.
35
+ */
36
+ function buildThreadRef(threadId) {
37
+ return `thread:${threadId}`;
38
+ }
39
+ /**
40
+ * Build a composite key for a link (matches entity-links.ts pattern).
41
+ */
42
+ function buildLinkKey(sourceType, sourceRef, targetType, targetRef) {
43
+ return `${sourceType}:${sourceRef}:${targetType}:${targetRef}`;
44
+ }
45
+ /**
46
+ * Build a tag for source-entity lookup (matches entity-links.ts pattern).
47
+ */
48
+ function buildSourceTag(entityType, entityRef) {
49
+ return `src:${entityType}:${entityRef}`;
50
+ }
51
+ /**
52
+ * Create a bidirectional entity link via skill_store.
53
+ * Returns true if both directions were created successfully.
54
+ */
55
+ async function createEntityLink(client, userId, logger, sourceType, sourceId, targetType, targetRef, label) {
56
+ const now = new Date().toISOString();
57
+ const forwardKey = buildLinkKey(sourceType, sourceId, targetType, targetRef);
58
+ const reverseKey = buildLinkKey(targetType, targetRef, sourceType, sourceId);
59
+ const forwardData = {
60
+ source_type: sourceType,
61
+ source_id: sourceId,
62
+ target_type: targetType,
63
+ target_ref: targetRef,
64
+ label: label ?? null,
65
+ created_at: now,
66
+ auto_linked: true,
67
+ };
68
+ const reverseData = {
69
+ source_type: targetType,
70
+ source_id: targetRef,
71
+ target_type: sourceType,
72
+ target_ref: sourceId,
73
+ label: label ?? null,
74
+ created_at: now,
75
+ auto_linked: true,
76
+ };
77
+ // Create forward link
78
+ const forwardResponse = await client.post('/api/skill-store/items', {
79
+ skill_id: SKILL_ID,
80
+ collection: COLLECTION,
81
+ key: forwardKey,
82
+ data: forwardData,
83
+ tags: [buildSourceTag(sourceType, sourceId)],
84
+ }, { userId });
85
+ if (!forwardResponse.success) {
86
+ logger.error('auto-linker: forward link creation failed', {
87
+ userId,
88
+ sourceType,
89
+ targetType,
90
+ status: forwardResponse.error.status,
91
+ });
92
+ return false;
93
+ }
94
+ // Create reverse link
95
+ const reverseResponse = await client.post('/api/skill-store/items', {
96
+ skill_id: SKILL_ID,
97
+ collection: COLLECTION,
98
+ key: reverseKey,
99
+ data: reverseData,
100
+ tags: [buildSourceTag(targetType, targetRef)],
101
+ }, { userId });
102
+ if (!reverseResponse.success) {
103
+ logger.error('auto-linker: reverse link creation failed', {
104
+ userId,
105
+ sourceType,
106
+ targetType,
107
+ status: reverseResponse.error.status,
108
+ });
109
+ // Best-effort rollback of orphaned forward link
110
+ const rollbackResponse = await client.delete(`/api/skill-store/items/${forwardResponse.data.id}`, { userId });
111
+ if (!rollbackResponse.success) {
112
+ logger.error('auto-linker: rollback of orphaned forward link failed — partial state', {
113
+ userId,
114
+ forwardId: forwardResponse.data.id,
115
+ rollbackStatus: rollbackResponse.error.status,
116
+ });
117
+ }
118
+ return false;
119
+ }
120
+ return true;
121
+ }
122
+ // ==================== Sender matching ====================
123
+ /**
124
+ * Search for contacts matching the sender's email and/or phone.
125
+ * Searches both identifiers separately when both are present,
126
+ * then deduplicates matched contact IDs.
127
+ * Returns matched contact IDs and creates links for each match.
128
+ */
129
+ async function matchSenderToContacts(client, logger, userId, threadId, senderEmail, senderPhone) {
130
+ if (!senderEmail && !senderPhone) {
131
+ return [];
132
+ }
133
+ // Search both email and phone separately to catch all matches
134
+ const searchQueries = [];
135
+ if (senderEmail)
136
+ searchQueries.push(senderEmail);
137
+ if (senderPhone)
138
+ searchQueries.push(senderPhone);
139
+ // Collect all contacts from all searches
140
+ const allContacts = [];
141
+ for (const searchQuery of searchQueries) {
142
+ const queryParams = new URLSearchParams({
143
+ search: searchQuery,
144
+ limit: '5',
145
+ user_email: userId,
146
+ });
147
+ const response = await client.get(`/api/contacts?${queryParams.toString()}`, { userId });
148
+ if (!response.success) {
149
+ logger.error('auto-linker: contact search failed', {
150
+ userId,
151
+ status: response.error.status,
152
+ code: response.error.code,
153
+ });
154
+ continue;
155
+ }
156
+ const contacts = response.data.contacts ?? response.data.items ?? [];
157
+ allContacts.push(...contacts);
158
+ }
159
+ if (allContacts.length === 0) {
160
+ return [];
161
+ }
162
+ // Filter to exact matches on email or phone, deduplicate by ID
163
+ const seen = new Set();
164
+ const matchedContacts = [];
165
+ for (const c of allContacts) {
166
+ if (seen.has(c.id))
167
+ continue;
168
+ const emailMatch = senderEmail != null && c.email?.toLowerCase() === senderEmail.toLowerCase();
169
+ const phoneMatch = senderPhone != null && c.phone === senderPhone;
170
+ if (emailMatch || phoneMatch) {
171
+ seen.add(c.id);
172
+ matchedContacts.push(c);
173
+ }
174
+ }
175
+ const threadRef = buildThreadRef(threadId);
176
+ const linkedContactIds = [];
177
+ for (const contact of matchedContacts) {
178
+ try {
179
+ // Use 'url' type with thread: URI since entity-links schema does not have
180
+ // a 'message_thread' type yet. See buildThreadRef() for details.
181
+ const linked = await createEntityLink(client, userId, logger, 'contact', contact.id, 'url', threadRef, 'inbound-message-sender');
182
+ if (linked) {
183
+ linkedContactIds.push(contact.id);
184
+ }
185
+ }
186
+ catch (error) {
187
+ logger.error('auto-linker: failed to create contact link', {
188
+ userId,
189
+ contactId: contact.id,
190
+ error: sanitizeErrorMessage(error),
191
+ });
192
+ }
193
+ }
194
+ return linkedContactIds;
195
+ }
196
+ // ==================== Content matching ====================
197
+ /**
198
+ * Search for projects and todos matching the message content.
199
+ * Returns matched entity IDs by type and creates links for each match.
200
+ */
201
+ async function matchContentToWorkItems(client, logger, userId, threadId, content, similarityThreshold) {
202
+ // Sanitize external content before using it in search queries.
203
+ // Removes control characters, unicode invisibles, and potential injection payloads.
204
+ const sanitizedContent = sanitizeExternalMessage(content);
205
+ if (sanitizedContent.length === 0) {
206
+ return { projects: [], todos: [] };
207
+ }
208
+ // Truncate for search
209
+ const searchQuery = sanitizedContent.substring(0, MAX_SEARCH_QUERY_LENGTH);
210
+ const queryParams = new URLSearchParams({
211
+ q: searchQuery,
212
+ types: 'work_item',
213
+ limit: '10',
214
+ semantic: 'true',
215
+ user_email: userId,
216
+ });
217
+ const response = await client.get(`/api/search?${queryParams.toString()}`, { userId });
218
+ if (!response.success) {
219
+ logger.error('auto-linker: content search failed', {
220
+ userId,
221
+ status: response.error.status,
222
+ code: response.error.code,
223
+ });
224
+ return { projects: [], todos: [] };
225
+ }
226
+ const results = response.data.results ?? [];
227
+ // Filter to items above the similarity threshold
228
+ const highConfidenceResults = results.filter((r) => r.score >= similarityThreshold);
229
+ if (highConfidenceResults.length === 0) {
230
+ logger.debug('auto-linker: no content matches above threshold', {
231
+ userId,
232
+ threshold: similarityThreshold,
233
+ totalResults: results.length,
234
+ topScore: results[0]?.score ?? 0,
235
+ });
236
+ return { projects: [], todos: [] };
237
+ }
238
+ const threadRef = buildThreadRef(threadId);
239
+ const linkedProjects = [];
240
+ const linkedTodos = [];
241
+ for (const item of highConfidenceResults) {
242
+ const kind = item.metadata?.kind;
243
+ const isProject = kind !== undefined && PROJECT_KINDS.has(kind);
244
+ const isTodo = kind !== undefined && TODO_KINDS.has(kind);
245
+ if (!isProject && !isTodo) {
246
+ continue;
247
+ }
248
+ const sourceType = isProject ? 'project' : 'todo';
249
+ try {
250
+ // Use 'url' type with thread: URI since entity-links schema does not have
251
+ // a 'message_thread' type yet. See buildThreadRef() for details.
252
+ const linked = await createEntityLink(client, userId, logger, sourceType, item.id, 'url', threadRef, `auto-linked:${item.title.substring(0, 50)}`);
253
+ if (linked) {
254
+ if (isProject) {
255
+ linkedProjects.push(item.id);
256
+ }
257
+ else {
258
+ linkedTodos.push(item.id);
259
+ }
260
+ }
261
+ }
262
+ catch (error) {
263
+ logger.error('auto-linker: failed to create work item link', {
264
+ userId,
265
+ itemId: item.id,
266
+ itemKind: kind,
267
+ error: sanitizeErrorMessage(error),
268
+ });
269
+ }
270
+ }
271
+ return { projects: linkedProjects, todos: linkedTodos };
272
+ }
273
+ // ==================== Main function ====================
274
+ /**
275
+ * Auto-link an inbound message to related entities.
276
+ *
277
+ * Performs sender matching first. Content matching only runs when at least
278
+ * one contact matched — this prevents untrusted/unknown senders from
279
+ * creating spurious links to projects and todos.
280
+ *
281
+ * Never throws - all errors are caught and logged.
282
+ *
283
+ * @param options - Auto-link configuration and message data
284
+ * @returns Summary of links created and entities matched
285
+ */
286
+ export async function autoLinkInboundMessage(options) {
287
+ const { client, logger, userId, message, similarityThreshold = DEFAULT_SIMILARITY_THRESHOLD, } = options;
288
+ const emptyResult = {
289
+ linksCreated: 0,
290
+ matches: { contacts: [], projects: [], todos: [] },
291
+ };
292
+ try {
293
+ logger.info('auto-linker: processing inbound message', {
294
+ userId,
295
+ threadId: message.threadId,
296
+ hasSenderEmail: !!message.senderEmail,
297
+ hasSenderPhone: !!message.senderPhone,
298
+ contentLength: message.content.length,
299
+ });
300
+ // Step 1: Sender matching — always runs when sender info is available
301
+ const contactMatches = await matchSenderToContacts(client, logger, userId, message.threadId, message.senderEmail, message.senderPhone).catch((error) => {
302
+ logger.error('auto-linker: sender matching failed', {
303
+ userId,
304
+ error: sanitizeErrorMessage(error),
305
+ });
306
+ return [];
307
+ });
308
+ // Step 2: Content matching — only runs when sender is a known contact.
309
+ // Safety decision: untrusted/unknown senders should not trigger content-based
310
+ // linking to prevent spam or malicious messages from creating spurious links
311
+ // to the user's projects and todos.
312
+ let contentMatches = { projects: [], todos: [] };
313
+ if (contactMatches.length > 0) {
314
+ contentMatches = await matchContentToWorkItems(client, logger, userId, message.threadId, message.content, similarityThreshold).catch((error) => {
315
+ logger.error('auto-linker: content matching failed', {
316
+ userId,
317
+ error: sanitizeErrorMessage(error),
318
+ });
319
+ return { projects: [], todos: [] };
320
+ });
321
+ }
322
+ else {
323
+ logger.debug('auto-linker: skipping content matching — no known sender contact', {
324
+ userId,
325
+ threadId: message.threadId,
326
+ });
327
+ }
328
+ const result = {
329
+ linksCreated: contactMatches.length + contentMatches.projects.length + contentMatches.todos.length,
330
+ matches: {
331
+ contacts: contactMatches,
332
+ projects: contentMatches.projects,
333
+ todos: contentMatches.todos,
334
+ },
335
+ };
336
+ logger.info('auto-linker: completed', {
337
+ userId,
338
+ threadId: message.threadId,
339
+ linksCreated: result.linksCreated,
340
+ contactMatches: result.matches.contacts.length,
341
+ projectMatches: result.matches.projects.length,
342
+ todoMatches: result.matches.todos.length,
343
+ });
344
+ return result;
345
+ }
346
+ catch (error) {
347
+ logger.error('auto-linker: unexpected failure', {
348
+ userId,
349
+ error: sanitizeErrorMessage(error),
350
+ });
351
+ return emptyResult;
352
+ }
353
+ }
354
+ //# sourceMappingURL=auto-linker.js.map