@troykelly/openclaw-projects 0.0.11 → 0.0.12
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/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +17 -3
- package/dist/hooks.js.map +1 -1
- package/dist/register-openclaw.d.ts +3 -1
- package/dist/register-openclaw.d.ts.map +1 -1
- package/dist/register-openclaw.js +611 -55
- package/dist/register-openclaw.js.map +1 -1
- package/dist/tools/context-search.d.ts +79 -0
- package/dist/tools/context-search.d.ts.map +1 -0
- package/dist/tools/context-search.js +265 -0
- package/dist/tools/context-search.js.map +1 -0
- package/dist/tools/email-send.d.ts.map +1 -1
- package/dist/tools/email-send.js +1 -14
- package/dist/tools/email-send.js.map +1 -1
- package/dist/tools/entity-links.d.ts +117 -0
- package/dist/tools/entity-links.d.ts.map +1 -0
- package/dist/tools/entity-links.js +446 -0
- package/dist/tools/entity-links.js.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/memory-recall.d.ts +28 -0
- package/dist/tools/memory-recall.d.ts.map +1 -1
- package/dist/tools/memory-recall.js +42 -3
- package/dist/tools/memory-recall.js.map +1 -1
- package/dist/tools/memory-store.d.ts +57 -0
- package/dist/tools/memory-store.d.ts.map +1 -1
- package/dist/tools/memory-store.js +29 -2
- package/dist/tools/memory-store.js.map +1 -1
- package/dist/tools/message-search.d.ts +1 -1
- package/dist/tools/message-search.d.ts.map +1 -1
- package/dist/tools/message-search.js +20 -2
- package/dist/tools/message-search.js.map +1 -1
- package/dist/tools/notes.d.ts +2 -2
- package/dist/tools/project-search.d.ts +92 -0
- package/dist/tools/project-search.d.ts.map +1 -0
- package/dist/tools/project-search.js +160 -0
- package/dist/tools/project-search.js.map +1 -0
- package/dist/tools/skill-store.d.ts +12 -12
- package/dist/tools/threads.d.ts +2 -2
- package/dist/tools/threads.d.ts.map +1 -1
- package/dist/tools/threads.js +30 -6
- package/dist/tools/threads.js.map +1 -1
- package/dist/tools/todo-search.d.ts +95 -0
- package/dist/tools/todo-search.d.ts.map +1 -0
- package/dist/tools/todo-search.js +164 -0
- package/dist/tools/todo-search.js.map +1 -0
- package/dist/types/openclaw-api.d.ts +15 -0
- package/dist/types/openclaw-api.d.ts.map +1 -1
- package/dist/utils/auto-linker.d.ts +66 -0
- package/dist/utils/auto-linker.d.ts.map +1 -0
- package/dist/utils/auto-linker.js +354 -0
- package/dist/utils/auto-linker.js.map +1 -0
- package/dist/utils/geo.d.ts +24 -0
- package/dist/utils/geo.d.ts.map +1 -0
- package/dist/utils/geo.js +38 -0
- package/dist/utils/geo.js.map +1 -0
- package/dist/utils/inbound-gate.d.ts +85 -0
- package/dist/utils/inbound-gate.d.ts.map +1 -0
- package/dist/utils/inbound-gate.js +133 -0
- package/dist/utils/inbound-gate.js.map +1 -0
- package/dist/utils/injection-protection.d.ts +81 -0
- package/dist/utils/injection-protection.d.ts.map +1 -0
- package/dist/utils/injection-protection.js +179 -0
- package/dist/utils/injection-protection.js.map +1 -0
- package/dist/utils/nominatim.d.ts +18 -0
- package/dist/utils/nominatim.d.ts.map +1 -0
- package/dist/utils/nominatim.js +56 -0
- package/dist/utils/nominatim.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +81 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +188 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/spam-filter.d.ts +79 -0
- package/dist/utils/spam-filter.d.ts.map +1 -0
- package/dist/utils/spam-filter.js +237 -0
- package/dist/utils/spam-filter.js.map +1 -0
- package/dist/utils/token-budget.d.ts +68 -0
- package/dist/utils/token-budget.d.ts.map +1 -0
- package/dist/utils/token-budget.js +142 -0
- package/dist/utils/token-budget.js.map +1 -0
- package/openclaw.plugin.json +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* project_search tool implementation.
|
|
3
|
+
* Provides semantic search specifically for projects (kind=project work items).
|
|
4
|
+
* Uses the unified search API with work_item type filter and client-side kind filtering.
|
|
5
|
+
* Geo-contextual ranking added in Issue #1218.
|
|
6
|
+
*
|
|
7
|
+
* Part of Issue #1217.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { sanitizeErrorMessage } from '../utils/sanitize.js';
|
|
11
|
+
import { reverseGeocode } from '../utils/nominatim.js';
|
|
12
|
+
/** Parameters for project_search tool */
|
|
13
|
+
export const ProjectSearchParamsSchema = 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
|
+
status: z.enum(['active', 'completed', 'archived']).optional(),
|
|
17
|
+
location: z.object({
|
|
18
|
+
lat: z.number().min(-90).max(90),
|
|
19
|
+
lng: z.number().min(-180).max(180),
|
|
20
|
+
}).optional(),
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Sanitize query input to prevent injection and remove control characters.
|
|
24
|
+
*/
|
|
25
|
+
function sanitizeQuery(query) {
|
|
26
|
+
return query.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '').trim();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates the project_search tool.
|
|
30
|
+
*/
|
|
31
|
+
export function createProjectSearchTool(options) {
|
|
32
|
+
const { client, logger, config, userId } = options;
|
|
33
|
+
return {
|
|
34
|
+
name: 'project_search',
|
|
35
|
+
description: 'Search projects by natural language query. Uses semantic and text search to find relevant projects. ' +
|
|
36
|
+
'Optionally filter by status (active, completed, archived). ' +
|
|
37
|
+
'Provide location (lat/lng) to boost results relevant to the current location.',
|
|
38
|
+
parameters: ProjectSearchParamsSchema,
|
|
39
|
+
async execute(params) {
|
|
40
|
+
const parseResult = ProjectSearchParamsSchema.safeParse(params);
|
|
41
|
+
if (!parseResult.success) {
|
|
42
|
+
const errorMessage = parseResult.error.errors.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
|
43
|
+
return { success: false, error: errorMessage };
|
|
44
|
+
}
|
|
45
|
+
const { query, limit = 10, status, location } = parseResult.data;
|
|
46
|
+
const sanitizedQuery = sanitizeQuery(query);
|
|
47
|
+
if (sanitizedQuery.length === 0) {
|
|
48
|
+
return { success: false, error: 'Query cannot be empty after sanitization' };
|
|
49
|
+
}
|
|
50
|
+
logger.info('project_search invoked', {
|
|
51
|
+
userId,
|
|
52
|
+
queryLength: sanitizedQuery.length,
|
|
53
|
+
limit,
|
|
54
|
+
status: status ?? 'all',
|
|
55
|
+
hasLocation: !!location,
|
|
56
|
+
});
|
|
57
|
+
try {
|
|
58
|
+
// Augment query with location context for geo-contextual ranking (Issue #1218)
|
|
59
|
+
let searchQuery = sanitizedQuery;
|
|
60
|
+
if (location && config.nominatimUrl) {
|
|
61
|
+
try {
|
|
62
|
+
const geo = await reverseGeocode(location.lat, location.lng, config.nominatimUrl);
|
|
63
|
+
if (geo?.placeLabel) {
|
|
64
|
+
const augmented = `${sanitizedQuery} near ${geo.placeLabel}`;
|
|
65
|
+
if (augmented.length <= 1000) {
|
|
66
|
+
searchQuery = augmented;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
logger.warn('Reverse geocode failed, proceeding without location augmentation', { userId });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Always over-fetch by 3x since we always filter to kind=project client-side
|
|
75
|
+
const fetchLimit = Math.min(limit * 3, 50);
|
|
76
|
+
const queryParams = new URLSearchParams({
|
|
77
|
+
q: searchQuery,
|
|
78
|
+
types: 'work_item',
|
|
79
|
+
limit: String(fetchLimit),
|
|
80
|
+
semantic: 'true',
|
|
81
|
+
user_email: userId,
|
|
82
|
+
});
|
|
83
|
+
const response = await client.get(`/api/search?${queryParams.toString()}`, { userId });
|
|
84
|
+
if (!response.success) {
|
|
85
|
+
logger.error('project_search API error', {
|
|
86
|
+
userId,
|
|
87
|
+
status: response.error.status,
|
|
88
|
+
code: response.error.code,
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
error: response.error.message || 'Failed to search projects',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
let results = response.data.results ?? [];
|
|
96
|
+
// Always filter to kind=project since this is a project-specific search tool
|
|
97
|
+
results = results.filter((r) => r.metadata?.kind === 'project');
|
|
98
|
+
// Additional client-side filtering by status if specified
|
|
99
|
+
if (status) {
|
|
100
|
+
results = results.filter((r) => r.metadata?.status === status);
|
|
101
|
+
}
|
|
102
|
+
results = results.slice(0, limit);
|
|
103
|
+
const items = results.map((r) => ({
|
|
104
|
+
id: r.id,
|
|
105
|
+
title: r.title,
|
|
106
|
+
snippet: r.snippet,
|
|
107
|
+
score: r.score,
|
|
108
|
+
kind: r.metadata?.kind,
|
|
109
|
+
status: r.metadata?.status,
|
|
110
|
+
}));
|
|
111
|
+
if (items.length === 0) {
|
|
112
|
+
return {
|
|
113
|
+
success: true,
|
|
114
|
+
data: {
|
|
115
|
+
content: 'No matching projects found.',
|
|
116
|
+
details: {
|
|
117
|
+
count: 0,
|
|
118
|
+
results: [],
|
|
119
|
+
searchType: response.data.search_type,
|
|
120
|
+
userId,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const content = items
|
|
126
|
+
.map((item) => {
|
|
127
|
+
const statusStr = item.status ? ` (${item.status})` : '';
|
|
128
|
+
const snippetStr = item.snippet ? ` - ${item.snippet}` : '';
|
|
129
|
+
return `- **${item.title}**${statusStr}${snippetStr}`;
|
|
130
|
+
})
|
|
131
|
+
.join('\n');
|
|
132
|
+
logger.debug('project_search completed', {
|
|
133
|
+
userId,
|
|
134
|
+
resultCount: items.length,
|
|
135
|
+
searchType: response.data.search_type,
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
success: true,
|
|
139
|
+
data: {
|
|
140
|
+
content,
|
|
141
|
+
details: {
|
|
142
|
+
count: items.length,
|
|
143
|
+
results: items,
|
|
144
|
+
searchType: response.data.search_type,
|
|
145
|
+
userId,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
logger.error('project_search failed', {
|
|
152
|
+
userId,
|
|
153
|
+
error: error instanceof Error ? error.message : String(error),
|
|
154
|
+
});
|
|
155
|
+
return { success: false, error: sanitizeErrorMessage(error) };
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=project-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-search.js","sourceRoot":"","sources":["../../src/tools/project-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,11 +32,11 @@ export declare const SkillStorePutParamsSchema: z.ZodObject<{
|
|
|
32
32
|
skill_id: string;
|
|
33
33
|
key?: string | undefined;
|
|
34
34
|
data?: Record<string, unknown> | undefined;
|
|
35
|
+
user_email?: string | undefined;
|
|
35
36
|
tags?: string[] | undefined;
|
|
36
37
|
content?: string | undefined;
|
|
37
38
|
title?: string | undefined;
|
|
38
39
|
summary?: string | undefined;
|
|
39
|
-
user_email?: string | undefined;
|
|
40
40
|
collection?: string | undefined;
|
|
41
41
|
media_url?: string | undefined;
|
|
42
42
|
media_type?: string | undefined;
|
|
@@ -48,11 +48,11 @@ export declare const SkillStorePutParamsSchema: z.ZodObject<{
|
|
|
48
48
|
skill_id: string;
|
|
49
49
|
key?: string | undefined;
|
|
50
50
|
data?: Record<string, unknown> | undefined;
|
|
51
|
+
user_email?: string | undefined;
|
|
51
52
|
tags?: string[] | undefined;
|
|
52
53
|
content?: string | undefined;
|
|
53
54
|
title?: string | undefined;
|
|
54
55
|
summary?: string | undefined;
|
|
55
|
-
user_email?: string | undefined;
|
|
56
56
|
collection?: string | undefined;
|
|
57
57
|
media_url?: string | undefined;
|
|
58
58
|
media_type?: string | undefined;
|
|
@@ -95,21 +95,21 @@ export declare const SkillStoreListParamsSchema: z.ZodObject<{
|
|
|
95
95
|
skill_id: string;
|
|
96
96
|
status?: "active" | "archived" | "processing" | undefined;
|
|
97
97
|
limit?: number | undefined;
|
|
98
|
+
since?: string | undefined;
|
|
99
|
+
user_email?: string | undefined;
|
|
98
100
|
tags?: string[] | undefined;
|
|
99
101
|
offset?: number | undefined;
|
|
100
|
-
user_email?: string | undefined;
|
|
101
102
|
collection?: string | undefined;
|
|
102
|
-
since?: string | undefined;
|
|
103
103
|
order_by?: "title" | "priority" | "created_at" | "updated_at" | undefined;
|
|
104
104
|
}, {
|
|
105
105
|
skill_id: string;
|
|
106
106
|
status?: "active" | "archived" | "processing" | undefined;
|
|
107
107
|
limit?: number | undefined;
|
|
108
|
+
since?: string | undefined;
|
|
109
|
+
user_email?: string | undefined;
|
|
108
110
|
tags?: string[] | undefined;
|
|
109
111
|
offset?: number | undefined;
|
|
110
|
-
user_email?: string | undefined;
|
|
111
112
|
collection?: string | undefined;
|
|
112
|
-
since?: string | undefined;
|
|
113
113
|
order_by?: "title" | "priority" | "created_at" | "updated_at" | undefined;
|
|
114
114
|
}>;
|
|
115
115
|
export type SkillStoreListParams = z.infer<typeof SkillStoreListParamsSchema>;
|
|
@@ -210,18 +210,18 @@ export declare const SkillStoreSearchParamsSchema: z.ZodObject<{
|
|
|
210
210
|
query: string;
|
|
211
211
|
skill_id: string;
|
|
212
212
|
limit?: number | undefined;
|
|
213
|
-
tags?: string[] | undefined;
|
|
214
|
-
semantic?: boolean | undefined;
|
|
215
213
|
user_email?: string | undefined;
|
|
214
|
+
semantic?: boolean | undefined;
|
|
215
|
+
tags?: string[] | undefined;
|
|
216
216
|
collection?: string | undefined;
|
|
217
217
|
min_similarity?: number | undefined;
|
|
218
218
|
}, {
|
|
219
219
|
query: string;
|
|
220
220
|
skill_id: string;
|
|
221
221
|
limit?: number | undefined;
|
|
222
|
-
tags?: string[] | undefined;
|
|
223
|
-
semantic?: boolean | undefined;
|
|
224
222
|
user_email?: string | undefined;
|
|
223
|
+
semantic?: boolean | undefined;
|
|
224
|
+
tags?: string[] | undefined;
|
|
225
225
|
collection?: string | undefined;
|
|
226
226
|
min_similarity?: number | undefined;
|
|
227
227
|
}>;
|
|
@@ -249,16 +249,16 @@ export declare const SkillStoreAggregateParamsSchema: z.ZodObject<{
|
|
|
249
249
|
}, "strip", z.ZodTypeAny, {
|
|
250
250
|
skill_id: string;
|
|
251
251
|
operation: "count" | "count_by_tag" | "count_by_status" | "latest" | "oldest";
|
|
252
|
+
since?: string | undefined;
|
|
252
253
|
user_email?: string | undefined;
|
|
253
254
|
collection?: string | undefined;
|
|
254
|
-
since?: string | undefined;
|
|
255
255
|
until?: string | undefined;
|
|
256
256
|
}, {
|
|
257
257
|
skill_id: string;
|
|
258
258
|
operation: "count" | "count_by_tag" | "count_by_status" | "latest" | "oldest";
|
|
259
|
+
since?: string | undefined;
|
|
259
260
|
user_email?: string | undefined;
|
|
260
261
|
collection?: string | undefined;
|
|
261
|
-
since?: string | undefined;
|
|
262
262
|
until?: string | undefined;
|
|
263
263
|
}>;
|
|
264
264
|
export type SkillStoreAggregateParams = z.infer<typeof SkillStoreAggregateParamsSchema>;
|
package/dist/tools/threads.d.ts
CHANGED
|
@@ -13,12 +13,12 @@ export declare const ThreadListParamsSchema: z.ZodObject<{
|
|
|
13
13
|
limit: z.ZodDefault<z.ZodNumber>;
|
|
14
14
|
}, "strip", z.ZodTypeAny, {
|
|
15
15
|
limit: number;
|
|
16
|
-
contactId?: string | undefined;
|
|
17
16
|
channel?: "email" | "sms" | undefined;
|
|
17
|
+
contactId?: string | undefined;
|
|
18
18
|
}, {
|
|
19
19
|
limit?: number | undefined;
|
|
20
|
-
contactId?: string | undefined;
|
|
21
20
|
channel?: "email" | "sms" | undefined;
|
|
21
|
+
contactId?: string | undefined;
|
|
22
22
|
}>;
|
|
23
23
|
export type ThreadListParams = z.infer<typeof ThreadListParamsSchema>;
|
|
24
24
|
/** Thread in list response (mapped from search results) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"threads.d.ts","sourceRoot":"","sources":["../../src/tools/threads.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"threads.d.ts","sourceRoot":"","sources":["../../src/tools/threads.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/threads.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Provides thread_list and thread_get tools for viewing message threads.
|
|
4
4
|
*/
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
+
import { detectInjectionPatterns, sanitizeMetadataField, sanitizeMessageForContext, wrapExternalMessage } from '../utils/injection-protection.js';
|
|
6
7
|
/** Channel type enum */
|
|
7
8
|
const ChannelType = z.enum(['sms', 'email']);
|
|
8
9
|
/** Default and max limits */
|
|
@@ -82,11 +83,14 @@ export function createThreadListTool(options) {
|
|
|
82
83
|
resultCount: threadItems.length,
|
|
83
84
|
total,
|
|
84
85
|
});
|
|
85
|
-
// Format content for display
|
|
86
|
+
// Format content for display with injection protection.
|
|
87
|
+
// Sanitize snippet/title since they may contain external message content.
|
|
86
88
|
const content = threadItems.length > 0
|
|
87
89
|
? threadItems
|
|
88
90
|
.map((t) => {
|
|
89
|
-
|
|
91
|
+
const safeTitle = sanitizeMetadataField(t.title);
|
|
92
|
+
const wrappedSnippet = wrapExternalMessage(t.snippet);
|
|
93
|
+
return `${safeTitle}: ${wrappedSnippet}`;
|
|
90
94
|
})
|
|
91
95
|
.join('\n')
|
|
92
96
|
: 'No threads found.';
|
|
@@ -168,15 +172,35 @@ export function createThreadGetTool(options) {
|
|
|
168
172
|
threadId,
|
|
169
173
|
messageCount: messages.length,
|
|
170
174
|
});
|
|
171
|
-
// Format content for display
|
|
172
|
-
const contact = thread.contact?.displayName || 'Unknown';
|
|
173
|
-
const
|
|
175
|
+
// Format content for display — sanitize metadata fields interpolated outside boundary wrappers
|
|
176
|
+
const contact = sanitizeMetadataField(thread.contact?.displayName || 'Unknown');
|
|
177
|
+
const safeChannel = sanitizeMetadataField(thread.channel);
|
|
178
|
+
const header = `Thread with ${contact} [${safeChannel}]`;
|
|
179
|
+
// Detect and log potential injection patterns in inbound messages
|
|
180
|
+
for (const m of messages) {
|
|
181
|
+
if (m.direction === 'inbound' && m.body) {
|
|
182
|
+
const detection = detectInjectionPatterns(m.body);
|
|
183
|
+
if (detection.detected) {
|
|
184
|
+
logger.warn('potential prompt injection detected in thread_get result', {
|
|
185
|
+
userId,
|
|
186
|
+
threadId,
|
|
187
|
+
messageId: m.id,
|
|
188
|
+
patterns: detection.patterns,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
174
193
|
const messageContent = messages.length > 0
|
|
175
194
|
? messages
|
|
176
195
|
.map((m) => {
|
|
177
196
|
const prefix = m.direction === 'inbound' ? '←' : '→';
|
|
178
197
|
const timestamp = new Date(m.createdAt).toLocaleString();
|
|
179
|
-
|
|
198
|
+
const body = sanitizeMessageForContext(m.body || '', {
|
|
199
|
+
direction: m.direction,
|
|
200
|
+
channel: thread.channel,
|
|
201
|
+
sender: contact,
|
|
202
|
+
});
|
|
203
|
+
return `${prefix} [${timestamp}] ${body}`;
|
|
180
204
|
})
|
|
181
205
|
.join('\n')
|
|
182
206
|
: 'No messages in this thread.';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"threads.js","sourceRoot":"","sources":["../../src/tools/threads.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"threads.js","sourceRoot":"","sources":["../../src/tools/threads.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
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 type { ApiClient } from '../api-client.js';
|
|
11
|
+
import type { Logger } from '../logger.js';
|
|
12
|
+
import type { PluginConfig } from '../config.js';
|
|
13
|
+
/** Parameters for todo_search tool */
|
|
14
|
+
export declare const TodoSearchParamsSchema: z.ZodObject<{
|
|
15
|
+
query: z.ZodString;
|
|
16
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
kind: z.ZodOptional<z.ZodEnum<["task", "project", "initiative", "epic", "issue"]>>;
|
|
18
|
+
status: z.ZodOptional<z.ZodString>;
|
|
19
|
+
location: z.ZodOptional<z.ZodObject<{
|
|
20
|
+
lat: z.ZodNumber;
|
|
21
|
+
lng: z.ZodNumber;
|
|
22
|
+
}, "strip", z.ZodTypeAny, {
|
|
23
|
+
lat: number;
|
|
24
|
+
lng: number;
|
|
25
|
+
}, {
|
|
26
|
+
lat: number;
|
|
27
|
+
lng: number;
|
|
28
|
+
}>>;
|
|
29
|
+
}, "strip", z.ZodTypeAny, {
|
|
30
|
+
query: string;
|
|
31
|
+
status?: string | undefined;
|
|
32
|
+
limit?: number | undefined;
|
|
33
|
+
location?: {
|
|
34
|
+
lat: number;
|
|
35
|
+
lng: number;
|
|
36
|
+
} | undefined;
|
|
37
|
+
kind?: "project" | "task" | "issue" | "epic" | "initiative" | undefined;
|
|
38
|
+
}, {
|
|
39
|
+
query: string;
|
|
40
|
+
status?: string | undefined;
|
|
41
|
+
limit?: number | undefined;
|
|
42
|
+
location?: {
|
|
43
|
+
lat: number;
|
|
44
|
+
lng: number;
|
|
45
|
+
} | undefined;
|
|
46
|
+
kind?: "project" | "task" | "issue" | "epic" | "initiative" | undefined;
|
|
47
|
+
}>;
|
|
48
|
+
export type TodoSearchParams = z.infer<typeof TodoSearchParamsSchema>;
|
|
49
|
+
/** Search result item */
|
|
50
|
+
export interface TodoSearchItem {
|
|
51
|
+
id: string;
|
|
52
|
+
title: string;
|
|
53
|
+
snippet: string;
|
|
54
|
+
score: number;
|
|
55
|
+
kind?: string;
|
|
56
|
+
status?: string;
|
|
57
|
+
}
|
|
58
|
+
/** Successful search result */
|
|
59
|
+
export interface TodoSearchSuccess {
|
|
60
|
+
success: true;
|
|
61
|
+
data: {
|
|
62
|
+
content: string;
|
|
63
|
+
details: {
|
|
64
|
+
count: number;
|
|
65
|
+
results: TodoSearchItem[];
|
|
66
|
+
searchType: string;
|
|
67
|
+
userId: string;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Failed result */
|
|
72
|
+
export interface TodoSearchFailure {
|
|
73
|
+
success: false;
|
|
74
|
+
error: string;
|
|
75
|
+
}
|
|
76
|
+
export type TodoSearchResult = TodoSearchSuccess | TodoSearchFailure;
|
|
77
|
+
/** Tool configuration */
|
|
78
|
+
export interface TodoSearchToolOptions {
|
|
79
|
+
client: ApiClient;
|
|
80
|
+
logger: Logger;
|
|
81
|
+
config: PluginConfig;
|
|
82
|
+
userId: string;
|
|
83
|
+
}
|
|
84
|
+
/** Tool definition */
|
|
85
|
+
export interface TodoSearchTool {
|
|
86
|
+
name: string;
|
|
87
|
+
description: string;
|
|
88
|
+
parameters: typeof TodoSearchParamsSchema;
|
|
89
|
+
execute: (params: TodoSearchParams) => Promise<TodoSearchResult>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Creates the todo_search tool.
|
|
93
|
+
*/
|
|
94
|
+
export declare function createTodoSearchTool(options: TodoSearchToolOptions): TodoSearchTool;
|
|
95
|
+
//# sourceMappingURL=todo-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todo-search.d.ts","sourceRoot":"","sources":["../../src/tools/todo-search.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAIjD,sCAAsC;AACtC,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,yBAAyB;AACzB,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,+BAA+B;AAC/B,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE;YACP,KAAK,EAAE,MAAM,CAAC;YACd,OAAO,EAAE,cAAc,EAAE,CAAC;YAC1B,UAAU,EAAE,MAAM,CAAC;YACnB,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;CACH;AAED,oBAAoB;AACpB,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;AAErE,yBAAyB;AACzB,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sBAAsB;AACtB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,sBAAsB,CAAC;IAC1C,OAAO,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAClE;AASD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CA8JnF"}
|
|
@@ -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
|