@peopl-health/nexus 5.11.0-dev.1130 → 5.11.0-dev.1131

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.
@@ -11,26 +11,59 @@ const TOOLS_TABLE = 'tools';
11
11
  const CACHE_TTL = 5 * 60 * 1000;
12
12
  const CACHE_KEY = 'skills';
13
13
  const TOOLS_CACHE_KEY = 'toolCatalog';
14
+ const BACKTICK_SPAN_RX = /`([^`\n]+)`/g;
15
+ const LEADING_CALL_RX = /^([A-Za-z][A-Za-z0-9]*)\s*(?:[(.]|$)/;
16
+ const MULTI_WORD_RX = /[a-z][A-Z]/;
14
17
 
15
18
  const cache = new MapCache({ maxSize: 2, ttl: CACHE_TTL });
16
19
 
17
- async function unavailableToolNames() {
18
- let catalog = cache.get(TOOLS_CACHE_KEY);
19
- if (!catalog) {
20
- const records = await getRecordByFilter(Config_ID, TOOLS_TABLE, 'TRUE()');
21
- catalog = (records || []).map((row) => row.tool_id).filter(Boolean);
22
- cache.set(TOOLS_CACHE_KEY, catalog);
20
+ async function readToolCatalog() {
21
+ const cached = cache.get(TOOLS_CACHE_KEY);
22
+ if (cached) return cached;
23
+
24
+ let records;
25
+ try {
26
+ records = await getRecordByFilter(Config_ID, TOOLS_TABLE, 'TRUE()');
27
+ } catch (error) {
28
+ logger.warn('[skillService] tool catalog unreadable; drift check falls back to the skill body', { error: error.message });
29
+ return null;
30
+ }
31
+ if (!records) {
32
+ logger.warn('[skillService] tool catalog unreadable; drift check falls back to the skill body', { table: TOOLS_TABLE });
33
+ return null;
23
34
  }
35
+
36
+ const catalog = records.map((row) => row.tool_id).filter(Boolean);
37
+ cache.set(TOOLS_CACHE_KEY, catalog);
38
+ return catalog;
39
+ }
40
+
41
+ async function unavailableToolNames() {
42
+ const catalog = await readToolCatalog();
43
+ if (!catalog) return [];
24
44
  const registered = new Set(getRegisteredToolNames());
25
45
  return catalog.filter((id) => !registered.has(id));
26
46
  }
27
47
 
48
+ function mentionedToolNames(text) {
49
+ const names = new Set();
50
+ for (const [, span] of text.matchAll(BACKTICK_SPAN_RX)) {
51
+ const id = span.match(LEADING_CALL_RX)?.[1];
52
+ if (id && MULTI_WORD_RX.test(id)) names.add(id);
53
+ }
54
+ return names;
55
+ }
56
+
28
57
  async function warnOnRegistryDrift(name, body) {
29
- if (getRegisteredToolNames().length === 0) return;
58
+ const registeredNames = getRegisteredToolNames();
59
+ if (registeredNames.length === 0) return;
60
+ const registered = new Set(registeredNames);
30
61
  const text = String(body || '');
31
- const missing = (await unavailableToolNames())
32
- .filter((id) => new RegExp(`\\b${id}\\b`).test(text))
33
- .sort();
62
+
63
+ const fromCatalog = (await unavailableToolNames()).filter((id) => new RegExp(`\\b${id}\\b`).test(text));
64
+ const fromBody = [...mentionedToolNames(text)].filter((id) => !registered.has(id));
65
+ const missing = [...new Set([...fromCatalog, ...fromBody])].sort();
66
+
34
67
  if (missing.length > 0) {
35
68
  logger.error('[skillService] Skill body calls tools that are not registered — the model cannot follow this body', {
36
69
  skill: name, missing,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "5.11.0-dev.1130",
3
+ "version": "5.11.0-dev.1131",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",