@troykelly/openclaw-projects 0.0.25 → 0.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +19 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +35 -0
- package/dist/context.js.map +1 -1
- package/dist/gateway/oauth-rpc-methods.d.ts +1 -1
- package/dist/gateway/oauth-rpc-methods.d.ts.map +1 -1
- package/dist/gateway/oauth-rpc-methods.js +11 -1
- package/dist/gateway/oauth-rpc-methods.js.map +1 -1
- package/dist/gateway/rpc-methods.d.ts +2 -2
- package/dist/gateway/rpc-methods.d.ts.map +1 -1
- package/dist/gateway/rpc-methods.js +4 -1
- 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 +6 -3
- package/dist/hooks.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/register-openclaw.d.ts +6 -3
- package/dist/register-openclaw.d.ts.map +1 -1
- package/dist/register-openclaw.js +126 -80
- package/dist/register-openclaw.js.map +1 -1
- package/dist/services/notification-service.d.ts +2 -2
- package/dist/services/notification-service.d.ts.map +1 -1
- package/dist/services/notification-service.js +5 -2
- package/dist/services/notification-service.js.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 +2 -1
- package/dist/utils/auto-linker.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { ZodError } from 'zod';
|
|
11
11
|
import { createApiClient } from './api-client.js';
|
|
12
12
|
import { redactConfig, resolveConfigSecretsSync, resolveNamespaceConfig, validateRawConfig } from './config.js';
|
|
13
|
-
import { extractContext, getUserScopeKey } from './context.js';
|
|
13
|
+
import { extractContext, getUserScopeKey, resolveAgentId } from './context.js';
|
|
14
14
|
import { createOAuthGatewayMethods, registerOAuthGatewayRpcMethods } from './gateway/oauth-rpc-methods.js';
|
|
15
15
|
import { createGatewayMethods, registerGatewayRpcMethods } from './gateway/rpc-methods.js';
|
|
16
16
|
import { createAutoCaptureHook, createGraphAwareRecallHook } from './hooks.js';
|
|
@@ -1449,7 +1449,7 @@ export async function refreshNamespacesAsync(state) {
|
|
|
1449
1449
|
return;
|
|
1450
1450
|
state.refreshInFlight = true;
|
|
1451
1451
|
try {
|
|
1452
|
-
const response = await state.apiClient.get('/api/namespaces', { user_id: state.
|
|
1452
|
+
const response = await state.apiClient.get('/api/namespaces', { user_id: state.agentId, user_email: state.agentEmail });
|
|
1453
1453
|
if (!response.success) {
|
|
1454
1454
|
state.logger.warn('Namespace discovery failed, keeping cached list', { error: response.error.message });
|
|
1455
1455
|
// Do NOT update timestamp on failure — let the next check retry sooner
|
|
@@ -1487,18 +1487,20 @@ export async function refreshNamespacesAsync(state) {
|
|
|
1487
1487
|
* Create tool execution handlers
|
|
1488
1488
|
*/
|
|
1489
1489
|
function createToolHandlers(state) {
|
|
1490
|
-
const { config, logger, apiClient
|
|
1491
|
-
|
|
1492
|
-
const
|
|
1493
|
-
/**
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1490
|
+
const { config, logger, apiClient } = state;
|
|
1491
|
+
// Issue #1644: Read user_id from mutable state on every call.
|
|
1492
|
+
const getAgentId = () => state.agentId;
|
|
1493
|
+
/** Read user_id from mutable state on every call (Issue #1644) */
|
|
1494
|
+
const reqOpts = () => ({
|
|
1495
|
+
user_id: state.agentId,
|
|
1496
|
+
user_email: state.agentEmail,
|
|
1497
|
+
});
|
|
1498
|
+
/** Read namespace from mutable state on every call (Issue #1644) */
|
|
1497
1499
|
function getStoreNamespace(params) {
|
|
1498
1500
|
const ns = params.namespace;
|
|
1499
1501
|
if (typeof ns === 'string' && ns.length > 0)
|
|
1500
1502
|
return ns;
|
|
1501
|
-
return resolvedNamespace.default;
|
|
1503
|
+
return state.resolvedNamespace.default;
|
|
1502
1504
|
}
|
|
1503
1505
|
/**
|
|
1504
1506
|
* Get the effective namespaces for a query/list operation.
|
|
@@ -1514,7 +1516,7 @@ function createToolHandlers(state) {
|
|
|
1514
1516
|
if (interval > 0 && !state.hasStaticRecall && Date.now() - state.lastNamespaceRefreshMs > interval) {
|
|
1515
1517
|
refreshNamespacesAsync(state);
|
|
1516
1518
|
}
|
|
1517
|
-
return resolvedNamespace.recall;
|
|
1519
|
+
return state.resolvedNamespace.recall;
|
|
1518
1520
|
}
|
|
1519
1521
|
return {
|
|
1520
1522
|
async memory_recall(params) {
|
|
@@ -1572,7 +1574,7 @@ function createToolHandlers(state) {
|
|
|
1572
1574
|
success: true,
|
|
1573
1575
|
data: {
|
|
1574
1576
|
content,
|
|
1575
|
-
details: { count: memories.length, memories, user_id },
|
|
1577
|
+
details: { count: memories.length, memories, user_id: state.agentId },
|
|
1576
1578
|
},
|
|
1577
1579
|
};
|
|
1578
1580
|
}
|
|
@@ -1704,7 +1706,7 @@ function createToolHandlers(state) {
|
|
|
1704
1706
|
const queryParams = new URLSearchParams({ item_type: 'project', limit: String(limit) });
|
|
1705
1707
|
if (status !== 'all')
|
|
1706
1708
|
queryParams.set('status', status);
|
|
1707
|
-
queryParams.set('user_email',
|
|
1709
|
+
queryParams.set('user_email', state.agentId); // Issue #1172: scope by user
|
|
1708
1710
|
// Namespace scoping (Issue #1428)
|
|
1709
1711
|
const projListNs = getRecallNamespaces(params);
|
|
1710
1712
|
if (projListNs.length > 0)
|
|
@@ -1728,7 +1730,7 @@ function createToolHandlers(state) {
|
|
|
1728
1730
|
async project_get(params) {
|
|
1729
1731
|
const { project_id } = params;
|
|
1730
1732
|
try {
|
|
1731
|
-
const response = await apiClient.get(`/api/work-items/${project_id}?user_email=${encodeURIComponent(
|
|
1733
|
+
const response = await apiClient.get(`/api/work-items/${project_id}?user_email=${encodeURIComponent(state.agentId)}`, reqOpts());
|
|
1732
1734
|
if (!response.success) {
|
|
1733
1735
|
return { success: false, error: response.error.message };
|
|
1734
1736
|
}
|
|
@@ -1749,7 +1751,7 @@ function createToolHandlers(state) {
|
|
|
1749
1751
|
async project_create(params) {
|
|
1750
1752
|
const { name, description, status = 'active', } = params;
|
|
1751
1753
|
try {
|
|
1752
|
-
const response = await apiClient.post('/api/work-items', { title: name, description, item_type: 'project', status, user_email:
|
|
1754
|
+
const response = await apiClient.post('/api/work-items', { title: name, description, item_type: 'project', status, user_email: state.agentId, namespace: getStoreNamespace(params) }, reqOpts());
|
|
1753
1755
|
if (!response.success) {
|
|
1754
1756
|
return { success: false, error: response.error.message };
|
|
1755
1757
|
}
|
|
@@ -1773,7 +1775,7 @@ function createToolHandlers(state) {
|
|
|
1773
1775
|
item_type: 'task',
|
|
1774
1776
|
limit: String(limit),
|
|
1775
1777
|
offset: String(offset),
|
|
1776
|
-
user_email:
|
|
1778
|
+
user_email: state.agentId, // Issue #1172: scope by user
|
|
1777
1779
|
});
|
|
1778
1780
|
if (project_id)
|
|
1779
1781
|
queryParams.set('parent_work_item_id', project_id);
|
|
@@ -1816,7 +1818,7 @@ function createToolHandlers(state) {
|
|
|
1816
1818
|
async todo_create(params) {
|
|
1817
1819
|
const { title, description, project_id, priority = 'medium', dueDate, } = params;
|
|
1818
1820
|
try {
|
|
1819
|
-
const body = { title, description, item_type: 'task', priority, user_email:
|
|
1821
|
+
const body = { title, description, item_type: 'task', priority, user_email: state.agentId, namespace: getStoreNamespace(params) };
|
|
1820
1822
|
if (project_id)
|
|
1821
1823
|
body.parent_work_item_id = project_id;
|
|
1822
1824
|
if (dueDate)
|
|
@@ -1841,7 +1843,7 @@ function createToolHandlers(state) {
|
|
|
1841
1843
|
async todo_complete(params) {
|
|
1842
1844
|
const { todoId } = params;
|
|
1843
1845
|
try {
|
|
1844
|
-
const response = await apiClient.patch(`/api/work-items/${todoId}/status?user_email=${encodeURIComponent(
|
|
1846
|
+
const response = await apiClient.patch(`/api/work-items/${todoId}/status?user_email=${encodeURIComponent(state.agentId)}`, { status: 'completed' }, reqOpts());
|
|
1845
1847
|
if (!response.success) {
|
|
1846
1848
|
return { success: false, error: response.error.message };
|
|
1847
1849
|
}
|
|
@@ -1868,7 +1870,7 @@ function createToolHandlers(state) {
|
|
|
1868
1870
|
types: 'work_item',
|
|
1869
1871
|
limit: String(fetchLimit),
|
|
1870
1872
|
semantic: 'true',
|
|
1871
|
-
user_email:
|
|
1873
|
+
user_email: state.agentId, // Issue #1216: scope results to current user
|
|
1872
1874
|
});
|
|
1873
1875
|
// Namespace scoping (Issue #1428)
|
|
1874
1876
|
const todoSearchNs = getRecallNamespaces(params);
|
|
@@ -1929,22 +1931,22 @@ function createToolHandlers(state) {
|
|
|
1929
1931
|
}
|
|
1930
1932
|
},
|
|
1931
1933
|
async project_search(params) {
|
|
1932
|
-
const tool = createProjectSearchTool({ client: apiClient, logger, config, user_id });
|
|
1934
|
+
const tool = createProjectSearchTool({ client: apiClient, logger, config, user_id: getAgentId() });
|
|
1933
1935
|
return tool.execute(params);
|
|
1934
1936
|
},
|
|
1935
1937
|
async context_search(params) {
|
|
1936
|
-
const tool = createContextSearchTool({ client: apiClient, logger, config, user_id });
|
|
1938
|
+
const tool = createContextSearchTool({ client: apiClient, logger, config, user_id: getAgentId() });
|
|
1937
1939
|
return tool.execute(params);
|
|
1938
1940
|
},
|
|
1939
1941
|
async contact_search(params) {
|
|
1940
1942
|
const { query, limit = 10 } = params;
|
|
1941
1943
|
try {
|
|
1942
|
-
const queryParams = new URLSearchParams({ search: query, limit: String(limit), user_email:
|
|
1944
|
+
const queryParams = new URLSearchParams({ search: query, limit: String(limit), user_email: state.agentId });
|
|
1943
1945
|
const contactSearchNs = getRecallNamespaces(params);
|
|
1944
1946
|
if (contactSearchNs.length > 0)
|
|
1945
1947
|
queryParams.set('namespaces', contactSearchNs.join(','));
|
|
1946
1948
|
const response = await apiClient.get(`/api/contacts?${queryParams}`, {
|
|
1947
|
-
user_id,
|
|
1949
|
+
user_id: state.agentId,
|
|
1948
1950
|
});
|
|
1949
1951
|
if (!response.success) {
|
|
1950
1952
|
return { success: false, error: response.error.message };
|
|
@@ -1964,7 +1966,7 @@ function createToolHandlers(state) {
|
|
|
1964
1966
|
async contact_get(params) {
|
|
1965
1967
|
const { contact_id } = params;
|
|
1966
1968
|
try {
|
|
1967
|
-
const response = await apiClient.get(`/api/contacts/${contact_id}?user_email=${encodeURIComponent(
|
|
1969
|
+
const response = await apiClient.get(`/api/contacts/${contact_id}?user_email=${encodeURIComponent(state.agentId)}`, reqOpts());
|
|
1968
1970
|
if (!response.success) {
|
|
1969
1971
|
return { success: false, error: response.error.message };
|
|
1970
1972
|
}
|
|
@@ -1995,7 +1997,7 @@ function createToolHandlers(state) {
|
|
|
1995
1997
|
}
|
|
1996
1998
|
try {
|
|
1997
1999
|
const body = {
|
|
1998
|
-
user_email:
|
|
2000
|
+
user_email: state.agentId,
|
|
1999
2001
|
namespace: getStoreNamespace(params),
|
|
2000
2002
|
notes,
|
|
2001
2003
|
contact_kind: contact_kind ?? 'person',
|
|
@@ -2206,7 +2208,7 @@ function createToolHandlers(state) {
|
|
|
2206
2208
|
};
|
|
2207
2209
|
}
|
|
2208
2210
|
logger.info('sms_send invoked', {
|
|
2209
|
-
user_id,
|
|
2211
|
+
user_id: state.agentId,
|
|
2210
2212
|
bodyLength: body.length,
|
|
2211
2213
|
hasIdempotencyKey: !!idempotency_key,
|
|
2212
2214
|
});
|
|
@@ -2214,7 +2216,7 @@ function createToolHandlers(state) {
|
|
|
2214
2216
|
const response = await apiClient.post('/api/twilio/sms/send', { to, body, idempotency_key }, reqOpts());
|
|
2215
2217
|
if (!response.success) {
|
|
2216
2218
|
logger.error('sms_send API error', {
|
|
2217
|
-
user_id,
|
|
2219
|
+
user_id: state.agentId,
|
|
2218
2220
|
status: response.error.status,
|
|
2219
2221
|
code: response.error.code,
|
|
2220
2222
|
});
|
|
@@ -2225,7 +2227,7 @@ function createToolHandlers(state) {
|
|
|
2225
2227
|
}
|
|
2226
2228
|
const { message_id, thread_id, status } = response.data;
|
|
2227
2229
|
logger.debug('sms_send completed', {
|
|
2228
|
-
user_id,
|
|
2230
|
+
user_id: state.agentId,
|
|
2229
2231
|
message_id,
|
|
2230
2232
|
status,
|
|
2231
2233
|
});
|
|
@@ -2233,13 +2235,13 @@ function createToolHandlers(state) {
|
|
|
2233
2235
|
success: true,
|
|
2234
2236
|
data: {
|
|
2235
2237
|
content: `SMS sent successfully (ID: ${message_id}, Status: ${status})`,
|
|
2236
|
-
details: { message_id, thread_id, status, user_id },
|
|
2238
|
+
details: { message_id, thread_id, status, user_id: state.agentId },
|
|
2237
2239
|
},
|
|
2238
2240
|
};
|
|
2239
2241
|
}
|
|
2240
2242
|
catch (error) {
|
|
2241
2243
|
logger.error('sms_send failed', {
|
|
2242
|
-
user_id,
|
|
2244
|
+
user_id: state.agentId,
|
|
2243
2245
|
error: error instanceof Error ? error.message : String(error),
|
|
2244
2246
|
});
|
|
2245
2247
|
// Sanitize error message (remove phone numbers for privacy)
|
|
@@ -2284,7 +2286,7 @@ function createToolHandlers(state) {
|
|
|
2284
2286
|
};
|
|
2285
2287
|
}
|
|
2286
2288
|
logger.info('email_send invoked', {
|
|
2287
|
-
user_id,
|
|
2289
|
+
user_id: state.agentId,
|
|
2288
2290
|
subjectLength: subject.length,
|
|
2289
2291
|
bodyLength: body.length,
|
|
2290
2292
|
hasHtmlBody: !!html_body,
|
|
@@ -2295,7 +2297,7 @@ function createToolHandlers(state) {
|
|
|
2295
2297
|
const response = await apiClient.post('/api/postmark/email/send', { to, subject, body, html_body, thread_id, idempotency_key }, reqOpts());
|
|
2296
2298
|
if (!response.success) {
|
|
2297
2299
|
logger.error('email_send API error', {
|
|
2298
|
-
user_id,
|
|
2300
|
+
user_id: state.agentId,
|
|
2299
2301
|
status: response.error.status,
|
|
2300
2302
|
code: response.error.code,
|
|
2301
2303
|
});
|
|
@@ -2306,7 +2308,7 @@ function createToolHandlers(state) {
|
|
|
2306
2308
|
}
|
|
2307
2309
|
const { message_id, thread_id: responseThreadId, status } = response.data;
|
|
2308
2310
|
logger.debug('email_send completed', {
|
|
2309
|
-
user_id,
|
|
2311
|
+
user_id: state.agentId,
|
|
2310
2312
|
message_id,
|
|
2311
2313
|
status,
|
|
2312
2314
|
});
|
|
@@ -2314,13 +2316,13 @@ function createToolHandlers(state) {
|
|
|
2314
2316
|
success: true,
|
|
2315
2317
|
data: {
|
|
2316
2318
|
content: `Email sent successfully (ID: ${message_id}, Status: ${status})`,
|
|
2317
|
-
details: { message_id, thread_id: responseThreadId, status, user_id },
|
|
2319
|
+
details: { message_id, thread_id: responseThreadId, status, user_id: state.agentId },
|
|
2318
2320
|
},
|
|
2319
2321
|
};
|
|
2320
2322
|
}
|
|
2321
2323
|
catch (error) {
|
|
2322
2324
|
logger.error('email_send failed', {
|
|
2323
|
-
user_id,
|
|
2325
|
+
user_id: state.agentId,
|
|
2324
2326
|
error: error instanceof Error ? error.message : String(error),
|
|
2325
2327
|
});
|
|
2326
2328
|
// Sanitize error message (remove email addresses for privacy)
|
|
@@ -2344,7 +2346,7 @@ function createToolHandlers(state) {
|
|
|
2344
2346
|
};
|
|
2345
2347
|
}
|
|
2346
2348
|
logger.info('message_search invoked', {
|
|
2347
|
-
user_id,
|
|
2349
|
+
user_id: state.agentId,
|
|
2348
2350
|
queryLength: query.length,
|
|
2349
2351
|
channel,
|
|
2350
2352
|
hasContactId: !!contact_id,
|
|
@@ -2370,7 +2372,7 @@ function createToolHandlers(state) {
|
|
|
2370
2372
|
const response = await apiClient.get(`/api/search?${queryParams}`, reqOpts());
|
|
2371
2373
|
if (!response.success) {
|
|
2372
2374
|
logger.error('message_search API error', {
|
|
2373
|
-
user_id,
|
|
2375
|
+
user_id: state.agentId,
|
|
2374
2376
|
status: response.error.status,
|
|
2375
2377
|
code: response.error.code,
|
|
2376
2378
|
});
|
|
@@ -2391,7 +2393,7 @@ function createToolHandlers(state) {
|
|
|
2391
2393
|
similarity: r.score,
|
|
2392
2394
|
}));
|
|
2393
2395
|
logger.debug('message_search completed', {
|
|
2394
|
-
user_id,
|
|
2396
|
+
user_id: state.agentId,
|
|
2395
2397
|
resultCount: messages.length,
|
|
2396
2398
|
total,
|
|
2397
2399
|
});
|
|
@@ -2406,10 +2408,10 @@ function createToolHandlers(state) {
|
|
|
2406
2408
|
promptGuardUrl: config.promptGuardUrl,
|
|
2407
2409
|
});
|
|
2408
2410
|
if (detection.detected) {
|
|
2409
|
-
const logDecision = injectionLogLimiter.shouldLog(
|
|
2411
|
+
const logDecision = injectionLogLimiter.shouldLog(state.agentId);
|
|
2410
2412
|
if (logDecision.log) {
|
|
2411
2413
|
logger.warn(logDecision.summary ? 'injection detection log summary for previous window' : 'potential prompt injection detected in message_search result', {
|
|
2412
|
-
user_id,
|
|
2414
|
+
user_id: state.agentId,
|
|
2413
2415
|
message_id: m.id,
|
|
2414
2416
|
patterns: detection.patterns,
|
|
2415
2417
|
source: detection.source,
|
|
@@ -2446,13 +2448,13 @@ function createToolHandlers(state) {
|
|
|
2446
2448
|
success: true,
|
|
2447
2449
|
data: {
|
|
2448
2450
|
content,
|
|
2449
|
-
details: { messages, total, user_id },
|
|
2451
|
+
details: { messages, total, user_id: state.agentId },
|
|
2450
2452
|
},
|
|
2451
2453
|
};
|
|
2452
2454
|
}
|
|
2453
2455
|
catch (error) {
|
|
2454
2456
|
logger.error('message_search failed', {
|
|
2455
|
-
user_id,
|
|
2457
|
+
user_id: state.agentId,
|
|
2456
2458
|
error: error instanceof Error ? error.message : String(error),
|
|
2457
2459
|
});
|
|
2458
2460
|
return {
|
|
@@ -2464,7 +2466,7 @@ function createToolHandlers(state) {
|
|
|
2464
2466
|
async thread_list(params) {
|
|
2465
2467
|
const { channel, contact_id, limit = 20, } = params;
|
|
2466
2468
|
logger.info('thread_list invoked', {
|
|
2467
|
-
user_id,
|
|
2469
|
+
user_id: state.agentId,
|
|
2468
2470
|
channel,
|
|
2469
2471
|
hasContactId: !!contact_id,
|
|
2470
2472
|
limit,
|
|
@@ -2483,7 +2485,7 @@ function createToolHandlers(state) {
|
|
|
2483
2485
|
const response = await apiClient.get(`/api/search?${queryParams}`, reqOpts());
|
|
2484
2486
|
if (!response.success) {
|
|
2485
2487
|
logger.error('thread_list API error', {
|
|
2486
|
-
user_id,
|
|
2488
|
+
user_id: state.agentId,
|
|
2487
2489
|
status: response.error.status,
|
|
2488
2490
|
code: response.error.code,
|
|
2489
2491
|
});
|
|
@@ -2496,7 +2498,7 @@ function createToolHandlers(state) {
|
|
|
2496
2498
|
const results = response.data.results ?? response.data.threads ?? [];
|
|
2497
2499
|
const total = response.data.total ?? results.length;
|
|
2498
2500
|
logger.debug('thread_list completed', {
|
|
2499
|
-
user_id,
|
|
2501
|
+
user_id: state.agentId,
|
|
2500
2502
|
threadCount: results.length,
|
|
2501
2503
|
total,
|
|
2502
2504
|
});
|
|
@@ -2525,13 +2527,13 @@ function createToolHandlers(state) {
|
|
|
2525
2527
|
success: true,
|
|
2526
2528
|
data: {
|
|
2527
2529
|
content,
|
|
2528
|
-
details: { threads: results, total, user_id },
|
|
2530
|
+
details: { threads: results, total, user_id: state.agentId },
|
|
2529
2531
|
},
|
|
2530
2532
|
};
|
|
2531
2533
|
}
|
|
2532
2534
|
catch (error) {
|
|
2533
2535
|
logger.error('thread_list failed', {
|
|
2534
|
-
user_id,
|
|
2536
|
+
user_id: state.agentId,
|
|
2535
2537
|
error: error instanceof Error ? error.message : String(error),
|
|
2536
2538
|
});
|
|
2537
2539
|
return {
|
|
@@ -2550,7 +2552,7 @@ function createToolHandlers(state) {
|
|
|
2550
2552
|
};
|
|
2551
2553
|
}
|
|
2552
2554
|
logger.info('thread_get invoked', {
|
|
2553
|
-
user_id,
|
|
2555
|
+
user_id: state.agentId,
|
|
2554
2556
|
thread_id,
|
|
2555
2557
|
message_limit,
|
|
2556
2558
|
});
|
|
@@ -2560,7 +2562,7 @@ function createToolHandlers(state) {
|
|
|
2560
2562
|
const response = await apiClient.get(`/api/threads/${thread_id}/history?${queryParams}`, reqOpts());
|
|
2561
2563
|
if (!response.success) {
|
|
2562
2564
|
logger.error('thread_get API error', {
|
|
2563
|
-
user_id,
|
|
2565
|
+
user_id: state.agentId,
|
|
2564
2566
|
thread_id,
|
|
2565
2567
|
status: response.error.status,
|
|
2566
2568
|
code: response.error.code,
|
|
@@ -2572,7 +2574,7 @@ function createToolHandlers(state) {
|
|
|
2572
2574
|
}
|
|
2573
2575
|
const { thread, messages } = response.data;
|
|
2574
2576
|
logger.debug('thread_get completed', {
|
|
2575
|
-
user_id,
|
|
2577
|
+
user_id: state.agentId,
|
|
2576
2578
|
thread_id,
|
|
2577
2579
|
message_count: messages.length,
|
|
2578
2580
|
});
|
|
@@ -2589,10 +2591,10 @@ function createToolHandlers(state) {
|
|
|
2589
2591
|
promptGuardUrl: config.promptGuardUrl,
|
|
2590
2592
|
});
|
|
2591
2593
|
if (detection.detected) {
|
|
2592
|
-
const logDecision = injectionLogLimiter.shouldLog(
|
|
2594
|
+
const logDecision = injectionLogLimiter.shouldLog(state.agentId);
|
|
2593
2595
|
if (logDecision.log) {
|
|
2594
2596
|
logger.warn(logDecision.summary ? 'injection detection log summary for previous window' : 'potential prompt injection detected in thread_get result', {
|
|
2595
|
-
user_id,
|
|
2597
|
+
user_id: state.agentId,
|
|
2596
2598
|
thread_id,
|
|
2597
2599
|
message_id: m.id,
|
|
2598
2600
|
patterns: detection.patterns,
|
|
@@ -2623,13 +2625,13 @@ function createToolHandlers(state) {
|
|
|
2623
2625
|
success: true,
|
|
2624
2626
|
data: {
|
|
2625
2627
|
content,
|
|
2626
|
-
details: { thread, messages, user_id },
|
|
2628
|
+
details: { thread, messages, user_id: state.agentId },
|
|
2627
2629
|
},
|
|
2628
2630
|
};
|
|
2629
2631
|
}
|
|
2630
2632
|
catch (error) {
|
|
2631
2633
|
logger.error('thread_get failed', {
|
|
2632
|
-
user_id,
|
|
2634
|
+
user_id: state.agentId,
|
|
2633
2635
|
thread_id,
|
|
2634
2636
|
error: error instanceof Error ? error.message : String(error),
|
|
2635
2637
|
});
|
|
@@ -2648,7 +2650,7 @@ function createToolHandlers(state) {
|
|
|
2648
2650
|
};
|
|
2649
2651
|
}
|
|
2650
2652
|
logger.info('relationship_set invoked', {
|
|
2651
|
-
user_id,
|
|
2653
|
+
user_id: state.agentId,
|
|
2652
2654
|
contactALength: contact_a.length,
|
|
2653
2655
|
contactBLength: contact_b.length,
|
|
2654
2656
|
relationshipLength: relationship.length,
|
|
@@ -2659,7 +2661,7 @@ function createToolHandlers(state) {
|
|
|
2659
2661
|
contact_a,
|
|
2660
2662
|
contact_b,
|
|
2661
2663
|
relationship_type: relationship,
|
|
2662
|
-
user_email:
|
|
2664
|
+
user_email: state.agentId, // Issue #1172: scope by user
|
|
2663
2665
|
namespace: getStoreNamespace(params), // Issue #1428
|
|
2664
2666
|
};
|
|
2665
2667
|
if (notes) {
|
|
@@ -2683,7 +2685,7 @@ function createToolHandlers(state) {
|
|
|
2683
2685
|
contact_a: respA,
|
|
2684
2686
|
contact_b: respB,
|
|
2685
2687
|
relationship_type,
|
|
2686
|
-
user_id,
|
|
2688
|
+
user_id: state.agentId,
|
|
2687
2689
|
},
|
|
2688
2690
|
},
|
|
2689
2691
|
};
|
|
@@ -2702,7 +2704,7 @@ function createToolHandlers(state) {
|
|
|
2702
2704
|
};
|
|
2703
2705
|
}
|
|
2704
2706
|
logger.info('relationship_query invoked', {
|
|
2705
|
-
user_id,
|
|
2707
|
+
user_id: state.agentId,
|
|
2706
2708
|
contactLength: contact.length,
|
|
2707
2709
|
hasTypeFilter: !!type_filter,
|
|
2708
2710
|
});
|
|
@@ -2715,7 +2717,7 @@ function createToolHandlers(state) {
|
|
|
2715
2717
|
}
|
|
2716
2718
|
else {
|
|
2717
2719
|
// Search for contact by name (Issue #1172: scope by user_email)
|
|
2718
|
-
const searchParams = new URLSearchParams({ search: contact, limit: '1', user_email:
|
|
2720
|
+
const searchParams = new URLSearchParams({ search: contact, limit: '1', user_email: state.agentId });
|
|
2719
2721
|
const searchResponse = await apiClient.get(`/api/contacts?${searchParams}`, reqOpts());
|
|
2720
2722
|
if (!searchResponse.success) {
|
|
2721
2723
|
return { success: false, error: searchResponse.error.message };
|
|
@@ -2727,7 +2729,7 @@ function createToolHandlers(state) {
|
|
|
2727
2729
|
contact_id = contacts[0].id;
|
|
2728
2730
|
}
|
|
2729
2731
|
// Use graph traversal endpoint which returns related_contacts
|
|
2730
|
-
const response = await apiClient.get(`/api/contacts/${contact_id}/relationships?user_email=${encodeURIComponent(
|
|
2732
|
+
const response = await apiClient.get(`/api/contacts/${contact_id}/relationships?user_email=${encodeURIComponent(state.agentId)}`, reqOpts());
|
|
2731
2733
|
if (!response.success) {
|
|
2732
2734
|
if (response.error.code === 'NOT_FOUND') {
|
|
2733
2735
|
return { success: false, error: 'Contact not found.' };
|
|
@@ -2746,7 +2748,7 @@ function createToolHandlers(state) {
|
|
|
2746
2748
|
success: true,
|
|
2747
2749
|
data: {
|
|
2748
2750
|
content: `No relationships found for ${contact_name}.`,
|
|
2749
|
-
details: { contact_id, contact_name, related_contacts: [], user_id },
|
|
2751
|
+
details: { contact_id, contact_name, related_contacts: [], user_id: state.agentId },
|
|
2750
2752
|
},
|
|
2751
2753
|
};
|
|
2752
2754
|
}
|
|
@@ -2760,7 +2762,7 @@ function createToolHandlers(state) {
|
|
|
2760
2762
|
success: true,
|
|
2761
2763
|
data: {
|
|
2762
2764
|
content: lines.join('\n'),
|
|
2763
|
-
details: { contact_id, contact_name, related_contacts, user_id },
|
|
2765
|
+
details: { contact_id, contact_name, related_contacts, user_id: state.agentId },
|
|
2764
2766
|
},
|
|
2765
2767
|
};
|
|
2766
2768
|
}
|
|
@@ -2785,7 +2787,7 @@ function createToolHandlers(state) {
|
|
|
2785
2787
|
};
|
|
2786
2788
|
}
|
|
2787
2789
|
logger.info('file_share invoked', {
|
|
2788
|
-
user_id,
|
|
2790
|
+
user_id: state.agentId,
|
|
2789
2791
|
file_id: fileId,
|
|
2790
2792
|
expires_in: expiresIn,
|
|
2791
2793
|
max_downloads: maxDownloads,
|
|
@@ -2798,7 +2800,7 @@ function createToolHandlers(state) {
|
|
|
2798
2800
|
const response = await apiClient.post(`/api/files/${fileId}/share`, body, reqOpts());
|
|
2799
2801
|
if (!response.success) {
|
|
2800
2802
|
logger.error('file_share API error', {
|
|
2801
|
-
user_id,
|
|
2803
|
+
user_id: state.agentId,
|
|
2802
2804
|
file_id: fileId,
|
|
2803
2805
|
status: response.error.status,
|
|
2804
2806
|
code: response.error.code,
|
|
@@ -2810,7 +2812,7 @@ function createToolHandlers(state) {
|
|
|
2810
2812
|
}
|
|
2811
2813
|
const { url, share_token, expires_at, filename, content_type, size_bytes } = response.data;
|
|
2812
2814
|
logger.debug('file_share completed', {
|
|
2813
|
-
user_id,
|
|
2815
|
+
user_id: state.agentId,
|
|
2814
2816
|
file_id: fileId,
|
|
2815
2817
|
share_token,
|
|
2816
2818
|
expires_at,
|
|
@@ -2850,14 +2852,14 @@ function createToolHandlers(state) {
|
|
|
2850
2852
|
filename,
|
|
2851
2853
|
content_type,
|
|
2852
2854
|
size_bytes,
|
|
2853
|
-
user_id,
|
|
2855
|
+
user_id: state.agentId,
|
|
2854
2856
|
},
|
|
2855
2857
|
},
|
|
2856
2858
|
};
|
|
2857
2859
|
}
|
|
2858
2860
|
catch (error) {
|
|
2859
2861
|
logger.error('file_share failed', {
|
|
2860
|
-
user_id,
|
|
2862
|
+
user_id: state.agentId,
|
|
2861
2863
|
file_id: fileId,
|
|
2862
2864
|
error: error instanceof Error ? error.message : String(error),
|
|
2863
2865
|
});
|
|
@@ -2869,8 +2871,12 @@ function createToolHandlers(state) {
|
|
|
2869
2871
|
},
|
|
2870
2872
|
// Skill store tools: delegate to tool modules for Zod validation,
|
|
2871
2873
|
// credential detection, text sanitization, and error sanitization (Issue #824)
|
|
2874
|
+
// NOTE: These tools capture user_id at creation time. When state.agentId is updated
|
|
2875
|
+
// by hook context (Issue #1644), skill store tools will use the registration-time value.
|
|
2876
|
+
// This is acceptable because skill store operations are scoped by API key, not user_id.
|
|
2877
|
+
// A follow-up issue should refactor tool modules to accept getter functions.
|
|
2872
2878
|
...(() => {
|
|
2873
|
-
const toolOptions = { client: apiClient, logger, config, user_id };
|
|
2879
|
+
const toolOptions = { client: apiClient, logger, config, user_id: getAgentId() };
|
|
2874
2880
|
const putTool = createSkillStorePutTool(toolOptions);
|
|
2875
2881
|
const getTool = createSkillStoreGetTool(toolOptions);
|
|
2876
2882
|
const listTool = createSkillStoreListTool(toolOptions);
|
|
@@ -2890,7 +2896,8 @@ function createToolHandlers(state) {
|
|
|
2890
2896
|
})(),
|
|
2891
2897
|
// Entity link tools: delegate to tool modules (Issue #1220)
|
|
2892
2898
|
...(() => {
|
|
2893
|
-
const toolOptions = { client: apiClient, logger, config, user_id };
|
|
2899
|
+
const toolOptions = { client: apiClient, logger, config, user_id: getAgentId() };
|
|
2900
|
+
// NOTE: Same caveat as skill store tools above (Issue #1644).
|
|
2894
2901
|
const setTool = createLinksSetTool(toolOptions);
|
|
2895
2902
|
const queryTool = createLinksQueryTool(toolOptions);
|
|
2896
2903
|
const removeTool = createLinksRemoveTool(toolOptions);
|
|
@@ -3253,7 +3260,7 @@ export const registerOpenClaw = (api) => {
|
|
|
3253
3260
|
// user_setting.email. The email is needed for FK-constrained operations.
|
|
3254
3261
|
const user_email = context.user?.email;
|
|
3255
3262
|
// Store plugin state
|
|
3256
|
-
const state = { config, logger, apiClient, user_id, user_email, resolvedNamespace, hasStaticRecall, lastNamespaceRefreshMs: 0, refreshInFlight: false };
|
|
3263
|
+
const state = { config, logger, apiClient, agentId: user_id, agentEmail: user_email, resolvedNamespace, hasStaticRecall, lastNamespaceRefreshMs: 0, refreshInFlight: false };
|
|
3257
3264
|
// Create tool handlers
|
|
3258
3265
|
const handlers = createToolHandlers(state);
|
|
3259
3266
|
// Register all 30 tools with correct OpenClaw Gateway execute signature
|
|
@@ -3766,7 +3773,7 @@ export const registerOpenClaw = (api) => {
|
|
|
3766
3773
|
client: apiClient,
|
|
3767
3774
|
logger,
|
|
3768
3775
|
config,
|
|
3769
|
-
|
|
3776
|
+
getAgentId: () => state.agentId,
|
|
3770
3777
|
timeoutMs: HOOK_TIMEOUT_MS,
|
|
3771
3778
|
});
|
|
3772
3779
|
/**
|
|
@@ -3774,7 +3781,29 @@ export const registerOpenClaw = (api) => {
|
|
|
3774
3781
|
* performs semantic memory search, and returns { prependContext } to inject
|
|
3775
3782
|
* relevant memories into the conversation.
|
|
3776
3783
|
*/
|
|
3777
|
-
const beforeAgentStartHandler = async (event,
|
|
3784
|
+
const beforeAgentStartHandler = async (event, ctx) => {
|
|
3785
|
+
// Issue #1655: Detect concurrent session conflict
|
|
3786
|
+
if (state.activeSessionKey && ctx.sessionKey && state.activeSessionKey !== ctx.sessionKey) {
|
|
3787
|
+
logger.warn('Concurrent session detected — agent identity may be stale', {
|
|
3788
|
+
previousSession: state.activeSessionKey,
|
|
3789
|
+
newSession: ctx.sessionKey,
|
|
3790
|
+
previousAgentId: state.agentId,
|
|
3791
|
+
});
|
|
3792
|
+
}
|
|
3793
|
+
state.activeSessionKey = ctx.sessionKey;
|
|
3794
|
+
// Issue #1644: resolve agent ID from hook context and update state
|
|
3795
|
+
const resolvedId = resolveAgentId(ctx, config.agentId, state.agentId);
|
|
3796
|
+
if (resolvedId !== state.agentId) {
|
|
3797
|
+
const previousId = state.agentId;
|
|
3798
|
+
state.agentId = resolvedId;
|
|
3799
|
+
state.resolvedNamespace = resolveNamespaceConfig(config.namespace, resolvedId);
|
|
3800
|
+
logger.info('Agent ID resolved from hook context', {
|
|
3801
|
+
previousId,
|
|
3802
|
+
resolvedId,
|
|
3803
|
+
defaultNamespace: state.resolvedNamespace.default,
|
|
3804
|
+
recallNamespaces: state.resolvedNamespace.recall,
|
|
3805
|
+
});
|
|
3806
|
+
}
|
|
3778
3807
|
logger.debug('Auto-recall hook triggered', {
|
|
3779
3808
|
promptLength: event.prompt?.length ?? 0,
|
|
3780
3809
|
});
|
|
@@ -3811,14 +3840,24 @@ export const registerOpenClaw = (api) => {
|
|
|
3811
3840
|
client: apiClient,
|
|
3812
3841
|
logger,
|
|
3813
3842
|
config,
|
|
3814
|
-
|
|
3843
|
+
getAgentId: () => state.agentId,
|
|
3815
3844
|
timeoutMs: HOOK_TIMEOUT_MS * 2, // Allow more time for capture (10s)
|
|
3816
3845
|
});
|
|
3817
3846
|
/**
|
|
3818
3847
|
* agent_end handler: Extracts messages from the completed conversation,
|
|
3819
3848
|
* filters sensitive content, and posts to the capture API for memory storage.
|
|
3820
3849
|
*/
|
|
3821
|
-
const agentEndHandler = async (event,
|
|
3850
|
+
const agentEndHandler = async (event, ctx) => {
|
|
3851
|
+
// Issue #1644: ensure agent ID is resolved even if before_agent_start didn't fire
|
|
3852
|
+
const resolvedId = resolveAgentId(ctx, config.agentId, state.agentId);
|
|
3853
|
+
if (resolvedId !== state.agentId) {
|
|
3854
|
+
state.agentId = resolvedId;
|
|
3855
|
+
state.resolvedNamespace = resolveNamespaceConfig(config.namespace, resolvedId);
|
|
3856
|
+
logger.info('Agent ID resolved from agent_end context', {
|
|
3857
|
+
resolvedId,
|
|
3858
|
+
defaultNamespace: state.resolvedNamespace.default,
|
|
3859
|
+
});
|
|
3860
|
+
}
|
|
3822
3861
|
logger.debug('Auto-capture hook triggered', {
|
|
3823
3862
|
message_count: event.messages?.length ?? 0,
|
|
3824
3863
|
success: event.success,
|
|
@@ -3843,6 +3882,8 @@ export const registerOpenClaw = (api) => {
|
|
|
3843
3882
|
error: error instanceof Error ? error.message : String(error),
|
|
3844
3883
|
});
|
|
3845
3884
|
}
|
|
3885
|
+
// Issue #1655: Clear session key after agent ends
|
|
3886
|
+
state.activeSessionKey = undefined;
|
|
3846
3887
|
};
|
|
3847
3888
|
if (typeof api.on === 'function') {
|
|
3848
3889
|
// Modern registration: api.on('agent_end', handler)
|
|
@@ -3880,7 +3921,7 @@ export const registerOpenClaw = (api) => {
|
|
|
3880
3921
|
await autoLinkInboundMessage({
|
|
3881
3922
|
client: apiClient,
|
|
3882
3923
|
logger,
|
|
3883
|
-
|
|
3924
|
+
getAgentId: () => state.agentId,
|
|
3884
3925
|
message: {
|
|
3885
3926
|
thread_id: event.thread_id,
|
|
3886
3927
|
senderEmail: event.senderEmail ?? (event.sender?.includes('@') ? event.sender : undefined),
|
|
@@ -3907,14 +3948,14 @@ export const registerOpenClaw = (api) => {
|
|
|
3907
3948
|
const gatewayMethods = createGatewayMethods({
|
|
3908
3949
|
logger,
|
|
3909
3950
|
apiClient,
|
|
3910
|
-
|
|
3951
|
+
getAgentId: () => state.agentId,
|
|
3911
3952
|
});
|
|
3912
3953
|
registerGatewayRpcMethods(api, gatewayMethods);
|
|
3913
3954
|
// Register OAuth Gateway RPC methods (Issue #1054)
|
|
3914
3955
|
const oauthGatewayMethods = createOAuthGatewayMethods({
|
|
3915
3956
|
logger,
|
|
3916
3957
|
apiClient,
|
|
3917
|
-
|
|
3958
|
+
getAgentId: () => state.agentId,
|
|
3918
3959
|
});
|
|
3919
3960
|
registerOAuthGatewayRpcMethods(api, oauthGatewayMethods);
|
|
3920
3961
|
// Register background notification service (Issue #325)
|
|
@@ -3935,7 +3976,7 @@ export const registerOpenClaw = (api) => {
|
|
|
3935
3976
|
const notificationService = createNotificationService({
|
|
3936
3977
|
logger,
|
|
3937
3978
|
apiClient,
|
|
3938
|
-
|
|
3979
|
+
getAgentId: () => state.agentId,
|
|
3939
3980
|
events: eventEmitter,
|
|
3940
3981
|
config: {
|
|
3941
3982
|
enabled: config.autoRecall, // Only enable if auto-recall is enabled
|
|
@@ -3950,7 +3991,7 @@ export const registerOpenClaw = (api) => {
|
|
|
3950
3991
|
.description('Show plugin status and statistics')
|
|
3951
3992
|
.action(async () => {
|
|
3952
3993
|
try {
|
|
3953
|
-
const response = await apiClient.get('/api/health', { user_id, user_email });
|
|
3994
|
+
const response = await apiClient.get('/api/health', { user_id: state.agentId, user_email: state.agentEmail });
|
|
3954
3995
|
if (response.success) {
|
|
3955
3996
|
console.log('Plugin Status: Connected');
|
|
3956
3997
|
}
|
|
@@ -3981,10 +4022,15 @@ export const registerOpenClaw = (api) => {
|
|
|
3981
4022
|
}
|
|
3982
4023
|
});
|
|
3983
4024
|
});
|
|
4025
|
+
// Issue #1644: warn if agent ID is "unknown" at registration (will be resolved from hook context later)
|
|
4026
|
+
if (context.agent.agentId === 'unknown' && !config.agentId) {
|
|
4027
|
+
logger.warn('Agent ID not available at registration time — will resolve from hook context. ' +
|
|
4028
|
+
'Set config.agentId for explicit override. (Issue #1644)');
|
|
4029
|
+
}
|
|
3984
4030
|
logger.info('OpenClaw Projects plugin registered', {
|
|
3985
4031
|
agentId: context.agent.agentId,
|
|
3986
4032
|
sessionId: context.session.sessionId,
|
|
3987
|
-
user_id,
|
|
4033
|
+
user_id: state.agentId,
|
|
3988
4034
|
toolCount: tools.length,
|
|
3989
4035
|
config: redactConfig(config),
|
|
3990
4036
|
});
|