@rubytech/create-realagent 1.0.715 → 1.0.717
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/index.js +33 -0
- package/package.json +1 -1
- package/payload/platform/lib/graph-search/dist/index.d.ts +27 -11
- package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-search/dist/index.js +29 -13
- package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +272 -0
- package/payload/platform/lib/graph-search/src/index.ts +27 -12
- package/payload/platform/neo4j/schema.cypher +57 -7
- package/payload/platform/plugins/docs/references/graph.md +8 -0
- package/payload/platform/plugins/docs/references/internals.md +4 -4
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/connections.md +3 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js +51 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +17 -5
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/package.json +4 -2
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +15 -0
- package/payload/platform/plugins/memory/references/graph-primitives.md +9 -3
- package/payload/platform/scripts/embed-backfill.sh +370 -0
- package/payload/platform/scripts/seed-neo4j.sh +10 -4
- package/payload/server/public/assets/graph-qKc9cr2K.js +50 -0
- package/payload/server/public/graph.html +1 -1
- package/payload/server/server.js +57 -2
- package/payload/server/public/assets/graph-CkcuUDtA.js +0 -50
|
@@ -258,13 +258,63 @@ OPTIONS {
|
|
|
258
258
|
}
|
|
259
259
|
};
|
|
260
260
|
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
261
|
+
// Universal full-text BM25 index for hybrid keyword search (Task 748).
|
|
262
|
+
//
|
|
263
|
+
// Every operator-meaningful label written by the platform is in the index union;
|
|
264
|
+
// every textual property a writer assigns is in the property union. Neo4j silently
|
|
265
|
+
// ignores absent properties on a given label, so over-inclusion is harmless.
|
|
266
|
+
//
|
|
267
|
+
// **Doctrine.** Search is "find any node in my graph that mentions this term" —
|
|
268
|
+
// not "find a knowledge document". Pre-Task-748 the index name `knowledge_fulltext`
|
|
269
|
+
// covered only `KnowledgeDocument | Section | Chunk` (3 of ~40 written labels), so
|
|
270
|
+
// BM25 silently returned zero hits for Person/Organization/Task/Conversation/etc.
|
|
271
|
+
// regardless of query. Universal coverage is the doctrine; the doctrine test at
|
|
272
|
+
// `platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts` parses this
|
|
273
|
+
// declaration and asserts label-set ⊇ union(GRAPH_LABEL_COLOURS, schema-declared)
|
|
274
|
+
// so future label additions cannot silently re-narrow.
|
|
275
|
+
//
|
|
276
|
+
// Label union — every operator-meaningful label:
|
|
277
|
+
// - Business identity: LocalBusiness, Service, PriceSpecification, OpeningHoursSpecification, Organization
|
|
278
|
+
// - People: Person, UserProfile, Preference, AdminUser, AccessGrant
|
|
279
|
+
// - Knowledge: KnowledgeDocument, Section, Chunk (legacy), DigitalDocument, CreativeWork,
|
|
280
|
+
// Question, FAQPage, DefinedTerm, Review, ImageObject
|
|
281
|
+
// - Conversational: Conversation, AdminConversation, PublicConversation, Message,
|
|
282
|
+
// UserMessage, AssistantMessage, ToolCall
|
|
283
|
+
// - Tasks/projects/events: Task, Project, Event
|
|
284
|
+
// - Workflows: Workflow, WorkflowStep, WorkflowRun, StepResult
|
|
285
|
+
// - Onboarding: OnboardingState
|
|
286
|
+
// - Email: Email, EmailAccount
|
|
287
|
+
// - Review signals: ReviewAlert
|
|
288
|
+
// - CV/career sublabels: Position, Credential
|
|
289
|
+
//
|
|
290
|
+
// Property union — every textual property the schema's writers assign:
|
|
291
|
+
// - Generic: name, title, summary, body, content, text, description, headline, abstract,
|
|
292
|
+
// note, label, value, message, preview, tagline
|
|
293
|
+
// - Person: firstName, lastName, givenName, familyName, email,
|
|
294
|
+
// currentTitle (Task 753 — denormalised from [:WORKS_FOR].title for
|
|
295
|
+
// LinkedIn imports, since edge properties are invisible to a
|
|
296
|
+
// node-only fulltext index. `currentTitle` (not `title`) avoids
|
|
297
|
+
// shadowing :Position.title semantics in cross-label cypher.)
|
|
298
|
+
// - Email: subject, bodyPreview, fromName, fromAddress
|
|
299
|
+
// - EmailAccount: agentAddress
|
|
300
|
+
// - Email: screeningReason
|
|
301
|
+
// - Credential: authority
|
|
302
|
+
// - AccessGrant: contactValue
|
|
303
|
+
// - ToolCall: toolName
|
|
304
|
+
CREATE FULLTEXT INDEX entity_search IF NOT EXISTS
|
|
305
|
+
FOR (n:LocalBusiness|Service|PriceSpecification|OpeningHoursSpecification|Organization
|
|
306
|
+
|Person|UserProfile|Preference|AdminUser|AccessGrant
|
|
307
|
+
|KnowledgeDocument|Section|Chunk|DigitalDocument|CreativeWork|Question|FAQPage|DefinedTerm|Review|ImageObject
|
|
308
|
+
|Conversation|AdminConversation|PublicConversation|Message|UserMessage|AssistantMessage|ToolCall
|
|
309
|
+
|Task|Project|Event
|
|
310
|
+
|Workflow|WorkflowStep|WorkflowRun|StepResult
|
|
311
|
+
|OnboardingState|Email|EmailAccount|ReviewAlert
|
|
312
|
+
|Position|Credential)
|
|
313
|
+
ON EACH [n.name, n.firstName, n.lastName, n.givenName, n.familyName,
|
|
314
|
+
n.title, n.currentTitle, n.summary, n.body, n.content, n.text, n.description, n.headline, n.abstract,
|
|
315
|
+
n.email, n.note, n.label, n.value, n.message, n.preview, n.tagline,
|
|
316
|
+
n.subject, n.bodyPreview, n.fromName, n.fromAddress, n.agentAddress, n.screeningReason,
|
|
317
|
+
n.authority, n.contactValue, n.toolName];
|
|
268
318
|
|
|
269
319
|
// Project node (Task 740) — a standalone creative-output node distinct from
|
|
270
320
|
// :Section. Anchored via (:UserProfile)-[:CREATED]->(:Project), with optional
|
|
@@ -4,6 +4,14 @@ The **Graph** admin page (`/graph`) renders a force-directed view of your
|
|
|
4
4
|
account's Neo4j subgraph. Labels on the canvas follow the zoom level, so you
|
|
5
5
|
see the most useful identity at every scale.
|
|
6
6
|
|
|
7
|
+
## Search and pivot
|
|
8
|
+
|
|
9
|
+
Type a term in the search box to highlight matching nodes on the canvas. Hits get an amber border so you can pick them out of a busy view. Click any highlighted node to open its side panel and pivot into its neighbourhood — both clicks (hit and non-hit) behave identically.
|
|
10
|
+
|
|
11
|
+
When a search is active and you click a node, the neighbourhood you pivot into is **narrowed to the search-relevant subset**. For example: searching "david" with 175 matches and clicking yourself returns the Davids you're connected to, not your entire LinkedIn graph. The narrowing applies once per pivot — clearing the search and pivoting again returns the full neighbourhood.
|
|
12
|
+
|
|
13
|
+
Searches reach **every textual property** of every operator-meaningful label, including denormalised fields the platform writes specifically so search can reach them — for example, the current job title of each LinkedIn connection (originally stored on the connection edge, copied to the Person node so the fulltext index can match it).
|
|
14
|
+
|
|
7
15
|
## Conversation label tiers
|
|
8
16
|
|
|
9
17
|
Conversation nodes carry the most operator-meaningful identity in the
|
|
@@ -18,7 +18,7 @@ QUERY
|
|
|
18
18
|
│ ├──► MERGE ──► EXPAND ──► RESULTS
|
|
19
19
|
│ │
|
|
20
20
|
└── ESCAPE (Lucene special chars) ──────► BM25 FULL-TEXT ──┘
|
|
21
|
-
(
|
|
21
|
+
(entity_search index — universal coverage)
|
|
22
22
|
|
|
23
23
|
Merge formula: combined = 0.7 × vector_score + 0.3 × normalised_bm25_score
|
|
24
24
|
Deduplication: by nodeId — when a node appears in both paths, keep the max score from each method independently, then combine.
|
|
@@ -29,7 +29,7 @@ Fallback: if the full-text index doesn't exist, vector-only results are returned
|
|
|
29
29
|
|
|
30
30
|
**Vector path:** The query is embedded via Ollama (model per `EMBED_MODEL` env var, default `nomic-embed-text`). The resulting vector is compared against Neo4j's HNSW cosine indexes — one per indexed label. Dimensions are configured at install time (default 768). The search runs against all discovered indexes (or a subset if the caller specifies label filters). Scores are in [0, 1] (cosine similarity).
|
|
31
31
|
|
|
32
|
-
**BM25 path:** The raw query text is escaped for Lucene special characters and run against the `
|
|
32
|
+
**BM25 path:** The raw query text is escaped for Lucene special characters and run against the `entity_search` full-text index (Task 748 — universal coverage), which spans every operator-meaningful label written by the platform on the canonical text-property union (~28 properties: `name`, `firstName`, `lastName`, `givenName`, `familyName`, `title`, `summary`, `body`, `content`, `description`, `headline`, `email`, `subject`, `bodyPreview`, etc.). Pre-Task-748 the index was named `knowledge_fulltext` and covered only `KnowledgeDocument | Section | Chunk` — that gap silently hid Person/Organization/Task/Event/etc. from BM25 regardless of query. Raw BM25 scores are in [0, infinity) — they are normalised to [0, 1] via min-max scaling within the result set before merging. When all scores are equal (or a single result), all normalise to 1.0.
|
|
33
33
|
|
|
34
34
|
**Merge:** Results from both paths are collected in a single map keyed by `nodeId`. A node appearing in both paths accumulates the max vector score and max BM25 score independently. The combined score is `0.7 * vectorScore + 0.3 * bm25Score`. Results are sorted descending by combined score, then sliced to the requested limit (default 10).
|
|
35
35
|
|
|
@@ -59,7 +59,7 @@ Indexed labels: `Question`, `DefinedTerm`, `Review`, `Service`, `Person`, `Local
|
|
|
59
59
|
|
|
60
60
|
| Index name | Labels | Properties | Purpose |
|
|
61
61
|
|---|---|---|---|
|
|
62
|
-
| `
|
|
62
|
+
| `entity_search` | All operator-meaningful labels (~40, see [`schema.cypher`](../../../neo4j/schema.cypher)) | Canonical text-property union (~28) | Universal BM25 keyword matching across the whole graph (Task 748) |
|
|
63
63
|
|
|
64
64
|
### Embedding lifecycle
|
|
65
65
|
|
|
@@ -282,7 +282,7 @@ Each public agent can subscribe to up to 5 keywords via `knowledgeKeywords` in i
|
|
|
282
282
|
|
|
283
283
|
For each subscription keyword, two complementary searches run:
|
|
284
284
|
|
|
285
|
-
1. **BM25 full-text search** — queries the `
|
|
285
|
+
1. **BM25 full-text search** — queries the universal `entity_search` index (Task 748) with the keyword as the search term. Catches content that mentions the keyword in its text across every operator-meaningful label.
|
|
286
286
|
|
|
287
287
|
2. **Property-based search** — finds nodes whose `keywords` array property contains the subscription keyword (case-insensitive). Catches nodes explicitly tagged with that keyword topic. These matches are boosted to maximum BM25 score (1.0) since they are exact tag matches.
|
|
288
288
|
|
package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/connections.md
CHANGED
|
@@ -28,9 +28,11 @@ The real column header is **line 4**. Either skip the first three lines before p
|
|
|
28
28
|
| URL | `Person.linkedinUrl` (natural key) |
|
|
29
29
|
| Email Address | `Person.email` (only when non-empty) |
|
|
30
30
|
| Company | `Organization.name` |
|
|
31
|
-
| Position | `[:WORKS_FOR].title` |
|
|
31
|
+
| Position | `[:WORKS_FOR].title` AND `Person.currentTitle` (denormalised — Task 753) |
|
|
32
32
|
| Connected On | `[:CONNECTED_ON_LINKEDIN].connectedOn` (ISO 8601) |
|
|
33
33
|
|
|
34
|
+
The `Position` column is written **twice**: once on the `[:WORKS_FOR]` edge as the source of truth (preserves the per-employer history when a connection later moves), and again as `Person.currentTitle` so Neo4j's `entity_search` BM25 index reaches it. Edge properties are invisible to a node-only fulltext index, so without the denormalisation an operator searching "director" against 5K connections returns near-zero hits regardless of how many directors are in the graph. The SET is unconditional — re-importing a row with an empty Position clears `currentTitle`, matching the LinkedIn export's "current snapshot" semantics. To filter your import to a specific role before writing, use the [Selective-ingest threshold](#selective-ingest-threshold) gate.
|
|
35
|
+
|
|
34
36
|
LinkedIn only emits email for connections who opted in, so most rows have a blank email. The MCP tool writes `email` only when non-empty — avoids colliding with `person_email_unique` on empty strings.
|
|
35
37
|
|
|
36
38
|
## Natural keys
|
package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-archive-write.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/memory-archive-write.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 753 acceptance gate — `p.currentTitle` denormalisation for LinkedIn
|
|
3
|
+
* connections.
|
|
4
|
+
*
|
|
5
|
+
* Pre-fix: LinkedIn import wrote the connection's current job title to the
|
|
6
|
+
* `[:WORKS_FOR].title` edge property. Neo4j's `entity_search` BM25 index is
|
|
7
|
+
* node-only, so an operator searching "director" against an account with
|
|
8
|
+
* 5,099 LinkedIn connections returned 26 hits — name-field anomalies and
|
|
9
|
+
* CV-driven Position nodes. The data was correctly stored; it was
|
|
10
|
+
* unreachable by the index.
|
|
11
|
+
*
|
|
12
|
+
* Post-fix: the same SET clause that writes `p.givenName`, `p.familyName`,
|
|
13
|
+
* and `p.name` also writes `p.currentTitle = row.title`. The schema's
|
|
14
|
+
* `entity_search ON EACH` list carries `n.currentTitle`, so a fresh import
|
|
15
|
+
* (or the one-shot backfill cypher in `.docs/neo4j.md`) closes the gap.
|
|
16
|
+
*
|
|
17
|
+
* This test is a string-content gate against the cypher constant — no live
|
|
18
|
+
* Neo4j needed. The doctrine that matters: the SET clause writes
|
|
19
|
+
* `p.currentTitle = row.title` unconditionally inside the existing SET
|
|
20
|
+
* (not inside a FOREACH-with-non-empty-guard, because the LinkedIn export
|
|
21
|
+
* is a current-job snapshot — re-import without a title is authoritative).
|
|
22
|
+
*/
|
|
23
|
+
import { describe, it, expect } from "vitest";
|
|
24
|
+
import { PERSON_AND_CONNECTED_EDGE_CYPHER, ORG_AND_WORKS_FOR_CYPHER, } from "../memory-archive-write.js";
|
|
25
|
+
describe("memory-archive-write — Task 753 currentTitle denormalisation", () => {
|
|
26
|
+
it("PERSON_AND_CONNECTED_EDGE_CYPHER writes p.currentTitle = row.title in the SET clause", () => {
|
|
27
|
+
expect(PERSON_AND_CONNECTED_EDGE_CYPHER).toMatch(/p\.currentTitle\s*=\s*row\.title/);
|
|
28
|
+
});
|
|
29
|
+
it("p.currentTitle write is unconditional (in the main SET, not inside a FOREACH non-empty guard)", () => {
|
|
30
|
+
// Locate the bounds of the unconditional SET block: from the first
|
|
31
|
+
// `SET\n` after the MERGE-with-ON-CREATE-SET to the next `FOREACH`
|
|
32
|
+
// (which is the email guard, the first conditional block).
|
|
33
|
+
const setBlockMatch = PERSON_AND_CONNECTED_EDGE_CYPHER.match(/SET\n([\s\S]+?)FOREACH/);
|
|
34
|
+
expect(setBlockMatch).not.toBeNull();
|
|
35
|
+
const setBlock = setBlockMatch[1];
|
|
36
|
+
expect(setBlock).toMatch(/p\.currentTitle\s*=\s*row\.title/);
|
|
37
|
+
});
|
|
38
|
+
it("ORG_AND_WORKS_FOR_CYPHER still writes title to the WORKS_FOR edge — denormalisation is additive, not a move", () => {
|
|
39
|
+
// Edge-property write retained: the Person-side denormalisation gives
|
|
40
|
+
// BM25 reach, but the edge stays the source of truth so cypher queries
|
|
41
|
+
// that walk WORKS_FOR keep working.
|
|
42
|
+
expect(ORG_AND_WORKS_FOR_CYPHER).toMatch(/w\.title\s*=\s*row\.title/);
|
|
43
|
+
});
|
|
44
|
+
it("p.currentTitle is named distinctly from generic n.title (avoids :Position.title shadowing)", () => {
|
|
45
|
+
// The brief calls this out: `p.title` would collide with :Position.title
|
|
46
|
+
// semantics in cross-label cypher — separate node types with separate
|
|
47
|
+
// meanings. `currentTitle` makes the intent explicit.
|
|
48
|
+
expect(PERSON_AND_CONNECTED_EDGE_CYPHER).not.toMatch(/p\.title\s*=\s*row\.title/);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=memory-archive-write.test.js.map
|
package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-archive-write.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/memory-archive-write.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,gCAAgC,EAChC,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AAEpC,QAAQ,CAAC,8DAA8D,EAAE,GAAG,EAAE;IAC5E,EAAE,CAAC,sFAAsF,EAAE,GAAG,EAAE;QAC9F,MAAM,CAAC,gCAAgC,CAAC,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+FAA+F,EAAE,GAAG,EAAE;QACvG,mEAAmE;QACnE,mEAAmE;QACnE,2DAA2D;QAC3D,MAAM,aAAa,GAAG,gCAAgC,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACvF,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,aAAc,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6GAA6G,EAAE,GAAG,EAAE;QACrH,sEAAsE;QACtE,uEAAuE;QACvE,oCAAoC;QACpC,MAAM,CAAC,wBAAwB,CAAC,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4FAA4F,EAAE,GAAG,EAAE;QACpG,yEAAyE;QACzE,sEAAsE;QACtE,sDAAsD;QACtD,MAAM,CAAC,gCAAgC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACpF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -30,4 +30,6 @@ export interface ArchiveWriteResult {
|
|
|
30
30
|
}>;
|
|
31
31
|
}
|
|
32
32
|
export declare function memoryArchiveWrite(params: ArchiveWriteParams): Promise<ArchiveWriteResult>;
|
|
33
|
+
export declare const PERSON_AND_CONNECTED_EDGE_CYPHER = "\nMATCH (owner) WHERE elementId(owner) = $ownerNodeId\nWITH owner, $rows AS rows\nUNWIND rows AS row\nMERGE (p:Person {linkedinUrl: row.linkedinUrl})\n ON CREATE SET\n p.accountId = $accountId,\n p.source = 'linkedin',\n p.createdByAgent = 'linkedin-import',\n p.createdBySource = 'linkedin-import',\n p.createdBySession = $sessionId,\n p.createdAt = datetime(),\n p.scope = 'admin'\nSET\n p.givenName = row.givenName,\n p.familyName = row.familyName,\n p.name = trim(coalesce(row.givenName,'') + ' ' + coalesce(row.familyName,'')),\n p.currentTitle = row.title\nFOREACH (_ IN CASE WHEN row.email IS NOT NULL AND row.email <> '' THEN [1] ELSE [] END |\n SET p.email = row.email\n)\nMERGE (owner)-[c:CONNECTED_ON_LINKEDIN]->(p)\n ON CREATE SET\n c.connectedOn = date(row.connectedOn),\n c.source = 'linkedin',\n c.createdAt = datetime()\nRETURN count(*) AS processed\n";
|
|
34
|
+
export declare const ORG_AND_WORKS_FOR_CYPHER = "\nUNWIND $rows AS row\nWITH row WHERE row.company IS NOT NULL AND row.company <> ''\nMATCH (p:Person {linkedinUrl: row.linkedinUrl})\nMERGE (o:Organization {accountId: $accountId, name: row.company})\n ON CREATE SET\n o.source = 'linkedin',\n o.createdByAgent = 'linkedin-import',\n o.createdBySource = 'linkedin-import',\n o.createdBySession = $sessionId,\n o.createdAt = datetime(),\n o.scope = 'admin'\nMERGE (p)-[w:WORKS_FOR]->(o)\n ON CREATE SET\n w.title = row.title,\n w.source = 'linkedin',\n w.current = true,\n w.createdAt = datetime()\n ON MATCH SET\n w.title = coalesce(row.title, w.title)\nRETURN count(*) AS processed\n";
|
|
33
35
|
//# sourceMappingURL=memory-archive-write.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-archive-write.d.ts","sourceRoot":"","sources":["../../src/tools/memory-archive-write.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,UAAU,GAAG,qBAAqB,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAwBD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CA0E7B"}
|
|
1
|
+
{"version":3,"file":"memory-archive-write.d.ts","sourceRoot":"","sources":["../../src/tools/memory-archive-write.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,UAAU,GAAG,qBAAqB,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAwBD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CA0E7B;AAqCD,eAAO,MAAM,gCAAgC,+8BA2B5C,CAAC;AAEF,eAAO,MAAM,wBAAwB,8sBAqBpC,CAAC"}
|
|
@@ -112,8 +112,19 @@ export async function memoryArchiveWrite(params) {
|
|
|
112
112
|
//
|
|
113
113
|
// We split each batch into two tx.run calls inside one executeWrite so each
|
|
114
114
|
// statement's summary counters give a clean per-label breakdown.
|
|
115
|
+
//
|
|
116
|
+
// Task 753 — `p.currentTitle` denormalises `[:WORKS_FOR].title` onto the
|
|
117
|
+
// Person node so the `entity_search` BM25 fulltext index reaches it.
|
|
118
|
+
// Pre-task an operator searching "director" against an account with 5K
|
|
119
|
+
// LinkedIn connections returned 26 hits — name-field anomalies and
|
|
120
|
+
// CV-driven Position nodes only — because edge properties are invisible
|
|
121
|
+
// to a node-only fulltext index. Naming `currentTitle` (not `title`)
|
|
122
|
+
// avoids shadowing :Position.title semantics in cross-label cypher.
|
|
123
|
+
// Empty `row.title` writes null (the property is removed); the LinkedIn
|
|
124
|
+
// export is a current-job snapshot, so a re-import without a title is
|
|
125
|
+
// authoritative.
|
|
115
126
|
// ---------------------------------------------------------------------------
|
|
116
|
-
const PERSON_AND_CONNECTED_EDGE_CYPHER = `
|
|
127
|
+
export const PERSON_AND_CONNECTED_EDGE_CYPHER = `
|
|
117
128
|
MATCH (owner) WHERE elementId(owner) = $ownerNodeId
|
|
118
129
|
WITH owner, $rows AS rows
|
|
119
130
|
UNWIND rows AS row
|
|
@@ -127,9 +138,10 @@ MERGE (p:Person {linkedinUrl: row.linkedinUrl})
|
|
|
127
138
|
p.createdAt = datetime(),
|
|
128
139
|
p.scope = 'admin'
|
|
129
140
|
SET
|
|
130
|
-
p.givenName
|
|
131
|
-
p.familyName= row.familyName,
|
|
132
|
-
p.name
|
|
141
|
+
p.givenName = row.givenName,
|
|
142
|
+
p.familyName = row.familyName,
|
|
143
|
+
p.name = trim(coalesce(row.givenName,'') + ' ' + coalesce(row.familyName,'')),
|
|
144
|
+
p.currentTitle = row.title
|
|
133
145
|
FOREACH (_ IN CASE WHEN row.email IS NOT NULL AND row.email <> '' THEN [1] ELSE [] END |
|
|
134
146
|
SET p.email = row.email
|
|
135
147
|
)
|
|
@@ -140,7 +152,7 @@ MERGE (owner)-[c:CONNECTED_ON_LINKEDIN]->(p)
|
|
|
140
152
|
c.createdAt = datetime()
|
|
141
153
|
RETURN count(*) AS processed
|
|
142
154
|
`;
|
|
143
|
-
const ORG_AND_WORKS_FOR_CYPHER = `
|
|
155
|
+
export const ORG_AND_WORKS_FOR_CYPHER = `
|
|
144
156
|
UNWIND $rows AS row
|
|
145
157
|
WITH row WHERE row.company IS NOT NULL AND row.company <> ''
|
|
146
158
|
MATCH (p:Person {linkedinUrl: row.linkedinUrl})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-archive-write.js","sourceRoot":"","sources":["../../src/tools/memory-archive-write.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"memory-archive-write.js","sourceRoot":"","sources":["../../src/tools/memory-archive-write.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"build": "tsc",
|
|
9
9
|
"start": "node dist/index.js",
|
|
10
10
|
"smoke": "bash ./scripts/boot-smoke.sh",
|
|
11
|
-
"prepublish": "bash ./scripts/boot-smoke.sh"
|
|
11
|
+
"prepublish": "bash ./scripts/boot-smoke.sh",
|
|
12
|
+
"test": "vitest run"
|
|
12
13
|
},
|
|
13
14
|
"dependencies": {
|
|
14
15
|
"@anthropic-ai/sdk": "^0.81.0",
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
19
20
|
"typescript": "^5.7.0",
|
|
20
|
-
"@types/node": "^22.0.0"
|
|
21
|
+
"@types/node": "^22.0.0",
|
|
22
|
+
"vitest": "^4.1.2"
|
|
21
23
|
}
|
|
22
24
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
environment: "node",
|
|
6
|
+
// Task 753 — narrow to `src/tools/__tests__/` so vitest does not pick up
|
|
7
|
+
// pre-existing `src/lib/__tests__/` files that use Node's built-in
|
|
8
|
+
// `node:test` runner (schema-loader / schema-validator / live-schema-source
|
|
9
|
+
// tests, Task 743). Those files run under their own harness; broadening
|
|
10
|
+
// this glob to `src/**/__tests__/**` would surface them as parse failures
|
|
11
|
+
// here without porting them — out of scope for this task.
|
|
12
|
+
include: ["src/tools/__tests__/**/*.test.ts"],
|
|
13
|
+
testTimeout: 10_000,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
@@ -292,11 +292,13 @@ Or use `maxy-graph-get_neo4j_schema` for a richer one-shot structural summary.
|
|
|
292
292
|
|
|
293
293
|
### Fulltext
|
|
294
294
|
|
|
295
|
-
Use the `
|
|
296
|
-
|
|
295
|
+
Use the universal `entity_search` index (Task 748) for keyword-style search
|
|
296
|
+
across every operator-meaningful label — Person, Organization, Task, Event,
|
|
297
|
+
Conversation, KnowledgeDocument, Email, etc. — on every textual property
|
|
298
|
+
the platform's writers assign:
|
|
297
299
|
|
|
298
300
|
```cypher
|
|
299
|
-
CALL db.index.fulltext.queryNodes('
|
|
301
|
+
CALL db.index.fulltext.queryNodes('entity_search', $query)
|
|
300
302
|
YIELD node, score
|
|
301
303
|
WHERE score > 0.5
|
|
302
304
|
RETURN labels(node)[0] AS type,
|
|
@@ -306,6 +308,10 @@ RETURN labels(node)[0] AS type,
|
|
|
306
308
|
LIMIT 20
|
|
307
309
|
```
|
|
308
310
|
|
|
311
|
+
Pre-Task-748 the index was named `knowledge_fulltext` and covered only
|
|
312
|
+
`KnowledgeDocument | Section | Chunk`. Existing Pis pick up the rename on
|
|
313
|
+
the next install via `seed-neo4j.sh`.
|
|
314
|
+
|
|
309
315
|
### Filter by status or category
|
|
310
316
|
|
|
311
317
|
Events that are cancelled:
|