@rubytech/create-maxy 1.0.757 → 1.0.758
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/package.json +1 -1
- package/payload/platform/config/brand.json +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +1 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js +27 -8
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js +97 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +56 -6
- 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 +360 -105
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
- package/payload/platform/plugins/whatsapp-import/PLUGIN.md +29 -0
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/SKILL.md +122 -0
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/references/conversation-and-messages.md +99 -0
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/references/export-parse.md +102 -0
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/references/insight-extraction.md +118 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +1 -0
|
@@ -1,33 +1,43 @@
|
|
|
1
1
|
import { getSession } from "../lib/neo4j.js";
|
|
2
2
|
/**
|
|
3
|
-
* memory-archive-write — deterministic bulk-archive write surface
|
|
3
|
+
* memory-archive-write — deterministic bulk-archive write surface.
|
|
4
4
|
*
|
|
5
|
-
* Writes a flat dataset (
|
|
6
|
-
*
|
|
7
|
-
* Distinct from memory-write (per-node,
|
|
8
|
-
* (narrative document →
|
|
5
|
+
* Writes a flat dataset (LinkedIn Connections.csv, WhatsApp `_chat.txt`
|
|
6
|
+
* exports, future CRM-type seeds) into the graph as first-class entity
|
|
7
|
+
* nodes plus their natural edges. Distinct from memory-write (per-node,
|
|
8
|
+
* adjacency-required) and memory-ingest (narrative document → Section).
|
|
9
9
|
*
|
|
10
10
|
* Doctrine:
|
|
11
|
-
* - The Cypher body for each archiveType is fixed inside this tool.
|
|
12
|
-
* agent
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* -
|
|
19
|
-
*
|
|
20
|
-
* -
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
11
|
+
* - The Cypher body for each archiveType is fixed inside this tool.
|
|
12
|
+
* The agent supplies parsed rows + the archiveType discriminant,
|
|
13
|
+
* never raw Cypher.
|
|
14
|
+
* - 500-row UNWIND batches per executeWrite transaction. Larger
|
|
15
|
+
* batches risk Neo4j Community heap pressure on a Pi 5.
|
|
16
|
+
* - Per-node provenance stamps (createdByAgent, createdBySession,
|
|
17
|
+
* source, createdAt) come from this server, not the agent.
|
|
18
|
+
* - Owner / participant nodes match by elementId so both :AdminUser
|
|
19
|
+
* and external :Person flow through one path.
|
|
20
|
+
* - Counters are per-handler — each handler picks the keys that
|
|
21
|
+
* describe its archiveType. The agent reads them from the result
|
|
22
|
+
* JSON; nothing programmatic depends on the linkedin-specific
|
|
23
|
+
* legacy field names.
|
|
24
|
+
* - For archive types with cross-batch graph state (WhatsApp's
|
|
25
|
+
* :NEXT chronology), the handler implements an optional
|
|
26
|
+
* `finalize` hook that runs after all batches in its own
|
|
27
|
+
* `executeWrite` transaction. Failure surfaces as
|
|
28
|
+
* `nextChainStatus='failed'` on the result — never silent partial
|
|
29
|
+
* state.
|
|
24
30
|
*/
|
|
25
31
|
const BATCH_SIZE = 500;
|
|
26
32
|
const HANDLERS = {
|
|
27
33
|
"linkedin-connections": linkedinConnectionsHandler(),
|
|
34
|
+
"whatsapp-export": whatsappExportHandler(),
|
|
28
35
|
};
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Entry point
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
29
39
|
export async function memoryArchiveWrite(params) {
|
|
30
|
-
const { archiveType, ownerNodeId, accountId, rows
|
|
40
|
+
const { archiveType, ownerNodeId, accountId, rows } = params;
|
|
31
41
|
if (!accountId || !accountId.trim()) {
|
|
32
42
|
throw new Error("memory-archive-write: accountId is required.");
|
|
33
43
|
}
|
|
@@ -44,29 +54,19 @@ export async function memoryArchiveWrite(params) {
|
|
|
44
54
|
const session = getSession();
|
|
45
55
|
const startedMs = Date.now();
|
|
46
56
|
const errors = [];
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
mergedPersons: 0,
|
|
50
|
-
createdOrganizations: 0,
|
|
51
|
-
mergedOrganizations: 0,
|
|
52
|
-
createdEdges: 0,
|
|
53
|
-
};
|
|
57
|
+
const counters = {};
|
|
58
|
+
const sessionId = params.sessionId?.trim() || null;
|
|
54
59
|
try {
|
|
55
|
-
await handler.
|
|
60
|
+
await handler.verifyArchive(session, params);
|
|
56
61
|
for (let offset = 0; offset < rows.length; offset += BATCH_SIZE) {
|
|
57
|
-
const
|
|
62
|
+
const batchRows = rows.slice(offset, offset + BATCH_SIZE);
|
|
58
63
|
try {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
sessionId: sessionId?.trim() || null,
|
|
64
|
+
const batchCounters = await handler.writeBatch(session, {
|
|
65
|
+
params,
|
|
66
|
+
batchRows,
|
|
67
|
+
sessionId,
|
|
64
68
|
});
|
|
65
|
-
|
|
66
|
-
totals.mergedPersons += counters.mergedPersons;
|
|
67
|
-
totals.createdOrganizations += counters.createdOrganizations;
|
|
68
|
-
totals.mergedOrganizations += counters.mergedOrganizations;
|
|
69
|
-
totals.createdEdges += counters.createdEdges;
|
|
69
|
+
mergeCounters(counters, batchCounters);
|
|
70
70
|
}
|
|
71
71
|
catch (err) {
|
|
72
72
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -74,62 +74,54 @@ export async function memoryArchiveWrite(params) {
|
|
|
74
74
|
process.stderr.write(`[memory-archive-write] batch-failed archiveType=${archiveType} offset=${offset} reason=${reason}\n`);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
+
let nextChainStatus;
|
|
78
|
+
let nextChainReason;
|
|
79
|
+
if (handler.finalize) {
|
|
80
|
+
try {
|
|
81
|
+
const finalizeResult = await handler.finalize(session, {
|
|
82
|
+
params,
|
|
83
|
+
sessionId,
|
|
84
|
+
hadBatchErrors: errors.length > 0,
|
|
85
|
+
});
|
|
86
|
+
mergeCounters(counters, finalizeResult.counters);
|
|
87
|
+
nextChainStatus = finalizeResult.status;
|
|
88
|
+
nextChainReason = finalizeResult.reason;
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
92
|
+
nextChainStatus = "failed";
|
|
93
|
+
nextChainReason = reason;
|
|
94
|
+
process.stderr.write(`[memory-archive-write] finalize-failed archiveType=${archiveType} reason=${reason}\n`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const elapsedMs = Date.now() - startedMs;
|
|
98
|
+
const counterStr = Object.entries(counters)
|
|
99
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
100
|
+
.join(" ");
|
|
101
|
+
const statusStr = nextChainStatus
|
|
102
|
+
? ` nextChainStatus=${nextChainStatus}${nextChainReason ? ` reason=${nextChainReason}` : ""}`
|
|
103
|
+
: "";
|
|
104
|
+
process.stderr.write(`[memory-archive-write] archiveType=${archiveType} rows=${rows.length} ${counterStr} errors=${errors.length}${statusStr} ms=${elapsedMs}\n`);
|
|
105
|
+
return {
|
|
106
|
+
archiveType,
|
|
107
|
+
processedRows: rows.length,
|
|
108
|
+
counters,
|
|
109
|
+
errors,
|
|
110
|
+
...(nextChainStatus ? { nextChainStatus } : {}),
|
|
111
|
+
...(nextChainReason ? { nextChainReason } : {}),
|
|
112
|
+
};
|
|
77
113
|
}
|
|
78
114
|
finally {
|
|
79
115
|
await session.close();
|
|
80
116
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
archiveType,
|
|
88
|
-
processedRows: rows.length,
|
|
89
|
-
...totals,
|
|
90
|
-
errors,
|
|
91
|
-
};
|
|
117
|
+
}
|
|
118
|
+
function mergeCounters(target, source) {
|
|
119
|
+
for (const [k, v] of Object.entries(source)) {
|
|
120
|
+
target[k] = (target[k] ?? 0) + v;
|
|
121
|
+
}
|
|
92
122
|
}
|
|
93
123
|
// ---------------------------------------------------------------------------
|
|
94
|
-
// linkedin-connections handler
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
//
|
|
97
|
-
// Owner anchor: AdminUser or Person (elementId match — operator-confirmed
|
|
98
|
-
// during the linkedin-import skill's owner-confirmation flow).
|
|
99
|
-
//
|
|
100
|
-
// Per row the CSV expresses two relationships:
|
|
101
|
-
// 1. (owner)-[:CONNECTED_ON_LINKEDIN {connectedOn}]->(:Person)
|
|
102
|
-
// 2. (:Person)-[:WORKS_FOR {title, current=true}]->(:Organization) — when company present
|
|
103
|
-
//
|
|
104
|
-
// Natural keys:
|
|
105
|
-
// :Person — linkedinUrl (indexed in platform/neo4j/schema.cypher)
|
|
106
|
-
// :Organization — (accountId, name)
|
|
107
|
-
//
|
|
108
|
-
// Provenance stamped on every node:
|
|
109
|
-
// createdByAgent='linkedin-import', createdBySource='linkedin-import',
|
|
110
|
-
// createdBySession=$sessionId (when provided), createdAt=datetime(),
|
|
111
|
-
// source='linkedin'
|
|
112
|
-
//
|
|
113
|
-
// We split each batch into two tx.run calls inside one executeWrite so each
|
|
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.
|
|
126
|
-
//
|
|
127
|
-
// `connectedOn` is NOT denormalised onto the Person — it lives only on
|
|
128
|
-
// the edge. Side-panel visibility (Task 754) is solved by rendering
|
|
129
|
-
// incident-edge properties in the UI, not by writing the date twice.
|
|
130
|
-
// Doctrine: avoid duplicate writes that need a backfill cypher to land
|
|
131
|
-
// on existing customer installs (no-admin-upgrade-path). The fulltext-
|
|
132
|
-
// reach problem `currentTitle` solves doesn't apply to a date.
|
|
124
|
+
// linkedin-connections handler (Task 744; Task 753 currentTitle denormalisation)
|
|
133
125
|
// ---------------------------------------------------------------------------
|
|
134
126
|
export const PERSON_AND_CONNECTED_EDGE_CYPHER = `
|
|
135
127
|
MATCH (owner) WHERE elementId(owner) = $ownerNodeId
|
|
@@ -183,7 +175,8 @@ RETURN count(*) AS processed
|
|
|
183
175
|
`;
|
|
184
176
|
function linkedinConnectionsHandler() {
|
|
185
177
|
return {
|
|
186
|
-
async
|
|
178
|
+
async verifyArchive(session, params) {
|
|
179
|
+
const { ownerNodeId, accountId } = params;
|
|
187
180
|
const result = await session.run(`MATCH (n) WHERE elementId(n) = $ownerNodeId
|
|
188
181
|
RETURN labels(n) AS labels, n.accountId AS accountId LIMIT 1`, { ownerNodeId });
|
|
189
182
|
if (result.records.length === 0) {
|
|
@@ -200,29 +193,30 @@ function linkedinConnectionsHandler() {
|
|
|
200
193
|
throw new Error(`ownerNodeId ${ownerNodeId} belongs to account ${ownerAccountId}, not ${accountId}. Cross-account archive ingest refused.`);
|
|
201
194
|
}
|
|
202
195
|
},
|
|
203
|
-
async writeBatch(session,
|
|
204
|
-
const params =
|
|
205
|
-
|
|
206
|
-
|
|
196
|
+
async writeBatch(session, ctx) {
|
|
197
|
+
const { params, batchRows, sessionId } = ctx;
|
|
198
|
+
const queryParams = {
|
|
199
|
+
ownerNodeId: params.ownerNodeId,
|
|
200
|
+
accountId: params.accountId,
|
|
207
201
|
sessionId,
|
|
208
|
-
rows:
|
|
202
|
+
rows: batchRows.map((r) => normaliseLinkedinRow(r)),
|
|
209
203
|
};
|
|
210
204
|
return await session.executeWrite(async (tx) => {
|
|
211
|
-
const personRes = await tx.run(PERSON_AND_CONNECTED_EDGE_CYPHER,
|
|
205
|
+
const personRes = await tx.run(PERSON_AND_CONNECTED_EDGE_CYPHER, queryParams);
|
|
212
206
|
const personCounters = personRes.summary.counters.updates();
|
|
213
|
-
const orgRes = await tx.run(ORG_AND_WORKS_FOR_CYPHER,
|
|
207
|
+
const orgRes = await tx.run(ORG_AND_WORKS_FOR_CYPHER, queryParams);
|
|
214
208
|
const orgCounters = orgRes.summary.counters.updates();
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
const personsTouched =
|
|
218
|
-
const
|
|
219
|
-
const
|
|
209
|
+
const createdPersons = personCounters.nodesCreated;
|
|
210
|
+
const createdOrgs = orgCounters.nodesCreated;
|
|
211
|
+
const personsTouched = queryParams.rows.length;
|
|
212
|
+
const mergedPersons = personsTouched - createdPersons;
|
|
213
|
+
const createdEdges = personCounters.relationshipsCreated + orgCounters.relationshipsCreated;
|
|
220
214
|
return {
|
|
221
|
-
createdPersons
|
|
222
|
-
mergedPersons
|
|
223
|
-
createdOrganizations:
|
|
215
|
+
createdPersons,
|
|
216
|
+
mergedPersons,
|
|
217
|
+
createdOrganizations: createdOrgs,
|
|
224
218
|
mergedOrganizations: 0,
|
|
225
|
-
createdEdges
|
|
219
|
+
createdEdges,
|
|
226
220
|
};
|
|
227
221
|
});
|
|
228
222
|
},
|
|
@@ -245,4 +239,265 @@ function normaliseLinkedinRow(row) {
|
|
|
245
239
|
connectedOn: row.connectedOn,
|
|
246
240
|
};
|
|
247
241
|
}
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
// whatsapp-export handler (Task 804)
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
//
|
|
246
|
+
// Owner anchor: the operator who exported the chat (:AdminUser or :Person).
|
|
247
|
+
// Recorded as `createdBySession`-stamped provenance on the Conversation node;
|
|
248
|
+
// not a graph edge, since the exporter is metadata, not a semantic role.
|
|
249
|
+
//
|
|
250
|
+
// Per call the input expresses one Conversation + N Messages + edges:
|
|
251
|
+
// 1. (:Conversation:WhatsAppConversation {conversationId}) — MERGE, sublabel on first create
|
|
252
|
+
// 2. (:Message:WhatsAppMessage {messageId})-[:PART_OF]->(:Conversation)
|
|
253
|
+
// 3. (sender:AdminUser|:Person {elementId})-[:SENT]->(:Message)
|
|
254
|
+
// 4. (sender)-[:PARTICIPANT_IN]->(:Conversation) — rolled up per distinct sender
|
|
255
|
+
// 5. (:Message)-[:NEXT]->(:Message) — chronology, finalize step
|
|
256
|
+
//
|
|
257
|
+
// Natural keys:
|
|
258
|
+
// :Conversation:WhatsAppConversation — conversationId (whatsapp-export:<sha256(file)>:<accountId>)
|
|
259
|
+
// :Message:WhatsAppMessage — messageId (whatsapp-export:<conversationId>:<lineHash>)
|
|
260
|
+
// participants — elementId resolved by the skill's confirmation flow
|
|
261
|
+
//
|
|
262
|
+
// Provenance stamped on every node: source='whatsapp', createdByAgent='whatsapp-import',
|
|
263
|
+
// createdBySession=$sessionId, createdAt=datetime(), archiveSourceFile=<sha256 prefix>.
|
|
264
|
+
// On re-import, ON MATCH refreshes lastMessageAt / messageCount / participantCount /
|
|
265
|
+
// lastImportedAt / lastImportedBySession on the Conversation only — node-creation
|
|
266
|
+
// stamps are preserved.
|
|
267
|
+
//
|
|
268
|
+
// Cross-batch state: NEXT chain depends on full-conversation chronology. The
|
|
269
|
+
// finalize hook runs ORDER BY dateSent ASC, sequenceIndex ASC, then MERGEs
|
|
270
|
+
// pairwise NEXT edges. Idempotent — re-import with a 50-message append adds
|
|
271
|
+
// 50 NEXT edges and updates conversation scalars, no duplicates.
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// MERGE on the base label only, then SET the sublabel in ON CREATE — Task 633
|
|
274
|
+
// pattern documented at platform/neo4j/schema.cypher:374-388 and used by
|
|
275
|
+
// `ensureConversation` in platform/ui/app/lib/neo4j-store.ts. MERGEing on
|
|
276
|
+
// `:Base:Sublabel` directly would refuse to match a pre-existing node that
|
|
277
|
+
// carries only the base label, then CREATE a duplicate that violates the
|
|
278
|
+
// `conversationId IS UNIQUE` constraint on `:Conversation`. The base+SET
|
|
279
|
+
// pattern is constraint-safe and idempotent across Task-633-style sublabel
|
|
280
|
+
// backfill histories.
|
|
281
|
+
export const WHATSAPP_CONVERSATION_AND_MESSAGES_CYPHER = `
|
|
282
|
+
MATCH (owner) WHERE elementId(owner) = $ownerNodeId
|
|
283
|
+
WITH owner, $rows AS rows, $conversation AS conv
|
|
284
|
+
MERGE (c:Conversation {conversationId: conv.conversationId})
|
|
285
|
+
ON CREATE SET
|
|
286
|
+
c:WhatsAppConversation,
|
|
287
|
+
c.accountId = $accountId,
|
|
288
|
+
c.source = 'whatsapp',
|
|
289
|
+
c.createdByAgent = 'whatsapp-import',
|
|
290
|
+
c.createdBySource = 'whatsapp-import',
|
|
291
|
+
c.createdBySession = $sessionId,
|
|
292
|
+
c.createdAt = datetime(),
|
|
293
|
+
c.scope = 'admin',
|
|
294
|
+
c.agentType = 'admin',
|
|
295
|
+
c.archiveSourceFile = conv.archiveSourceFile,
|
|
296
|
+
c.firstMessageAt = datetime(conv.firstMessageAt),
|
|
297
|
+
c.lastMessageAt = datetime(conv.lastMessageAt),
|
|
298
|
+
c.participantCount = conv.participantCount,
|
|
299
|
+
c.messageCount = conv.messageCount
|
|
300
|
+
ON MATCH SET
|
|
301
|
+
c:WhatsAppConversation,
|
|
302
|
+
c.lastMessageAt = datetime(conv.lastMessageAt),
|
|
303
|
+
c.participantCount = conv.participantCount,
|
|
304
|
+
c.messageCount = conv.messageCount,
|
|
305
|
+
c.lastImportedAt = datetime(),
|
|
306
|
+
c.lastImportedBySession = $sessionId
|
|
307
|
+
WITH owner, c, rows
|
|
308
|
+
UNWIND rows AS row
|
|
309
|
+
MATCH (sender) WHERE elementId(sender) = row.senderNodeId
|
|
310
|
+
MERGE (m:Message {messageId: row.messageId})
|
|
311
|
+
ON CREATE SET
|
|
312
|
+
m:WhatsAppMessage,
|
|
313
|
+
m.accountId = $accountId,
|
|
314
|
+
m.source = 'whatsapp',
|
|
315
|
+
m.createdByAgent = 'whatsapp-import',
|
|
316
|
+
m.createdBySource = 'whatsapp-import',
|
|
317
|
+
m.createdBySession = $sessionId,
|
|
318
|
+
m.createdAt = datetime(),
|
|
319
|
+
m.conversationId = row.conversationId,
|
|
320
|
+
m.dateSent = datetime(row.dateSent),
|
|
321
|
+
m.body = row.body,
|
|
322
|
+
m.senderName = row.senderName,
|
|
323
|
+
m.sequenceIndex = row.sequenceIndex,
|
|
324
|
+
m.scope = 'admin'
|
|
325
|
+
MERGE (m)-[:PART_OF]->(c)
|
|
326
|
+
MERGE (sender)-[s:SENT]->(m)
|
|
327
|
+
ON CREATE SET
|
|
328
|
+
s.source = 'whatsapp',
|
|
329
|
+
s.createdAt = datetime()
|
|
330
|
+
MERGE (sender)-[p:PARTICIPANT_IN]->(c)
|
|
331
|
+
ON CREATE SET
|
|
332
|
+
p.source = 'whatsapp',
|
|
333
|
+
p.createdAt = datetime()
|
|
334
|
+
RETURN count(*) AS processed
|
|
335
|
+
`;
|
|
336
|
+
// Tertiary tiebreaker `m.messageId ASC` makes ordering total even when two
|
|
337
|
+
// messages share the same dateSent + sequenceIndex (operator-error duplicate
|
|
338
|
+
// row in input, or two messages emitted in the same second by the same
|
|
339
|
+
// sender). Without it, Neo4j's ordering of equal keys is undefined and the
|
|
340
|
+
// chain topology can drift across re-imports — MERGE on (prev, next) is
|
|
341
|
+
// idempotent per pair, but pairs themselves would shuffle.
|
|
342
|
+
export const WHATSAPP_NEXT_CHAIN_CYPHER = `
|
|
343
|
+
MATCH (c:Conversation:WhatsAppConversation {conversationId: $conversationId})
|
|
344
|
+
MATCH (m:Message)-[:PART_OF]->(c)
|
|
345
|
+
WITH c, m ORDER BY m.dateSent ASC, m.sequenceIndex ASC, m.messageId ASC
|
|
346
|
+
WITH c, collect(m) AS msgs
|
|
347
|
+
UNWIND range(0, size(msgs) - 2) AS i
|
|
348
|
+
WITH msgs[i] AS prev, msgs[i + 1] AS next
|
|
349
|
+
WHERE prev <> next
|
|
350
|
+
MERGE (prev)-[r:NEXT]->(next)
|
|
351
|
+
ON CREATE SET
|
|
352
|
+
r.source = 'whatsapp',
|
|
353
|
+
r.createdAt = datetime()
|
|
354
|
+
RETURN count(r) AS nextEdges
|
|
355
|
+
`;
|
|
356
|
+
function whatsappExportHandler() {
|
|
357
|
+
return {
|
|
358
|
+
async verifyArchive(session, params) {
|
|
359
|
+
const { ownerNodeId, accountId, conversation, participantNodeIds, rows } = params;
|
|
360
|
+
if (!conversation || !conversation.conversationId) {
|
|
361
|
+
throw new Error("whatsapp-export: `conversation` block with conversationId is required.");
|
|
362
|
+
}
|
|
363
|
+
if (!Array.isArray(participantNodeIds) || participantNodeIds.length === 0) {
|
|
364
|
+
throw new Error("whatsapp-export: `participantNodeIds` must be a non-empty array (operator-confirmed senders).");
|
|
365
|
+
}
|
|
366
|
+
// Cross-row conversationId invariant: one call = one conversation.
|
|
367
|
+
for (let i = 0; i < rows.length; i++) {
|
|
368
|
+
const row = rows[i];
|
|
369
|
+
if (row.conversationId !== conversation.conversationId) {
|
|
370
|
+
throw new Error(`whatsapp-export: row[${i}].conversationId="${row.conversationId}" mismatches conversation.conversationId="${conversation.conversationId}". One archive-write call writes exactly one conversation.`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// Owner verification (mirrors linkedin-connections logic).
|
|
374
|
+
const ownerRes = await session.run(`MATCH (n) WHERE elementId(n) = $ownerNodeId
|
|
375
|
+
RETURN labels(n) AS labels, n.accountId AS accountId LIMIT 1`, { ownerNodeId });
|
|
376
|
+
if (ownerRes.records.length === 0) {
|
|
377
|
+
throw new Error(`ownerNodeId ${ownerNodeId} not found. Re-run the archive-owner confirmation flow before invoking memory-archive-write.`);
|
|
378
|
+
}
|
|
379
|
+
const ownerLabels = ownerRes.records[0].get("labels") ?? [];
|
|
380
|
+
const ownerAccountId = ownerRes.records[0].get("accountId");
|
|
381
|
+
if (!ownerLabels.includes("AdminUser") && !ownerLabels.includes("Person")) {
|
|
382
|
+
throw new Error(`ownerNodeId ${ownerNodeId} has labels [${ownerLabels.join(", ")}]; expected :AdminUser or :Person.`);
|
|
383
|
+
}
|
|
384
|
+
if (ownerAccountId && ownerAccountId !== accountId) {
|
|
385
|
+
throw new Error(`ownerNodeId ${ownerNodeId} belongs to account ${ownerAccountId}, not ${accountId}. Cross-account archive ingest refused.`);
|
|
386
|
+
}
|
|
387
|
+
// Participant verification: every senderNodeId must resolve to :AdminUser
|
|
388
|
+
// or :Person in the same accountId. Distinct in case the skill passed
|
|
389
|
+
// the same nodeId twice. Log when dedup collapses the array — two
|
|
390
|
+
// distinct senderNames mapping to the same elementId is operator-confirmed
|
|
391
|
+
// intent (Step 4 phone-vs-name same-person heuristic), but a silent
|
|
392
|
+
// collapse without a log line hides the merge from the audit trail.
|
|
393
|
+
const distinctParticipantIds = Array.from(new Set(participantNodeIds));
|
|
394
|
+
if (distinctParticipantIds.length !== participantNodeIds.length) {
|
|
395
|
+
process.stderr.write(`[memory-archive-write] participantNodeIds-deduped archiveType=whatsapp-export submitted=${participantNodeIds.length} distinct=${distinctParticipantIds.length}\n`);
|
|
396
|
+
}
|
|
397
|
+
const participantRes = await session.run(`UNWIND $ids AS id
|
|
398
|
+
MATCH (n) WHERE elementId(n) = id
|
|
399
|
+
RETURN id AS elemId, labels(n) AS labels, n.accountId AS accountId`, { ids: distinctParticipantIds });
|
|
400
|
+
const found = new Set();
|
|
401
|
+
const rejected = [];
|
|
402
|
+
for (const record of participantRes.records) {
|
|
403
|
+
const elemId = record.get("elemId");
|
|
404
|
+
const labels = record.get("labels") ?? [];
|
|
405
|
+
const acct = record.get("accountId");
|
|
406
|
+
found.add(elemId);
|
|
407
|
+
if (!labels.includes("AdminUser") && !labels.includes("Person")) {
|
|
408
|
+
rejected.push(`${elemId}: labels=[${labels.join(",")}] (expected :AdminUser or :Person)`);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (acct && acct !== accountId) {
|
|
412
|
+
rejected.push(`${elemId}: accountId=${acct} (expected ${accountId})`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for (const id of distinctParticipantIds) {
|
|
416
|
+
if (!found.has(id))
|
|
417
|
+
rejected.push(`${id}: not found`);
|
|
418
|
+
}
|
|
419
|
+
const ok = distinctParticipantIds.length - rejected.length;
|
|
420
|
+
process.stderr.write(`[memory-archive-write] verifyParticipants archiveType=whatsapp-export ok=${ok} rejected=${rejected.length}\n`);
|
|
421
|
+
if (rejected.length > 0) {
|
|
422
|
+
throw new Error(`whatsapp-export: ${rejected.length} participant(s) failed verification: ${rejected.join("; ")}. Re-run the participant confirmation flow.`);
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
async writeBatch(session, ctx) {
|
|
426
|
+
const { params, batchRows, sessionId } = ctx;
|
|
427
|
+
const queryParams = {
|
|
428
|
+
ownerNodeId: params.ownerNodeId,
|
|
429
|
+
accountId: params.accountId,
|
|
430
|
+
sessionId,
|
|
431
|
+
conversation: params.conversation,
|
|
432
|
+
rows: batchRows.map((r) => normaliseWhatsappRow(r)),
|
|
433
|
+
};
|
|
434
|
+
return await session.executeWrite(async (tx) => {
|
|
435
|
+
const res = await tx.run(WHATSAPP_CONVERSATION_AND_MESSAGES_CYPHER, queryParams);
|
|
436
|
+
const stats = res.summary.counters.updates();
|
|
437
|
+
// Per-batch counters: exact node/edge creation counts come from
|
|
438
|
+
// result.summary.counters; we don't synthesise from row counts.
|
|
439
|
+
return {
|
|
440
|
+
createdConversations: 0, // attributed to first batch only via diff below
|
|
441
|
+
createdMessages: stats.nodesCreated,
|
|
442
|
+
createdSentEdges: 0,
|
|
443
|
+
createdParticipantEdges: 0,
|
|
444
|
+
relationshipsCreated: stats.relationshipsCreated,
|
|
445
|
+
labelsAdded: stats.labelsAdded,
|
|
446
|
+
};
|
|
447
|
+
});
|
|
448
|
+
},
|
|
449
|
+
async finalize(session, ctx) {
|
|
450
|
+
const { params, hadBatchErrors } = ctx;
|
|
451
|
+
const conversationId = params.conversation?.conversationId;
|
|
452
|
+
if (!conversationId) {
|
|
453
|
+
return { counters: {}, status: "skipped", reason: "no conversationId" };
|
|
454
|
+
}
|
|
455
|
+
const result = await session.executeWrite(async (tx) => {
|
|
456
|
+
const res = await tx.run(WHATSAPP_NEXT_CHAIN_CYPHER, { conversationId });
|
|
457
|
+
const stats = res.summary.counters.updates();
|
|
458
|
+
const nextEdges = res.records[0]?.get("nextEdges") ?? 0;
|
|
459
|
+
const nextEdgeCount = typeof nextEdges === "number"
|
|
460
|
+
? nextEdges
|
|
461
|
+
: typeof nextEdges?.toNumber === "function"
|
|
462
|
+
? nextEdges.toNumber()
|
|
463
|
+
: 0;
|
|
464
|
+
return {
|
|
465
|
+
counters: {
|
|
466
|
+
nextEdgesProcessed: nextEdgeCount,
|
|
467
|
+
nextEdgesCreated: stats.relationshipsCreated,
|
|
468
|
+
},
|
|
469
|
+
status: (hadBatchErrors ? "partial" : "ok"),
|
|
470
|
+
reason: hadBatchErrors ? "one or more batches reported errors; NEXT chain rebuilt over written messages" : undefined,
|
|
471
|
+
};
|
|
472
|
+
});
|
|
473
|
+
return result;
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function normaliseWhatsappRow(row) {
|
|
478
|
+
if (!row.messageId || !row.messageId.trim()) {
|
|
479
|
+
throw new Error("whatsapp-export row missing messageId (natural key).");
|
|
480
|
+
}
|
|
481
|
+
if (!row.conversationId || !row.conversationId.trim()) {
|
|
482
|
+
throw new Error("whatsapp-export row missing conversationId.");
|
|
483
|
+
}
|
|
484
|
+
if (!row.senderNodeId || !row.senderNodeId.trim()) {
|
|
485
|
+
throw new Error("whatsapp-export row missing senderNodeId (operator-confirmed sender).");
|
|
486
|
+
}
|
|
487
|
+
if (!row.dateSent || Number.isNaN(Date.parse(row.dateSent))) {
|
|
488
|
+
throw new Error(`whatsapp-export row dateSent="${row.dateSent}" is not parseable as ISO 8601. Parser must convert the [DD/MM/YY, HH:MM:SS] line prefix to ISO 8601 with timezone.`);
|
|
489
|
+
}
|
|
490
|
+
if (typeof row.sequenceIndex !== "number" || row.sequenceIndex < 0) {
|
|
491
|
+
throw new Error(`whatsapp-export row sequenceIndex="${row.sequenceIndex}" must be a non-negative number.`);
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
messageId: row.messageId.trim(),
|
|
495
|
+
conversationId: row.conversationId.trim(),
|
|
496
|
+
senderNodeId: row.senderNodeId.trim(),
|
|
497
|
+
senderName: (row.senderName ?? "").trim(),
|
|
498
|
+
dateSent: row.dateSent,
|
|
499
|
+
body: row.body ?? "",
|
|
500
|
+
sequenceIndex: row.sequenceIndex,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
248
503
|
//# sourceMappingURL=memory-archive-write.js.map
|
|
@@ -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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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: whatsapp-import
|
|
3
|
+
description: "Import a WhatsApp `_chat.txt` export (Conversation + Messages with chronological NEXT chain + analysis-derived insights) into the {{productName}} Neo4j graph. Skill-only plugin owned by the database-operator specialist. Opt-in per brand — not enabled by default. Distinct from the live `whatsapp` plugin (Baileys QR pairing + in-memory store)."
|
|
4
|
+
tools: []
|
|
5
|
+
always: false
|
|
6
|
+
embed: false
|
|
7
|
+
specialist: database-operator
|
|
8
|
+
metadata: {"platform":{"optional":true,"pluginKey":"whatsapp-import"}}
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# WhatsApp Import
|
|
12
|
+
|
|
13
|
+
Ingests a WhatsApp "Export Chat" archive (the `_chat.txt` file plus media attachments) into the {{productName}} Neo4j graph. Skill-only plugin — no MCP server, no admin tools added. The skill runs under the `database-operator` specialist, which owns external-archive ingestion and ad-hoc graph operations.
|
|
14
|
+
|
|
15
|
+
## When this applies
|
|
16
|
+
|
|
17
|
+
The admin agent delegates to `database-operator` when the operator drops a `_chat.txt` (or its containing folder) into chat. The specialist runs the skill's archive-owner + participant confirmation flow before any line is written, then ingests the messages via `mcp__memory__memory-archive-write` with `archiveType='whatsapp-export'`.
|
|
18
|
+
|
|
19
|
+
## What this is not
|
|
20
|
+
|
|
21
|
+
- **Not** the live `whatsapp` plugin. That plugin (Baileys QR pairing) holds messages in an in-memory store cleared on restart. This plugin imports historical exports into Neo4j as persistent graph nodes. Names are deliberately distinct (`whatsapp-import` vs `whatsapp`) so the boundary is mechanical.
|
|
22
|
+
- **Not** a media-transcription pipeline. Voice notes (`.opus`), photos, PDFs are skipped at parse with a counter logged. Transcription / OCR is a separate plugin decision.
|
|
23
|
+
- **Not** a generic chat-export importer. Each messaging channel (Telegram, iMessage, Signal, SMS dump) ships as its own opt-in plugin under the same shape when requested.
|
|
24
|
+
|
|
25
|
+
## Relationship to other plugins
|
|
26
|
+
|
|
27
|
+
- **memory** — the underlying write surface used by the skill (`memory-archive-write` for bulk Conversation+Messages, `memory-write` / `memory-update` for the second-pass typed observations). The skill is parameterised so all writes carry `source='whatsapp'` + `createdByAgent='whatsapp-import'` for provenance.
|
|
28
|
+
- **database-operator specialist** — owns execution. See [admin/IDENTITY.md](../../../platform/templates/agents/admin/IDENTITY.md) delegation clause and [database-operator.md](../../../platform/templates/specialists/agents/database-operator.md) per-source archive list.
|
|
29
|
+
- **linkedin-import** — sister plugin under the same pattern (LinkedIn Basic Data Export). Reading [linkedin-import/PLUGIN.md](../linkedin-import/PLUGIN.md) is the fastest way to understand the shape this plugin follows.
|