@rubytech/create-maxy 1.0.808 → 1.0.810

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 (39) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/memory/mcp/dist/index.js +86 -0
  3. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  4. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts +23 -0
  5. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts.map +1 -0
  6. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js +401 -0
  7. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js.map +1 -0
  8. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.d.ts +28 -0
  9. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.d.ts.map +1 -0
  10. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.js +34 -0
  11. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.js.map +1 -0
  12. package/payload/platform/plugins/memory/references/schema-base.md +12 -0
  13. package/payload/platform/plugins/whatsapp/PLUGIN.md +3 -1
  14. package/payload/platform/plugins/whatsapp-import/bin/ingest.mjs +225 -346
  15. package/payload/platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh +28 -10
  16. package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.d.ts +21 -0
  17. package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.d.ts.map +1 -0
  18. package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.js +41 -0
  19. package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.js.map +1 -0
  20. package/payload/platform/plugins/whatsapp-import/lib/dist/filter.d.ts +29 -0
  21. package/payload/platform/plugins/whatsapp-import/lib/dist/filter.d.ts.map +1 -0
  22. package/payload/platform/plugins/whatsapp-import/lib/dist/filter.js +123 -0
  23. package/payload/platform/plugins/whatsapp-import/lib/dist/filter.js.map +1 -0
  24. package/payload/platform/plugins/whatsapp-import/lib/dist/index.d.ts +4 -0
  25. package/payload/platform/plugins/whatsapp-import/lib/dist/index.d.ts.map +1 -1
  26. package/payload/platform/plugins/whatsapp-import/lib/dist/index.js +9 -1
  27. package/payload/platform/plugins/whatsapp-import/lib/dist/index.js.map +1 -1
  28. package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/filter-gate.test.ts +170 -0
  29. package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/ingest-idempotence.test.ts +141 -0
  30. package/payload/platform/plugins/whatsapp-import/lib/src/derive-keys.ts +59 -0
  31. package/payload/platform/plugins/whatsapp-import/lib/src/filter.ts +136 -0
  32. package/payload/platform/plugins/whatsapp-import/lib/src/index.ts +12 -0
  33. package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/SKILL.md +80 -25
  34. package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import-enrich/SKILL.md +22 -3
  35. package/payload/platform/templates/specialists/agents/database-operator.md +9 -4
  36. package/payload/server/public/assets/admin-Bwrd2DBq.js +352 -0
  37. package/payload/server/public/index.html +1 -1
  38. package/payload/server/server.js +271 -188
  39. package/payload/server/public/assets/admin-MxaCgGHZ.js +0 -352
@@ -0,0 +1,401 @@
1
+ import { createHash } from "node:crypto";
2
+ import { getSession } from "../lib/neo4j.js";
3
+ import { callOauthLlm } from "../../../../../lib/oauth-llm/dist/index.js";
4
+ import { HAIKU_MODEL } from "../../../../../lib/models/dist/index.js";
5
+ // ---------------------------------------------------------------------------
6
+ // whatsapp-export-insight-pass — Phase 2 chunked-Haiku insight extraction
7
+ // over an already-loaded WhatsApp Conversation (Task 871).
8
+ //
9
+ // Phase 1 (whatsapp-ingest.sh) is now LLM-free. The operator triggers this
10
+ // tool consciously via the `whatsapp-import-enrich` skill ("enrich the X
11
+ // chat"). Each chunk runs Haiku with a forced submit-tool call; the server
12
+ // applies a confidence>=0.8 gate per item before MERGE-keying it as an
13
+ // :Observation node in the graph. Re-running over the same conversation is
14
+ // idempotent — the MERGE key collapses identical items into one row.
15
+ //
16
+ // Doctrine alignment:
17
+ // - feedback_deterministic_means_remove_llm.md — Phase 1 has no LLM;
18
+ // this tool is the only sanctioned LLM entry for WhatsApp ingest.
19
+ // - feedback_compress_at_ingest_for_bulk_archives.md — small chunks
20
+ // (50 msgs, overlap 5) keep per-message attention, and the confidence
21
+ // gate compresses on write.
22
+ // - feedback_classifier_runs_off_oauth_not_api_key.md — admin-side
23
+ // classifier; OAuth, never the API key.
24
+ // - feedback_loud_failures.md — Haiku fallback / write failures log a
25
+ // structured line; the per-chunk loop continues on per-call errors so
26
+ // one chunk does not pollute the full pass.
27
+ // ---------------------------------------------------------------------------
28
+ const INSIGHT_CHUNK_SIZE = 50;
29
+ const INSIGHT_CHUNK_OVERLAP = 5;
30
+ const CONFIDENCE_THRESHOLD = 0.8;
31
+ const INSIGHT_SYSTEM_PROMPT = `You extract structured insights from a chunk of a WhatsApp conversation.
32
+
33
+ Return STRICT JSON via the provided tool. No prose, no commentary. Only items with concrete, verbatim evidence in the chunk. Empty arrays are valid; prefer omission to invention.
34
+
35
+ Definitions:
36
+ - "mention": a person, organisation, place, or named topic referred to by name.
37
+ - "task": something a participant committed to do or asked another to do (imperative or future-tense).
38
+ - "preference": stated like, dislike, opinion, or rule of behaviour.
39
+ - "observedRelationship": an explicit relational claim (works at, is married to, manages, etc.).
40
+
41
+ Every item carries a "confidence" float in [0,1]. Confidence reflects how unambiguously the evidence in the chunk supports the claim — not your prior belief about the world. Conversational filler ("let me know"), throwaway names without context, or speculative language ("maybe Ben works at Acme") MUST score below 0.8. The server discards items below 0.8.
42
+
43
+ Snippets must be ≤80 characters of the original message body, no sender names, no timestamps.`;
44
+ const INSIGHT_TOOL = {
45
+ name: "submit_insights",
46
+ description: "Submit the structured insights extracted from the chunk.",
47
+ input_schema: {
48
+ type: "object",
49
+ properties: {
50
+ mentions: {
51
+ type: "array",
52
+ items: {
53
+ type: "object",
54
+ properties: {
55
+ name: { type: "string" },
56
+ snippet: { type: "string" },
57
+ confidence: { type: "number" },
58
+ },
59
+ required: ["name", "snippet", "confidence"],
60
+ },
61
+ },
62
+ tasks: {
63
+ type: "array",
64
+ items: {
65
+ type: "object",
66
+ properties: {
67
+ task: { type: "string" },
68
+ snippet: { type: "string" },
69
+ confidence: { type: "number" },
70
+ },
71
+ required: ["task", "snippet", "confidence"],
72
+ },
73
+ },
74
+ preferences: {
75
+ type: "array",
76
+ items: {
77
+ type: "object",
78
+ properties: {
79
+ subject: { type: "string" },
80
+ preference: { type: "string" },
81
+ confidence: { type: "number" },
82
+ },
83
+ required: ["subject", "preference", "confidence"],
84
+ },
85
+ },
86
+ observedRelationships: {
87
+ type: "array",
88
+ items: {
89
+ type: "object",
90
+ properties: {
91
+ from: { type: "string" },
92
+ to: { type: "string" },
93
+ relationship: { type: "string" },
94
+ confidence: { type: "number" },
95
+ },
96
+ required: ["from", "to", "relationship", "confidence"],
97
+ },
98
+ },
99
+ },
100
+ required: ["mentions", "tasks", "preferences", "observedRelationships"],
101
+ },
102
+ };
103
+ // MERGE-keyed Observation write. The natural key is
104
+ // (conversationId, sourceMessageRef, kind, contentHash) — Task 870's
105
+ // idempotence contract. `sourceMessageRef` is `chunk:<idx>` for items the
106
+ // chunked pass cannot attribute to a single :Message; Phase 2 enrichment
107
+ // recovers a message-level ref by snippet match later.
108
+ const INSIGHT_WRITE_CYPHER = `
109
+ MATCH (c:Conversation:WhatsAppConversation {conversationId: $conversationId})
110
+ UNWIND $observations AS obs
111
+ MERGE (o:Observation {
112
+ conversationId: $conversationId,
113
+ sourceMessageRef: obs.sourceMessageRef,
114
+ kind: obs.kind,
115
+ contentHash: obs.contentHash
116
+ })
117
+ ON CREATE SET
118
+ o:WhatsAppObservation,
119
+ o.accountId = $accountId,
120
+ o.summary = obs.summary,
121
+ o.snippet = obs.snippet,
122
+ o.subject = obs.subject,
123
+ o.from = obs.from,
124
+ o.to = obs.to,
125
+ o.confidence = obs.confidence,
126
+ o.source = 'whatsapp',
127
+ o.createdByAgent = 'whatsapp-export-insight-pass',
128
+ o.createdBySource = 'whatsapp-export-insight-pass',
129
+ o.createdBySession = $sessionId,
130
+ o.createdAt = datetime(),
131
+ o.scope = 'admin',
132
+ o.insightPass = true,
133
+ o.observationStatus = 'auto-extracted'
134
+ ON MATCH SET
135
+ o.lastSeenAt = datetime(),
136
+ o.lastSeenBySession = $sessionId
137
+ MERGE (o)-[r:OBSERVED_IN]->(c)
138
+ ON CREATE SET r.source = 'whatsapp', r.createdAt = datetime()
139
+ RETURN count(o) AS touched
140
+ `;
141
+ export async function whatsappExportInsightPass(params) {
142
+ const { conversationId, accountId } = params;
143
+ const sessionId = params.sessionId ?? null;
144
+ const startedMs = Date.now();
145
+ const session = getSession();
146
+ let messages;
147
+ try {
148
+ const res = await session.run(`MATCH (m:Message)-[:PART_OF]->(c:Conversation:WhatsAppConversation {conversationId: $conversationId})
149
+ WHERE m.accountId = $accountId
150
+ WITH m ORDER BY m.dateSent ASC, m.sequenceIndex ASC, m.messageId ASC
151
+ RETURN m.messageId AS messageId, m.senderName AS senderName, m.body AS body, m.dateSent AS dateSent`, { conversationId, accountId });
152
+ messages = res.records.map((r) => ({
153
+ messageId: r.get("messageId"),
154
+ senderName: r.get("senderName"),
155
+ body: r.get("body"),
156
+ dateSent: String(r.get("dateSent")),
157
+ }));
158
+ }
159
+ finally {
160
+ await session.close();
161
+ }
162
+ if (messages.length === 0) {
163
+ process.stderr.write(`[whatsapp-export-insight-pass] empty-conversation conversationId=${conversationId}\n`);
164
+ return emptyResult(conversationId, Date.now() - startedMs);
165
+ }
166
+ // Build chunks with overlap. Step = chunkSize - overlap so consecutive
167
+ // chunks share the last `overlap` messages — preserves cross-message
168
+ // claims that straddle a chunk boundary.
169
+ const step = INSIGHT_CHUNK_SIZE - INSIGHT_CHUNK_OVERLAP;
170
+ const chunks = [];
171
+ for (let start = 0; start < messages.length; start += step) {
172
+ chunks.push(messages.slice(start, start + INSIGHT_CHUNK_SIZE));
173
+ if (start + INSIGHT_CHUNK_SIZE >= messages.length)
174
+ break;
175
+ }
176
+ const totals = {
177
+ mentions: 0,
178
+ tasks: 0,
179
+ preferences: 0,
180
+ observedRelationships: 0,
181
+ rejectedLowConfidence: 0,
182
+ written: 0,
183
+ };
184
+ process.stderr.write(`[whatsapp-export-insight-pass] start chunkSize=${INSIGHT_CHUNK_SIZE} overlap=${INSIGHT_CHUNK_OVERLAP} confidenceThreshold=${CONFIDENCE_THRESHOLD} chunks=${chunks.length} conversationId=${conversationId}\n`);
185
+ for (let idx = 0; idx < chunks.length; idx++) {
186
+ const chunk = chunks[idx];
187
+ const transcript = chunk
188
+ .map((m, j) => `[${j + 1}] ${m.senderName}: ${m.body}`)
189
+ .join("\n");
190
+ let llmResult;
191
+ try {
192
+ llmResult = await callOauthLlm({
193
+ model: HAIKU_MODEL,
194
+ system: INSIGHT_SYSTEM_PROMPT,
195
+ userMessage: transcript,
196
+ maxTokens: 8192,
197
+ timeoutMs: 180_000,
198
+ tools: [INSIGHT_TOOL],
199
+ toolChoiceName: INSIGHT_TOOL.name,
200
+ });
201
+ }
202
+ catch (err) {
203
+ process.stderr.write(`[whatsapp-export-insight-pass] chunk=${idx + 1}/${chunks.length} threw="${err instanceof Error ? err.message : String(err)}"\n`);
204
+ continue;
205
+ }
206
+ if (llmResult.kind === "fallback") {
207
+ process.stderr.write(`[whatsapp-export-insight-pass] chunk=${idx + 1}/${chunks.length} fallback cause=${llmResult.cause} reason="${llmResult.reason}"\n`);
208
+ continue;
209
+ }
210
+ if (llmResult.kind !== "ok-tool") {
211
+ process.stderr.write(`[whatsapp-export-insight-pass] chunk=${idx + 1}/${chunks.length} unexpected-result kind=${llmResult.kind}\n`);
212
+ continue;
213
+ }
214
+ const input = (llmResult.input ?? {});
215
+ let chunkMentions = 0;
216
+ let chunkTasks = 0;
217
+ let chunkPreferences = 0;
218
+ let chunkObserved = 0;
219
+ let chunkRejected = 0;
220
+ // sourceMessageRef is constant `chunk-pass` for chunked items: the
221
+ // chunked Haiku has no per-message provenance, AND the 5-message overlap
222
+ // window deliberately surfaces the same message in two chunks. If we
223
+ // keyed by `chunk:<idx>`, the same mention extracted from both halves
224
+ // would land as two rows (different MERGE key), defeating idempotence.
225
+ // A constant ref means identical `(kind, contentHash)` collapses to one
226
+ // row regardless of which chunk surfaced it; cross-run idempotence holds
227
+ // even when chunk indices shift after a Phase-1 delta re-import.
228
+ // Phase 2 enrichment recovers a per-message ref by snippet match later.
229
+ const sourceMessageRef = "chunk-pass";
230
+ const observations = [];
231
+ for (const m of asArray(input.mentions)) {
232
+ const conf = numericConfidence(m.confidence);
233
+ if (conf < CONFIDENCE_THRESHOLD) {
234
+ chunkRejected++;
235
+ continue;
236
+ }
237
+ const summary = strSlice(m.name, 200);
238
+ const snippet = strSlice(m.snippet, 200);
239
+ observations.push({
240
+ kind: "mention",
241
+ sourceMessageRef,
242
+ contentHash: contentHashFor("mention", { summary, snippet }),
243
+ summary,
244
+ snippet,
245
+ subject: null,
246
+ from: null,
247
+ to: null,
248
+ confidence: conf,
249
+ });
250
+ chunkMentions++;
251
+ }
252
+ for (const t of asArray(input.tasks)) {
253
+ const conf = numericConfidence(t.confidence);
254
+ if (conf < CONFIDENCE_THRESHOLD) {
255
+ chunkRejected++;
256
+ continue;
257
+ }
258
+ const summary = strSlice(t.task, 200);
259
+ const snippet = strSlice(t.snippet, 200);
260
+ observations.push({
261
+ kind: "task",
262
+ sourceMessageRef,
263
+ contentHash: contentHashFor("task", { summary, snippet }),
264
+ summary,
265
+ snippet,
266
+ subject: null,
267
+ from: null,
268
+ to: null,
269
+ confidence: conf,
270
+ });
271
+ chunkTasks++;
272
+ }
273
+ for (const p of asArray(input.preferences)) {
274
+ const conf = numericConfidence(p.confidence);
275
+ if (conf < CONFIDENCE_THRESHOLD) {
276
+ chunkRejected++;
277
+ continue;
278
+ }
279
+ const summary = strSlice(p.preference, 200);
280
+ const subject = strSlice(p.subject, 200);
281
+ observations.push({
282
+ kind: "preference",
283
+ sourceMessageRef,
284
+ contentHash: contentHashFor("preference", { summary, subject }),
285
+ summary,
286
+ snippet: null,
287
+ subject,
288
+ from: null,
289
+ to: null,
290
+ confidence: conf,
291
+ });
292
+ chunkPreferences++;
293
+ }
294
+ for (const r of asArray(input.observedRelationships)) {
295
+ const conf = numericConfidence(r.confidence);
296
+ if (conf < CONFIDENCE_THRESHOLD) {
297
+ chunkRejected++;
298
+ continue;
299
+ }
300
+ const summary = strSlice(r.relationship, 200);
301
+ const from = strSlice(r.from, 200);
302
+ const to = strSlice(r.to, 200);
303
+ observations.push({
304
+ kind: "observed-relationship",
305
+ sourceMessageRef,
306
+ contentHash: contentHashFor("observed-relationship", { summary, from, to }),
307
+ summary,
308
+ snippet: null,
309
+ subject: null,
310
+ from,
311
+ to,
312
+ confidence: conf,
313
+ });
314
+ chunkObserved++;
315
+ }
316
+ totals.mentions += chunkMentions;
317
+ totals.tasks += chunkTasks;
318
+ totals.preferences += chunkPreferences;
319
+ totals.observedRelationships += chunkObserved;
320
+ totals.rejectedLowConfidence += chunkRejected;
321
+ process.stderr.write(`[whatsapp-export-insight-pass] chunk=${idx + 1}/${chunks.length} mentions=${chunkMentions} tasks=${chunkTasks} preferences=${chunkPreferences} observed=${chunkObserved} rejected-low-confidence=${chunkRejected}\n`);
322
+ if (observations.length === 0)
323
+ continue;
324
+ const writeSession = getSession();
325
+ try {
326
+ const writeRes = await writeSession.executeWrite(async (tx) => {
327
+ const res = await tx.run(INSIGHT_WRITE_CYPHER, {
328
+ conversationId,
329
+ accountId,
330
+ sessionId,
331
+ observations,
332
+ });
333
+ const stats = res.summary.counters.updates();
334
+ return stats.nodesCreated;
335
+ });
336
+ totals.written += writeRes;
337
+ }
338
+ catch (err) {
339
+ process.stderr.write(`[whatsapp-export-insight-pass] chunk=${idx + 1}/${chunks.length} write-failed reason="${err instanceof Error ? err.message : String(err)}"\n`);
340
+ }
341
+ finally {
342
+ await writeSession.close().catch(() => { });
343
+ }
344
+ }
345
+ const elapsedMs = Date.now() - startedMs;
346
+ process.stderr.write(`[whatsapp-export-insight-pass] done conversationId=${conversationId} chunks=${chunks.length} mentions=${totals.mentions} tasks=${totals.tasks} preferences=${totals.preferences} observed=${totals.observedRelationships} rejected-low-confidence=${totals.rejectedLowConfidence} written=${totals.written} ms=${elapsedMs}\n`);
347
+ return {
348
+ conversationId,
349
+ chunks: chunks.length,
350
+ chunkSize: INSIGHT_CHUNK_SIZE,
351
+ overlap: INSIGHT_CHUNK_OVERLAP,
352
+ confidenceThreshold: CONFIDENCE_THRESHOLD,
353
+ totals,
354
+ ms: elapsedMs,
355
+ };
356
+ }
357
+ function emptyResult(conversationId, ms) {
358
+ return {
359
+ conversationId,
360
+ chunks: 0,
361
+ chunkSize: INSIGHT_CHUNK_SIZE,
362
+ overlap: INSIGHT_CHUNK_OVERLAP,
363
+ confidenceThreshold: CONFIDENCE_THRESHOLD,
364
+ totals: {
365
+ mentions: 0,
366
+ tasks: 0,
367
+ preferences: 0,
368
+ observedRelationships: 0,
369
+ rejectedLowConfidence: 0,
370
+ written: 0,
371
+ },
372
+ ms,
373
+ };
374
+ }
375
+ function asArray(v) {
376
+ return Array.isArray(v) ? v : [];
377
+ }
378
+ function numericConfidence(v) {
379
+ const n = typeof v === "number" ? v : Number(v);
380
+ if (!Number.isFinite(n))
381
+ return 0;
382
+ if (n < 0)
383
+ return 0;
384
+ if (n > 1)
385
+ return 1;
386
+ return n;
387
+ }
388
+ function strSlice(v, max) {
389
+ return String(v ?? "").slice(0, max);
390
+ }
391
+ function contentHashFor(kind, fields) {
392
+ // Stable normalisation: NFKC + trim + lowercase per Task 870's idempotence
393
+ // contract. The hash is the natural-key tail; identical (kind, fields)
394
+ // collapse to one MERGE row across re-runs.
395
+ const ordered = Object.keys(fields).sort();
396
+ const norm = ordered
397
+ .map((k) => `${k}=${(fields[k] ?? "").normalize("NFKC").trim().toLowerCase()}`)
398
+ .join("|");
399
+ return createHash("sha256").update(`${kind}|${norm}`).digest("hex");
400
+ }
401
+ //# sourceMappingURL=whatsapp-export-insight-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp-export-insight-pass.js","sourceRoot":"","sources":["../../src/tools/whatsapp-export-insight-pass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ export interface WhatsappExportPreviewParams {
2
+ filePath: string;
3
+ accountId: string;
4
+ timezone: string;
5
+ /** Same enum as whatsapp-export-parse — propagated through to the parser. */
6
+ dateFormat?: "DD/MM/YY" | "MM/DD/YY" | "DD/MM/YYYY" | "MM/DD/YYYY";
7
+ }
8
+ export interface WhatsappExportPreviewSender {
9
+ name: string;
10
+ messageCount: number;
11
+ }
12
+ export interface WhatsappExportPreviewResult {
13
+ /** sha256 hex of the raw file bytes — same value as `archiveSourceFile` minus the `whatsapp-export:` prefix. */
14
+ conversationSha256: string;
15
+ archiveSourceFile: string;
16
+ archiveBytes: number;
17
+ parsed: number;
18
+ mediaSkipped: number;
19
+ systemSkipped: number;
20
+ totalMessages: number;
21
+ dateRange: {
22
+ first: string | null;
23
+ last: string | null;
24
+ };
25
+ senders: WhatsappExportPreviewSender[];
26
+ }
27
+ export declare function whatsappExportPreview(params: WhatsappExportPreviewParams): Promise<WhatsappExportPreviewResult>;
28
+ //# sourceMappingURL=whatsapp-export-preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp-export-preview.d.ts","sourceRoot":"","sources":["../../src/tools/whatsapp-export-preview.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,YAAY,GAAG,YAAY,CAAC;CACpE;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,2BAA2B;IAC1C,gHAAgH;IAChH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE;QACT,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;KACrB,CAAC;IACF,OAAO,EAAE,2BAA2B,EAAE,CAAC;CACxC;AAED,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,2BAA2B,CAAC,CAuCtC"}
@@ -0,0 +1,34 @@
1
+ import { statSync } from "node:fs";
2
+ import { parseExport } from "../../../../whatsapp-import/lib/dist/parse-export.js";
3
+ export async function whatsappExportPreview(params) {
4
+ const { filePath, accountId, timezone, dateFormat } = params;
5
+ const archiveBytes = statSync(filePath).size;
6
+ const input = { filePath, accountId, timezone, dateFormat };
7
+ const result = parseExport(input);
8
+ // Build sender histogram in encounter order — operator scans top-down.
9
+ const counts = new Map();
10
+ for (const line of result.parsedLines) {
11
+ counts.set(line.senderName, (counts.get(line.senderName) ?? 0) + 1);
12
+ }
13
+ const senders = Array.from(counts.entries())
14
+ .map(([name, messageCount]) => ({ name, messageCount }))
15
+ .sort((a, b) => b.messageCount - a.messageCount);
16
+ const first = result.parsedLines[0]?.dateSent ?? null;
17
+ const last = result.parsedLines[result.parsedLines.length - 1]?.dateSent ?? null;
18
+ // archiveSourceFile is `whatsapp-export:<sha>` — strip prefix for the
19
+ // shorter scalar the operator surfaces.
20
+ const conversationSha256 = result.archiveSourceFile.replace(/^whatsapp-export:/, "");
21
+ process.stderr.write(`[whatsapp-ingest] preview parsed=${result.counters.parsed} senders=${senders.length} first=${first ?? "-"} last=${last ?? "-"}\n`);
22
+ return {
23
+ conversationSha256,
24
+ archiveSourceFile: result.archiveSourceFile,
25
+ archiveBytes,
26
+ parsed: result.counters.parsed,
27
+ mediaSkipped: result.counters.mediaSkipped,
28
+ systemSkipped: result.counters.systemSkipped,
29
+ totalMessages: result.parsedLines.length,
30
+ dateRange: { first, last },
31
+ senders,
32
+ };
33
+ }
34
+ //# sourceMappingURL=whatsapp-export-preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp-export-preview.js","sourceRoot":"","sources":["../../src/tools/whatsapp-export-preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,sDAAsD,CAAC;AAqDnF,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAmC;IAEnC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;IAE7D,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;IAC7C,MAAM,KAAK,GAAqB,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAE9E,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAElC,uEAAuE;IACvE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,OAAO,GAAkC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;SACxE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;SACvD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;IAEnD,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IAEjF,sEAAsE;IACtE,wCAAwC;IACxC,MAAM,kBAAkB,GAAG,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;IAErF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oCAAoC,MAAM,CAAC,QAAQ,CAAC,MAAM,YAAY,OAAO,CAAC,MAAM,UAAU,KAAK,IAAI,GAAG,SAAS,IAAI,IAAI,GAAG,IAAI,CACnI,CAAC;IAEF,OAAO;QACL,kBAAkB;QAClB,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,YAAY;QACZ,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM;QAC9B,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,YAAY;QAC1C,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,aAAa;QAC5C,aAAa,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM;QACxC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QAC1B,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -34,6 +34,8 @@ When loading this reference, confirm which schema files were consulted by noting
34
34
  | DefinedTerm | `DefinedTerm` | `schema:DefinedTerm` | — | `accountId`, `name`, `description` |
35
35
  | AccessGrant | `AccessGrant` | platform-native | — | `accountId`, `agentSlug`, `contactMethod`, `contactValue`, `status` (scope must be `admin`) |
36
36
  | Position | `Position` | platform-native (analogue of `schema:OrganizationRole`) | — | `accountId`, `title`, `startDate` |
37
+ | WhatsApp Conversation | `WhatsAppConversation` | extends `schema:Conversation` | — | `accountId`, `conversationId`, `archiveSourceFile`, `firstMessageAt`, `lastMessageAt`, `participantCount`, `messageCount`, `scope`, `createdByAgent`, `createdBySession`, `createdAt` |
38
+ | WhatsApp Message | `WhatsAppMessage` | extends `schema:Message` | — | `accountId`, `conversationId`, `messageId`, `dateSent`, `body`, `senderName`, `sequenceIndex`, `scope`, `createdByAgent`, `createdBySession`, `createdAt` |
37
39
 
38
40
  **Branding properties on LocalBusiness:** `primaryColor`, `accentColor`, `backgroundColor`, `tagline` — optional, used to brand the public chat endpoint. Written via `memory-update` on the LocalBusiness node. Hex color values must match `#[0-9a-fA-F]{3,8}`. Logo and icon are linked via `HAS_BRAND_ASSET → ImageObject` with `purpose: "logo"` or `"icon"`.
39
41
 
@@ -117,8 +119,18 @@ Do not write placeholder values ("TBD", "unknown", empty strings) for properties
117
119
  (:UserProfile)-[:HAS_SKILL]->(:DefinedTerm {category:'skill'})
118
120
  (:KnowledgeDocument)-[:REFERENCES]->(:*)
119
121
  (:KnowledgeDocument)-[:HAS_SECTION]->(:Section)
122
+ (:Message)-[:PART_OF]->(:Conversation)
123
+ (:Person|:AdminUser)-[:SENT]->(:Message)
124
+ (:Person|:AdminUser)-[:PARTICIPANT_IN]->(:Conversation)
125
+ (:Message)-[:NEXT]->(:Message)
120
126
  ```
121
127
 
128
+ ### WhatsApp ingest natural-key contract (Task 870)
129
+
130
+ Live nodes carry compound labels: `:Conversation:WhatsAppConversation` and `:Message:WhatsAppMessage`. The Node Types table registers `WhatsAppConversation` and `WhatsAppMessage` individually — the schema validator matches on the more-specific sublabel. Base `Conversation` and `Message` rules (e.g. `dateSent` on Message) still apply via the base label.
131
+
132
+ `:Message:WhatsAppMessage` natural key is `messageId = whatsapp-export:msg:<conversationSha256>:<dateSentISO>:<NFKC-trim-lower(senderName)>:<sha256-hex(body)>`. Re-imports of the same archive — same operator-confirmed timezone — collapse to the same Message identity. The chain `(:Message)-[:NEXT]->(:Message)` is rebuilt deterministically by ordering on `(dateSent, sequenceIndex, messageId)`; `sequenceIndex` is preserved on the node only as a tiebreaker, never as part of `messageId`.
133
+
122
134
  ### Document-ingestion section kinds (Task 740, replacing Task 737's typed-vs-UNMAPPED fork)
123
135
 
124
136
  The `document-ingest` skill maps unstructured-document sections onto a closed enumeration of `kind` values. Every section becomes one `:Section` node; recognised kinds carry a secondary label so the same node serves as both the document section and the typed entity. Identity-kind anchor edges point from the document subject directly at the multi-labeled section node — there is no parallel "Section vs typed-node" concept.
@@ -45,7 +45,9 @@ Three tools for reading WhatsApp conversation context. Messages come from the in
45
45
 
46
46
  ## Live persistence (Task 857)
47
47
 
48
- Every `messages.upsert` event (both `notify` and `append`, both `fromMe` directions) writes a `:Message:WhatsAppMessage` row to Neo4j attached to the sessionKey-keyed `:Conversation`. A single capture site at `platform/ui/app/lib/whatsapp/manager.ts` covers inbound, outbound (Baileys echoes agent-sent messages back through `messages.upsert` with `fromMe=true`), and owner-mirror — without touching `outbound/send.ts`. `messageId` namespace is `whatsapp-live:<accountId>:<remoteJid>:<msg.key.id>`, collision-free with the `whatsapp-export:` namespace used by the offline `whatsapp-import` plugin. Persist failures are loud (`[whatsapp-persist] FAIL …`) and never block dispatch — silent loss is the worse failure mode.
48
+ Every `messages.upsert` event (both `notify` and `append`, both `fromMe` directions) writes a `:Message:WhatsAppMessage` row to Neo4j attached to the sessionKey-keyed `:Conversation`. A single capture site at `platform/ui/app/lib/whatsapp/manager.ts` covers inbound, outbound (Baileys echoes agent-sent messages back through `messages.upsert` with `fromMe=true`), and owner-mirror — without touching `outbound/send.ts`. `messageId` namespace is `whatsapp-live:<waName>:<remoteJid>:<msg.key.id>` where `<waName>` is the Baileys credential dirname (e.g. `default`); collision-free with the `whatsapp-export:` namespace used by the offline `whatsapp-import` plugin. Persist failures are loud (`[whatsapp-persist] FAIL …`) and never block dispatch — silent loss is the worse failure mode.
49
+
50
+ **`accountId` contract (Task 872).** `n.accountId` on every `:Conversation`, `:Person`, and `:Message:WhatsAppMessage` row stamped by this plugin is the **platform-side UUID** resolved by [`resolvePlatformAccountId()`](../../ui/app/lib/whatsapp/platform-account-id.ts) from `data/accounts/<uuid>/account.json` — NOT the Baileys credential dirname (which is only used as the `messageId`/`sessionKey` namespace token). The boot-time line `[whatsapp-persist] resolved-account-id waname=<dir> uuid=<uuid>` records the resolution. Doctrine: see `.docs/neo4j.md` "Account isolation invariant" — migration 004 `pruneAlienAccounts` `DETACH DELETE`s any node whose `accountId` is not a UUID dir on every boot. The helper loud-throws on zero or multi accounts (Phase 0 single-account invariant), aborting the WhatsApp connection start before any write can occur.
49
51
 
50
52
  ## Skills
51
53