@rubytech/create-realagent 1.0.823 → 1.0.824
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/neo4j/migrations/006-prune-bogus-whatsapp-persons.ts +132 -0
- package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +1 -1
- package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +2 -2
- package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
- 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 +10 -5
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts +4 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js +106 -30
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js.map +1 -1
- package/payload/platform/plugins/whatsapp-import/bin/ingest.mjs +174 -115
- package/payload/platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh +18 -7
- package/payload/platform/plugins/whatsapp-import/lib/dist/parse-export.js +24 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/parse-export.js.map +1 -1
- package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/filter-gate.test.ts +2 -0
- package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/parse-export-lrm.test.ts +83 -0
- package/payload/platform/plugins/whatsapp-import/lib/src/parse-export.ts +25 -0
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/SKILL.md +11 -9
- package/payload/platform/templates/specialists/agents/database-operator.md +1 -1
- package/payload/server/chunk-JNVZQMTZ.js +593 -0
- package/payload/server/chunk-L3T7ECLI.js +1116 -0
- package/payload/server/chunk-LFOR2ACU.js +10079 -0
- package/payload/server/chunk-NAP6XMLW.js +2233 -0
- package/payload/server/client-pool-M2DU74ZP.js +32 -0
- package/payload/server/cloudflare-task-tracker-6S23B7QX.js +17 -0
- package/payload/server/maxy-edge.js +3 -3
- package/payload/server/neo4j-migrations-YED5CFPF.js +428 -0
- package/payload/server/public/assets/{admin-Bnj-1qCb.js → admin-DOkUspG1.js} +1 -1
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +120 -7
|
@@ -11,10 +11,19 @@
|
|
|
11
11
|
//
|
|
12
12
|
// Argv (positional): <archive-path>
|
|
13
13
|
// Argv (flags): --owner-element-id <id> --scope <admin|public>
|
|
14
|
+
// --subject-person-id <id>
|
|
14
15
|
// --filter <all|senders=<csv>|date-range=<isoFrom>..<isoTo>>
|
|
15
16
|
// [--account-id <accountId>] [--timezone <iana>]
|
|
16
17
|
// [--date-format <DD/MM/YY|MM/DD/YY|DD/MM/YYYY|MM/DD/YYYY>]
|
|
17
18
|
//
|
|
19
|
+
// Task 887 §A0 — `--subject-person-id` is required. DM scope: the operator
|
|
20
|
+
// confirms the third party's :Person elementId from the preview histogram
|
|
21
|
+
// before invoking. The writer accepts EXACTLY the senderNames carried by
|
|
22
|
+
// {owner, subject}; any parsed senderName outside that set LOUD-FAILs with
|
|
23
|
+
// `parser-miss reason="senderName=<verbatim> not in preview histogram
|
|
24
|
+
// (parser failure — re-export or report)"`. Bounds writer cardinality to
|
|
25
|
+
// the deterministic preview output — closes the auto-Person leak structurally.
|
|
26
|
+
//
|
|
18
27
|
// Stdout (success): one JSON line — Honest counters per Task 871.5.
|
|
19
28
|
// {conversationElementId, conversationId,
|
|
20
29
|
// parsed, mediaSkipped, systemSkipped,
|
|
@@ -139,6 +148,13 @@ function parseArgv(argv) {
|
|
|
139
148
|
if (flags.scope !== "admin" && flags.scope !== "public") {
|
|
140
149
|
fail("argv", { reason: `invalid --scope "${flags.scope}" (admin|public)` });
|
|
141
150
|
}
|
|
151
|
+
// Task 887 §A0 — DM scope: a single `--subject-person-id` identifies the
|
|
152
|
+
// third party in the conversation. The owner + subject pair is the
|
|
153
|
+
// canonical sender set; the writer rejects any other senderName as
|
|
154
|
+
// parser-miss. Group-chat ingest (>2 distinct senders) is a future task.
|
|
155
|
+
if (!flags.subjectPersonId) {
|
|
156
|
+
fail("argv", { reason: "missing --subject-person-id (Task 887: operator-confirmed third-party :Person elementId from preview histogram)" });
|
|
157
|
+
}
|
|
142
158
|
// Task 871: --filter is mandatory. The deterministic Bash entry refuses
|
|
143
159
|
// bulk archive writes without an operator-supplied filter — closes the
|
|
144
160
|
// doctrine gap named in feedback_compress_at_ingest_for_bulk_archives.md.
|
|
@@ -247,143 +263,176 @@ function resolveAccountId(flags) {
|
|
|
247
263
|
}
|
|
248
264
|
|
|
249
265
|
// ---------------------------------------------------------------------------
|
|
250
|
-
// 6.
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
//
|
|
266
|
+
// 6. Bind senders to canonical {owner, subject} pair (Task 887 §A0).
|
|
267
|
+
//
|
|
268
|
+
// Pre-887 behaviour: a `resolveParticipants` step ran each parsed senderName
|
|
269
|
+
// against an account-wide canonical index, falling through to auto-create a
|
|
270
|
+
// `:Person {participantStatus:'auto-created'}` node for any miss. That
|
|
271
|
+
// fallback path was the structural defect: any parser failure that produced
|
|
272
|
+
// a polluted senderName (Task 845's LRM-prefixed body glued onto the prior
|
|
273
|
+
// header → senderName="\"Adam Mackay:\\n[04/02/2026, 11:52:16] Adam Mackay\"")
|
|
274
|
+
// leaked one bogus :Person per distinct miss. 23 leaked from the Adam Mackay
|
|
275
|
+
// archive in a single ingest.
|
|
276
|
+
//
|
|
277
|
+
// 887 §A0 closes the leak by deleting the fallback. The operator now passes
|
|
278
|
+
// `--subject-person-id` (third-party Person elementId from preview), the
|
|
279
|
+
// dispatch passes `--owner-element-id`, and the writer accepts EXACTLY the
|
|
280
|
+
// name candidates of those two nodes. Any parsed senderName outside that
|
|
281
|
+
// closed set LOUD-FAILs the ingest with the verbatim string — surfacing a
|
|
282
|
+
// parser bug as a hard exit instead of as graph pollution.
|
|
256
283
|
// ---------------------------------------------------------------------------
|
|
257
284
|
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
WHERE
|
|
261
|
-
RETURN elementId(
|
|
262
|
-
|
|
263
|
-
coalesce(
|
|
264
|
-
|
|
265
|
-
'
|
|
266
|
-
|
|
267
|
-
MATCH (u:AdminUser {accountId: $accountId})
|
|
268
|
-
RETURN elementId(u) AS elemId,
|
|
269
|
-
'' AS givenName,
|
|
270
|
-
'' AS familyName,
|
|
271
|
-
coalesce(u.name, '') AS adminName,
|
|
272
|
-
'AdminUser' AS label
|
|
285
|
+
const CANONICAL_PAIR_FETCH_CYPHER = `
|
|
286
|
+
UNWIND $ids AS id
|
|
287
|
+
MATCH (n) WHERE elementId(n) = id
|
|
288
|
+
RETURN elementId(n) AS elemId,
|
|
289
|
+
labels(n) AS labels,
|
|
290
|
+
coalesce(n.name, '') AS name,
|
|
291
|
+
coalesce(n.givenName, '') AS givenName,
|
|
292
|
+
coalesce(n.familyName, '') AS familyName,
|
|
293
|
+
coalesce(n.accountId, '') AS accountId
|
|
273
294
|
`;
|
|
274
295
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
296
|
+
/**
|
|
297
|
+
* Sentinel error class so `main()`'s try/catch can recognise an operator
|
|
298
|
+
* LOUD-FAIL (already-emitted FAIL line) and exit cleanly with cleanup.
|
|
299
|
+
* Plain `process.exit(1)` from inside `bindCanonicalSenders` would skip
|
|
300
|
+
* `main()`'s `cleanup()` (unzip tmp dir) and `session.close()` paths.
|
|
301
|
+
*/
|
|
302
|
+
class IngestUserFacingError extends Error {
|
|
303
|
+
constructor(message) {
|
|
304
|
+
super(message);
|
|
305
|
+
this.name = "IngestUserFacingError";
|
|
306
|
+
this.userFacing = true;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
287
309
|
|
|
288
|
-
async function
|
|
289
|
-
|
|
290
|
-
|
|
310
|
+
async function bindCanonicalSenders({
|
|
311
|
+
session,
|
|
312
|
+
accountId,
|
|
313
|
+
ownerElementId,
|
|
314
|
+
subjectPersonId,
|
|
315
|
+
senderNames,
|
|
316
|
+
normaliseSenderName,
|
|
317
|
+
}) {
|
|
318
|
+
// Distinctness pre-flight — operator passing the same id for both flags
|
|
319
|
+
// collapses to a single bound participant; the drift between
|
|
320
|
+
// `participantCount=2` (from distinct senderNames) and
|
|
321
|
+
// `participantsAlreadyExisted=1` would propagate to the JSON summary
|
|
322
|
+
// silently. Refuse early, name the cause.
|
|
323
|
+
if (ownerElementId === subjectPersonId) {
|
|
324
|
+
throw new IngestUserFacingError(
|
|
325
|
+
`--owner-element-id and --subject-person-id must be distinct elementIds (both supplied as "${ownerElementId}")`,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const ids = [ownerElementId, subjectPersonId];
|
|
291
330
|
const res = await session.executeRead(async (tx) =>
|
|
292
|
-
tx.run(
|
|
331
|
+
tx.run(CANONICAL_PAIR_FETCH_CYPHER, { ids }),
|
|
293
332
|
);
|
|
333
|
+
|
|
334
|
+
// Build normalised-name → elementId index from owner + subject candidates.
|
|
335
|
+
// For each node we accept the full name, given name, family name, and
|
|
336
|
+
// "given family" composite as match candidates so an export header that
|
|
337
|
+
// says "Adam" or "Adam Mackay" both resolve to the same node.
|
|
294
338
|
const index = new Map();
|
|
339
|
+
const seenIds = new Set();
|
|
340
|
+
const labelByElemId = new Map();
|
|
295
341
|
for (const r of res.records) {
|
|
296
342
|
const elemId = r.get("elemId");
|
|
297
|
-
const
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
343
|
+
const labels = r.get("labels") || [];
|
|
344
|
+
const acct = r.get("accountId") || "";
|
|
345
|
+
// Empty accountId on a canonical Person/AdminUser is a graph-data
|
|
346
|
+
// defect (migration 004 normally prunes account-less nodes). Refuse
|
|
347
|
+
// rather than silently accept — bound-pair correctness depends on
|
|
348
|
+
// accountId being present and matching.
|
|
349
|
+
if (!acct) {
|
|
350
|
+
throw new IngestUserFacingError(
|
|
351
|
+
`node ${elemId} has no accountId — corrupt canonical Person/AdminUser; refusing ingest`,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
if (acct !== accountId) {
|
|
355
|
+
throw new IngestUserFacingError(
|
|
356
|
+
`node ${elemId} belongs to account ${acct}, not ${accountId} — refusing cross-account ingest`,
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
if (!labels.includes("Person") && !labels.includes("AdminUser")) {
|
|
360
|
+
throw new IngestUserFacingError(
|
|
361
|
+
`node ${elemId} has labels [${labels.join(",")}]; expected :Person or :AdminUser`,
|
|
362
|
+
);
|
|
308
363
|
}
|
|
364
|
+
seenIds.add(elemId);
|
|
365
|
+
labelByElemId.set(
|
|
366
|
+
elemId,
|
|
367
|
+
labels.includes("Person") ? "Person" : "AdminUser",
|
|
368
|
+
);
|
|
369
|
+
const candidates = [];
|
|
370
|
+
const name = r.get("name") || "";
|
|
371
|
+
const given = r.get("givenName") || "";
|
|
372
|
+
const family = r.get("familyName") || "";
|
|
373
|
+
if (name) candidates.push(name);
|
|
374
|
+
if (given && family) candidates.push(`${given} ${family}`);
|
|
375
|
+
if (given) candidates.push(given);
|
|
376
|
+
if (family) candidates.push(family);
|
|
309
377
|
for (const c of candidates) {
|
|
310
378
|
const norm = normaliseSenderName(c);
|
|
311
379
|
if (!norm) continue;
|
|
312
|
-
|
|
313
|
-
if (!index.has(norm)) index.set(norm, { elemId, label });
|
|
380
|
+
if (!index.has(norm)) index.set(norm, elemId);
|
|
314
381
|
}
|
|
315
382
|
}
|
|
316
|
-
return index;
|
|
317
|
-
}
|
|
318
383
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
384
|
+
// Both ids must resolve. A typo'd or stale id is operator error; LOUD-FAIL
|
|
385
|
+
// before any parser work touches the graph.
|
|
386
|
+
if (!seenIds.has(ownerElementId)) {
|
|
387
|
+
throw new IngestUserFacingError(
|
|
388
|
+
`--owner-element-id ${ownerElementId} not found in graph`,
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
if (!seenIds.has(subjectPersonId)) {
|
|
392
|
+
throw new IngestUserFacingError(
|
|
393
|
+
`--subject-person-id ${subjectPersonId} not found in graph`,
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
// Subject must specifically be a `:Person` — the third party in a DM is
|
|
397
|
+
// never the operator's `:AdminUser`. (Owner can be either; both Adam and
|
|
398
|
+
// an external collaborator owning an export are operator-curated cases.)
|
|
399
|
+
if (labelByElemId.get(subjectPersonId) !== "Person") {
|
|
400
|
+
throw new IngestUserFacingError(
|
|
401
|
+
`--subject-person-id ${subjectPersonId} resolves to a :${labelByElemId.get(subjectPersonId)} — subject must be a :Person`,
|
|
402
|
+
);
|
|
329
403
|
}
|
|
330
404
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
405
|
+
// Group-chat early-detect: the singular `--subject-person-id` flag is
|
|
406
|
+
// DM-scoped. A `_chat.txt` carrying ≥3 distinct senders is an unsupported
|
|
407
|
+
// scope, NOT a parser bug. Emit a distinct reason so the operator does
|
|
408
|
+
// not chase a phantom parser regression. Group-chat support is the
|
|
409
|
+
// separate Task 889 lane.
|
|
410
|
+
if (senderNames.length > 2) {
|
|
411
|
+
throw new IngestUserFacingError(
|
|
412
|
+
`unsupported-scope reason="archive carries ${senderNames.length} distinct senders; --subject-person-id is DM-only (≤2 senders) — group-chat ingest is the separate Task 889 lane"`,
|
|
413
|
+
);
|
|
414
|
+
}
|
|
336
415
|
|
|
416
|
+
// Validate every distinct parsed senderName against the closed candidate
|
|
417
|
+
// set. The first miss is the LOUD-FAIL — operators see one parser-miss
|
|
418
|
+
// line per bad import, not 23.
|
|
337
419
|
const idsByName = new Map();
|
|
338
|
-
const fallbackSenders = [];
|
|
339
|
-
let canonicalMatches = 0;
|
|
340
420
|
for (const senderName of senderNames) {
|
|
341
421
|
const norm = normaliseSenderName(senderName);
|
|
342
|
-
const hit =
|
|
343
|
-
if (hit) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
log(
|
|
347
|
-
`participant-resolved senderName="${senderName}" matched=canonical nodeId=${hit.elemId} label=${hit.label}`,
|
|
348
|
-
);
|
|
349
|
-
} else {
|
|
350
|
-
fallbackSenders.push(senderName);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
let autoCreated = 0;
|
|
355
|
-
if (fallbackSenders.length > 0) {
|
|
356
|
-
const result = await session.executeWrite(async (tx) => {
|
|
357
|
-
const res = await tx.run(PARTICIPANT_UPSERT_CYPHER, {
|
|
358
|
-
names: fallbackSenders,
|
|
359
|
-
accountId,
|
|
360
|
-
scope,
|
|
361
|
-
sessionId,
|
|
362
|
-
});
|
|
363
|
-
const m = new Map();
|
|
364
|
-
for (const r of res.records) {
|
|
365
|
-
m.set(r.get("name"), r.get("elemId"));
|
|
366
|
-
}
|
|
367
|
-
const stats = res.summary.counters.updates();
|
|
368
|
-
return { m, created: stats.nodesCreated };
|
|
369
|
-
});
|
|
370
|
-
autoCreated = result.created;
|
|
371
|
-
for (const senderName of fallbackSenders) {
|
|
372
|
-
const elemId = result.m.get(senderName);
|
|
373
|
-
if (!elemId) continue;
|
|
374
|
-
idsByName.set(senderName, elemId);
|
|
375
|
-
log(
|
|
376
|
-
`participant-resolved senderName="${senderName}" matched=auto nodeId=${elemId} label=Person`,
|
|
422
|
+
const hit = index.get(norm);
|
|
423
|
+
if (!hit) {
|
|
424
|
+
throw new IngestUserFacingError(
|
|
425
|
+
`parser-miss reason="senderName=${senderName} not in preview histogram (parser failure — re-export or report)"`,
|
|
377
426
|
);
|
|
378
427
|
}
|
|
428
|
+
idsByName.set(senderName, hit);
|
|
429
|
+
log(
|
|
430
|
+
`participant-resolved senderName="${senderName}" matched=canonical nodeId=${hit}`,
|
|
431
|
+
);
|
|
379
432
|
}
|
|
380
|
-
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
return {
|
|
384
|
-
idsByName,
|
|
385
|
-
participantsAlreadyExisted: canonicalMatches + autoAlreadyExisted,
|
|
386
|
-
};
|
|
433
|
+
// participantsAlreadyExisted = always 2 (owner + subject) under the
|
|
434
|
+
// bound-pair contract; expose it for the JSON summary's existing field.
|
|
435
|
+
return { idsByName, participantsAlreadyExisted: seenIds.size };
|
|
387
436
|
}
|
|
388
437
|
|
|
389
438
|
// ---------------------------------------------------------------------------
|
|
@@ -394,6 +443,7 @@ async function main() {
|
|
|
394
443
|
const startedMs = Date.now();
|
|
395
444
|
const { archive, flags } = parseArgv(process.argv);
|
|
396
445
|
const ownerElementId = flags.ownerElementId;
|
|
446
|
+
const subjectPersonId = flags.subjectPersonId;
|
|
397
447
|
const scope = flags.scope;
|
|
398
448
|
const accountId = resolveAccountId(flags);
|
|
399
449
|
const timezone = flags.timezone || "Europe/London";
|
|
@@ -468,19 +518,28 @@ async function main() {
|
|
|
468
518
|
);
|
|
469
519
|
|
|
470
520
|
try {
|
|
471
|
-
participantUpsert = await
|
|
521
|
+
participantUpsert = await bindCanonicalSenders({
|
|
472
522
|
session,
|
|
473
523
|
accountId,
|
|
474
|
-
|
|
475
|
-
|
|
524
|
+
ownerElementId,
|
|
525
|
+
subjectPersonId,
|
|
476
526
|
senderNames: distinctSenderNames,
|
|
477
527
|
normaliseSenderName,
|
|
478
528
|
});
|
|
479
529
|
} catch (err) {
|
|
480
530
|
await session.close().catch(() => {});
|
|
481
531
|
cleanup();
|
|
532
|
+
// IngestUserFacingError carries a brief-shaped FAIL line (parser-miss /
|
|
533
|
+
// unsupported-scope / argv mismatch) that the operator's grep recipes
|
|
534
|
+
// already match on. Preserve it verbatim instead of wrapping in
|
|
535
|
+
// phase=archive-write — wrapping would defeat
|
|
536
|
+
// `grep '\[whatsapp-ingest\] FAIL parser-miss'` and friends.
|
|
537
|
+
if (err && err.userFacing) {
|
|
538
|
+
process.stderr.write(`[whatsapp-ingest] FAIL ${err.message}\n`);
|
|
539
|
+
process.exit(1);
|
|
540
|
+
}
|
|
482
541
|
fail("archive-write", {
|
|
483
|
-
phase: "
|
|
542
|
+
phase: "bind-canonical-senders",
|
|
484
543
|
reason: err instanceof Error ? err.message : String(err),
|
|
485
544
|
});
|
|
486
545
|
}
|
|
@@ -10,12 +10,19 @@
|
|
|
10
10
|
# Usage:
|
|
11
11
|
# bash whatsapp-ingest.sh <archive.zip|dir|_chat.txt>
|
|
12
12
|
# --owner-element-id <id>
|
|
13
|
+
# --subject-person-id <id>
|
|
13
14
|
# --scope <admin|public>
|
|
14
15
|
# --filter <all|senders=<csv>|date-range=<isoFrom>..<isoTo>>
|
|
15
16
|
# [--account-id <accountId>]
|
|
16
17
|
# [--timezone <iana-zone>]
|
|
17
18
|
# [--date-format <DD/MM/YY|MM/DD/YY|DD/MM/YYYY|MM/DD/YYYY>]
|
|
18
19
|
#
|
|
20
|
+
# `--subject-person-id` is required (Task 887 §A0). DM scope: the
|
|
21
|
+
# operator-confirmed third-party :Person elementId from the preview
|
|
22
|
+
# histogram. Owner + subject form the closed sender set; any parsed
|
|
23
|
+
# senderName outside that set LOUD-FAILs with `parser-miss` and exits
|
|
24
|
+
# non-zero — bounds writer cardinality to the deterministic preview output.
|
|
25
|
+
#
|
|
19
26
|
# `--filter` is mandatory (Task 871). Forms:
|
|
20
27
|
# all — write every parsed row
|
|
21
28
|
# senders=Alice,Bob Carter — keep rows whose senderName ∈ csv
|
|
@@ -54,9 +61,11 @@ fi
|
|
|
54
61
|
# wrong invocation, not on a missing password.
|
|
55
62
|
ARCHIVE=""
|
|
56
63
|
OWNER_VAL=""
|
|
64
|
+
SUBJECT_VAL=""
|
|
57
65
|
SCOPE_VAL=""
|
|
58
66
|
FILTER_VAL=""
|
|
59
67
|
HAS_OWNER=0
|
|
68
|
+
HAS_SUBJECT=0
|
|
60
69
|
HAS_SCOPE=0
|
|
61
70
|
HAS_FILTER=0
|
|
62
71
|
|
|
@@ -65,11 +74,12 @@ i=0
|
|
|
65
74
|
while [ $i -lt ${#ARGS[@]} ]; do
|
|
66
75
|
a="${ARGS[$i]}"
|
|
67
76
|
case "$a" in
|
|
68
|
-
--owner-element-id)
|
|
69
|
-
--
|
|
70
|
-
--
|
|
77
|
+
--owner-element-id) HAS_OWNER=1; OWNER_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
|
|
78
|
+
--subject-person-id) HAS_SUBJECT=1; SUBJECT_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
|
|
79
|
+
--scope) HAS_SCOPE=1; SCOPE_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
|
|
80
|
+
--filter) HAS_FILTER=1; FILTER_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
|
|
71
81
|
--account-id|--timezone|--date-format) i=$((i + 2)); continue ;;
|
|
72
|
-
--*)
|
|
82
|
+
--*) i=$((i + 2)); continue ;;
|
|
73
83
|
*)
|
|
74
84
|
if [ -z "$ARCHIVE" ]; then ARCHIVE="$a"; fi
|
|
75
85
|
i=$((i + 1))
|
|
@@ -78,9 +88,10 @@ while [ $i -lt ${#ARGS[@]} ]; do
|
|
|
78
88
|
esac
|
|
79
89
|
done
|
|
80
90
|
|
|
81
|
-
[ -n "$ARCHIVE" ]
|
|
82
|
-
[ "$HAS_OWNER"
|
|
83
|
-
[ "$
|
|
91
|
+
[ -n "$ARCHIVE" ] || arg_fail "missing positional <archive>"
|
|
92
|
+
[ "$HAS_OWNER" -eq 1 ] && [ -n "$OWNER_VAL" ] || arg_fail "missing --owner-element-id (or empty value)"
|
|
93
|
+
[ "$HAS_SUBJECT" -eq 1 ] && [ -n "$SUBJECT_VAL" ] || arg_fail "missing --subject-person-id (Task 887: operator-confirmed third-party :Person elementId from preview histogram)"
|
|
94
|
+
[ "$HAS_SCOPE" -eq 1 ] && [ -n "$SCOPE_VAL" ] || arg_fail "missing --scope (or empty value)"
|
|
84
95
|
case "$SCOPE_VAL" in
|
|
85
96
|
admin|public) : ;;
|
|
86
97
|
*) arg_fail "invalid --scope \"$SCOPE_VAL\" (admin|public)" ;;
|
|
@@ -177,6 +177,30 @@ function decodeAndNormalise(bytes) {
|
|
|
177
177
|
}
|
|
178
178
|
// Normalise mixed line endings to LF.
|
|
179
179
|
text = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
180
|
+
// Task 887 — strip Unicode bidi marks (U+200E LRM, U+200F RLM) only at
|
|
181
|
+
// line-start, where some WhatsApp builds prefix the timestamp header.
|
|
182
|
+
// Without stripping, `^\[(\d{2})\/...` fails on the prefixed line, the
|
|
183
|
+
// line is appended as a continuation of the previous body, and the next
|
|
184
|
+
// clean header parses its senderName off the polluted body — leaking 23
|
|
185
|
+
// bogus :Person nodes per import in the Adam Mackay archive. Body-internal
|
|
186
|
+
// bidi marks (e.g. the LRM in `: Forwarded`) are preserved — they carry
|
|
187
|
+
// semantic information about message origin and are exercised by
|
|
188
|
+
// parse-export.test.ts. Counts emitted to stderr for the operator's tail.
|
|
189
|
+
const leadingBidiMatches = text.match(/(?:^|\n)[]+/g) || [];
|
|
190
|
+
let lrmStripped = 0;
|
|
191
|
+
let rlmStripped = 0;
|
|
192
|
+
for (const m of leadingBidiMatches) {
|
|
193
|
+
for (const ch of m) {
|
|
194
|
+
if (ch === "")
|
|
195
|
+
lrmStripped++;
|
|
196
|
+
else if (ch === "")
|
|
197
|
+
rlmStripped++;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (leadingBidiMatches.length > 0) {
|
|
201
|
+
text = text.replace(/(^|\n)[]+/g, "$1");
|
|
202
|
+
process.stderr.write(`[whatsapp-ingest] decoded normalised lrm-stripped=${lrmStripped} rlm-stripped=${rlmStripped}\n`);
|
|
203
|
+
}
|
|
180
204
|
return text;
|
|
181
205
|
}
|
|
182
206
|
function matchTimestampPrefix(line, ordering) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-export.js","sourceRoot":"","sources":["../src/parse-export.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parse-export.js","sourceRoot":"","sources":["../src/parse-export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { parseExport } from "../parse-export.js";
|
|
6
|
+
|
|
7
|
+
// Task 887 — bidi-strip regression. Some WhatsApp exports prefix every
|
|
8
|
+
// timestamp header with U+200E (LEFT-TO-RIGHT MARK) or U+200F (RTL MARK).
|
|
9
|
+
// Pre-fix `decodeAndNormalise` left those bytes in place; the timestamp
|
|
10
|
+
// regex (`^\[(\d{2})\/...`) failed; the LRM-prefixed line was glued onto
|
|
11
|
+
// the previous body as a continuation; the next clean header parsed its
|
|
12
|
+
// senderName off the polluted body — leaking 23 :Person nodes per import
|
|
13
|
+
// in the Adam Mackay archive. The fix strips U+200E/U+200F before
|
|
14
|
+
// tokenisation; this test reproduces the failure shape.
|
|
15
|
+
|
|
16
|
+
let workDir: string;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
workDir = mkdtempSync(join(tmpdir(), "whatsapp-export-lrm-"));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function writeChat(name: string, content: string): string {
|
|
27
|
+
const filePath = join(workDir, name);
|
|
28
|
+
writeFileSync(filePath, content);
|
|
29
|
+
return filePath;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("parseExport — bidi-strip (Task 887)", () => {
|
|
33
|
+
it("strips U+200E from timestamp headers and parses each row independently", () => {
|
|
34
|
+
const LRM = "";
|
|
35
|
+
const filePath = writeChat(
|
|
36
|
+
"_chat.txt",
|
|
37
|
+
[
|
|
38
|
+
`${LRM}[04/02/26, 11:52:16] Adam Mackay: hi`,
|
|
39
|
+
`${LRM}[04/02/26, 11:52:30] Joel Smalley: hey`,
|
|
40
|
+
"",
|
|
41
|
+
].join("\n"),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const result = parseExport({
|
|
45
|
+
filePath,
|
|
46
|
+
accountId: "acct-887",
|
|
47
|
+
timezone: "Europe/London",
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
expect(result.parsedLines).toHaveLength(2);
|
|
51
|
+
expect(result.parsedLines.map((l) => l.senderName).sort()).toEqual([
|
|
52
|
+
"Adam Mackay",
|
|
53
|
+
"Joel Smalley",
|
|
54
|
+
]);
|
|
55
|
+
for (const line of result.parsedLines) {
|
|
56
|
+
expect(line.senderName).not.toContain("\n");
|
|
57
|
+
expect(line.senderName).not.toContain("[");
|
|
58
|
+
expect(line.senderName).not.toContain(LRM);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("strips U+200F (RLM) on the timestamp line", () => {
|
|
63
|
+
const RLM = "";
|
|
64
|
+
const filePath = writeChat(
|
|
65
|
+
"_chat.txt",
|
|
66
|
+
[
|
|
67
|
+
`${RLM}[14/03/26, 10:15:23] Joel: Hello`,
|
|
68
|
+
`${RLM}[14/03/26, 10:16:01] Sarah: Hi back`,
|
|
69
|
+
"",
|
|
70
|
+
].join("\n"),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const result = parseExport({
|
|
74
|
+
filePath,
|
|
75
|
+
accountId: "acct-887",
|
|
76
|
+
timezone: "Europe/London",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(result.parsedLines).toHaveLength(2);
|
|
80
|
+
expect(result.parsedLines[0].senderName).toBe("Joel");
|
|
81
|
+
expect(result.parsedLines[1].senderName).toBe("Sarah");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -292,6 +292,31 @@ function decodeAndNormalise(bytes: Buffer): string {
|
|
|
292
292
|
// Normalise mixed line endings to LF.
|
|
293
293
|
text = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
294
294
|
|
|
295
|
+
// Task 887 — strip Unicode bidi marks (U+200E LRM, U+200F RLM) only at
|
|
296
|
+
// line-start, where some WhatsApp builds prefix the timestamp header.
|
|
297
|
+
// Without stripping, `^\[(\d{2})\/...` fails on the prefixed line, the
|
|
298
|
+
// line is appended as a continuation of the previous body, and the next
|
|
299
|
+
// clean header parses its senderName off the polluted body — leaking 23
|
|
300
|
+
// bogus :Person nodes per import in the Adam Mackay archive. Body-internal
|
|
301
|
+
// bidi marks (e.g. the LRM in `: Forwarded`) are preserved — they carry
|
|
302
|
+
// semantic information about message origin and are exercised by
|
|
303
|
+
// parse-export.test.ts. Counts emitted to stderr for the operator's tail.
|
|
304
|
+
const leadingBidiMatches = text.match(/(?:^|\n)[]+/g) || [];
|
|
305
|
+
let lrmStripped = 0;
|
|
306
|
+
let rlmStripped = 0;
|
|
307
|
+
for (const m of leadingBidiMatches) {
|
|
308
|
+
for (const ch of m) {
|
|
309
|
+
if (ch === "") lrmStripped++;
|
|
310
|
+
else if (ch === "") rlmStripped++;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (leadingBidiMatches.length > 0) {
|
|
314
|
+
text = text.replace(/(^|\n)[]+/g, "$1");
|
|
315
|
+
process.stderr.write(
|
|
316
|
+
`[whatsapp-ingest] decoded normalised lrm-stripped=${lrmStripped} rlm-stripped=${rlmStripped}\n`,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
295
320
|
return text;
|
|
296
321
|
}
|
|
297
322
|
|