@troykelly/openclaw-projects 0.0.16 → 0.0.20
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/api-client.d.ts +1 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +3 -3
- package/dist/api-client.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +10 -10
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +82 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +56 -5
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +3 -3
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +3 -3
- package/dist/context.js.map +1 -1
- package/dist/gateway/oauth-rpc-methods.d.ts +44 -44
- package/dist/gateway/oauth-rpc-methods.d.ts.map +1 -1
- package/dist/gateway/oauth-rpc-methods.js +106 -106
- package/dist/gateway/oauth-rpc-methods.js.map +1 -1
- package/dist/gateway/rpc-methods.d.ts +3 -3
- package/dist/gateway/rpc-methods.d.ts.map +1 -1
- package/dist/gateway/rpc-methods.js +11 -11
- package/dist/gateway/rpc-methods.js.map +1 -1
- package/dist/hooks.d.ts +3 -3
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +42 -42
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -19
- package/dist/index.js.map +1 -1
- package/dist/register-openclaw.d.ts.map +1 -1
- package/dist/register-openclaw.js +741 -262
- package/dist/register-openclaw.js.map +1 -1
- package/dist/services/notification-service.d.ts +1 -1
- package/dist/services/notification-service.d.ts.map +1 -1
- package/dist/services/notification-service.js +11 -11
- package/dist/services/notification-service.js.map +1 -1
- package/dist/tools/contacts.d.ts +7 -7
- package/dist/tools/contacts.d.ts.map +1 -1
- package/dist/tools/contacts.js +30 -30
- package/dist/tools/contacts.js.map +1 -1
- package/dist/tools/context-search.d.ts +2 -2
- package/dist/tools/context-search.d.ts.map +1 -1
- package/dist/tools/context-search.js +14 -14
- package/dist/tools/context-search.js.map +1 -1
- package/dist/tools/email-send.d.ts +13 -13
- package/dist/tools/email-send.d.ts.map +1 -1
- package/dist/tools/email-send.js +22 -22
- package/dist/tools/email-send.js.map +1 -1
- package/dist/tools/entity-links.d.ts +1 -1
- package/dist/tools/entity-links.d.ts.map +1 -1
- package/dist/tools/entity-links.js +33 -33
- package/dist/tools/entity-links.js.map +1 -1
- package/dist/tools/file-share.d.ts +15 -15
- package/dist/tools/file-share.d.ts.map +1 -1
- package/dist/tools/file-share.js +30 -30
- package/dist/tools/file-share.js.map +1 -1
- package/dist/tools/memory-forget.d.ts +7 -7
- package/dist/tools/memory-forget.d.ts.map +1 -1
- package/dist/tools/memory-forget.js +34 -34
- package/dist/tools/memory-forget.js.map +1 -1
- package/dist/tools/memory-recall.d.ts +4 -4
- package/dist/tools/memory-recall.d.ts.map +1 -1
- package/dist/tools/memory-recall.js +9 -9
- package/dist/tools/memory-recall.js.map +1 -1
- package/dist/tools/memory-store.d.ts +3 -3
- package/dist/tools/memory-store.d.ts.map +1 -1
- package/dist/tools/memory-store.js +13 -13
- package/dist/tools/memory-store.js.map +1 -1
- package/dist/tools/message-search.d.ts +8 -8
- package/dist/tools/message-search.d.ts.map +1 -1
- package/dist/tools/message-search.js +19 -19
- package/dist/tools/message-search.js.map +1 -1
- package/dist/tools/notebooks.d.ts +20 -20
- package/dist/tools/notebooks.d.ts.map +1 -1
- package/dist/tools/notebooks.js +40 -40
- package/dist/tools/notebooks.js.map +1 -1
- package/dist/tools/notes.d.ts +35 -35
- package/dist/tools/notes.d.ts.map +1 -1
- package/dist/tools/notes.js +67 -67
- package/dist/tools/notes.js.map +1 -1
- package/dist/tools/project-search.d.ts +3 -3
- package/dist/tools/project-search.d.ts.map +1 -1
- package/dist/tools/project-search.js +15 -15
- package/dist/tools/project-search.js.map +1 -1
- package/dist/tools/projects.d.ts +6 -6
- package/dist/tools/projects.d.ts.map +1 -1
- package/dist/tools/projects.js +25 -25
- package/dist/tools/projects.js.map +1 -1
- package/dist/tools/relationships.d.ts +19 -19
- package/dist/tools/relationships.d.ts.map +1 -1
- package/dist/tools/relationships.js +38 -38
- package/dist/tools/relationships.js.map +1 -1
- package/dist/tools/skill-store.d.ts +11 -11
- package/dist/tools/skill-store.d.ts.map +1 -1
- package/dist/tools/skill-store.js +51 -51
- package/dist/tools/skill-store.js.map +1 -1
- package/dist/tools/sms-send.d.ts +7 -7
- package/dist/tools/sms-send.d.ts.map +1 -1
- package/dist/tools/sms-send.js +16 -16
- package/dist/tools/sms-send.js.map +1 -1
- package/dist/tools/threads.d.ts +18 -18
- package/dist/tools/threads.d.ts.map +1 -1
- package/dist/tools/threads.js +33 -33
- package/dist/tools/threads.js.map +1 -1
- package/dist/tools/todo-search.d.ts +3 -3
- package/dist/tools/todo-search.d.ts.map +1 -1
- package/dist/tools/todo-search.js +15 -15
- package/dist/tools/todo-search.js.map +1 -1
- package/dist/tools/todos.d.ts +13 -13
- package/dist/tools/todos.d.ts.map +1 -1
- package/dist/tools/todos.js +37 -37
- package/dist/tools/todos.js.map +1 -1
- package/dist/types/openclaw-api.d.ts +2 -2
- package/dist/types/openclaw-api.d.ts.map +1 -1
- package/dist/utils/auto-linker.d.ts +2 -2
- package/dist/utils/auto-linker.d.ts.map +1 -1
- package/dist/utils/auto-linker.js +40 -40
- package/dist/utils/auto-linker.js.map +1 -1
- package/dist/utils/injection-log-rate-limiter.d.ts +1 -1
- package/dist/utils/injection-log-rate-limiter.js +1 -1
- package/dist/utils/nominatim.d.ts +1 -1
- package/dist/utils/nominatim.d.ts.map +1 -1
- package/dist/utils/nominatim.js +1 -1
- package/dist/utils/nominatim.js.map +1 -1
- package/openclaw.plugin.json +40 -4
- package/package.json +2 -2
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { ZodError } from 'zod';
|
|
11
11
|
import { createApiClient } from './api-client.js';
|
|
12
|
-
import { redactConfig, resolveConfigSecretsSync, validateRawConfig } from './config.js';
|
|
12
|
+
import { redactConfig, resolveConfigSecretsSync, resolveNamespaceConfig, validateRawConfig } from './config.js';
|
|
13
13
|
import { extractContext, getUserScopeKey } from './context.js';
|
|
14
14
|
import { createOAuthGatewayMethods, registerOAuthGatewayRpcMethods } from './gateway/oauth-rpc-methods.js';
|
|
15
15
|
import { createGatewayMethods, registerGatewayRpcMethods } from './gateway/rpc-methods.js';
|
|
@@ -40,6 +40,37 @@ function toAgentToolResult(result) {
|
|
|
40
40
|
content: [{ type: 'text', text: `Error: ${errorText}` }],
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
/** Namespace property for store/create tools (Issue #1428) */
|
|
44
|
+
const namespaceProperty = {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Target namespace for this operation. Defaults to the agent\'s configured namespace.',
|
|
47
|
+
pattern: '^[a-z0-9][a-z0-9._-]*$',
|
|
48
|
+
maxLength: 63,
|
|
49
|
+
};
|
|
50
|
+
/** Namespaces property for query/list tools (Issue #1428) */
|
|
51
|
+
const namespacesProperty = {
|
|
52
|
+
type: 'array',
|
|
53
|
+
description: 'Namespaces to search. Defaults to the agent\'s configured recall namespaces.',
|
|
54
|
+
items: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
pattern: '^[a-z0-9][a-z0-9._-]*$',
|
|
57
|
+
maxLength: 63,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
/** Add namespace param to a store/create tool schema (Issue #1428) */
|
|
61
|
+
function withNamespace(schema) {
|
|
62
|
+
return {
|
|
63
|
+
...schema,
|
|
64
|
+
properties: { ...schema.properties, namespace: namespaceProperty },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Add namespaces param to a query/list tool schema (Issue #1428) */
|
|
68
|
+
function withNamespaces(schema) {
|
|
69
|
+
return {
|
|
70
|
+
...schema,
|
|
71
|
+
properties: { ...schema.properties, namespaces: namespacesProperty },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
43
74
|
/**
|
|
44
75
|
* Memory recall tool JSON Schema
|
|
45
76
|
*/
|
|
@@ -186,7 +217,7 @@ const memoryStoreSchema = {
|
|
|
186
217
|
const memoryForgetSchema = {
|
|
187
218
|
type: 'object',
|
|
188
219
|
properties: {
|
|
189
|
-
|
|
220
|
+
memory_id: {
|
|
190
221
|
type: 'string',
|
|
191
222
|
description: 'ID of the memory to forget',
|
|
192
223
|
format: 'uuid',
|
|
@@ -224,13 +255,13 @@ const projectListSchema = {
|
|
|
224
255
|
const projectGetSchema = {
|
|
225
256
|
type: 'object',
|
|
226
257
|
properties: {
|
|
227
|
-
|
|
258
|
+
project_id: {
|
|
228
259
|
type: 'string',
|
|
229
260
|
description: 'Project ID to retrieve',
|
|
230
261
|
format: 'uuid',
|
|
231
262
|
},
|
|
232
263
|
},
|
|
233
|
-
required: ['
|
|
264
|
+
required: ['project_id'],
|
|
234
265
|
};
|
|
235
266
|
/**
|
|
236
267
|
* Project create tool JSON Schema
|
|
@@ -264,7 +295,7 @@ const projectCreateSchema = {
|
|
|
264
295
|
const todoListSchema = {
|
|
265
296
|
type: 'object',
|
|
266
297
|
properties: {
|
|
267
|
-
|
|
298
|
+
project_id: {
|
|
268
299
|
type: 'string',
|
|
269
300
|
description: 'Filter by project ID',
|
|
270
301
|
format: 'uuid',
|
|
@@ -305,7 +336,7 @@ const todoCreateSchema = {
|
|
|
305
336
|
description: 'Todo description',
|
|
306
337
|
maxLength: 5000,
|
|
307
338
|
},
|
|
308
|
-
|
|
339
|
+
project_id: {
|
|
309
340
|
type: 'string',
|
|
310
341
|
description: 'Project to add the todo to',
|
|
311
342
|
format: 'uuid',
|
|
@@ -455,13 +486,13 @@ const contactSearchSchema = {
|
|
|
455
486
|
const contactGetSchema = {
|
|
456
487
|
type: 'object',
|
|
457
488
|
properties: {
|
|
458
|
-
|
|
489
|
+
contact_id: {
|
|
459
490
|
type: 'string',
|
|
460
491
|
description: 'Contact ID to retrieve',
|
|
461
492
|
format: 'uuid',
|
|
462
493
|
},
|
|
463
494
|
},
|
|
464
|
-
required: ['
|
|
495
|
+
required: ['contact_id'],
|
|
465
496
|
};
|
|
466
497
|
/**
|
|
467
498
|
* Contact create tool JSON Schema
|
|
@@ -509,7 +540,7 @@ const smsSendSchema = {
|
|
|
509
540
|
minLength: 1,
|
|
510
541
|
maxLength: 1600,
|
|
511
542
|
},
|
|
512
|
-
|
|
543
|
+
idempotency_key: {
|
|
513
544
|
type: 'string',
|
|
514
545
|
description: 'Optional key to prevent duplicate sends',
|
|
515
546
|
},
|
|
@@ -538,15 +569,15 @@ const emailSendSchema = {
|
|
|
538
569
|
description: 'Plain text email body',
|
|
539
570
|
minLength: 1,
|
|
540
571
|
},
|
|
541
|
-
|
|
572
|
+
html_body: {
|
|
542
573
|
type: 'string',
|
|
543
574
|
description: 'Optional HTML email body',
|
|
544
575
|
},
|
|
545
|
-
|
|
576
|
+
thread_id: {
|
|
546
577
|
type: 'string',
|
|
547
578
|
description: 'Optional thread ID for replies',
|
|
548
579
|
},
|
|
549
|
-
|
|
580
|
+
idempotency_key: {
|
|
550
581
|
type: 'string',
|
|
551
582
|
description: 'Optional unique key to prevent duplicate sends',
|
|
552
583
|
},
|
|
@@ -570,7 +601,7 @@ const messageSearchSchema = {
|
|
|
570
601
|
enum: ['sms', 'email', 'all'],
|
|
571
602
|
default: 'all',
|
|
572
603
|
},
|
|
573
|
-
|
|
604
|
+
contact_id: {
|
|
574
605
|
type: 'string',
|
|
575
606
|
description: 'Filter by contact ID',
|
|
576
607
|
format: 'uuid',
|
|
@@ -582,7 +613,7 @@ const messageSearchSchema = {
|
|
|
582
613
|
maximum: 100,
|
|
583
614
|
default: 10,
|
|
584
615
|
},
|
|
585
|
-
|
|
616
|
+
include_thread: {
|
|
586
617
|
type: 'boolean',
|
|
587
618
|
description: 'Include full thread context',
|
|
588
619
|
default: false,
|
|
@@ -601,7 +632,7 @@ const threadListSchema = {
|
|
|
601
632
|
description: 'Filter by channel type',
|
|
602
633
|
enum: ['sms', 'email'],
|
|
603
634
|
},
|
|
604
|
-
|
|
635
|
+
contact_id: {
|
|
605
636
|
type: 'string',
|
|
606
637
|
description: 'Filter by contact ID',
|
|
607
638
|
format: 'uuid',
|
|
@@ -621,11 +652,11 @@ const threadListSchema = {
|
|
|
621
652
|
const threadGetSchema = {
|
|
622
653
|
type: 'object',
|
|
623
654
|
properties: {
|
|
624
|
-
|
|
655
|
+
thread_id: {
|
|
625
656
|
type: 'string',
|
|
626
657
|
description: 'Thread ID to retrieve',
|
|
627
658
|
},
|
|
628
|
-
|
|
659
|
+
message_limit: {
|
|
629
660
|
type: 'integer',
|
|
630
661
|
description: 'Maximum messages to return',
|
|
631
662
|
minimum: 1,
|
|
@@ -633,7 +664,7 @@ const threadGetSchema = {
|
|
|
633
664
|
default: 50,
|
|
634
665
|
},
|
|
635
666
|
},
|
|
636
|
-
required: ['
|
|
667
|
+
required: ['thread_id'],
|
|
637
668
|
};
|
|
638
669
|
/**
|
|
639
670
|
* Relationship set tool JSON Schema
|
|
@@ -693,25 +724,25 @@ const relationshipQuerySchema = {
|
|
|
693
724
|
const fileShareSchema = {
|
|
694
725
|
type: 'object',
|
|
695
726
|
properties: {
|
|
696
|
-
|
|
727
|
+
file_id: {
|
|
697
728
|
type: 'string',
|
|
698
729
|
description: 'The file ID to create a share link for',
|
|
699
730
|
format: 'uuid',
|
|
700
731
|
},
|
|
701
|
-
|
|
732
|
+
expires_in: {
|
|
702
733
|
type: 'integer',
|
|
703
734
|
description: 'Link expiry time in seconds (default: 3600, max: 604800)',
|
|
704
735
|
minimum: 60,
|
|
705
736
|
maximum: 604800,
|
|
706
737
|
default: 3600,
|
|
707
738
|
},
|
|
708
|
-
|
|
739
|
+
max_downloads: {
|
|
709
740
|
type: 'integer',
|
|
710
741
|
description: 'Optional maximum number of downloads',
|
|
711
742
|
minimum: 1,
|
|
712
743
|
},
|
|
713
744
|
},
|
|
714
|
-
required: ['
|
|
745
|
+
required: ['file_id'],
|
|
715
746
|
};
|
|
716
747
|
/**
|
|
717
748
|
* Skill store put tool JSON Schema
|
|
@@ -1110,11 +1141,126 @@ const linksRemoveSchema = {
|
|
|
1110
1141
|
},
|
|
1111
1142
|
required: ['source_type', 'source_id', 'target_type', 'target_ref'],
|
|
1112
1143
|
};
|
|
1144
|
+
// Prompt template tool schemas (Epic #1497, Issue #1499)
|
|
1145
|
+
const promptTemplateListSchema = {
|
|
1146
|
+
type: 'object',
|
|
1147
|
+
properties: {
|
|
1148
|
+
channel_type: { type: 'string', description: 'Filter by channel type: sms, email, ha_observation, general', enum: ['sms', 'email', 'ha_observation', 'general'] },
|
|
1149
|
+
limit: { type: 'number', description: 'Max results to return (default 20)', minimum: 1, maximum: 100 },
|
|
1150
|
+
offset: { type: 'number', description: 'Pagination offset (default 0)', minimum: 0 },
|
|
1151
|
+
},
|
|
1152
|
+
required: [],
|
|
1153
|
+
};
|
|
1154
|
+
const promptTemplateGetSchema = {
|
|
1155
|
+
type: 'object',
|
|
1156
|
+
properties: {
|
|
1157
|
+
id: { type: 'string', description: 'ID of the prompt template to retrieve' },
|
|
1158
|
+
},
|
|
1159
|
+
required: ['id'],
|
|
1160
|
+
};
|
|
1161
|
+
const promptTemplateCreateSchema = {
|
|
1162
|
+
type: 'object',
|
|
1163
|
+
properties: {
|
|
1164
|
+
label: { type: 'string', description: 'Human-readable name for the template', minLength: 1 },
|
|
1165
|
+
content: { type: 'string', description: 'The prompt text', minLength: 1 },
|
|
1166
|
+
channel_type: { type: 'string', description: 'Channel type: sms, email, ha_observation, general', enum: ['sms', 'email', 'ha_observation', 'general'] },
|
|
1167
|
+
is_default: { type: 'boolean', description: 'Whether this is the default template for its channel type' },
|
|
1168
|
+
},
|
|
1169
|
+
required: ['label', 'content', 'channel_type'],
|
|
1170
|
+
};
|
|
1171
|
+
const promptTemplateUpdateSchema = {
|
|
1172
|
+
type: 'object',
|
|
1173
|
+
properties: {
|
|
1174
|
+
id: { type: 'string', description: 'ID of the prompt template to update' },
|
|
1175
|
+
label: { type: 'string', description: 'New label' },
|
|
1176
|
+
content: { type: 'string', description: 'New prompt text' },
|
|
1177
|
+
channel_type: { type: 'string', description: 'New channel type', enum: ['sms', 'email', 'ha_observation', 'general'] },
|
|
1178
|
+
is_default: { type: 'boolean', description: 'Set as default for channel type' },
|
|
1179
|
+
},
|
|
1180
|
+
required: ['id'],
|
|
1181
|
+
};
|
|
1182
|
+
const promptTemplateDeleteSchema = {
|
|
1183
|
+
type: 'object',
|
|
1184
|
+
properties: {
|
|
1185
|
+
id: { type: 'string', description: 'ID of the prompt template to delete (soft-delete)' },
|
|
1186
|
+
},
|
|
1187
|
+
required: ['id'],
|
|
1188
|
+
};
|
|
1189
|
+
// ── Inbound Destination schemas (Issue #1500) ──────────────
|
|
1190
|
+
const inboundDestinationListSchema = {
|
|
1191
|
+
type: 'object',
|
|
1192
|
+
properties: {
|
|
1193
|
+
channel_type: { type: 'string', description: 'Filter by channel type (sms, email)' },
|
|
1194
|
+
search: { type: 'string', description: 'Search by address or display name' },
|
|
1195
|
+
limit: { type: 'number', description: 'Max results (default 50, max 100)' },
|
|
1196
|
+
offset: { type: 'number', description: 'Offset for pagination' },
|
|
1197
|
+
},
|
|
1198
|
+
};
|
|
1199
|
+
const inboundDestinationGetSchema = {
|
|
1200
|
+
type: 'object',
|
|
1201
|
+
properties: {
|
|
1202
|
+
id: { type: 'string', description: 'ID of the inbound destination' },
|
|
1203
|
+
},
|
|
1204
|
+
required: ['id'],
|
|
1205
|
+
};
|
|
1206
|
+
const inboundDestinationUpdateSchema = {
|
|
1207
|
+
type: 'object',
|
|
1208
|
+
properties: {
|
|
1209
|
+
id: { type: 'string', description: 'ID of the inbound destination to update' },
|
|
1210
|
+
display_name: { type: 'string', description: 'Human-readable display name' },
|
|
1211
|
+
agent_id: { type: 'string', description: 'Agent ID for routing (null to clear)' },
|
|
1212
|
+
prompt_template_id: { type: 'string', description: 'Prompt template ID for routing (null to clear)' },
|
|
1213
|
+
context_id: { type: 'string', description: 'Context ID for routing (null to clear)' },
|
|
1214
|
+
},
|
|
1215
|
+
required: ['id'],
|
|
1216
|
+
};
|
|
1217
|
+
// ── Channel Default schemas (Issue #1501) ──────────────────
|
|
1218
|
+
const channelDefaultListSchema = {
|
|
1219
|
+
type: 'object',
|
|
1220
|
+
properties: {},
|
|
1221
|
+
};
|
|
1222
|
+
const channelDefaultGetSchema = {
|
|
1223
|
+
type: 'object',
|
|
1224
|
+
properties: {
|
|
1225
|
+
channel_type: { type: 'string', description: 'Channel type: sms, email, or ha_observation' },
|
|
1226
|
+
},
|
|
1227
|
+
required: ['channel_type'],
|
|
1228
|
+
};
|
|
1229
|
+
const channelDefaultSetSchema = {
|
|
1230
|
+
type: 'object',
|
|
1231
|
+
properties: {
|
|
1232
|
+
channel_type: { type: 'string', description: 'Channel type: sms, email, or ha_observation' },
|
|
1233
|
+
agent_id: { type: 'string', description: 'Agent ID for this channel type' },
|
|
1234
|
+
prompt_template_id: { type: 'string', description: 'Prompt template ID (optional)' },
|
|
1235
|
+
context_id: { type: 'string', description: 'Context ID (optional)' },
|
|
1236
|
+
},
|
|
1237
|
+
required: ['channel_type', 'agent_id'],
|
|
1238
|
+
};
|
|
1113
1239
|
/**
|
|
1114
1240
|
* Create tool execution handlers
|
|
1115
1241
|
*/
|
|
1116
1242
|
function createToolHandlers(state) {
|
|
1117
|
-
const { config, logger, apiClient,
|
|
1243
|
+
const { config, logger, apiClient, user_id, resolvedNamespace } = state;
|
|
1244
|
+
/**
|
|
1245
|
+
* Get the effective namespace for a store/create operation.
|
|
1246
|
+
* Uses explicit tool param if provided, otherwise falls back to config default.
|
|
1247
|
+
*/
|
|
1248
|
+
function getStoreNamespace(params) {
|
|
1249
|
+
const ns = params.namespace;
|
|
1250
|
+
if (typeof ns === 'string' && ns.length > 0)
|
|
1251
|
+
return ns;
|
|
1252
|
+
return resolvedNamespace.default;
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Get the effective namespaces for a query/list operation.
|
|
1256
|
+
* Uses explicit tool param if provided, otherwise falls back to config recall list.
|
|
1257
|
+
*/
|
|
1258
|
+
function getRecallNamespaces(params) {
|
|
1259
|
+
const ns = params.namespaces;
|
|
1260
|
+
if (Array.isArray(ns) && ns.length > 0)
|
|
1261
|
+
return ns;
|
|
1262
|
+
return resolvedNamespace.recall;
|
|
1263
|
+
}
|
|
1118
1264
|
return {
|
|
1119
1265
|
async memory_recall(params) {
|
|
1120
1266
|
const { query, limit = config.maxRecallMemories, category, tags, relationship_id, location, location_radius_km, location_weight, } = params;
|
|
@@ -1128,7 +1274,11 @@ function createToolHandlers(state) {
|
|
|
1128
1274
|
queryParams.set('tags', tags.join(','));
|
|
1129
1275
|
if (relationship_id)
|
|
1130
1276
|
queryParams.set('relationship_id', relationship_id);
|
|
1131
|
-
|
|
1277
|
+
// Namespace scoping (Issue #1428)
|
|
1278
|
+
const ns = getRecallNamespaces(params);
|
|
1279
|
+
if (ns.length > 0)
|
|
1280
|
+
queryParams.set('namespaces', ns.join(','));
|
|
1281
|
+
const response = await apiClient.get(`/api/memories/search?${queryParams}`, { user_id });
|
|
1132
1282
|
if (!response.success) {
|
|
1133
1283
|
return { success: false, error: response.error.message };
|
|
1134
1284
|
}
|
|
@@ -1167,7 +1317,7 @@ function createToolHandlers(state) {
|
|
|
1167
1317
|
success: true,
|
|
1168
1318
|
data: {
|
|
1169
1319
|
content,
|
|
1170
|
-
details: { count: memories.length, memories,
|
|
1320
|
+
details: { count: memories.length, memories, user_id },
|
|
1171
1321
|
},
|
|
1172
1322
|
};
|
|
1173
1323
|
}
|
|
@@ -1190,6 +1340,7 @@ function createToolHandlers(state) {
|
|
|
1190
1340
|
content: memoryText,
|
|
1191
1341
|
memory_type: category === 'entity' ? 'reference' : category === 'other' ? 'note' : category,
|
|
1192
1342
|
importance,
|
|
1343
|
+
namespace: getStoreNamespace(params), // Issue #1428
|
|
1193
1344
|
};
|
|
1194
1345
|
if (tags && tags.length > 0)
|
|
1195
1346
|
payload.tags = tags;
|
|
@@ -1203,8 +1354,8 @@ function createToolHandlers(state) {
|
|
|
1203
1354
|
const geocoded = await reverseGeocode(location.lat, location.lng, config.nominatimUrl);
|
|
1204
1355
|
if (geocoded) {
|
|
1205
1356
|
payload.address = geocoded.address;
|
|
1206
|
-
if (!location.place_label && geocoded.
|
|
1207
|
-
payload.place_label = geocoded.
|
|
1357
|
+
if (!location.place_label && geocoded.place_label) {
|
|
1358
|
+
payload.place_label = geocoded.place_label;
|
|
1208
1359
|
}
|
|
1209
1360
|
}
|
|
1210
1361
|
}
|
|
@@ -1213,7 +1364,7 @@ function createToolHandlers(state) {
|
|
|
1213
1364
|
if (location.place_label)
|
|
1214
1365
|
payload.place_label = location.place_label;
|
|
1215
1366
|
}
|
|
1216
|
-
const response = await apiClient.post('/api/memories/unified', payload, {
|
|
1367
|
+
const response = await apiClient.post('/api/memories/unified', payload, { user_id });
|
|
1217
1368
|
if (!response.success) {
|
|
1218
1369
|
return { success: false, error: response.error.message };
|
|
1219
1370
|
}
|
|
@@ -1231,22 +1382,26 @@ function createToolHandlers(state) {
|
|
|
1231
1382
|
}
|
|
1232
1383
|
},
|
|
1233
1384
|
async memory_forget(params) {
|
|
1234
|
-
const {
|
|
1385
|
+
const { memory_id, query } = params;
|
|
1235
1386
|
try {
|
|
1236
|
-
if (
|
|
1237
|
-
const response = await apiClient.delete(`/api/memories/${
|
|
1387
|
+
if (memory_id) {
|
|
1388
|
+
const response = await apiClient.delete(`/api/memories/${memory_id}`, { user_id });
|
|
1238
1389
|
if (!response.success) {
|
|
1239
1390
|
return { success: false, error: response.error.message };
|
|
1240
1391
|
}
|
|
1241
1392
|
return {
|
|
1242
1393
|
success: true,
|
|
1243
|
-
data: { content: `Memory ${
|
|
1394
|
+
data: { content: `Memory ${memory_id} forgotten successfully` },
|
|
1244
1395
|
};
|
|
1245
1396
|
}
|
|
1246
1397
|
if (query) {
|
|
1247
1398
|
// Match OpenClaw gateway memory_forget behavior:
|
|
1248
1399
|
// Search → single high-confidence match auto-deletes, multiple returns candidates.
|
|
1249
|
-
const
|
|
1400
|
+
const forgetQp = new URLSearchParams({ q: query, limit: '5' });
|
|
1401
|
+
const forgetNs = getRecallNamespaces(params);
|
|
1402
|
+
if (forgetNs.length > 0)
|
|
1403
|
+
forgetQp.set('namespaces', forgetNs.join(','));
|
|
1404
|
+
const searchResponse = await apiClient.get(`/api/memories/search?${forgetQp}`, { user_id });
|
|
1250
1405
|
if (!searchResponse.success) {
|
|
1251
1406
|
return { success: false, error: searchResponse.error.message };
|
|
1252
1407
|
}
|
|
@@ -1259,7 +1414,7 @@ function createToolHandlers(state) {
|
|
|
1259
1414
|
}
|
|
1260
1415
|
// Single high-confidence match → auto-delete
|
|
1261
1416
|
if (matches.length === 1 && (matches[0].similarity ?? 0) > 0.9) {
|
|
1262
|
-
const delResponse = await apiClient.delete(`/api/memories/${matches[0].id}`, {
|
|
1417
|
+
const delResponse = await apiClient.delete(`/api/memories/${matches[0].id}`, { user_id });
|
|
1263
1418
|
if (!delResponse.success) {
|
|
1264
1419
|
return { success: false, error: delResponse.error.message };
|
|
1265
1420
|
}
|
|
@@ -1276,12 +1431,12 @@ function createToolHandlers(state) {
|
|
|
1276
1431
|
return {
|
|
1277
1432
|
success: true,
|
|
1278
1433
|
data: {
|
|
1279
|
-
content: `Found ${matches.length} candidates. Specify
|
|
1434
|
+
content: `Found ${matches.length} candidates. Specify memory_id:\n${list}`,
|
|
1280
1435
|
details: { action: 'candidates', candidates: matches.map((m) => ({ id: m.id, content: m.content, similarity: m.similarity })) },
|
|
1281
1436
|
},
|
|
1282
1437
|
};
|
|
1283
1438
|
}
|
|
1284
|
-
return { success: false, error: 'Either
|
|
1439
|
+
return { success: false, error: 'Either memory_id or query is required' };
|
|
1285
1440
|
}
|
|
1286
1441
|
catch (error) {
|
|
1287
1442
|
logger.error('memory_forget failed', { error });
|
|
@@ -1294,8 +1449,12 @@ function createToolHandlers(state) {
|
|
|
1294
1449
|
const queryParams = new URLSearchParams({ item_type: 'project', limit: String(limit) });
|
|
1295
1450
|
if (status !== 'all')
|
|
1296
1451
|
queryParams.set('status', status);
|
|
1297
|
-
queryParams.set('user_email',
|
|
1298
|
-
|
|
1452
|
+
queryParams.set('user_email', user_id); // Issue #1172: scope by user
|
|
1453
|
+
// Namespace scoping (Issue #1428)
|
|
1454
|
+
const projListNs = getRecallNamespaces(params);
|
|
1455
|
+
if (projListNs.length > 0)
|
|
1456
|
+
queryParams.set('namespaces', projListNs.join(','));
|
|
1457
|
+
const response = await apiClient.get(`/api/work-items?${queryParams}`, { user_id });
|
|
1299
1458
|
if (!response.success) {
|
|
1300
1459
|
return { success: false, error: response.error.message };
|
|
1301
1460
|
}
|
|
@@ -1312,9 +1471,9 @@ function createToolHandlers(state) {
|
|
|
1312
1471
|
}
|
|
1313
1472
|
},
|
|
1314
1473
|
async project_get(params) {
|
|
1315
|
-
const {
|
|
1474
|
+
const { project_id } = params;
|
|
1316
1475
|
try {
|
|
1317
|
-
const response = await apiClient.get(`/api/work-items/${
|
|
1476
|
+
const response = await apiClient.get(`/api/work-items/${project_id}?user_email=${encodeURIComponent(user_id)}`, { user_id });
|
|
1318
1477
|
if (!response.success) {
|
|
1319
1478
|
return { success: false, error: response.error.message };
|
|
1320
1479
|
}
|
|
@@ -1335,7 +1494,7 @@ function createToolHandlers(state) {
|
|
|
1335
1494
|
async project_create(params) {
|
|
1336
1495
|
const { name, description, status = 'active', } = params;
|
|
1337
1496
|
try {
|
|
1338
|
-
const response = await apiClient.post('/api/work-items', { title: name, description, item_type: 'project', status, user_email:
|
|
1497
|
+
const response = await apiClient.post('/api/work-items', { title: name, description, item_type: 'project', status, user_email: user_id, namespace: getStoreNamespace(params) }, { user_id });
|
|
1339
1498
|
if (!response.success) {
|
|
1340
1499
|
return { success: false, error: response.error.message };
|
|
1341
1500
|
}
|
|
@@ -1353,20 +1512,24 @@ function createToolHandlers(state) {
|
|
|
1353
1512
|
}
|
|
1354
1513
|
},
|
|
1355
1514
|
async todo_list(params) {
|
|
1356
|
-
const {
|
|
1515
|
+
const { project_id, completed, limit = 50, offset = 0, } = params;
|
|
1357
1516
|
try {
|
|
1358
1517
|
const queryParams = new URLSearchParams({
|
|
1359
1518
|
item_type: 'task',
|
|
1360
1519
|
limit: String(limit),
|
|
1361
1520
|
offset: String(offset),
|
|
1362
|
-
user_email:
|
|
1521
|
+
user_email: user_id, // Issue #1172: scope by user
|
|
1363
1522
|
});
|
|
1364
|
-
if (
|
|
1365
|
-
queryParams.set('parent_work_item_id',
|
|
1523
|
+
if (project_id)
|
|
1524
|
+
queryParams.set('parent_work_item_id', project_id);
|
|
1366
1525
|
if (completed !== undefined) {
|
|
1367
1526
|
queryParams.set('status', completed ? 'completed' : 'active');
|
|
1368
1527
|
}
|
|
1369
|
-
|
|
1528
|
+
// Namespace scoping (Issue #1428)
|
|
1529
|
+
const todoListNs = getRecallNamespaces(params);
|
|
1530
|
+
if (todoListNs.length > 0)
|
|
1531
|
+
queryParams.set('namespaces', todoListNs.join(','));
|
|
1532
|
+
const response = await apiClient.get(`/api/work-items?${queryParams}`, { user_id });
|
|
1370
1533
|
if (!response.success) {
|
|
1371
1534
|
return { success: false, error: response.error.message };
|
|
1372
1535
|
}
|
|
@@ -1396,14 +1559,14 @@ function createToolHandlers(state) {
|
|
|
1396
1559
|
}
|
|
1397
1560
|
},
|
|
1398
1561
|
async todo_create(params) {
|
|
1399
|
-
const { title, description,
|
|
1562
|
+
const { title, description, project_id, priority = 'medium', dueDate, } = params;
|
|
1400
1563
|
try {
|
|
1401
|
-
const body = { title, description, item_type: 'task', priority, user_email:
|
|
1402
|
-
if (
|
|
1403
|
-
body.parent_work_item_id =
|
|
1564
|
+
const body = { title, description, item_type: 'task', priority, user_email: user_id, namespace: getStoreNamespace(params) };
|
|
1565
|
+
if (project_id)
|
|
1566
|
+
body.parent_work_item_id = project_id;
|
|
1404
1567
|
if (dueDate)
|
|
1405
1568
|
body.not_after = dueDate;
|
|
1406
|
-
const response = await apiClient.post('/api/work-items', body, {
|
|
1569
|
+
const response = await apiClient.post('/api/work-items', body, { user_id });
|
|
1407
1570
|
if (!response.success) {
|
|
1408
1571
|
return { success: false, error: response.error.message };
|
|
1409
1572
|
}
|
|
@@ -1423,7 +1586,7 @@ function createToolHandlers(state) {
|
|
|
1423
1586
|
async todo_complete(params) {
|
|
1424
1587
|
const { todoId } = params;
|
|
1425
1588
|
try {
|
|
1426
|
-
const response = await apiClient.patch(`/api/work-items/${todoId}/status?user_email=${encodeURIComponent(
|
|
1589
|
+
const response = await apiClient.patch(`/api/work-items/${todoId}/status?user_email=${encodeURIComponent(user_id)}`, { status: 'completed' }, { user_id });
|
|
1427
1590
|
if (!response.success) {
|
|
1428
1591
|
return { success: false, error: response.error.message };
|
|
1429
1592
|
}
|
|
@@ -1450,9 +1613,13 @@ function createToolHandlers(state) {
|
|
|
1450
1613
|
types: 'work_item',
|
|
1451
1614
|
limit: String(fetchLimit),
|
|
1452
1615
|
semantic: 'true',
|
|
1453
|
-
user_email:
|
|
1616
|
+
user_email: user_id, // Issue #1216: scope results to current user
|
|
1454
1617
|
});
|
|
1455
|
-
|
|
1618
|
+
// Namespace scoping (Issue #1428)
|
|
1619
|
+
const todoSearchNs = getRecallNamespaces(params);
|
|
1620
|
+
if (todoSearchNs.length > 0)
|
|
1621
|
+
queryParams.set('namespaces', todoSearchNs.join(','));
|
|
1622
|
+
const response = await apiClient.get(`/api/search?${queryParams}`, { user_id });
|
|
1456
1623
|
if (!response.success) {
|
|
1457
1624
|
return { success: false, error: response.error.message };
|
|
1458
1625
|
}
|
|
@@ -1470,7 +1637,7 @@ function createToolHandlers(state) {
|
|
|
1470
1637
|
success: true,
|
|
1471
1638
|
data: {
|
|
1472
1639
|
content: 'No matching work items found.',
|
|
1473
|
-
details: { count: 0, results: [],
|
|
1640
|
+
details: { count: 0, results: [], search_type: response.data.search_type },
|
|
1474
1641
|
},
|
|
1475
1642
|
};
|
|
1476
1643
|
}
|
|
@@ -1496,7 +1663,7 @@ function createToolHandlers(state) {
|
|
|
1496
1663
|
kind: r.metadata?.kind,
|
|
1497
1664
|
status: r.metadata?.status,
|
|
1498
1665
|
})),
|
|
1499
|
-
|
|
1666
|
+
search_type: response.data.search_type,
|
|
1500
1667
|
},
|
|
1501
1668
|
},
|
|
1502
1669
|
};
|
|
@@ -1507,25 +1674,28 @@ function createToolHandlers(state) {
|
|
|
1507
1674
|
}
|
|
1508
1675
|
},
|
|
1509
1676
|
async project_search(params) {
|
|
1510
|
-
const tool = createProjectSearchTool({ client: apiClient, logger, config,
|
|
1677
|
+
const tool = createProjectSearchTool({ client: apiClient, logger, config, user_id });
|
|
1511
1678
|
return tool.execute(params);
|
|
1512
1679
|
},
|
|
1513
1680
|
async context_search(params) {
|
|
1514
|
-
const tool = createContextSearchTool({ client: apiClient, logger, config,
|
|
1681
|
+
const tool = createContextSearchTool({ client: apiClient, logger, config, user_id });
|
|
1515
1682
|
return tool.execute(params);
|
|
1516
1683
|
},
|
|
1517
1684
|
async contact_search(params) {
|
|
1518
1685
|
const { query, limit = 10 } = params;
|
|
1519
1686
|
try {
|
|
1520
|
-
const queryParams = new URLSearchParams({ search: query, limit: String(limit), user_email:
|
|
1687
|
+
const queryParams = new URLSearchParams({ search: query, limit: String(limit), user_email: user_id });
|
|
1688
|
+
const contactSearchNs = getRecallNamespaces(params);
|
|
1689
|
+
if (contactSearchNs.length > 0)
|
|
1690
|
+
queryParams.set('namespaces', contactSearchNs.join(','));
|
|
1521
1691
|
const response = await apiClient.get(`/api/contacts?${queryParams}`, {
|
|
1522
|
-
|
|
1692
|
+
user_id,
|
|
1523
1693
|
});
|
|
1524
1694
|
if (!response.success) {
|
|
1525
1695
|
return { success: false, error: response.error.message };
|
|
1526
1696
|
}
|
|
1527
1697
|
const contacts = response.data.contacts ?? [];
|
|
1528
|
-
const content = contacts.length > 0 ? contacts.map((c) => `- ${c.
|
|
1698
|
+
const content = contacts.length > 0 ? contacts.map((c) => `- ${c.display_name}${c.email ? ` (${c.email})` : ''}`).join('\n') : 'No contacts found.';
|
|
1529
1699
|
return {
|
|
1530
1700
|
success: true,
|
|
1531
1701
|
data: { content, details: { count: contacts.length, contacts } },
|
|
@@ -1537,9 +1707,9 @@ function createToolHandlers(state) {
|
|
|
1537
1707
|
}
|
|
1538
1708
|
},
|
|
1539
1709
|
async contact_get(params) {
|
|
1540
|
-
const {
|
|
1710
|
+
const { contact_id } = params;
|
|
1541
1711
|
try {
|
|
1542
|
-
const response = await apiClient.get(`/api/contacts/${
|
|
1712
|
+
const response = await apiClient.get(`/api/contacts/${contact_id}?user_email=${encodeURIComponent(user_id)}`, { user_id });
|
|
1543
1713
|
if (!response.success) {
|
|
1544
1714
|
return { success: false, error: response.error.message };
|
|
1545
1715
|
}
|
|
@@ -1564,8 +1734,8 @@ function createToolHandlers(state) {
|
|
|
1564
1734
|
async contact_create(params) {
|
|
1565
1735
|
const { name, notes } = params;
|
|
1566
1736
|
try {
|
|
1567
|
-
// API requires
|
|
1568
|
-
const response = await apiClient.post('/api/contacts', {
|
|
1737
|
+
// API requires display_name, not name. Email/phone are stored as separate contact_endpoint records.
|
|
1738
|
+
const response = await apiClient.post('/api/contacts', { display_name: name, notes, user_email: user_id, namespace: getStoreNamespace(params) }, { user_id });
|
|
1569
1739
|
if (!response.success) {
|
|
1570
1740
|
return { success: false, error: response.error.message };
|
|
1571
1741
|
}
|
|
@@ -1583,7 +1753,7 @@ function createToolHandlers(state) {
|
|
|
1583
1753
|
}
|
|
1584
1754
|
},
|
|
1585
1755
|
async sms_send(params) {
|
|
1586
|
-
const { to, body,
|
|
1756
|
+
const { to, body, idempotency_key } = params;
|
|
1587
1757
|
// Check Twilio configuration
|
|
1588
1758
|
if (!config.twilioAccountSid || !config.twilioAuthToken || !config.twilioPhoneNumber) {
|
|
1589
1759
|
return {
|
|
@@ -1613,15 +1783,15 @@ function createToolHandlers(state) {
|
|
|
1613
1783
|
};
|
|
1614
1784
|
}
|
|
1615
1785
|
logger.info('sms_send invoked', {
|
|
1616
|
-
|
|
1786
|
+
user_id,
|
|
1617
1787
|
bodyLength: body.length,
|
|
1618
|
-
hasIdempotencyKey: !!
|
|
1788
|
+
hasIdempotencyKey: !!idempotency_key,
|
|
1619
1789
|
});
|
|
1620
1790
|
try {
|
|
1621
|
-
const response = await apiClient.post('/api/twilio/sms/send', { to, body,
|
|
1791
|
+
const response = await apiClient.post('/api/twilio/sms/send', { to, body, idempotency_key }, { user_id });
|
|
1622
1792
|
if (!response.success) {
|
|
1623
1793
|
logger.error('sms_send API error', {
|
|
1624
|
-
|
|
1794
|
+
user_id,
|
|
1625
1795
|
status: response.error.status,
|
|
1626
1796
|
code: response.error.code,
|
|
1627
1797
|
});
|
|
@@ -1630,23 +1800,23 @@ function createToolHandlers(state) {
|
|
|
1630
1800
|
error: response.error.message || 'Failed to send SMS',
|
|
1631
1801
|
};
|
|
1632
1802
|
}
|
|
1633
|
-
const {
|
|
1803
|
+
const { message_id, thread_id, status } = response.data;
|
|
1634
1804
|
logger.debug('sms_send completed', {
|
|
1635
|
-
|
|
1636
|
-
|
|
1805
|
+
user_id,
|
|
1806
|
+
message_id,
|
|
1637
1807
|
status,
|
|
1638
1808
|
});
|
|
1639
1809
|
return {
|
|
1640
1810
|
success: true,
|
|
1641
1811
|
data: {
|
|
1642
|
-
content: `SMS sent successfully (ID: ${
|
|
1643
|
-
details: {
|
|
1812
|
+
content: `SMS sent successfully (ID: ${message_id}, Status: ${status})`,
|
|
1813
|
+
details: { message_id, thread_id, status, user_id },
|
|
1644
1814
|
},
|
|
1645
1815
|
};
|
|
1646
1816
|
}
|
|
1647
1817
|
catch (error) {
|
|
1648
1818
|
logger.error('sms_send failed', {
|
|
1649
|
-
|
|
1819
|
+
user_id,
|
|
1650
1820
|
error: error instanceof Error ? error.message : String(error),
|
|
1651
1821
|
});
|
|
1652
1822
|
// Sanitize error message (remove phone numbers for privacy)
|
|
@@ -1661,7 +1831,7 @@ function createToolHandlers(state) {
|
|
|
1661
1831
|
}
|
|
1662
1832
|
},
|
|
1663
1833
|
async email_send(params) {
|
|
1664
|
-
const { to, subject, body,
|
|
1834
|
+
const { to, subject, body, html_body, thread_id, idempotency_key } = params;
|
|
1665
1835
|
// Validate email format
|
|
1666
1836
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1667
1837
|
if (!emailRegex.test(to)) {
|
|
@@ -1691,18 +1861,18 @@ function createToolHandlers(state) {
|
|
|
1691
1861
|
};
|
|
1692
1862
|
}
|
|
1693
1863
|
logger.info('email_send invoked', {
|
|
1694
|
-
|
|
1864
|
+
user_id,
|
|
1695
1865
|
subjectLength: subject.length,
|
|
1696
1866
|
bodyLength: body.length,
|
|
1697
|
-
hasHtmlBody: !!
|
|
1698
|
-
hasThreadId: !!
|
|
1699
|
-
hasIdempotencyKey: !!
|
|
1867
|
+
hasHtmlBody: !!html_body,
|
|
1868
|
+
hasThreadId: !!thread_id,
|
|
1869
|
+
hasIdempotencyKey: !!idempotency_key,
|
|
1700
1870
|
});
|
|
1701
1871
|
try {
|
|
1702
|
-
const response = await apiClient.post('/api/postmark/email/send', { to, subject, body,
|
|
1872
|
+
const response = await apiClient.post('/api/postmark/email/send', { to, subject, body, html_body, thread_id, idempotency_key }, { user_id });
|
|
1703
1873
|
if (!response.success) {
|
|
1704
1874
|
logger.error('email_send API error', {
|
|
1705
|
-
|
|
1875
|
+
user_id,
|
|
1706
1876
|
status: response.error.status,
|
|
1707
1877
|
code: response.error.code,
|
|
1708
1878
|
});
|
|
@@ -1711,23 +1881,23 @@ function createToolHandlers(state) {
|
|
|
1711
1881
|
error: response.error.message || 'Failed to send email',
|
|
1712
1882
|
};
|
|
1713
1883
|
}
|
|
1714
|
-
const {
|
|
1884
|
+
const { message_id, thread_id: responseThreadId, status } = response.data;
|
|
1715
1885
|
logger.debug('email_send completed', {
|
|
1716
|
-
|
|
1717
|
-
|
|
1886
|
+
user_id,
|
|
1887
|
+
message_id,
|
|
1718
1888
|
status,
|
|
1719
1889
|
});
|
|
1720
1890
|
return {
|
|
1721
1891
|
success: true,
|
|
1722
1892
|
data: {
|
|
1723
|
-
content: `Email sent successfully (ID: ${
|
|
1724
|
-
details: {
|
|
1893
|
+
content: `Email sent successfully (ID: ${message_id}, Status: ${status})`,
|
|
1894
|
+
details: { message_id, thread_id: responseThreadId, status, user_id },
|
|
1725
1895
|
},
|
|
1726
1896
|
};
|
|
1727
1897
|
}
|
|
1728
1898
|
catch (error) {
|
|
1729
1899
|
logger.error('email_send failed', {
|
|
1730
|
-
|
|
1900
|
+
user_id,
|
|
1731
1901
|
error: error instanceof Error ? error.message : String(error),
|
|
1732
1902
|
});
|
|
1733
1903
|
// Sanitize error message (remove email addresses for privacy)
|
|
@@ -1742,7 +1912,7 @@ function createToolHandlers(state) {
|
|
|
1742
1912
|
}
|
|
1743
1913
|
},
|
|
1744
1914
|
async message_search(params) {
|
|
1745
|
-
const { query, channel = 'all',
|
|
1915
|
+
const { query, channel = 'all', contact_id, limit = 10, include_thread = false, } = params;
|
|
1746
1916
|
// Validate query
|
|
1747
1917
|
if (!query || query.length === 0) {
|
|
1748
1918
|
return {
|
|
@@ -1751,12 +1921,12 @@ function createToolHandlers(state) {
|
|
|
1751
1921
|
};
|
|
1752
1922
|
}
|
|
1753
1923
|
logger.info('message_search invoked', {
|
|
1754
|
-
|
|
1924
|
+
user_id,
|
|
1755
1925
|
queryLength: query.length,
|
|
1756
1926
|
channel,
|
|
1757
|
-
hasContactId: !!
|
|
1927
|
+
hasContactId: !!contact_id,
|
|
1758
1928
|
limit,
|
|
1759
|
-
|
|
1929
|
+
include_thread,
|
|
1760
1930
|
});
|
|
1761
1931
|
try {
|
|
1762
1932
|
// Build query parameters
|
|
@@ -1767,17 +1937,17 @@ function createToolHandlers(state) {
|
|
|
1767
1937
|
if (channel !== 'all') {
|
|
1768
1938
|
queryParams.set('channel', channel);
|
|
1769
1939
|
}
|
|
1770
|
-
if (
|
|
1771
|
-
queryParams.set('
|
|
1940
|
+
if (contact_id) {
|
|
1941
|
+
queryParams.set('contact_id', contact_id);
|
|
1772
1942
|
}
|
|
1773
|
-
if (
|
|
1774
|
-
queryParams.set('
|
|
1943
|
+
if (include_thread) {
|
|
1944
|
+
queryParams.set('include_thread', 'true');
|
|
1775
1945
|
}
|
|
1776
1946
|
// Unified search API: returns { results: [{ type, id, title, snippet, score, metadata }], total }
|
|
1777
|
-
const response = await apiClient.get(`/api/search?${queryParams}`, {
|
|
1947
|
+
const response = await apiClient.get(`/api/search?${queryParams}`, { user_id });
|
|
1778
1948
|
if (!response.success) {
|
|
1779
1949
|
logger.error('message_search API error', {
|
|
1780
|
-
|
|
1950
|
+
user_id,
|
|
1781
1951
|
status: response.error.status,
|
|
1782
1952
|
code: response.error.code,
|
|
1783
1953
|
});
|
|
@@ -1793,12 +1963,12 @@ function createToolHandlers(state) {
|
|
|
1793
1963
|
body: r.snippet,
|
|
1794
1964
|
direction: r.metadata?.direction || 'inbound',
|
|
1795
1965
|
channel: r.metadata?.channel || 'unknown',
|
|
1796
|
-
|
|
1966
|
+
contact_name: r.metadata?.contact_name,
|
|
1797
1967
|
timestamp: r.metadata?.received_at || '',
|
|
1798
1968
|
similarity: r.score,
|
|
1799
1969
|
}));
|
|
1800
1970
|
logger.debug('message_search completed', {
|
|
1801
|
-
|
|
1971
|
+
user_id,
|
|
1802
1972
|
resultCount: messages.length,
|
|
1803
1973
|
total,
|
|
1804
1974
|
});
|
|
@@ -1813,11 +1983,11 @@ function createToolHandlers(state) {
|
|
|
1813
1983
|
promptGuardUrl: config.promptGuardUrl,
|
|
1814
1984
|
});
|
|
1815
1985
|
if (detection.detected) {
|
|
1816
|
-
const logDecision = injectionLogLimiter.shouldLog(
|
|
1986
|
+
const logDecision = injectionLogLimiter.shouldLog(user_id);
|
|
1817
1987
|
if (logDecision.log) {
|
|
1818
1988
|
logger.warn(logDecision.summary ? 'injection detection log summary for previous window' : 'potential prompt injection detected in message_search result', {
|
|
1819
|
-
|
|
1820
|
-
|
|
1989
|
+
user_id,
|
|
1990
|
+
message_id: m.id,
|
|
1821
1991
|
patterns: detection.patterns,
|
|
1822
1992
|
source: detection.source,
|
|
1823
1993
|
...(logDecision.suppressed > 0 && { suppressedCount: logDecision.suppressed }),
|
|
@@ -1834,18 +2004,18 @@ function createToolHandlers(state) {
|
|
|
1834
2004
|
? messages
|
|
1835
2005
|
.map((m) => {
|
|
1836
2006
|
const prefix = m.direction === 'inbound' ? '←' : '→';
|
|
1837
|
-
const contact = sanitizeMetadataField(m.
|
|
2007
|
+
const contact = sanitizeMetadataField(m.contact_name || 'Unknown', nonce);
|
|
1838
2008
|
const safeChannel = sanitizeMetadataField(m.channel, nonce);
|
|
1839
2009
|
const similarity = `(${Math.round(m.similarity * 100)}%)`;
|
|
1840
2010
|
const rawBody = m.body || '';
|
|
1841
2011
|
const truncatedBody = rawBody.substring(0, 100) + (rawBody.length > 100 ? '...' : '');
|
|
1842
|
-
const
|
|
2012
|
+
const body_text = sanitizeMessageForContext(truncatedBody, {
|
|
1843
2013
|
direction: m.direction,
|
|
1844
2014
|
channel: m.channel,
|
|
1845
|
-
sender: m.
|
|
2015
|
+
sender: m.contact_name || 'Unknown',
|
|
1846
2016
|
nonce,
|
|
1847
2017
|
});
|
|
1848
|
-
return `${prefix} [${safeChannel}] ${contact} ${similarity}: ${
|
|
2018
|
+
return `${prefix} [${safeChannel}] ${contact} ${similarity}: ${body_text}`;
|
|
1849
2019
|
})
|
|
1850
2020
|
.join('\n')
|
|
1851
2021
|
: 'No messages found matching your query.';
|
|
@@ -1853,13 +2023,13 @@ function createToolHandlers(state) {
|
|
|
1853
2023
|
success: true,
|
|
1854
2024
|
data: {
|
|
1855
2025
|
content,
|
|
1856
|
-
details: { messages, total,
|
|
2026
|
+
details: { messages, total, user_id },
|
|
1857
2027
|
},
|
|
1858
2028
|
};
|
|
1859
2029
|
}
|
|
1860
2030
|
catch (error) {
|
|
1861
2031
|
logger.error('message_search failed', {
|
|
1862
|
-
|
|
2032
|
+
user_id,
|
|
1863
2033
|
error: error instanceof Error ? error.message : String(error),
|
|
1864
2034
|
});
|
|
1865
2035
|
return {
|
|
@@ -1869,11 +2039,11 @@ function createToolHandlers(state) {
|
|
|
1869
2039
|
}
|
|
1870
2040
|
},
|
|
1871
2041
|
async thread_list(params) {
|
|
1872
|
-
const { channel,
|
|
2042
|
+
const { channel, contact_id, limit = 20, } = params;
|
|
1873
2043
|
logger.info('thread_list invoked', {
|
|
1874
|
-
|
|
2044
|
+
user_id,
|
|
1875
2045
|
channel,
|
|
1876
|
-
hasContactId: !!
|
|
2046
|
+
hasContactId: !!contact_id,
|
|
1877
2047
|
limit,
|
|
1878
2048
|
});
|
|
1879
2049
|
try {
|
|
@@ -1884,13 +2054,13 @@ function createToolHandlers(state) {
|
|
|
1884
2054
|
if (channel) {
|
|
1885
2055
|
queryParams.set('channel', channel);
|
|
1886
2056
|
}
|
|
1887
|
-
if (
|
|
1888
|
-
queryParams.set('
|
|
2057
|
+
if (contact_id) {
|
|
2058
|
+
queryParams.set('contact_id', contact_id);
|
|
1889
2059
|
}
|
|
1890
|
-
const response = await apiClient.get(`/api/search?${queryParams}`, {
|
|
2060
|
+
const response = await apiClient.get(`/api/search?${queryParams}`, { user_id });
|
|
1891
2061
|
if (!response.success) {
|
|
1892
2062
|
logger.error('thread_list API error', {
|
|
1893
|
-
|
|
2063
|
+
user_id,
|
|
1894
2064
|
status: response.error.status,
|
|
1895
2065
|
code: response.error.code,
|
|
1896
2066
|
});
|
|
@@ -1903,7 +2073,7 @@ function createToolHandlers(state) {
|
|
|
1903
2073
|
const results = response.data.results ?? response.data.threads ?? [];
|
|
1904
2074
|
const total = response.data.total ?? results.length;
|
|
1905
2075
|
logger.debug('thread_list completed', {
|
|
1906
|
-
|
|
2076
|
+
user_id,
|
|
1907
2077
|
threadCount: results.length,
|
|
1908
2078
|
total,
|
|
1909
2079
|
});
|
|
@@ -1917,9 +2087,9 @@ function createToolHandlers(state) {
|
|
|
1917
2087
|
// Handle both thread and search result formats
|
|
1918
2088
|
if ('channel' in r) {
|
|
1919
2089
|
const t = r;
|
|
1920
|
-
const safeContact = sanitizeMetadataField(t.
|
|
2090
|
+
const safeContact = sanitizeMetadataField(t.contact_name || t.endpoint_value || 'Unknown', threadListNonce);
|
|
1921
2091
|
const safeChannel = sanitizeMetadataField(t.channel, threadListNonce);
|
|
1922
|
-
const msgCount = t.
|
|
2092
|
+
const msgCount = t.message_count ? `${t.message_count} message${t.message_count !== 1 ? 's' : ''}` : '';
|
|
1923
2093
|
return `[${safeChannel}] ${safeContact}${msgCount ? ` - ${msgCount}` : ''}`;
|
|
1924
2094
|
}
|
|
1925
2095
|
const safeTitle = r.title ? sanitizeMetadataField(r.title, threadListNonce) : '';
|
|
@@ -1932,13 +2102,13 @@ function createToolHandlers(state) {
|
|
|
1932
2102
|
success: true,
|
|
1933
2103
|
data: {
|
|
1934
2104
|
content,
|
|
1935
|
-
details: { threads: results, total,
|
|
2105
|
+
details: { threads: results, total, user_id },
|
|
1936
2106
|
},
|
|
1937
2107
|
};
|
|
1938
2108
|
}
|
|
1939
2109
|
catch (error) {
|
|
1940
2110
|
logger.error('thread_list failed', {
|
|
1941
|
-
|
|
2111
|
+
user_id,
|
|
1942
2112
|
error: error instanceof Error ? error.message : String(error),
|
|
1943
2113
|
});
|
|
1944
2114
|
return {
|
|
@@ -1948,27 +2118,27 @@ function createToolHandlers(state) {
|
|
|
1948
2118
|
}
|
|
1949
2119
|
},
|
|
1950
2120
|
async thread_get(params) {
|
|
1951
|
-
const {
|
|
1952
|
-
// Validate
|
|
1953
|
-
if (!
|
|
2121
|
+
const { thread_id, message_limit = 50 } = params;
|
|
2122
|
+
// Validate thread_id
|
|
2123
|
+
if (!thread_id || thread_id.length === 0) {
|
|
1954
2124
|
return {
|
|
1955
2125
|
success: false,
|
|
1956
|
-
error: '
|
|
2126
|
+
error: 'thread_id: Thread ID is required',
|
|
1957
2127
|
};
|
|
1958
2128
|
}
|
|
1959
2129
|
logger.info('thread_get invoked', {
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
2130
|
+
user_id,
|
|
2131
|
+
thread_id,
|
|
2132
|
+
message_limit,
|
|
1963
2133
|
});
|
|
1964
2134
|
try {
|
|
1965
2135
|
const queryParams = new URLSearchParams();
|
|
1966
|
-
queryParams.set('limit', String(
|
|
1967
|
-
const response = await apiClient.get(`/api/threads/${
|
|
2136
|
+
queryParams.set('limit', String(message_limit));
|
|
2137
|
+
const response = await apiClient.get(`/api/threads/${thread_id}/history?${queryParams}`, { user_id });
|
|
1968
2138
|
if (!response.success) {
|
|
1969
2139
|
logger.error('thread_get API error', {
|
|
1970
|
-
|
|
1971
|
-
|
|
2140
|
+
user_id,
|
|
2141
|
+
thread_id,
|
|
1972
2142
|
status: response.error.status,
|
|
1973
2143
|
code: response.error.code,
|
|
1974
2144
|
});
|
|
@@ -1979,13 +2149,13 @@ function createToolHandlers(state) {
|
|
|
1979
2149
|
}
|
|
1980
2150
|
const { thread, messages } = response.data;
|
|
1981
2151
|
logger.debug('thread_get completed', {
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
2152
|
+
user_id,
|
|
2153
|
+
thread_id,
|
|
2154
|
+
message_count: messages.length,
|
|
1985
2155
|
});
|
|
1986
2156
|
// Generate a per-invocation nonce for boundary markers (#1255)
|
|
1987
2157
|
const { nonce: threadGetNonce } = createBoundaryMarkers();
|
|
1988
|
-
const contact = sanitizeMetadataField(thread.
|
|
2158
|
+
const contact = sanitizeMetadataField(thread.contact_name || thread.endpoint_value || 'Unknown', threadGetNonce);
|
|
1989
2159
|
const safeChannel = sanitizeMetadataField(thread.channel, threadGetNonce);
|
|
1990
2160
|
const header = `Thread with ${contact} [${safeChannel}]`;
|
|
1991
2161
|
// Detect and log potential injection patterns in inbound messages
|
|
@@ -1996,12 +2166,12 @@ function createToolHandlers(state) {
|
|
|
1996
2166
|
promptGuardUrl: config.promptGuardUrl,
|
|
1997
2167
|
});
|
|
1998
2168
|
if (detection.detected) {
|
|
1999
|
-
const logDecision = injectionLogLimiter.shouldLog(
|
|
2169
|
+
const logDecision = injectionLogLimiter.shouldLog(user_id);
|
|
2000
2170
|
if (logDecision.log) {
|
|
2001
2171
|
logger.warn(logDecision.summary ? 'injection detection log summary for previous window' : 'potential prompt injection detected in thread_get result', {
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2172
|
+
user_id,
|
|
2173
|
+
thread_id,
|
|
2174
|
+
message_id: m.id,
|
|
2005
2175
|
patterns: detection.patterns,
|
|
2006
2176
|
source: detection.source,
|
|
2007
2177
|
...(logDecision.suppressed > 0 && { suppressedCount: logDecision.suppressed }),
|
|
@@ -2014,7 +2184,7 @@ function createToolHandlers(state) {
|
|
|
2014
2184
|
? messages
|
|
2015
2185
|
.map((m) => {
|
|
2016
2186
|
const prefix = m.direction === 'inbound' ? '←' : '→';
|
|
2017
|
-
const timestamp = new Date(m.
|
|
2187
|
+
const timestamp = new Date(m.created_at).toLocaleString();
|
|
2018
2188
|
const body = sanitizeMessageForContext(m.body || '', {
|
|
2019
2189
|
direction: m.direction,
|
|
2020
2190
|
channel: thread.channel,
|
|
@@ -2030,14 +2200,14 @@ function createToolHandlers(state) {
|
|
|
2030
2200
|
success: true,
|
|
2031
2201
|
data: {
|
|
2032
2202
|
content,
|
|
2033
|
-
details: { thread, messages,
|
|
2203
|
+
details: { thread, messages, user_id },
|
|
2034
2204
|
},
|
|
2035
2205
|
};
|
|
2036
2206
|
}
|
|
2037
2207
|
catch (error) {
|
|
2038
2208
|
logger.error('thread_get failed', {
|
|
2039
|
-
|
|
2040
|
-
|
|
2209
|
+
user_id,
|
|
2210
|
+
thread_id,
|
|
2041
2211
|
error: error instanceof Error ? error.message : String(error),
|
|
2042
2212
|
});
|
|
2043
2213
|
return {
|
|
@@ -2055,7 +2225,7 @@ function createToolHandlers(state) {
|
|
|
2055
2225
|
};
|
|
2056
2226
|
}
|
|
2057
2227
|
logger.info('relationship_set invoked', {
|
|
2058
|
-
|
|
2228
|
+
user_id,
|
|
2059
2229
|
contactALength: contact_a.length,
|
|
2060
2230
|
contactBLength: contact_b.length,
|
|
2061
2231
|
relationshipLength: relationship.length,
|
|
@@ -2066,30 +2236,31 @@ function createToolHandlers(state) {
|
|
|
2066
2236
|
contact_a,
|
|
2067
2237
|
contact_b,
|
|
2068
2238
|
relationship_type: relationship,
|
|
2069
|
-
user_email:
|
|
2239
|
+
user_email: user_id, // Issue #1172: scope by user
|
|
2240
|
+
namespace: getStoreNamespace(params), // Issue #1428
|
|
2070
2241
|
};
|
|
2071
2242
|
if (notes) {
|
|
2072
2243
|
body.notes = notes;
|
|
2073
2244
|
}
|
|
2074
|
-
const response = await apiClient.post('/api/relationships/set', body, {
|
|
2245
|
+
const response = await apiClient.post('/api/relationships/set', body, { user_id });
|
|
2075
2246
|
if (!response.success) {
|
|
2076
2247
|
return { success: false, error: response.error.message };
|
|
2077
2248
|
}
|
|
2078
|
-
const { relationship: rel,
|
|
2249
|
+
const { relationship: rel, contact_a: respA, contact_b: respB, relationship_type, created } = response.data;
|
|
2079
2250
|
const content = created
|
|
2080
|
-
? `Recorded: ${
|
|
2081
|
-
: `Relationship already exists: ${
|
|
2251
|
+
? `Recorded: ${respA.display_name} [${relationship_type.label}] ${respB.display_name}`
|
|
2252
|
+
: `Relationship already exists: ${respA.display_name} [${relationship_type.label}] ${respB.display_name}`;
|
|
2082
2253
|
return {
|
|
2083
2254
|
success: true,
|
|
2084
2255
|
data: {
|
|
2085
2256
|
content,
|
|
2086
2257
|
details: {
|
|
2087
|
-
|
|
2258
|
+
relationship_id: rel.id,
|
|
2088
2259
|
created,
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2260
|
+
contact_a: respA,
|
|
2261
|
+
contact_b: respB,
|
|
2262
|
+
relationship_type,
|
|
2263
|
+
user_id,
|
|
2093
2264
|
},
|
|
2094
2265
|
},
|
|
2095
2266
|
};
|
|
@@ -2108,21 +2279,21 @@ function createToolHandlers(state) {
|
|
|
2108
2279
|
};
|
|
2109
2280
|
}
|
|
2110
2281
|
logger.info('relationship_query invoked', {
|
|
2111
|
-
|
|
2282
|
+
user_id,
|
|
2112
2283
|
contactLength: contact.length,
|
|
2113
2284
|
hasTypeFilter: !!type_filter,
|
|
2114
2285
|
});
|
|
2115
2286
|
try {
|
|
2116
2287
|
// Resolve contact to a UUID — accept UUID directly or search by name
|
|
2117
2288
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2118
|
-
let
|
|
2289
|
+
let contact_id;
|
|
2119
2290
|
if (uuidRegex.test(contact)) {
|
|
2120
|
-
|
|
2291
|
+
contact_id = contact;
|
|
2121
2292
|
}
|
|
2122
2293
|
else {
|
|
2123
2294
|
// Search for contact by name (Issue #1172: scope by user_email)
|
|
2124
|
-
const searchParams = new URLSearchParams({ search: contact, limit: '1', user_email:
|
|
2125
|
-
const searchResponse = await apiClient.get(`/api/contacts?${searchParams}`, {
|
|
2295
|
+
const searchParams = new URLSearchParams({ search: contact, limit: '1', user_email: user_id });
|
|
2296
|
+
const searchResponse = await apiClient.get(`/api/contacts?${searchParams}`, { user_id });
|
|
2126
2297
|
if (!searchResponse.success) {
|
|
2127
2298
|
return { success: false, error: searchResponse.error.message };
|
|
2128
2299
|
}
|
|
@@ -2130,43 +2301,43 @@ function createToolHandlers(state) {
|
|
|
2130
2301
|
if (contacts.length === 0) {
|
|
2131
2302
|
return { success: false, error: 'Contact not found.' };
|
|
2132
2303
|
}
|
|
2133
|
-
|
|
2304
|
+
contact_id = contacts[0].id;
|
|
2134
2305
|
}
|
|
2135
|
-
// Use graph traversal endpoint which returns
|
|
2136
|
-
const response = await apiClient.get(`/api/contacts/${
|
|
2306
|
+
// Use graph traversal endpoint which returns related_contacts
|
|
2307
|
+
const response = await apiClient.get(`/api/contacts/${contact_id}/relationships?user_email=${encodeURIComponent(user_id)}`, { user_id });
|
|
2137
2308
|
if (!response.success) {
|
|
2138
2309
|
if (response.error.code === 'NOT_FOUND') {
|
|
2139
2310
|
return { success: false, error: 'Contact not found.' };
|
|
2140
2311
|
}
|
|
2141
2312
|
return { success: false, error: response.error.message };
|
|
2142
2313
|
}
|
|
2143
|
-
let {
|
|
2144
|
-
const {
|
|
2314
|
+
let { related_contacts } = response.data;
|
|
2315
|
+
const { contact_name } = response.data;
|
|
2145
2316
|
// Apply type_filter client-side if provided
|
|
2146
|
-
if (type_filter &&
|
|
2317
|
+
if (type_filter && related_contacts.length > 0) {
|
|
2147
2318
|
const filterLower = type_filter.toLowerCase();
|
|
2148
|
-
|
|
2319
|
+
related_contacts = related_contacts.filter((rel) => rel.relationship_type_name.toLowerCase().includes(filterLower) || rel.relationship_type_label.toLowerCase().includes(filterLower));
|
|
2149
2320
|
}
|
|
2150
|
-
if (
|
|
2321
|
+
if (related_contacts.length === 0) {
|
|
2151
2322
|
return {
|
|
2152
2323
|
success: true,
|
|
2153
2324
|
data: {
|
|
2154
|
-
content: `No relationships found for ${
|
|
2155
|
-
details: {
|
|
2325
|
+
content: `No relationships found for ${contact_name}.`,
|
|
2326
|
+
details: { contact_id, contact_name, related_contacts: [], user_id },
|
|
2156
2327
|
},
|
|
2157
2328
|
};
|
|
2158
2329
|
}
|
|
2159
|
-
const lines = [`Relationships for ${
|
|
2160
|
-
for (const rel of
|
|
2161
|
-
const kindTag = rel.
|
|
2330
|
+
const lines = [`Relationships for ${contact_name}:`];
|
|
2331
|
+
for (const rel of related_contacts) {
|
|
2332
|
+
const kindTag = rel.contact_kind !== 'person' ? ` [${rel.contact_kind}]` : '';
|
|
2162
2333
|
const notesTag = rel.notes ? ` -- ${rel.notes}` : '';
|
|
2163
|
-
lines.push(`- ${rel.
|
|
2334
|
+
lines.push(`- ${rel.relationship_type_label}: ${rel.contact_name}${kindTag}${notesTag}`);
|
|
2164
2335
|
}
|
|
2165
2336
|
return {
|
|
2166
2337
|
success: true,
|
|
2167
2338
|
data: {
|
|
2168
2339
|
content: lines.join('\n'),
|
|
2169
|
-
details: {
|
|
2340
|
+
details: { contact_id, contact_name, related_contacts, user_id },
|
|
2170
2341
|
},
|
|
2171
2342
|
};
|
|
2172
2343
|
}
|
|
@@ -2176,36 +2347,36 @@ function createToolHandlers(state) {
|
|
|
2176
2347
|
}
|
|
2177
2348
|
},
|
|
2178
2349
|
async file_share(params) {
|
|
2179
|
-
const { fileId, expiresIn = 3600, maxDownloads, } = params;
|
|
2350
|
+
const { file_id: fileId, expires_in: expiresIn = 3600, max_downloads: maxDownloads, } = params;
|
|
2180
2351
|
if (!fileId) {
|
|
2181
2352
|
return {
|
|
2182
2353
|
success: false,
|
|
2183
|
-
error: '
|
|
2354
|
+
error: 'file_id is required',
|
|
2184
2355
|
};
|
|
2185
2356
|
}
|
|
2186
|
-
// Validate
|
|
2357
|
+
// Validate expires_in range
|
|
2187
2358
|
if (expiresIn < 60 || expiresIn > 604800) {
|
|
2188
2359
|
return {
|
|
2189
2360
|
success: false,
|
|
2190
|
-
error: '
|
|
2361
|
+
error: 'expires_in must be between 60 and 604800 seconds (1 minute to 7 days)',
|
|
2191
2362
|
};
|
|
2192
2363
|
}
|
|
2193
2364
|
logger.info('file_share invoked', {
|
|
2194
|
-
|
|
2195
|
-
fileId,
|
|
2196
|
-
expiresIn,
|
|
2197
|
-
maxDownloads,
|
|
2365
|
+
user_id,
|
|
2366
|
+
file_id: fileId,
|
|
2367
|
+
expires_in: expiresIn,
|
|
2368
|
+
max_downloads: maxDownloads,
|
|
2198
2369
|
});
|
|
2199
2370
|
try {
|
|
2200
|
-
const body = { expiresIn };
|
|
2371
|
+
const body = { expires_in: expiresIn };
|
|
2201
2372
|
if (maxDownloads !== undefined) {
|
|
2202
|
-
body.
|
|
2373
|
+
body.max_downloads = maxDownloads;
|
|
2203
2374
|
}
|
|
2204
|
-
const response = await apiClient.post(`/api/files/${fileId}/share`, body, {
|
|
2375
|
+
const response = await apiClient.post(`/api/files/${fileId}/share`, body, { user_id });
|
|
2205
2376
|
if (!response.success) {
|
|
2206
2377
|
logger.error('file_share API error', {
|
|
2207
|
-
|
|
2208
|
-
fileId,
|
|
2378
|
+
user_id,
|
|
2379
|
+
file_id: fileId,
|
|
2209
2380
|
status: response.error.status,
|
|
2210
2381
|
code: response.error.code,
|
|
2211
2382
|
});
|
|
@@ -2214,12 +2385,12 @@ function createToolHandlers(state) {
|
|
|
2214
2385
|
error: response.error.message || 'Failed to create share link',
|
|
2215
2386
|
};
|
|
2216
2387
|
}
|
|
2217
|
-
const { url,
|
|
2388
|
+
const { url, share_token, expires_at, filename, content_type, size_bytes } = response.data;
|
|
2218
2389
|
logger.debug('file_share completed', {
|
|
2219
|
-
|
|
2220
|
-
fileId,
|
|
2221
|
-
|
|
2222
|
-
|
|
2390
|
+
user_id,
|
|
2391
|
+
file_id: fileId,
|
|
2392
|
+
share_token,
|
|
2393
|
+
expires_at,
|
|
2223
2394
|
});
|
|
2224
2395
|
// Format file size
|
|
2225
2396
|
const formatSize = (bytes) => {
|
|
@@ -2242,7 +2413,7 @@ function createToolHandlers(state) {
|
|
|
2242
2413
|
return `${Math.floor(seconds / 86400)} days`;
|
|
2243
2414
|
};
|
|
2244
2415
|
const expiryText = formatDuration(expiresIn);
|
|
2245
|
-
const sizeText = formatSize(
|
|
2416
|
+
const sizeText = formatSize(size_bytes);
|
|
2246
2417
|
const downloadLimit = maxDownloads ? ` (max ${maxDownloads} downloads)` : '';
|
|
2247
2418
|
return {
|
|
2248
2419
|
success: true,
|
|
@@ -2250,21 +2421,21 @@ function createToolHandlers(state) {
|
|
|
2250
2421
|
content: `Share link created for "${filename}" (${sizeText}). ` + `Valid for ${expiryText}${downloadLimit}.\n\nURL: ${url}`,
|
|
2251
2422
|
details: {
|
|
2252
2423
|
url,
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
expiresIn,
|
|
2424
|
+
share_token,
|
|
2425
|
+
expires_at,
|
|
2426
|
+
expires_in: expiresIn,
|
|
2256
2427
|
filename,
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2428
|
+
content_type,
|
|
2429
|
+
size_bytes,
|
|
2430
|
+
user_id,
|
|
2260
2431
|
},
|
|
2261
2432
|
},
|
|
2262
2433
|
};
|
|
2263
2434
|
}
|
|
2264
2435
|
catch (error) {
|
|
2265
2436
|
logger.error('file_share failed', {
|
|
2266
|
-
|
|
2267
|
-
fileId,
|
|
2437
|
+
user_id,
|
|
2438
|
+
file_id: fileId,
|
|
2268
2439
|
error: error instanceof Error ? error.message : String(error),
|
|
2269
2440
|
});
|
|
2270
2441
|
return {
|
|
@@ -2276,7 +2447,7 @@ function createToolHandlers(state) {
|
|
|
2276
2447
|
// Skill store tools: delegate to tool modules for Zod validation,
|
|
2277
2448
|
// credential detection, text sanitization, and error sanitization (Issue #824)
|
|
2278
2449
|
...(() => {
|
|
2279
|
-
const toolOptions = { client: apiClient, logger, config,
|
|
2450
|
+
const toolOptions = { client: apiClient, logger, config, user_id };
|
|
2280
2451
|
const putTool = createSkillStorePutTool(toolOptions);
|
|
2281
2452
|
const getTool = createSkillStoreGetTool(toolOptions);
|
|
2282
2453
|
const listTool = createSkillStoreListTool(toolOptions);
|
|
@@ -2296,7 +2467,7 @@ function createToolHandlers(state) {
|
|
|
2296
2467
|
})(),
|
|
2297
2468
|
// Entity link tools: delegate to tool modules (Issue #1220)
|
|
2298
2469
|
...(() => {
|
|
2299
|
-
const toolOptions = { client: apiClient, logger, config,
|
|
2470
|
+
const toolOptions = { client: apiClient, logger, config, user_id };
|
|
2300
2471
|
const setTool = createLinksSetTool(toolOptions);
|
|
2301
2472
|
const queryTool = createLinksQueryTool(toolOptions);
|
|
2302
2473
|
const removeTool = createLinksRemoveTool(toolOptions);
|
|
@@ -2306,6 +2477,203 @@ function createToolHandlers(state) {
|
|
|
2306
2477
|
links_remove: (params) => removeTool.execute(params),
|
|
2307
2478
|
};
|
|
2308
2479
|
})(),
|
|
2480
|
+
// Prompt template tools (Epic #1497, Issue #1499)
|
|
2481
|
+
async prompt_template_list(params) {
|
|
2482
|
+
const { channel_type, limit = 20, offset = 0 } = params;
|
|
2483
|
+
try {
|
|
2484
|
+
const queryParams = new URLSearchParams({ limit: String(limit), offset: String(offset) });
|
|
2485
|
+
if (channel_type)
|
|
2486
|
+
queryParams.set('channel_type', channel_type);
|
|
2487
|
+
const response = await apiClient.get(`/api/prompt-templates?${queryParams.toString()}`, { user_id });
|
|
2488
|
+
if (!response.success) {
|
|
2489
|
+
return { success: false, error: response.error.message || 'Failed to list prompt templates' };
|
|
2490
|
+
}
|
|
2491
|
+
const items = response.data.items ?? [];
|
|
2492
|
+
const content = items.length === 0
|
|
2493
|
+
? 'No prompt templates found.'
|
|
2494
|
+
: items.map((t) => `- **${t.label}** [${t.channel_type}]${t.is_default ? ' (default)' : ''}`).join('\n');
|
|
2495
|
+
return { success: true, data: { content, details: { items, total: response.data.total ?? items.length } } };
|
|
2496
|
+
}
|
|
2497
|
+
catch (error) {
|
|
2498
|
+
logger.error('prompt_template_list failed', { error });
|
|
2499
|
+
return { success: false, error: 'Failed to list prompt templates' };
|
|
2500
|
+
}
|
|
2501
|
+
},
|
|
2502
|
+
async prompt_template_get(params) {
|
|
2503
|
+
const { id } = params;
|
|
2504
|
+
try {
|
|
2505
|
+
const response = await apiClient.get(`/api/prompt-templates/${id}`, { user_id });
|
|
2506
|
+
if (!response.success) {
|
|
2507
|
+
return { success: false, error: response.error.message || 'Prompt template not found' };
|
|
2508
|
+
}
|
|
2509
|
+
const t = response.data;
|
|
2510
|
+
return { success: true, data: { content: `**${t.label}** [${t.channel_type}]${t.is_default ? ' (default)' : ''}\n\n${t.content}`, details: t } };
|
|
2511
|
+
}
|
|
2512
|
+
catch (error) {
|
|
2513
|
+
logger.error('prompt_template_get failed', { error });
|
|
2514
|
+
return { success: false, error: 'Failed to get prompt template' };
|
|
2515
|
+
}
|
|
2516
|
+
},
|
|
2517
|
+
async prompt_template_create(params) {
|
|
2518
|
+
const { label, content, channel_type, is_default } = params;
|
|
2519
|
+
try {
|
|
2520
|
+
const response = await apiClient.post('/api/prompt-templates', { label, content, channel_type, is_default }, { user_id });
|
|
2521
|
+
if (!response.success) {
|
|
2522
|
+
return { success: false, error: response.error.message || 'Failed to create prompt template' };
|
|
2523
|
+
}
|
|
2524
|
+
return { success: true, data: { content: `Created prompt template "${response.data.label}" (${response.data.id})`, details: response.data } };
|
|
2525
|
+
}
|
|
2526
|
+
catch (error) {
|
|
2527
|
+
logger.error('prompt_template_create failed', { error });
|
|
2528
|
+
return { success: false, error: 'Failed to create prompt template' };
|
|
2529
|
+
}
|
|
2530
|
+
},
|
|
2531
|
+
async prompt_template_update(params) {
|
|
2532
|
+
const { id, ...updates } = params;
|
|
2533
|
+
try {
|
|
2534
|
+
const response = await apiClient.put(`/api/prompt-templates/${id}`, updates, { user_id });
|
|
2535
|
+
if (!response.success) {
|
|
2536
|
+
return { success: false, error: response.error.message || 'Failed to update prompt template' };
|
|
2537
|
+
}
|
|
2538
|
+
return { success: true, data: { content: `Updated prompt template "${response.data.label}"`, details: response.data } };
|
|
2539
|
+
}
|
|
2540
|
+
catch (error) {
|
|
2541
|
+
logger.error('prompt_template_update failed', { error });
|
|
2542
|
+
return { success: false, error: 'Failed to update prompt template' };
|
|
2543
|
+
}
|
|
2544
|
+
},
|
|
2545
|
+
async prompt_template_delete(params) {
|
|
2546
|
+
const { id } = params;
|
|
2547
|
+
try {
|
|
2548
|
+
const response = await apiClient.delete(`/api/prompt-templates/${id}`, { user_id });
|
|
2549
|
+
if (!response.success) {
|
|
2550
|
+
return { success: false, error: response.error.message || 'Failed to delete prompt template' };
|
|
2551
|
+
}
|
|
2552
|
+
return { success: true, data: { content: `Deleted prompt template ${id}`, details: { id } } };
|
|
2553
|
+
}
|
|
2554
|
+
catch (error) {
|
|
2555
|
+
logger.error('prompt_template_delete failed', { error });
|
|
2556
|
+
return { success: false, error: 'Failed to delete prompt template' };
|
|
2557
|
+
}
|
|
2558
|
+
},
|
|
2559
|
+
// ── Inbound Destination tools (Issue #1500) ──────────────
|
|
2560
|
+
async inbound_destination_list(params) {
|
|
2561
|
+
const { channel_type, search, limit, offset } = params;
|
|
2562
|
+
try {
|
|
2563
|
+
const queryParams = new URLSearchParams();
|
|
2564
|
+
if (channel_type)
|
|
2565
|
+
queryParams.set('channel_type', channel_type);
|
|
2566
|
+
if (search)
|
|
2567
|
+
queryParams.set('search', search);
|
|
2568
|
+
if (limit !== undefined)
|
|
2569
|
+
queryParams.set('limit', String(limit));
|
|
2570
|
+
if (offset !== undefined)
|
|
2571
|
+
queryParams.set('offset', String(offset));
|
|
2572
|
+
const response = await apiClient.get(`/api/inbound-destinations?${queryParams.toString()}`, { user_id });
|
|
2573
|
+
if (!response.success) {
|
|
2574
|
+
return { success: false, error: response.error.message || 'Failed to list inbound destinations' };
|
|
2575
|
+
}
|
|
2576
|
+
const items = response.data.items ?? [];
|
|
2577
|
+
const content = items.length === 0
|
|
2578
|
+
? 'No inbound destinations found.'
|
|
2579
|
+
: items.map((d) => `- **${d.address}** [${d.channel_type}]${d.display_name ? ` — ${d.display_name}` : ''}${d.agent_id ? ` (agent: ${d.agent_id})` : ''}`).join('\n');
|
|
2580
|
+
return { success: true, data: { content, details: { items, total: response.data.total ?? items.length } } };
|
|
2581
|
+
}
|
|
2582
|
+
catch (error) {
|
|
2583
|
+
logger.error('inbound_destination_list failed', { error });
|
|
2584
|
+
return { success: false, error: 'Failed to list inbound destinations' };
|
|
2585
|
+
}
|
|
2586
|
+
},
|
|
2587
|
+
async inbound_destination_get(params) {
|
|
2588
|
+
const { id } = params;
|
|
2589
|
+
try {
|
|
2590
|
+
const response = await apiClient.get(`/api/inbound-destinations/${id}`, { user_id });
|
|
2591
|
+
if (!response.success) {
|
|
2592
|
+
return { success: false, error: response.error.message || 'Inbound destination not found' };
|
|
2593
|
+
}
|
|
2594
|
+
const d = response.data;
|
|
2595
|
+
const lines = [`**${d.address}** [${d.channel_type}]`];
|
|
2596
|
+
if (d.display_name)
|
|
2597
|
+
lines.push(`Display: ${d.display_name}`);
|
|
2598
|
+
if (d.agent_id)
|
|
2599
|
+
lines.push(`Agent: ${d.agent_id}`);
|
|
2600
|
+
if (d.prompt_template_id)
|
|
2601
|
+
lines.push(`Prompt Template: ${d.prompt_template_id}`);
|
|
2602
|
+
if (d.context_id)
|
|
2603
|
+
lines.push(`Context: ${d.context_id}`);
|
|
2604
|
+
return { success: true, data: { content: lines.join('\n'), details: d } };
|
|
2605
|
+
}
|
|
2606
|
+
catch (error) {
|
|
2607
|
+
logger.error('inbound_destination_get failed', { error });
|
|
2608
|
+
return { success: false, error: 'Failed to get inbound destination' };
|
|
2609
|
+
}
|
|
2610
|
+
},
|
|
2611
|
+
async inbound_destination_update(params) {
|
|
2612
|
+
const { id, ...updates } = params;
|
|
2613
|
+
try {
|
|
2614
|
+
const response = await apiClient.put(`/api/inbound-destinations/${id}`, updates, { user_id });
|
|
2615
|
+
if (!response.success) {
|
|
2616
|
+
return { success: false, error: response.error.message || 'Failed to update inbound destination' };
|
|
2617
|
+
}
|
|
2618
|
+
return { success: true, data: { content: `Updated inbound destination "${response.data.address}"`, details: response.data } };
|
|
2619
|
+
}
|
|
2620
|
+
catch (error) {
|
|
2621
|
+
logger.error('inbound_destination_update failed', { error });
|
|
2622
|
+
return { success: false, error: 'Failed to update inbound destination' };
|
|
2623
|
+
}
|
|
2624
|
+
},
|
|
2625
|
+
// ── Channel Default tools (Issue #1501) ──────────────────
|
|
2626
|
+
async channel_default_list() {
|
|
2627
|
+
try {
|
|
2628
|
+
const response = await apiClient.get('/api/channel-defaults', { user_id });
|
|
2629
|
+
if (!response.success) {
|
|
2630
|
+
return { success: false, error: response.error.message || 'Failed to list channel defaults' };
|
|
2631
|
+
}
|
|
2632
|
+
const items = Array.isArray(response.data) ? response.data : [];
|
|
2633
|
+
const content = items.length === 0
|
|
2634
|
+
? 'No channel defaults configured.'
|
|
2635
|
+
: items.map((d) => `- **${d.channel_type}**: agent=${d.agent_id}${d.prompt_template_id ? ` prompt=${d.prompt_template_id}` : ''}`).join('\n');
|
|
2636
|
+
return { success: true, data: { content, details: { items } } };
|
|
2637
|
+
}
|
|
2638
|
+
catch (error) {
|
|
2639
|
+
logger.error('channel_default_list failed', { error });
|
|
2640
|
+
return { success: false, error: 'Failed to list channel defaults' };
|
|
2641
|
+
}
|
|
2642
|
+
},
|
|
2643
|
+
async channel_default_get(params) {
|
|
2644
|
+
const { channel_type } = params;
|
|
2645
|
+
try {
|
|
2646
|
+
const response = await apiClient.get(`/api/channel-defaults/${channel_type}`, { user_id });
|
|
2647
|
+
if (!response.success) {
|
|
2648
|
+
return { success: false, error: response.error.message || 'Channel default not found' };
|
|
2649
|
+
}
|
|
2650
|
+
const d = response.data;
|
|
2651
|
+
const lines = [`**${d.channel_type}** → agent: ${d.agent_id}`];
|
|
2652
|
+
if (d.prompt_template_id)
|
|
2653
|
+
lines.push(`Prompt Template: ${d.prompt_template_id}`);
|
|
2654
|
+
if (d.context_id)
|
|
2655
|
+
lines.push(`Context: ${d.context_id}`);
|
|
2656
|
+
return { success: true, data: { content: lines.join('\n'), details: d } };
|
|
2657
|
+
}
|
|
2658
|
+
catch (error) {
|
|
2659
|
+
logger.error('channel_default_get failed', { error });
|
|
2660
|
+
return { success: false, error: 'Failed to get channel default' };
|
|
2661
|
+
}
|
|
2662
|
+
},
|
|
2663
|
+
async channel_default_set(params) {
|
|
2664
|
+
const { channel_type, agent_id, prompt_template_id, context_id } = params;
|
|
2665
|
+
try {
|
|
2666
|
+
const response = await apiClient.put(`/api/channel-defaults/${channel_type}`, { agent_id, prompt_template_id, context_id }, { user_id });
|
|
2667
|
+
if (!response.success) {
|
|
2668
|
+
return { success: false, error: response.error.message || 'Failed to set channel default' };
|
|
2669
|
+
}
|
|
2670
|
+
return { success: true, data: { content: `Set ${channel_type} default → agent: ${response.data.agent_id}`, details: response.data } };
|
|
2671
|
+
}
|
|
2672
|
+
catch (error) {
|
|
2673
|
+
logger.error('channel_default_set failed', { error });
|
|
2674
|
+
return { success: false, error: 'Failed to set channel default' };
|
|
2675
|
+
}
|
|
2676
|
+
},
|
|
2309
2677
|
};
|
|
2310
2678
|
}
|
|
2311
2679
|
/**
|
|
@@ -2349,12 +2717,21 @@ export const registerOpenClaw = (api) => {
|
|
|
2349
2717
|
const apiClient = createApiClient({ config, logger });
|
|
2350
2718
|
// Extract context and user ID
|
|
2351
2719
|
const context = extractContext(api.runtime);
|
|
2352
|
-
const
|
|
2720
|
+
const user_id = getUserScopeKey({
|
|
2353
2721
|
agentId: context.agent.agentId,
|
|
2354
2722
|
sessionKey: context.session.sessionId,
|
|
2355
|
-
},
|
|
2723
|
+
},
|
|
2724
|
+
// Backward compat: use userScoping if provided, otherwise default to 'agent'
|
|
2725
|
+
config.userScoping ?? 'agent');
|
|
2726
|
+
// Resolve namespace config (Issue #1428)
|
|
2727
|
+
const resolvedNamespace = resolveNamespaceConfig(config.namespace, context.agent.agentId);
|
|
2728
|
+
logger.info('Namespace config resolved', {
|
|
2729
|
+
agentId: context.agent.agentId,
|
|
2730
|
+
defaultNamespace: resolvedNamespace.default,
|
|
2731
|
+
recallNamespaces: resolvedNamespace.recall,
|
|
2732
|
+
});
|
|
2356
2733
|
// Store plugin state
|
|
2357
|
-
const state = { config, logger, apiClient,
|
|
2734
|
+
const state = { config, logger, apiClient, user_id, resolvedNamespace };
|
|
2358
2735
|
// Create tool handlers
|
|
2359
2736
|
const handlers = createToolHandlers(state);
|
|
2360
2737
|
// Register all 30 tools with correct OpenClaw Gateway execute signature
|
|
@@ -2363,7 +2740,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2363
2740
|
{
|
|
2364
2741
|
name: 'memory_recall',
|
|
2365
2742
|
description: 'Search through long-term memories. Use when you need context about user preferences, past decisions, or previously discussed topics.',
|
|
2366
|
-
parameters: memoryRecallSchema,
|
|
2743
|
+
parameters: withNamespaces(memoryRecallSchema),
|
|
2367
2744
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2368
2745
|
const result = await handlers.memory_recall(params);
|
|
2369
2746
|
return toAgentToolResult(result);
|
|
@@ -2372,7 +2749,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2372
2749
|
{
|
|
2373
2750
|
name: 'memory_store',
|
|
2374
2751
|
description: 'Store a new memory for future reference. Use when the user shares important preferences, facts, or decisions.',
|
|
2375
|
-
parameters: memoryStoreSchema,
|
|
2752
|
+
parameters: withNamespace(memoryStoreSchema),
|
|
2376
2753
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2377
2754
|
const result = await handlers.memory_store(params);
|
|
2378
2755
|
return toAgentToolResult(result);
|
|
@@ -2381,7 +2758,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2381
2758
|
{
|
|
2382
2759
|
name: 'memory_forget',
|
|
2383
2760
|
description: 'Remove a memory by ID or search query. Use when information is outdated or the user requests deletion.',
|
|
2384
|
-
parameters: memoryForgetSchema,
|
|
2761
|
+
parameters: withNamespaces(memoryForgetSchema),
|
|
2385
2762
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2386
2763
|
const result = await handlers.memory_forget(params);
|
|
2387
2764
|
return toAgentToolResult(result);
|
|
@@ -2390,7 +2767,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2390
2767
|
{
|
|
2391
2768
|
name: 'project_list',
|
|
2392
2769
|
description: 'List projects for the user. Use to see what projects exist or filter by status.',
|
|
2393
|
-
parameters: projectListSchema,
|
|
2770
|
+
parameters: withNamespaces(projectListSchema),
|
|
2394
2771
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2395
2772
|
const result = await handlers.project_list(params);
|
|
2396
2773
|
return toAgentToolResult(result);
|
|
@@ -2399,7 +2776,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2399
2776
|
{
|
|
2400
2777
|
name: 'project_get',
|
|
2401
2778
|
description: 'Get details about a specific project. Use when you need full project information.',
|
|
2402
|
-
parameters: projectGetSchema,
|
|
2779
|
+
parameters: withNamespaces(projectGetSchema),
|
|
2403
2780
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2404
2781
|
const result = await handlers.project_get(params);
|
|
2405
2782
|
return toAgentToolResult(result);
|
|
@@ -2408,7 +2785,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2408
2785
|
{
|
|
2409
2786
|
name: 'project_create',
|
|
2410
2787
|
description: 'Create a new project. Use when the user wants to start tracking a new initiative.',
|
|
2411
|
-
parameters: projectCreateSchema,
|
|
2788
|
+
parameters: withNamespace(projectCreateSchema),
|
|
2412
2789
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2413
2790
|
const result = await handlers.project_create(params);
|
|
2414
2791
|
return toAgentToolResult(result);
|
|
@@ -2417,7 +2794,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2417
2794
|
{
|
|
2418
2795
|
name: 'todo_list',
|
|
2419
2796
|
description: 'List todos, optionally filtered by project or status. Use to see pending tasks.',
|
|
2420
|
-
parameters: todoListSchema,
|
|
2797
|
+
parameters: withNamespaces(todoListSchema),
|
|
2421
2798
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2422
2799
|
const result = await handlers.todo_list(params);
|
|
2423
2800
|
return toAgentToolResult(result);
|
|
@@ -2426,7 +2803,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2426
2803
|
{
|
|
2427
2804
|
name: 'todo_create',
|
|
2428
2805
|
description: 'Create a new todo item. Use when the user wants to track a task.',
|
|
2429
|
-
parameters: todoCreateSchema,
|
|
2806
|
+
parameters: withNamespace(todoCreateSchema),
|
|
2430
2807
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2431
2808
|
const result = await handlers.todo_create(params);
|
|
2432
2809
|
return toAgentToolResult(result);
|
|
@@ -2435,7 +2812,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2435
2812
|
{
|
|
2436
2813
|
name: 'todo_complete',
|
|
2437
2814
|
description: 'Mark a todo as complete. Use when a task is done.',
|
|
2438
|
-
parameters: todoCompleteSchema,
|
|
2815
|
+
parameters: withNamespaces(todoCompleteSchema),
|
|
2439
2816
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2440
2817
|
const result = await handlers.todo_complete(params);
|
|
2441
2818
|
return toAgentToolResult(result);
|
|
@@ -2444,7 +2821,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2444
2821
|
{
|
|
2445
2822
|
name: 'todo_search',
|
|
2446
2823
|
description: 'Search todos and work items by natural language query. Uses semantic and text search to find relevant items. Optionally filter by kind or status.',
|
|
2447
|
-
parameters: todoSearchSchema,
|
|
2824
|
+
parameters: withNamespaces(todoSearchSchema),
|
|
2448
2825
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2449
2826
|
const result = await handlers.todo_search(params);
|
|
2450
2827
|
return toAgentToolResult(result);
|
|
@@ -2453,7 +2830,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2453
2830
|
{
|
|
2454
2831
|
name: 'project_search',
|
|
2455
2832
|
description: 'Search projects by natural language query. Uses semantic and text search to find relevant projects. Optionally filter by status (active, completed, archived).',
|
|
2456
|
-
parameters: projectSearchSchema,
|
|
2833
|
+
parameters: withNamespaces(projectSearchSchema),
|
|
2457
2834
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2458
2835
|
const result = await handlers.project_search(params);
|
|
2459
2836
|
return toAgentToolResult(result);
|
|
@@ -2462,7 +2839,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2462
2839
|
{
|
|
2463
2840
|
name: 'context_search',
|
|
2464
2841
|
description: 'Search across memories, todos, projects, and messages simultaneously. Use when you need broad context about a topic, person, or project. Returns a blended ranked list from all entity types. Optionally filter by entity_types to narrow the search.',
|
|
2465
|
-
parameters: contextSearchSchema,
|
|
2842
|
+
parameters: withNamespaces(contextSearchSchema),
|
|
2466
2843
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2467
2844
|
const result = await handlers.context_search(params);
|
|
2468
2845
|
return toAgentToolResult(result);
|
|
@@ -2471,7 +2848,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2471
2848
|
{
|
|
2472
2849
|
name: 'contact_search',
|
|
2473
2850
|
description: 'Search contacts by name, email, or other fields. Use to find people.',
|
|
2474
|
-
parameters: contactSearchSchema,
|
|
2851
|
+
parameters: withNamespaces(contactSearchSchema),
|
|
2475
2852
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2476
2853
|
const result = await handlers.contact_search(params);
|
|
2477
2854
|
return toAgentToolResult(result);
|
|
@@ -2480,7 +2857,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2480
2857
|
{
|
|
2481
2858
|
name: 'contact_get',
|
|
2482
2859
|
description: 'Get details about a specific contact. Use when you need full contact information.',
|
|
2483
|
-
parameters: contactGetSchema,
|
|
2860
|
+
parameters: withNamespaces(contactGetSchema),
|
|
2484
2861
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2485
2862
|
const result = await handlers.contact_get(params);
|
|
2486
2863
|
return toAgentToolResult(result);
|
|
@@ -2489,7 +2866,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2489
2866
|
{
|
|
2490
2867
|
name: 'contact_create',
|
|
2491
2868
|
description: 'Create a new contact. Use when the user mentions someone new to track.',
|
|
2492
|
-
parameters: contactCreateSchema,
|
|
2869
|
+
parameters: withNamespace(contactCreateSchema),
|
|
2493
2870
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2494
2871
|
const result = await handlers.contact_create(params);
|
|
2495
2872
|
return toAgentToolResult(result);
|
|
@@ -2516,7 +2893,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2516
2893
|
{
|
|
2517
2894
|
name: 'message_search',
|
|
2518
2895
|
description: 'Search message history semantically. Use when you need to find past conversations, messages about specific topics, or communications with contacts. Supports filtering by channel (SMS/email) and contact.',
|
|
2519
|
-
parameters: messageSearchSchema,
|
|
2896
|
+
parameters: withNamespaces(messageSearchSchema),
|
|
2520
2897
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2521
2898
|
const result = await handlers.message_search(params);
|
|
2522
2899
|
return toAgentToolResult(result);
|
|
@@ -2525,7 +2902,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2525
2902
|
{
|
|
2526
2903
|
name: 'thread_list',
|
|
2527
2904
|
description: 'List message threads (conversations). Use to see recent conversations with contacts. Can filter by channel (SMS/email) or contact.',
|
|
2528
|
-
parameters: threadListSchema,
|
|
2905
|
+
parameters: withNamespaces(threadListSchema),
|
|
2529
2906
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2530
2907
|
const result = await handlers.thread_list(params);
|
|
2531
2908
|
return toAgentToolResult(result);
|
|
@@ -2534,7 +2911,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2534
2911
|
{
|
|
2535
2912
|
name: 'thread_get',
|
|
2536
2913
|
description: 'Get a thread with its message history. Use to view the full conversation in a thread.',
|
|
2537
|
-
parameters: threadGetSchema,
|
|
2914
|
+
parameters: withNamespaces(threadGetSchema),
|
|
2538
2915
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2539
2916
|
const result = await handlers.thread_get(params);
|
|
2540
2917
|
return toAgentToolResult(result);
|
|
@@ -2543,7 +2920,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2543
2920
|
{
|
|
2544
2921
|
name: 'relationship_set',
|
|
2545
2922
|
description: "Record a relationship between two people, groups, or organisations. Examples: 'Troy is Alex\\'s partner', 'Sam is a member of The Kelly Household', 'Troy works for Acme Corp'. The system handles directionality and type matching automatically.",
|
|
2546
|
-
parameters: relationshipSetSchema,
|
|
2923
|
+
parameters: withNamespace(relationshipSetSchema),
|
|
2547
2924
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2548
2925
|
const result = await handlers.relationship_set(params);
|
|
2549
2926
|
return toAgentToolResult(result);
|
|
@@ -2552,7 +2929,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2552
2929
|
{
|
|
2553
2930
|
name: 'relationship_query',
|
|
2554
2931
|
description: "Query a contact's relationships. Returns all relationships including family, partners, group memberships, professional connections, etc. Handles directional relationships automatically.",
|
|
2555
|
-
parameters: relationshipQuerySchema,
|
|
2932
|
+
parameters: withNamespaces(relationshipQuerySchema),
|
|
2556
2933
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2557
2934
|
const result = await handlers.relationship_query(params);
|
|
2558
2935
|
return toAgentToolResult(result);
|
|
@@ -2633,7 +3010,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2633
3010
|
{
|
|
2634
3011
|
name: 'links_set',
|
|
2635
3012
|
description: 'Create a link between two entities (memory, todo, project, contact, GitHub issue, or URL). Links are bidirectional and can be traversed from either end. Use to connect related items for cross-reference and context discovery.',
|
|
2636
|
-
parameters: linksSetSchema,
|
|
3013
|
+
parameters: withNamespace(linksSetSchema),
|
|
2637
3014
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2638
3015
|
const result = await handlers.links_set(params);
|
|
2639
3016
|
return toAgentToolResult(result);
|
|
@@ -2642,7 +3019,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2642
3019
|
{
|
|
2643
3020
|
name: 'links_query',
|
|
2644
3021
|
description: 'Query all links for an entity (memory, todo, project, or contact). Returns connected entities including other items, GitHub issues, and URLs. Optionally filter by link target types.',
|
|
2645
|
-
parameters: linksQuerySchema,
|
|
3022
|
+
parameters: withNamespaces(linksQuerySchema),
|
|
2646
3023
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2647
3024
|
const result = await handlers.links_query(params);
|
|
2648
3025
|
return toAgentToolResult(result);
|
|
@@ -2651,12 +3028,114 @@ export const registerOpenClaw = (api) => {
|
|
|
2651
3028
|
{
|
|
2652
3029
|
name: 'links_remove',
|
|
2653
3030
|
description: 'Remove a link between two entities. Deletes both directions of the link. Use when a connection is no longer relevant or was created in error.',
|
|
2654
|
-
parameters: linksRemoveSchema,
|
|
3031
|
+
parameters: withNamespaces(linksRemoveSchema),
|
|
2655
3032
|
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
2656
3033
|
const result = await handlers.links_remove(params);
|
|
2657
3034
|
return toAgentToolResult(result);
|
|
2658
3035
|
},
|
|
2659
3036
|
},
|
|
3037
|
+
// Prompt template tools (Epic #1497, Issue #1499)
|
|
3038
|
+
{
|
|
3039
|
+
name: 'prompt_template_list',
|
|
3040
|
+
description: 'List prompt templates used for inbound message triage. Filter by channel type (sms, email, ha_observation, general).',
|
|
3041
|
+
parameters: promptTemplateListSchema,
|
|
3042
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3043
|
+
const result = await handlers.prompt_template_list(params);
|
|
3044
|
+
return toAgentToolResult(result);
|
|
3045
|
+
},
|
|
3046
|
+
},
|
|
3047
|
+
{
|
|
3048
|
+
name: 'prompt_template_get',
|
|
3049
|
+
description: 'Get a prompt template by ID. Returns the full template including prompt content.',
|
|
3050
|
+
parameters: promptTemplateGetSchema,
|
|
3051
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3052
|
+
const result = await handlers.prompt_template_get(params);
|
|
3053
|
+
return toAgentToolResult(result);
|
|
3054
|
+
},
|
|
3055
|
+
},
|
|
3056
|
+
{
|
|
3057
|
+
name: 'prompt_template_create',
|
|
3058
|
+
description: 'Create a new prompt template for inbound message triage. Requires agentadmin access.',
|
|
3059
|
+
parameters: promptTemplateCreateSchema,
|
|
3060
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3061
|
+
const result = await handlers.prompt_template_create(params);
|
|
3062
|
+
return toAgentToolResult(result);
|
|
3063
|
+
},
|
|
3064
|
+
},
|
|
3065
|
+
{
|
|
3066
|
+
name: 'prompt_template_update',
|
|
3067
|
+
description: 'Update an existing prompt template. Can change label, content, channel type, or set as default.',
|
|
3068
|
+
parameters: promptTemplateUpdateSchema,
|
|
3069
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3070
|
+
const result = await handlers.prompt_template_update(params);
|
|
3071
|
+
return toAgentToolResult(result);
|
|
3072
|
+
},
|
|
3073
|
+
},
|
|
3074
|
+
{
|
|
3075
|
+
name: 'prompt_template_delete',
|
|
3076
|
+
description: 'Soft-delete a prompt template (sets is_active to false). Template can still be viewed but will not be used for routing.',
|
|
3077
|
+
parameters: promptTemplateDeleteSchema,
|
|
3078
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3079
|
+
const result = await handlers.prompt_template_delete(params);
|
|
3080
|
+
return toAgentToolResult(result);
|
|
3081
|
+
},
|
|
3082
|
+
},
|
|
3083
|
+
// ── Inbound Destination tools (Issue #1500) ──────────────
|
|
3084
|
+
{
|
|
3085
|
+
name: 'inbound_destination_list',
|
|
3086
|
+
description: 'List discovered inbound destinations (phone numbers and email addresses). Auto-created when messages arrive.',
|
|
3087
|
+
parameters: inboundDestinationListSchema,
|
|
3088
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3089
|
+
const result = await handlers.inbound_destination_list(params);
|
|
3090
|
+
return toAgentToolResult(result);
|
|
3091
|
+
},
|
|
3092
|
+
},
|
|
3093
|
+
{
|
|
3094
|
+
name: 'inbound_destination_get',
|
|
3095
|
+
description: 'Get an inbound destination by ID. Returns routing config (agent, prompt template, context).',
|
|
3096
|
+
parameters: inboundDestinationGetSchema,
|
|
3097
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3098
|
+
const result = await handlers.inbound_destination_get(params);
|
|
3099
|
+
return toAgentToolResult(result);
|
|
3100
|
+
},
|
|
3101
|
+
},
|
|
3102
|
+
{
|
|
3103
|
+
name: 'inbound_destination_update',
|
|
3104
|
+
description: 'Update routing overrides for an inbound destination. Set agent, prompt template, or context for routing.',
|
|
3105
|
+
parameters: inboundDestinationUpdateSchema,
|
|
3106
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3107
|
+
const result = await handlers.inbound_destination_update(params);
|
|
3108
|
+
return toAgentToolResult(result);
|
|
3109
|
+
},
|
|
3110
|
+
},
|
|
3111
|
+
// ── Channel Default tools (Issue #1501) ──────────────────
|
|
3112
|
+
{
|
|
3113
|
+
name: 'channel_default_list',
|
|
3114
|
+
description: 'List all channel defaults (per-channel routing config). Shows which agent handles each channel type.',
|
|
3115
|
+
parameters: channelDefaultListSchema,
|
|
3116
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3117
|
+
const result = await handlers.channel_default_list();
|
|
3118
|
+
return toAgentToolResult(result);
|
|
3119
|
+
},
|
|
3120
|
+
},
|
|
3121
|
+
{
|
|
3122
|
+
name: 'channel_default_get',
|
|
3123
|
+
description: 'Get the default routing config for a specific channel type (sms, email, ha_observation).',
|
|
3124
|
+
parameters: channelDefaultGetSchema,
|
|
3125
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3126
|
+
const result = await handlers.channel_default_get(params);
|
|
3127
|
+
return toAgentToolResult(result);
|
|
3128
|
+
},
|
|
3129
|
+
},
|
|
3130
|
+
{
|
|
3131
|
+
name: 'channel_default_set',
|
|
3132
|
+
description: 'Set or update the default routing config for a channel type. Requires agentadmin access.',
|
|
3133
|
+
parameters: channelDefaultSetSchema,
|
|
3134
|
+
execute: async (_toolCallId, params, _signal, _onUpdate) => {
|
|
3135
|
+
const result = await handlers.channel_default_set(params);
|
|
3136
|
+
return toAgentToolResult(result);
|
|
3137
|
+
},
|
|
3138
|
+
},
|
|
2660
3139
|
];
|
|
2661
3140
|
for (const tool of tools) {
|
|
2662
3141
|
api.registerTool(tool);
|
|
@@ -2674,7 +3153,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2674
3153
|
client: apiClient,
|
|
2675
3154
|
logger,
|
|
2676
3155
|
config,
|
|
2677
|
-
|
|
3156
|
+
user_id,
|
|
2678
3157
|
timeoutMs: HOOK_TIMEOUT_MS,
|
|
2679
3158
|
});
|
|
2680
3159
|
/**
|
|
@@ -2719,7 +3198,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2719
3198
|
client: apiClient,
|
|
2720
3199
|
logger,
|
|
2721
3200
|
config,
|
|
2722
|
-
|
|
3201
|
+
user_id,
|
|
2723
3202
|
timeoutMs: HOOK_TIMEOUT_MS * 2, // Allow more time for capture (10s)
|
|
2724
3203
|
});
|
|
2725
3204
|
/**
|
|
@@ -2728,7 +3207,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2728
3207
|
*/
|
|
2729
3208
|
const agentEndHandler = async (event, _ctx) => {
|
|
2730
3209
|
logger.debug('Auto-capture hook triggered', {
|
|
2731
|
-
|
|
3210
|
+
message_count: event.messages?.length ?? 0,
|
|
2732
3211
|
success: event.success,
|
|
2733
3212
|
});
|
|
2734
3213
|
try {
|
|
@@ -2775,8 +3254,8 @@ export const registerOpenClaw = (api) => {
|
|
|
2775
3254
|
*/
|
|
2776
3255
|
const messageReceivedHandler = async (event, _ctx) => {
|
|
2777
3256
|
// Skip if no thread ID (nothing to link to)
|
|
2778
|
-
if (!event.
|
|
2779
|
-
logger.debug('Auto-link skipped: no
|
|
3257
|
+
if (!event.thread_id) {
|
|
3258
|
+
logger.debug('Auto-link skipped: no thread_id in message_received event');
|
|
2780
3259
|
return;
|
|
2781
3260
|
}
|
|
2782
3261
|
// Skip if no content and no sender info (nothing to match on)
|
|
@@ -2788,9 +3267,9 @@ export const registerOpenClaw = (api) => {
|
|
|
2788
3267
|
await autoLinkInboundMessage({
|
|
2789
3268
|
client: apiClient,
|
|
2790
3269
|
logger,
|
|
2791
|
-
|
|
3270
|
+
user_id,
|
|
2792
3271
|
message: {
|
|
2793
|
-
|
|
3272
|
+
thread_id: event.thread_id,
|
|
2794
3273
|
senderEmail: event.senderEmail ?? (event.sender?.includes('@') ? event.sender : undefined),
|
|
2795
3274
|
senderPhone: event.senderPhone ?? (event.sender && !event.sender.includes('@') ? event.sender : undefined),
|
|
2796
3275
|
content: event.content ?? '',
|
|
@@ -2815,14 +3294,14 @@ export const registerOpenClaw = (api) => {
|
|
|
2815
3294
|
const gatewayMethods = createGatewayMethods({
|
|
2816
3295
|
logger,
|
|
2817
3296
|
apiClient,
|
|
2818
|
-
|
|
3297
|
+
user_id,
|
|
2819
3298
|
});
|
|
2820
3299
|
registerGatewayRpcMethods(api, gatewayMethods);
|
|
2821
3300
|
// Register OAuth Gateway RPC methods (Issue #1054)
|
|
2822
3301
|
const oauthGatewayMethods = createOAuthGatewayMethods({
|
|
2823
3302
|
logger,
|
|
2824
3303
|
apiClient,
|
|
2825
|
-
|
|
3304
|
+
user_id,
|
|
2826
3305
|
});
|
|
2827
3306
|
registerOAuthGatewayRpcMethods(api, oauthGatewayMethods);
|
|
2828
3307
|
// Register background notification service (Issue #325)
|
|
@@ -2843,7 +3322,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2843
3322
|
const notificationService = createNotificationService({
|
|
2844
3323
|
logger,
|
|
2845
3324
|
apiClient,
|
|
2846
|
-
|
|
3325
|
+
user_id,
|
|
2847
3326
|
events: eventEmitter,
|
|
2848
3327
|
config: {
|
|
2849
3328
|
enabled: config.autoRecall, // Only enable if auto-recall is enabled
|
|
@@ -2858,7 +3337,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2858
3337
|
.description('Show plugin status and statistics')
|
|
2859
3338
|
.action(async () => {
|
|
2860
3339
|
try {
|
|
2861
|
-
const response = await apiClient.get('/api/health', {
|
|
3340
|
+
const response = await apiClient.get('/api/health', { user_id });
|
|
2862
3341
|
if (response.success) {
|
|
2863
3342
|
console.log('Plugin Status: Connected');
|
|
2864
3343
|
}
|
|
@@ -2892,7 +3371,7 @@ export const registerOpenClaw = (api) => {
|
|
|
2892
3371
|
logger.info('OpenClaw Projects plugin registered', {
|
|
2893
3372
|
agentId: context.agent.agentId,
|
|
2894
3373
|
sessionId: context.session.sessionId,
|
|
2895
|
-
|
|
3374
|
+
user_id,
|
|
2896
3375
|
toolCount: tools.length,
|
|
2897
3376
|
config: redactConfig(config),
|
|
2898
3377
|
});
|
|
@@ -2901,27 +3380,27 @@ export const registerOpenClaw = (api) => {
|
|
|
2901
3380
|
export default registerOpenClaw;
|
|
2902
3381
|
/** Export JSON Schemas for external use */
|
|
2903
3382
|
export const schemas = {
|
|
2904
|
-
memoryRecall: memoryRecallSchema,
|
|
2905
|
-
memoryStore: memoryStoreSchema,
|
|
2906
|
-
memoryForget: memoryForgetSchema,
|
|
2907
|
-
projectList: projectListSchema,
|
|
2908
|
-
projectGet: projectGetSchema,
|
|
2909
|
-
projectCreate: projectCreateSchema,
|
|
2910
|
-
todoList: todoListSchema,
|
|
2911
|
-
todoCreate: todoCreateSchema,
|
|
2912
|
-
todoComplete: todoCompleteSchema,
|
|
2913
|
-
todoSearch: todoSearchSchema,
|
|
2914
|
-
projectSearch: projectSearchSchema,
|
|
2915
|
-
contactSearch: contactSearchSchema,
|
|
2916
|
-
contactGet: contactGetSchema,
|
|
2917
|
-
contactCreate: contactCreateSchema,
|
|
3383
|
+
memoryRecall: withNamespaces(memoryRecallSchema),
|
|
3384
|
+
memoryStore: withNamespace(memoryStoreSchema),
|
|
3385
|
+
memoryForget: withNamespaces(memoryForgetSchema),
|
|
3386
|
+
projectList: withNamespaces(projectListSchema),
|
|
3387
|
+
projectGet: withNamespaces(projectGetSchema),
|
|
3388
|
+
projectCreate: withNamespace(projectCreateSchema),
|
|
3389
|
+
todoList: withNamespaces(todoListSchema),
|
|
3390
|
+
todoCreate: withNamespace(todoCreateSchema),
|
|
3391
|
+
todoComplete: withNamespaces(todoCompleteSchema),
|
|
3392
|
+
todoSearch: withNamespaces(todoSearchSchema),
|
|
3393
|
+
projectSearch: withNamespaces(projectSearchSchema),
|
|
3394
|
+
contactSearch: withNamespaces(contactSearchSchema),
|
|
3395
|
+
contactGet: withNamespaces(contactGetSchema),
|
|
3396
|
+
contactCreate: withNamespace(contactCreateSchema),
|
|
2918
3397
|
smsSend: smsSendSchema,
|
|
2919
3398
|
emailSend: emailSendSchema,
|
|
2920
|
-
messageSearch: messageSearchSchema,
|
|
2921
|
-
threadList: threadListSchema,
|
|
2922
|
-
threadGet: threadGetSchema,
|
|
2923
|
-
relationshipSet: relationshipSetSchema,
|
|
2924
|
-
relationshipQuery: relationshipQuerySchema,
|
|
3399
|
+
messageSearch: withNamespaces(messageSearchSchema),
|
|
3400
|
+
threadList: withNamespaces(threadListSchema),
|
|
3401
|
+
threadGet: withNamespaces(threadGetSchema),
|
|
3402
|
+
relationshipSet: withNamespace(relationshipSetSchema),
|
|
3403
|
+
relationshipQuery: withNamespaces(relationshipQuerySchema),
|
|
2925
3404
|
fileShare: fileShareSchema,
|
|
2926
3405
|
skillStorePut: skillStorePutSchema,
|
|
2927
3406
|
skillStoreGet: skillStoreGetSchema,
|