@rubytech/create-realagent 1.0.691 → 1.0.695

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-search/dist/index.d.ts +127 -0
  3. package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -0
  4. package/payload/platform/lib/graph-search/dist/index.js +393 -0
  5. package/payload/platform/lib/graph-search/dist/index.js.map +1 -0
  6. package/payload/platform/lib/graph-search/src/__tests__/bm25-only.test.ts +129 -0
  7. package/payload/platform/lib/graph-search/src/__tests__/escape-and-normalise.test.ts +53 -0
  8. package/payload/platform/lib/graph-search/src/__tests__/hybrid.test.ts +190 -0
  9. package/payload/platform/lib/graph-search/src/index.ts +498 -0
  10. package/payload/platform/lib/graph-search/tsconfig.json +9 -0
  11. package/payload/platform/lib/graph-search/vitest.config.ts +9 -0
  12. package/payload/platform/lib/graph-write/dist/index.d.ts +61 -0
  13. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -0
  14. package/payload/platform/lib/graph-write/dist/index.js +97 -0
  15. package/payload/platform/lib/graph-write/dist/index.js.map +1 -0
  16. package/payload/platform/lib/graph-write/src/index.ts +167 -0
  17. package/payload/platform/lib/graph-write/tsconfig.json +8 -0
  18. package/payload/platform/package.json +2 -2
  19. package/payload/platform/plugins/admin/mcp/dist/index.js +69 -15
  20. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  21. package/payload/platform/plugins/contacts/mcp/dist/index.js +27 -3
  22. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  23. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +4 -0
  24. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
  25. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +10 -6
  26. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
  27. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts +2 -0
  28. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts.map +1 -1
  29. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js +43 -36
  30. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js.map +1 -1
  31. package/payload/platform/plugins/docs/references/memory-guide.md +6 -0
  32. package/payload/platform/plugins/memory/mcp/dist/index.js +44 -3
  33. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  34. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts +3 -32
  35. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -1
  36. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +18 -381
  37. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -1
  38. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +9 -5
  39. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  40. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +10 -23
  41. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  42. package/payload/platform/plugins/memory/references/graph-primitives.md +1 -1
  43. package/payload/platform/plugins/scheduling/mcp/dist/index.js +8 -1
  44. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  45. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +2 -0
  46. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
  47. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +24 -10
  48. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  49. package/payload/platform/plugins/tasks/mcp/dist/index.js +8 -2
  50. package/payload/platform/plugins/tasks/mcp/dist/index.js.map +1 -1
  51. package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.d.ts +2 -0
  52. package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.d.ts.map +1 -1
  53. package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.js +45 -18
  54. package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.js.map +1 -1
  55. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js +12 -2
  56. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js.map +1 -1
  57. package/payload/platform/scripts/logs-read.sh +63 -9
  58. package/payload/platform/scripts/logs-read.test.sh +212 -0
  59. package/payload/server/chunk-IAIGB5WN.js +11406 -0
  60. package/payload/server/chunk-Q6NDXCM6.js +11448 -0
  61. package/payload/server/maxy-edge.js +1 -1
  62. package/payload/server/public/assets/{admin-Db49Ee_m.js → admin-zbb1g-mh.js} +2 -2
  63. package/payload/server/public/assets/{data-DGzrcwpZ.js → data-BKexd229.js} +1 -1
  64. package/payload/server/public/assets/{file-9OCmBpAn.js → file-DZkqmm8M.js} +1 -1
  65. package/payload/server/public/assets/{graph-ByzS1__T.js → graph-CPqHYozW.js} +1 -1
  66. package/payload/server/public/assets/{house-5FuTj56_.js → house-CPxWBrMl.js} +1 -1
  67. package/payload/server/public/assets/jsx-runtime-2yRmkrVq.css +1 -0
  68. package/payload/server/public/assets/{public-ePQo7y74.js → public-BthX_YNC.js} +1 -1
  69. package/payload/server/public/assets/{share-2-DZHAp0uN.js → share-2-Bauv6ctA.js} +1 -1
  70. package/payload/server/public/assets/{useVoiceRecorder-4Cpt_4GU.js → useVoiceRecorder-BTEcf6H3.js} +1 -1
  71. package/payload/server/public/assets/{x-DRLvmPIm.js → x-Kc93nSru.js} +1 -1
  72. package/payload/server/public/data.html +6 -6
  73. package/payload/server/public/graph.html +6 -6
  74. package/payload/server/public/index.html +7 -7
  75. package/payload/server/public/public.html +4 -4
  76. package/payload/server/server.js +656 -21
  77. package/payload/server/public/assets/jsx-runtime-DdGj3ITV.css +0 -1
  78. /package/payload/server/public/assets/{jsx-runtime-CT8kMwlr.js → jsx-runtime-7o3Lvx89.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-realagent",
3
- "version": "1.0.691",
3
+ "version": "1.0.695",
4
4
  "description": "Install Real Agent — Built for agents. By agents.",
5
5
  "bin": {
6
6
  "create-realagent": "./dist/index.js"
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Shared hybrid search primitive over the Neo4j knowledge graph.
3
+ *
4
+ * Pre-Task-675 there were two BM25 implementations over the same
5
+ * `knowledge_fulltext` index — one in the memory MCP tool, one in the admin
6
+ * Hono route — with divergent filter semantics (soft-delete primitive) and
7
+ * divergent ranking (agent ran hybrid vector+BM25, UI ran BM25-only). Task
8
+ * 675 collapses both into this lib; memory MCP + admin route now share
9
+ * a single code path so `/graph` UI ranking matches what the agent sees.
10
+ *
11
+ * QUERY --> EMBED --> VECTOR SEARCH (per index) --> ┐
12
+ * │ ├--> MERGE --> EXPAND --> RESULTS
13
+ * └--> BM25 FULL-TEXT SEARCH -------------------> ┘
14
+ *
15
+ * Hybrid merge: normalise BM25 scores to [0,1] via min-max, then combine
16
+ * combined = 0.7 * vector_score + 0.3 * normalised_bm25_score
17
+ * Dedup by nodeId, keeping the higher combined score.
18
+ *
19
+ * Trashed nodes (`:Trashed` label + legacy `deletedAt` property) are
20
+ * excluded from every path via `notTrashed()` from `graph-trash`.
21
+ *
22
+ * The lib is STATELESS. Callers pass a `Session` and, for hybrid, an
23
+ * `embed` function. This keeps the lib independent of Ollama config and
24
+ * free of circular imports against each caller's own getSession/embed
25
+ * (memory MCP and the Hono ui build each have their own copies for
26
+ * cross-build-boundary reasons — see neo4j-store.ts:68 comment).
27
+ *
28
+ * Log emission is the caller's responsibility — the lib throws or returns
29
+ * `mode` in the result so the caller can format its own `[graph-search]`
30
+ * line with the prefix and fields it wants.
31
+ */
32
+ import { type Session } from "neo4j-driver";
33
+ export interface SearchHit {
34
+ nodeId: string;
35
+ labels: string[];
36
+ properties: Record<string, unknown>;
37
+ score: number;
38
+ }
39
+ export interface SearchResult extends SearchHit {
40
+ related: Array<{
41
+ relationship: string;
42
+ direction: string;
43
+ labels: string[];
44
+ properties: Record<string, unknown>;
45
+ }>;
46
+ }
47
+ export interface ScoredNode {
48
+ nodeId: string;
49
+ labels: string[];
50
+ properties: Record<string, unknown>;
51
+ vectorScore: number;
52
+ bm25Score: number;
53
+ }
54
+ export type SearchMode = "hybrid" | "bm25";
55
+ export interface Bm25OnlyParams {
56
+ query: string;
57
+ accountId?: string;
58
+ limit: number;
59
+ allowedScopes?: string[];
60
+ agentSlug?: string;
61
+ keywords?: string[];
62
+ keywordMatch?: "any" | "all";
63
+ }
64
+ export interface HybridParams extends Bm25OnlyParams {
65
+ labels?: string[];
66
+ expandHops?: number;
67
+ keywordSubscriptions?: string[];
68
+ /**
69
+ * When true, a failing `embed()` does NOT throw — the lib falls back to
70
+ * `bm25Only()` and returns `mode: "bm25"`. Admin-route callers set this
71
+ * true so Ollama-down degrades to BM25-only; MCP tool callers leave it
72
+ * false so embed failure surfaces loudly.
73
+ */
74
+ degradeOnEmbedFailure?: boolean;
75
+ }
76
+ export interface HybridResponse {
77
+ mode: SearchMode;
78
+ results: SearchResult[];
79
+ /** Populated when degradeOnEmbedFailure fired. Caller logs it. */
80
+ embedError?: string;
81
+ }
82
+ export type EmbedFn = (text: string) => Promise<number[]>;
83
+ /**
84
+ * Lucene-escape special characters per Neo4j's fulltext query grammar.
85
+ * `/[+\-&|!(){}[\]^"~*?:\\/]/g` — matches memory-search.ts:127 and
86
+ * neo4j-store.ts:2521 verbatim; consolidation preserves escape set.
87
+ */
88
+ export declare function escapeLucene(query: string): string;
89
+ /**
90
+ * Normalise BM25 scores to [0, 1] using min-max within the result set.
91
+ * If all scores are equal (or single result), returns 1.0 for all —
92
+ * the range-zero branch prevents divide-by-zero and keeps the combined
93
+ * score meaningful.
94
+ */
95
+ export declare function normaliseBm25Scores(scores: number[]): number[];
96
+ export declare function discoverIndexes(session: Session): Promise<Map<string, string>>;
97
+ /** Clear the index cache — call after schema changes. */
98
+ export declare function clearIndexCache(): void;
99
+ /**
100
+ * BM25 full-text search against the `knowledge_fulltext` index.
101
+ * Returns [] when the index doesn't exist — matches memory-search.ts
102
+ * graceful-fallback semantics so a fresh account with no documents
103
+ * doesn't 500 the caller.
104
+ */
105
+ export declare function bm25Only(session: Session, params: Bm25OnlyParams): Promise<SearchHit[]>;
106
+ /**
107
+ * Hybrid vector + BM25 search with graph expansion.
108
+ *
109
+ * Sequence:
110
+ * 1. embed(query) — may throw if Ollama is unreachable. When
111
+ * `degradeOnEmbedFailure=true`, caller receives bm25Only() result
112
+ * with `mode: "bm25"`; otherwise the throw propagates.
113
+ * 2. Vector search per label (one query per vector index discovered at
114
+ * boot). Nodes-by-label filter short-circuits when the requested
115
+ * labels have no index.
116
+ * 3. BM25 search on knowledge_fulltext — same filter semantics as
117
+ * vector half (scope/agent/keyword/trashed).
118
+ * 4. Keyword subscriptions (when set): BM25 per keyword + property
119
+ * lookup against `node.keywords` array. Both bypass the agentSlug
120
+ * filter — subscriptions are scope-inclusive by design (matches
121
+ * memory-search.ts comment L319).
122
+ * 5. Merge: 0.7*vector + 0.3*bm25_norm, dedup by nodeId, sort.
123
+ * 6. Graph expand (1 hop by default, 0 to skip) — notTrashed filter +
124
+ * scope + agent clauses mirrored.
125
+ */
126
+ export declare function hybrid(session: Session, embed: EmbedFn, params: HybridParams): Promise<HybridResponse>;
127
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAO,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAOjD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,OAAO,EAAE,KAAK,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrC,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;CAC9B;AAED,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAE1D;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAO9D;AAMD,wBAAsB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAapF;AAED,yDAAyD;AACzD,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAmBD;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,SAAS,EAAE,CAAC,CAoDtB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,MAAM,CAC1B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,cAAc,CAAC,CAuNzB"}
@@ -0,0 +1,393 @@
1
+ "use strict";
2
+ /**
3
+ * Shared hybrid search primitive over the Neo4j knowledge graph.
4
+ *
5
+ * Pre-Task-675 there were two BM25 implementations over the same
6
+ * `knowledge_fulltext` index — one in the memory MCP tool, one in the admin
7
+ * Hono route — with divergent filter semantics (soft-delete primitive) and
8
+ * divergent ranking (agent ran hybrid vector+BM25, UI ran BM25-only). Task
9
+ * 675 collapses both into this lib; memory MCP + admin route now share
10
+ * a single code path so `/graph` UI ranking matches what the agent sees.
11
+ *
12
+ * QUERY --> EMBED --> VECTOR SEARCH (per index) --> ┐
13
+ * │ ├--> MERGE --> EXPAND --> RESULTS
14
+ * └--> BM25 FULL-TEXT SEARCH -------------------> ┘
15
+ *
16
+ * Hybrid merge: normalise BM25 scores to [0,1] via min-max, then combine
17
+ * combined = 0.7 * vector_score + 0.3 * normalised_bm25_score
18
+ * Dedup by nodeId, keeping the higher combined score.
19
+ *
20
+ * Trashed nodes (`:Trashed` label + legacy `deletedAt` property) are
21
+ * excluded from every path via `notTrashed()` from `graph-trash`.
22
+ *
23
+ * The lib is STATELESS. Callers pass a `Session` and, for hybrid, an
24
+ * `embed` function. This keeps the lib independent of Ollama config and
25
+ * free of circular imports against each caller's own getSession/embed
26
+ * (memory MCP and the Hono ui build each have their own copies for
27
+ * cross-build-boundary reasons — see neo4j-store.ts:68 comment).
28
+ *
29
+ * Log emission is the caller's responsibility — the lib throws or returns
30
+ * `mode` in the result so the caller can format its own `[graph-search]`
31
+ * line with the prefix and fields it wants.
32
+ */
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.escapeLucene = escapeLucene;
35
+ exports.normaliseBm25Scores = normaliseBm25Scores;
36
+ exports.discoverIndexes = discoverIndexes;
37
+ exports.clearIndexCache = clearIndexCache;
38
+ exports.bm25Only = bm25Only;
39
+ exports.hybrid = hybrid;
40
+ const neo4j_driver_1 = require("neo4j-driver");
41
+ const index_js_1 = require("../../graph-trash/dist/index.js");
42
+ const VECTOR_WEIGHT = 0.7;
43
+ const BM25_WEIGHT = 0.3;
44
+ const FULLTEXT_INDEX_NAME = "knowledge_fulltext";
45
+ /**
46
+ * Lucene-escape special characters per Neo4j's fulltext query grammar.
47
+ * `/[+\-&|!(){}[\]^"~*?:\\/]/g` — matches memory-search.ts:127 and
48
+ * neo4j-store.ts:2521 verbatim; consolidation preserves escape set.
49
+ */
50
+ function escapeLucene(query) {
51
+ return query.replace(/[+\-&|!(){}[\]^"~*?:\\/]/g, "\\$&");
52
+ }
53
+ /**
54
+ * Normalise BM25 scores to [0, 1] using min-max within the result set.
55
+ * If all scores are equal (or single result), returns 1.0 for all —
56
+ * the range-zero branch prevents divide-by-zero and keeps the combined
57
+ * score meaningful.
58
+ */
59
+ function normaliseBm25Scores(scores) {
60
+ if (scores.length === 0)
61
+ return [];
62
+ const min = Math.min(...scores);
63
+ const max = Math.max(...scores);
64
+ const range = max - min;
65
+ if (range === 0)
66
+ return scores.map(() => 1.0);
67
+ return scores.map((s) => (s - min) / range);
68
+ }
69
+ // Module-scope vector-index cache — discovered from Neo4j at first query.
70
+ // Per-process singleton matches memory-search.ts behaviour.
71
+ let indexCache = null;
72
+ async function discoverIndexes(session) {
73
+ if (indexCache)
74
+ return indexCache;
75
+ const result = await session.run(`SHOW INDEXES YIELD name, labelsOrTypes, type WHERE type = 'VECTOR' RETURN name, labelsOrTypes`);
76
+ const fresh = new Map();
77
+ for (const record of result.records) {
78
+ const name = record.get("name");
79
+ const labels = record.get("labelsOrTypes");
80
+ for (const label of labels)
81
+ fresh.set(label, name);
82
+ }
83
+ indexCache = fresh;
84
+ return fresh;
85
+ }
86
+ /** Clear the index cache — call after schema changes. */
87
+ function clearIndexCache() {
88
+ indexCache = null;
89
+ }
90
+ function buildKeywordFilter(keywords, keywordMatch = "any") {
91
+ if (!keywords || keywords.length === 0)
92
+ return undefined;
93
+ const fn = keywordMatch === "all" ? "ALL" : "ANY";
94
+ return {
95
+ clause: `AND (node.keywords IS NULL OR ${fn}(kw IN $keywords WHERE kw IN node.keywords))`,
96
+ params: { keywords: keywords.map((k) => k.toLowerCase().trim()) },
97
+ };
98
+ }
99
+ /**
100
+ * BM25 full-text search against the `knowledge_fulltext` index.
101
+ * Returns [] when the index doesn't exist — matches memory-search.ts
102
+ * graceful-fallback semantics so a fresh account with no documents
103
+ * doesn't 500 the caller.
104
+ */
105
+ async function bm25Only(session, params) {
106
+ const { query, accountId, limit, allowedScopes, agentSlug, keywords, keywordMatch } = params;
107
+ const scopeClause = allowedScopes
108
+ ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)"
109
+ : "";
110
+ const agentClause = agentSlug
111
+ ? "AND node.agents IS NOT NULL AND $agentSlug IN node.agents"
112
+ : "";
113
+ const keywordFilter = buildKeywordFilter(keywords, keywordMatch);
114
+ const kwClause = keywordFilter?.clause ?? "";
115
+ const escaped = escapeLucene(query);
116
+ try {
117
+ const result = await session.run(`CALL db.index.fulltext.queryNodes($indexName, $query)
118
+ YIELD node, score
119
+ WHERE node.accountId = $accountId
120
+ ${scopeClause}
121
+ ${agentClause}
122
+ AND ${(0, index_js_1.notTrashed)("node")}
123
+ ${kwClause}
124
+ RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
125
+ ORDER BY score DESC
126
+ LIMIT $limit`, {
127
+ indexName: FULLTEXT_INDEX_NAME,
128
+ query: escaped,
129
+ accountId,
130
+ limit: (0, neo4j_driver_1.int)(limit),
131
+ ...(allowedScopes ? { allowedScopes } : {}),
132
+ ...(agentSlug ? { agentSlug } : {}),
133
+ ...(keywordFilter?.params ?? {}),
134
+ });
135
+ return result.records.map((r) => {
136
+ const scoreRaw = r.get("score");
137
+ const score = typeof scoreRaw === "number" ? scoreRaw : Number(scoreRaw);
138
+ const node = r.get("node");
139
+ return {
140
+ nodeId: r.get("nodeId"),
141
+ labels: r.get("nodeLabels"),
142
+ properties: plainProperties(node.properties),
143
+ score,
144
+ };
145
+ });
146
+ }
147
+ catch (err) {
148
+ const msg = err instanceof Error ? err.message : String(err);
149
+ if (msg.includes("index") || msg.includes("fulltext") || msg.includes("not found")) {
150
+ return [];
151
+ }
152
+ throw err;
153
+ }
154
+ }
155
+ /**
156
+ * Hybrid vector + BM25 search with graph expansion.
157
+ *
158
+ * Sequence:
159
+ * 1. embed(query) — may throw if Ollama is unreachable. When
160
+ * `degradeOnEmbedFailure=true`, caller receives bm25Only() result
161
+ * with `mode: "bm25"`; otherwise the throw propagates.
162
+ * 2. Vector search per label (one query per vector index discovered at
163
+ * boot). Nodes-by-label filter short-circuits when the requested
164
+ * labels have no index.
165
+ * 3. BM25 search on knowledge_fulltext — same filter semantics as
166
+ * vector half (scope/agent/keyword/trashed).
167
+ * 4. Keyword subscriptions (when set): BM25 per keyword + property
168
+ * lookup against `node.keywords` array. Both bypass the agentSlug
169
+ * filter — subscriptions are scope-inclusive by design (matches
170
+ * memory-search.ts comment L319).
171
+ * 5. Merge: 0.7*vector + 0.3*bm25_norm, dedup by nodeId, sort.
172
+ * 6. Graph expand (1 hop by default, 0 to skip) — notTrashed filter +
173
+ * scope + agent clauses mirrored.
174
+ */
175
+ async function hybrid(session, embed, params) {
176
+ const { query, labels, accountId, limit, allowedScopes, keywords, keywordMatch = "any", agentSlug, keywordSubscriptions, expandHops = 1, degradeOnEmbedFailure = false, } = params;
177
+ let queryEmbedding;
178
+ try {
179
+ queryEmbedding = await embed(query);
180
+ }
181
+ catch (err) {
182
+ if (!degradeOnEmbedFailure)
183
+ throw err;
184
+ const msg = err instanceof Error ? err.message : String(err);
185
+ const bm25Hits = await bm25Only(session, params);
186
+ const results = bm25Hits.map((h) => ({ ...h, related: [] }));
187
+ return { mode: "bm25", results, embedError: msg };
188
+ }
189
+ const labelToIndex = await discoverIndexes(session);
190
+ const keywordFilter = buildKeywordFilter(keywords, keywordMatch);
191
+ const keywordClause = keywordFilter?.clause ?? "";
192
+ const keywordParams = keywordFilter?.params ?? {};
193
+ const scoreMap = new Map();
194
+ // --- Vector search (per label) ---
195
+ let indexesToQuery;
196
+ if (labels && labels.length > 0) {
197
+ indexesToQuery = labels
198
+ .map((l) => labelToIndex.get(l))
199
+ .filter((idx) => idx !== undefined);
200
+ if (indexesToQuery.length === 0) {
201
+ return { mode: "hybrid", results: [] };
202
+ }
203
+ }
204
+ else {
205
+ indexesToQuery = [...new Set(labelToIndex.values())];
206
+ }
207
+ const scopeClause = allowedScopes
208
+ ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)"
209
+ : "";
210
+ const scopeParams = allowedScopes ? { allowedScopes } : {};
211
+ const agentClause = agentSlug
212
+ ? "AND node.agents IS NOT NULL AND $agentSlug IN node.agents"
213
+ : "";
214
+ const agentParams = agentSlug ? { agentSlug } : {};
215
+ for (const indexName of indexesToQuery) {
216
+ const vectorResult = await session.run(`CALL db.index.vector.queryNodes($indexName, $limit, $embedding)
217
+ YIELD node, score
218
+ WHERE node.accountId = $accountId
219
+ ${scopeClause}
220
+ ${agentClause}
221
+ AND ${(0, index_js_1.notTrashed)("node")}
222
+ ${keywordClause}
223
+ RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
224
+ ORDER BY score DESC
225
+ LIMIT $limit`, {
226
+ indexName,
227
+ embedding: queryEmbedding,
228
+ limit: (0, neo4j_driver_1.int)(limit),
229
+ accountId,
230
+ ...scopeParams,
231
+ ...agentParams,
232
+ ...keywordParams,
233
+ });
234
+ for (const record of vectorResult.records) {
235
+ const nodeId = record.get("nodeId");
236
+ const scoreRaw = record.get("score");
237
+ const score = typeof scoreRaw === "number" ? scoreRaw : Number(scoreRaw);
238
+ const existing = scoreMap.get(nodeId);
239
+ if (existing) {
240
+ existing.vectorScore = Math.max(existing.vectorScore, score);
241
+ }
242
+ else {
243
+ const node = record.get("node");
244
+ scoreMap.set(nodeId, {
245
+ nodeId,
246
+ labels: record.get("nodeLabels"),
247
+ properties: plainProperties(node.properties),
248
+ vectorScore: score,
249
+ bm25Score: 0,
250
+ });
251
+ }
252
+ }
253
+ }
254
+ // --- BM25 half ---
255
+ const bm25Hits = await bm25Only(session, params);
256
+ if (bm25Hits.length > 0) {
257
+ const rawScores = bm25Hits.map((h) => h.score);
258
+ const normalised = normaliseBm25Scores(rawScores);
259
+ for (let i = 0; i < bm25Hits.length; i++) {
260
+ mergeBm25Hit(scoreMap, bm25Hits[i], normalised[i]);
261
+ }
262
+ }
263
+ // --- Keyword subscriptions (scope-inclusive, agentSlug-bypassing) ---
264
+ if (keywordSubscriptions && keywordSubscriptions.length > 0) {
265
+ for (const kw of keywordSubscriptions) {
266
+ const kwHits = await bm25Only(session, {
267
+ query: kw,
268
+ accountId,
269
+ limit,
270
+ allowedScopes,
271
+ });
272
+ if (kwHits.length === 0)
273
+ continue;
274
+ const rawScores = kwHits.map((h) => h.score);
275
+ const normalised = normaliseBm25Scores(rawScores);
276
+ for (let i = 0; i < kwHits.length; i++) {
277
+ mergeBm25Hit(scoreMap, kwHits[i], normalised[i]);
278
+ }
279
+ }
280
+ const propScopeClause = allowedScopes
281
+ ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)"
282
+ : "";
283
+ const propResult = await session.run(`MATCH (node)
284
+ WHERE node.accountId = $accountId
285
+ AND ${(0, index_js_1.notTrashed)("node")}
286
+ AND node.keywords IS NOT NULL
287
+ AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
288
+ ${propScopeClause}
289
+ RETURN node, labels(node) AS nodeLabels, elementId(node) AS nodeId
290
+ LIMIT $limit`, {
291
+ accountId,
292
+ kwSubs: keywordSubscriptions,
293
+ limit: (0, neo4j_driver_1.int)(limit),
294
+ ...(allowedScopes ? { allowedScopes } : {}),
295
+ });
296
+ for (const record of propResult.records) {
297
+ const nodeId = record.get("nodeId");
298
+ const existing = scoreMap.get(nodeId);
299
+ if (existing) {
300
+ existing.bm25Score = Math.max(existing.bm25Score, 1.0);
301
+ }
302
+ else {
303
+ const node = record.get("node");
304
+ scoreMap.set(nodeId, {
305
+ nodeId,
306
+ labels: record.get("nodeLabels"),
307
+ properties: plainProperties(node.properties),
308
+ vectorScore: 0,
309
+ bm25Score: 1.0,
310
+ });
311
+ }
312
+ }
313
+ }
314
+ // --- Merge & rank ---
315
+ const merged = [...scoreMap.values()]
316
+ .map((node) => ({
317
+ ...node,
318
+ combinedScore: VECTOR_WEIGHT * node.vectorScore + BM25_WEIGHT * node.bm25Score,
319
+ }))
320
+ .sort((a, b) => b.combinedScore - a.combinedScore)
321
+ .slice(0, limit);
322
+ // --- Graph expand ---
323
+ const results = [];
324
+ for (const node of merged) {
325
+ const result = {
326
+ nodeId: node.nodeId,
327
+ labels: node.labels,
328
+ properties: node.properties,
329
+ score: node.combinedScore,
330
+ related: [],
331
+ };
332
+ if (expandHops > 0) {
333
+ const expandScopeClause = allowedScopes
334
+ ? "AND (related.scope IS NULL OR related.scope IN $allowedScopes)"
335
+ : "";
336
+ const expandAgentClause = agentSlug
337
+ ? "AND (related.agents IS NULL OR $agentSlug IN related.agents)"
338
+ : "";
339
+ const expandResult = await session.run(`MATCH (n)-[r]-(related)
340
+ WHERE elementId(n) = $nodeId
341
+ AND ${(0, index_js_1.notTrashed)("related")}
342
+ ${expandScopeClause}
343
+ ${expandAgentClause}
344
+ RETURN type(r) AS relType,
345
+ CASE WHEN startNode(r) = n THEN 'outgoing' ELSE 'incoming' END AS direction,
346
+ labels(related) AS relatedLabels,
347
+ related
348
+ LIMIT 20`, { nodeId: node.nodeId, ...scopeParams, ...agentParams });
349
+ for (const rec of expandResult.records) {
350
+ const related = rec.get("related");
351
+ result.related.push({
352
+ relationship: rec.get("relType"),
353
+ direction: rec.get("direction"),
354
+ labels: rec.get("relatedLabels"),
355
+ properties: plainProperties(related.properties),
356
+ });
357
+ }
358
+ }
359
+ results.push(result);
360
+ }
361
+ return { mode: "hybrid", results };
362
+ }
363
+ function mergeBm25Hit(map, hit, normalisedScore) {
364
+ const existing = map.get(hit.nodeId);
365
+ if (existing) {
366
+ existing.bm25Score = Math.max(existing.bm25Score, normalisedScore);
367
+ }
368
+ else {
369
+ map.set(hit.nodeId, {
370
+ nodeId: hit.nodeId,
371
+ labels: hit.labels,
372
+ properties: hit.properties,
373
+ vectorScore: 0,
374
+ bm25Score: normalisedScore,
375
+ });
376
+ }
377
+ }
378
+ /** Strip `embedding` and unwrap Neo4j Integers into JS numbers. */
379
+ function plainProperties(properties) {
380
+ const plain = {};
381
+ for (const [key, value] of Object.entries(properties)) {
382
+ if (key === "embedding")
383
+ continue;
384
+ if (value && typeof value === "object" && "toNumber" in value) {
385
+ plain[key] = value.toNumber();
386
+ }
387
+ else {
388
+ plain[key] = value;
389
+ }
390
+ }
391
+ return plain;
392
+ }
393
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}