@rubytech/create-maxy 1.0.830 → 1.0.832

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 (79) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/oauth-llm/dist/index.d.ts +8 -1
  3. package/payload/platform/lib/oauth-llm/dist/index.d.ts.map +1 -1
  4. package/payload/platform/lib/oauth-llm/dist/index.js +5 -1
  5. package/payload/platform/lib/oauth-llm/dist/index.js.map +1 -1
  6. package/payload/platform/lib/oauth-llm/src/index.ts +19 -4
  7. package/payload/platform/neo4j/migrations/004-project-admin-agent.ts +36 -3
  8. package/payload/platform/neo4j/migrations/008-adminuser-accountid-backfill.ts +85 -0
  9. package/payload/platform/plugins/admin/mcp/dist/index.js +25 -3
  10. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  11. package/payload/platform/plugins/docs/references/internals.md +1 -1
  12. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +293 -127
  13. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +41 -14
  14. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.d.ts +49 -0
  15. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.d.ts.map +1 -0
  16. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.js +35 -0
  17. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.js.map +1 -0
  18. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +6 -7
  19. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -1
  20. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +9 -1
  21. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -1
  22. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts.map +1 -1
  23. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js +49 -131
  24. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js.map +1 -1
  25. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts.map +1 -1
  26. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js +0 -1
  27. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js.map +1 -1
  28. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts +0 -2
  29. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts.map +1 -1
  30. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js +10 -11
  31. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts +3 -4
  33. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts.map +1 -1
  34. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js +11 -42
  35. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js.map +1 -1
  36. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts.map +1 -1
  37. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js +10 -8
  38. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js.map +1 -1
  39. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +10 -8
  40. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -1
  41. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +118 -43
  42. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
  43. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts +3 -2
  44. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +1 -1
  45. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +40 -18
  46. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +1 -1
  47. package/payload/platform/plugins/memory/mcp/dist/lib/uuid.d.ts +3 -0
  48. package/payload/platform/plugins/memory/mcp/dist/lib/uuid.d.ts.map +1 -0
  49. package/payload/platform/plugins/memory/mcp/dist/lib/uuid.js +12 -0
  50. package/payload/platform/plugins/memory/mcp/dist/lib/uuid.js.map +1 -0
  51. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js +4 -6
  52. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js.map +1 -1
  53. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js +59 -17
  54. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +11 -0
  56. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  57. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +17 -9
  58. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  59. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js +2 -2
  60. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js.map +1 -1
  61. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +41 -9
  62. package/payload/platform/scripts/lib/resolve-account-dir.sh +19 -1
  63. package/payload/server/chunk-25QDCOE5.js +1116 -0
  64. package/payload/server/chunk-35YZS3KL.js +328 -0
  65. package/payload/server/chunk-7CBRZKZS.js +654 -0
  66. package/payload/server/chunk-BCFM2UPH.js +2305 -0
  67. package/payload/server/chunk-CV3HPX46.js +10097 -0
  68. package/payload/server/chunk-IXOPV36P.js +2305 -0
  69. package/payload/server/chunk-J6YWEJBN.js +1116 -0
  70. package/payload/server/chunk-OCPJGZ6S.js +654 -0
  71. package/payload/server/chunk-ZKGAYLAK.js +10097 -0
  72. package/payload/server/client-pool-NBVGONQL.js +32 -0
  73. package/payload/server/client-pool-ZNGN66GN.js +32 -0
  74. package/payload/server/cloudflare-task-tracker-MHALDN54.js +19 -0
  75. package/payload/server/cloudflare-task-tracker-R4FIORFL.js +19 -0
  76. package/payload/server/maxy-edge.js +4 -4
  77. package/payload/server/neo4j-migrations-3A6K2EB5.js +552 -0
  78. package/payload/server/neo4j-migrations-6RW423E2.js +530 -0
  79. package/payload/server/server.js +43 -20
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env bash
2
2
  # =============================================================================
3
3
  # conversation-archive-ingest.sh — single deterministic Bash entry for
4
- # conversation-archive ingestion (Task 894 — supersedes whatsapp-ingest.sh).
4
+ # conversation-archive ingestion (Task 894 — supersedes whatsapp-ingest.sh;
5
+ # Task 900 — env-only identity, progress-file path on stdout).
5
6
  #
6
7
  # Source-agnostic: WhatsApp `_chat.txt`, Telegram, Signal, LinkedIn DMs, Zoom
7
8
  # transcript, meeting minutes, iMessage, Slack — every source flows through
@@ -9,26 +10,31 @@
9
10
  #
10
11
  # Pipeline: normalise (per source) → bind canonical sender set →
11
12
  # derive conversationIdentity → look up prior :ConversationArchive (delta
12
- # cursor) → sessionize delta at gap-hours boundary → classify each session
13
- # via Haiku (mode='chat') memory-ingest with parentLabel='ConversationArchive'.
13
+ # cursor) → sessionize delta at gap-hours boundary → for each session: classify
14
+ # via Haiku (mode='chat') + memory-ingest immediately (per-session checkpoint).
14
15
  #
15
16
  # Usage:
16
17
  # bash conversation-archive-ingest.sh <archive-path>
17
18
  # --source <whatsapp|telegram|signal|linkedin-messages|zoom-transcript|meeting-minutes|imessage|slack|other>
18
- # --owner-element-id <id>
19
19
  # --participant-person-ids <csv>
20
20
  # --scope <admin|public>
21
21
  # [--session-gap-hours <N>] (default 12)
22
- # [--account-id <accountId>]
23
22
  # [--timezone <iana-zone>]
24
23
  # [--date-format <DD/MM/YY|MM/DD/YY|DD/MM/YYYY|MM/DD/YYYY>] (whatsapp only)
24
+ # [--session-id <id>]
25
25
  #
26
- # `--owner-element-id` + `--participant-person-ids` form the closed sender
27
- # set; any parsed senderName outside that set LOUD-FAILs with `parser-miss`
28
- # and exits non-zero.
26
+ # Identity (Task 900): `ACCOUNT_ID` + `USER_ID` come from the caller's env
27
+ # (plumbed by `spawn-env.ts` into every Bash subprocess). The owner :AdminUser
28
+ # elementId is derived from (accountId, userId) inside the bin via Cypher.
29
+ # `--account-id` and `--owner-element-id` flags are GONE — env-only identity
30
+ # is doctrine. `--participant-person-ids` carries the operator-confirmed
31
+ # non-owner set.
29
32
  #
30
33
  # Exit 0 + JSON summary on stdout on success.
31
34
  # Exit !0 + one [conversation-archive] FAIL line on stderr on failure.
35
+ # Stdout (always, before exec node): `[conversation-archive] progress-file=<absolute-path>`
36
+ # Mid-run progress: line-by-line writes to that progress-file (fsync per line);
37
+ # the agent's heartbeat polls it via `tail -n <N> <path>` every 30 s.
32
38
  # =============================================================================
33
39
 
34
40
  set -euo pipefail
@@ -47,15 +53,25 @@ if [ ! -f "$INGEST_MJS" ]; then
47
53
  arg_fail "conversation-archive-ingest.mjs not found at $INGEST_MJS — run from a built install"
48
54
  fi
49
55
 
56
+ # Identity is env-only (Task 900). The mjs LOUD-FAILs on missing/malformed
57
+ # values, but we mirror the cheap accountId presence check here so the
58
+ # progress-file path can be computed and emitted before exec node — without
59
+ # accountId, the path is undefined.
60
+ if [ -z "${ACCOUNT_ID:-}" ]; then
61
+ arg_fail "ACCOUNT_ID env missing — bin must be invoked under an authenticated agent context (set by spawn-env.ts)"
62
+ fi
63
+ if [ -z "${USER_ID:-}" ]; then
64
+ arg_fail "USER_ID env missing — bin must be invoked under an authenticated user session (set by spawn-env.ts)"
65
+ fi
66
+
50
67
  ARCHIVE=""
51
68
  HAS_SOURCE=0
52
69
  SOURCE_VAL=""
53
- HAS_OWNER=0
54
- OWNER_VAL=""
55
70
  HAS_PARTICIPANTS=0
56
71
  PARTICIPANTS_VAL=""
57
72
  HAS_SCOPE=0
58
73
  SCOPE_VAL=""
74
+ SESSION_ID_VAL=""
59
75
 
60
76
  ARGS=("$@")
61
77
  i=0
@@ -63,10 +79,10 @@ while [ $i -lt ${#ARGS[@]} ]; do
63
79
  a="${ARGS[$i]}"
64
80
  case "$a" in
65
81
  --source) HAS_SOURCE=1; SOURCE_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
66
- --owner-element-id) HAS_OWNER=1; OWNER_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
67
82
  --participant-person-ids) HAS_PARTICIPANTS=1; PARTICIPANTS_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
68
83
  --scope) HAS_SCOPE=1; SCOPE_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
69
- --session-gap-hours|--account-id|--timezone|--date-format|--session-id) i=$((i + 2)); continue ;;
84
+ --session-id) SESSION_ID_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
85
+ --session-gap-hours|--timezone|--date-format) i=$((i + 2)); continue ;;
70
86
  --*) i=$((i + 2)); continue ;;
71
87
  *)
72
88
  if [ -z "$ARCHIVE" ]; then ARCHIVE="$a"; fi
@@ -78,7 +94,6 @@ done
78
94
 
79
95
  [ -n "$ARCHIVE" ] || arg_fail "missing positional <archive>"
80
96
  [ "$HAS_SOURCE" -eq 1 ] && [ -n "$SOURCE_VAL" ] || arg_fail "missing --source (whatsapp|telegram|signal|linkedin-messages|zoom-transcript|meeting-minutes|imessage|slack|other)"
81
- [ "$HAS_OWNER" -eq 1 ] && [ -n "$OWNER_VAL" ] || arg_fail "missing --owner-element-id (or empty value)"
82
97
  [ "$HAS_PARTICIPANTS" -eq 1 ] && [ -n "$PARTICIPANTS_VAL" ] || arg_fail "missing --participant-person-ids (csv of operator-confirmed :Person/:AdminUser elementIds, owner excluded)"
83
98
  [ "$HAS_SCOPE" -eq 1 ] && [ -n "$SCOPE_VAL" ] || arg_fail "missing --scope (or empty value)"
84
99
  case "$SCOPE_VAL" in
@@ -103,4 +118,16 @@ fi
103
118
  export NEO4J_USER="${NEO4J_USER:-neo4j}"
104
119
  export MAXY_PLATFORM_ROOT="$PLATFORM_ROOT"
105
120
 
106
- exec node "$INGEST_MJS" "$@"
121
+ # Progress-file path must match the path the mjs opens (deterministic from
122
+ # (ACCOUNT_ID, sessionId)). When the operator omits --session-id, the mjs
123
+ # mints one — the wrapper passes through the same value via env so both
124
+ # sides converge on one path.
125
+ if [ -z "$SESSION_ID_VAL" ]; then
126
+ SESSION_ID_VAL="conversation-archive:${SOURCE_VAL}:$(date +%s%N)"
127
+ ARGS+=(--session-id "$SESSION_ID_VAL")
128
+ fi
129
+ INSTALL_DIR="$(cd "$PLATFORM_ROOT/.." && pwd)"
130
+ PROGRESS_FILE="$INSTALL_DIR/data/accounts/$ACCOUNT_ID/logs/conversation-archive-$SESSION_ID_VAL.log"
131
+ echo "[conversation-archive] progress-file=$PROGRESS_FILE"
132
+
133
+ exec node "$INGEST_MJS" "${ARGS[@]}"
@@ -0,0 +1,49 @@
1
+ import type { ParsedLine } from "./types.js";
2
+ /**
3
+ * Regex contract: must capture date parts in fixed group order. Group 0 is
4
+ * the full prefix match; groups 1..6 are date parts (year, month, day, hour,
5
+ * minute, second). Source normalisers translate their grammar's groups into
6
+ * this fixed shape via the `extractDateParts` callback, so the scanner stays
7
+ * source-agnostic.
8
+ */
9
+ export interface TimestampPrefixMatch {
10
+ /** Year, four digits (e.g. 2024). */
11
+ year: number;
12
+ /** Month, 1-12. */
13
+ month: number;
14
+ /** Day-of-month, 1-31. */
15
+ day: number;
16
+ /** Hour, 0-23. */
17
+ hour: number;
18
+ /** Minute, 0-59. */
19
+ minute: number;
20
+ /** Second, 0-59. */
21
+ second: number;
22
+ /** Everything on the line after the consumed prefix. */
23
+ remainder: string;
24
+ }
25
+ export type TimestampPrefixMatcher = (line: string) => TimestampPrefixMatch | null;
26
+ export type IsoFormatter = (parts: Omit<TimestampPrefixMatch, "remainder">) => string;
27
+ export interface ScanInput {
28
+ /** Decoded text, lines split by `\n`. */
29
+ text: string;
30
+ /** Returns parsed timestamp + remainder for a matched line, or null. */
31
+ matchPrefix: TimestampPrefixMatcher;
32
+ /** Renders the matched date parts as the canonical ISO `dateSent`. */
33
+ formatIso: IsoFormatter;
34
+ }
35
+ export interface ScanResult {
36
+ parsedLines: ParsedLine[];
37
+ /** Number of lines that matched the timestamp prefix (= parsedLines.length). */
38
+ matchedCount: number;
39
+ }
40
+ /**
41
+ * Walk `text` line by line. Lines that match `matchPrefix` start a new entry;
42
+ * lines that don't match glue onto the preceding entry's body with `\n`.
43
+ *
44
+ * Returns an empty `parsedLines` array when no line matches the prefix —
45
+ * the caller decides whether to route the input to a non-conversation path
46
+ * (document chunker) or loud-fail.
47
+ */
48
+ export declare function scanTimestampedLines(input: ScanInput): ScanResult;
49
+ //# sourceMappingURL=timestamp-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestamp-scanner.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/timestamp-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAc7C;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,oBAAoB,GAAG,IAAI,CAAC;AAEnF,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,oBAAoB,EAAE,WAAW,CAAC,KAAK,MAAM,CAAC;AAEtF,MAAM,WAAW,SAAS;IACxB,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,WAAW,EAAE,sBAAsB,CAAC;IACpC,sEAAsE;IACtE,SAAS,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,gFAAgF;IAChF,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CA8BjE"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Walk `text` line by line. Lines that match `matchPrefix` start a new entry;
3
+ * lines that don't match glue onto the preceding entry's body with `\n`.
4
+ *
5
+ * Returns an empty `parsedLines` array when no line matches the prefix —
6
+ * the caller decides whether to route the input to a non-conversation path
7
+ * (document chunker) or loud-fail.
8
+ */
9
+ export function scanTimestampedLines(input) {
10
+ const { text, matchPrefix, formatIso } = input;
11
+ const lines = text.split("\n");
12
+ const raw = [];
13
+ for (let i = 0; i < lines.length; i++) {
14
+ const line = lines[i];
15
+ if (line.length === 0 && i === lines.length - 1)
16
+ continue;
17
+ const m = matchPrefix(line);
18
+ if (m) {
19
+ const { remainder, ...parts } = m;
20
+ raw.push({ parts, remainder });
21
+ }
22
+ else {
23
+ const last = raw[raw.length - 1];
24
+ if (last)
25
+ last.remainder += "\n" + line;
26
+ }
27
+ }
28
+ const parsedLines = raw.map((r, idx) => ({
29
+ dateSent: formatIso(r.parts),
30
+ body: r.remainder.replace(/\s+$/, ""),
31
+ sequenceIndex: idx,
32
+ }));
33
+ return { parsedLines, matchedCount: parsedLines.length };
34
+ }
35
+ //# sourceMappingURL=timestamp-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestamp-scanner.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/timestamp-scanner.ts"],"names":[],"mappings":"AAyDA;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAgB;IACnD,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAM/B,MAAM,GAAG,GAAiB,EAAE,CAAC;IAE7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAC1D,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,EAAE,CAAC;YACN,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,GAAG,CAAC,CAAC;YAClC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACjC,IAAI,IAAI;gBAAE,IAAI,CAAC,SAAS,IAAI,IAAI,GAAG,IAAI,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAiB,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACrD,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;QAC5B,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACrC,aAAa,EAAE,GAAG;KACnB,CAAC,CAAC,CAAC;IAEJ,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC;AAC3D,CAAC"}
@@ -2,18 +2,17 @@ export declare const CONVERSATION_SOURCES: readonly ["whatsapp", "telegram", "si
2
2
  export type ConversationSource = (typeof CONVERSATION_SOURCES)[number];
3
3
  export declare function isConversationSource(s: unknown): s is ConversationSource;
4
4
  /**
5
- * One message after source-specific parsing. Identical shape across every
6
- * source the normaliser is the only place that knows how the source emits
7
- * timestamps, sender display names, body line breaks, or quoted-reply markers.
5
+ * One message after source-specific timestamp scanning. Identical shape across
6
+ * every source. Bodies are the raw post-prefix remainder for WhatsApp this
7
+ * still carries the `Sender: ...` segment; the chat-mode classifier prompt
8
+ * preserves these prefixes verbatim in chunk bodies.
8
9
  */
9
10
  export interface ParsedLine {
10
- /** Sender display name as it appeared in the export (verbatim). */
11
- senderName: string;
12
11
  /** ISO 8601 instant with timezone offset (e.g. "2026-04-30T18:42:00+01:00"). */
13
12
  dateSent: string;
14
- /** Message body, multi-line preserved, trailing whitespace stripped. */
13
+ /** Everything after the timestamp prefix; multi-line continuations glued on `\n`. */
15
14
  body: string;
16
- /** Position within the post-skip parsed sequence, 0-based. */
15
+ /** Position within the parsed sequence, 0-based. */
17
16
  sequenceIndex: number;
18
17
  }
19
18
  export interface NormaliserCounters {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/types.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,oBAAoB,sIAUvB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,kBAAkB,CAExE;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B;qDACiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,MAAM,sBAAsB,GAAG,CACnC,KAAK,EAAE,eAAe,KACnB,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/types.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,oBAAoB,sIAUvB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,kBAAkB,CAExE;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B;qDACiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,MAAM,sBAAsB,GAAG,CACnC,KAAK,EAAE,eAAe,KACnB,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,CAAC"}
@@ -1,8 +1,16 @@
1
- // Conversation-archive ingest contracts (Task 894).
1
+ // Conversation-archive ingest contracts (Task 894 / 897).
2
2
  //
3
3
  // One skill, one writer, one bash entry — source becomes a property on
4
4
  // :ConversationArchive and a pluggable normaliser, never a separate skill.
5
5
  //
6
+ // Task 897 dropped per-line `senderName`. The normaliser is now a
7
+ // timestamp-prefix scanner — it walks lines, matches a source-specific
8
+ // regex, glues continuations onto the preceding match, and returns body
9
+ // strings verbatim. Bodies are opaque: vCards, system messages, forwarded
10
+ // blocks, and the bare `Sender:` segment all flow through unchanged.
11
+ // Operator-confirmed participants attach to the parent `:ConversationArchive`
12
+ // via `:PARTICIPANT_IN`, never per-message.
13
+ //
6
14
  // `ConversationSource` is the closed enum stamped on every :ConversationArchive
7
15
  // and :Section:Conversation. Adding a new source = adding a normaliser
8
16
  // implementation here + extending this enum, never a new skill or plugin.
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/types.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,uEAAuE;AACvE,2EAA2E;AAC3E,EAAE;AACF,gFAAgF;AAChF,uEAAuE;AACvE,0EAA0E;AAE1E,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,UAAU;IACV,UAAU;IACV,QAAQ;IACR,mBAAmB;IACnB,iBAAiB;IACjB,iBAAiB;IACjB,UAAU;IACV,OAAO;IACP,OAAO;CACC,CAAC;AAIX,MAAM,UAAU,oBAAoB,CAAC,CAAU;IAC7C,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAK,oBAA0C,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC1F,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/types.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,EAAE;AACF,uEAAuE;AACvE,2EAA2E;AAC3E,EAAE;AACF,kEAAkE;AAClE,uEAAuE;AACvE,wEAAwE;AACxE,0EAA0E;AAC1E,qEAAqE;AACrE,8EAA8E;AAC9E,4CAA4C;AAC5C,EAAE;AACF,gFAAgF;AAChF,uEAAuE;AACvE,0EAA0E;AAE1E,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,UAAU;IACV,UAAU;IACV,QAAQ;IACR,mBAAmB;IACnB,iBAAiB;IACjB,iBAAiB;IACjB,UAAU;IACV,OAAO;IACP,OAAO;CACC,CAAC;AAIX,MAAM,UAAU,oBAAoB,CAAC,CAAU;IAC7C,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAK,oBAA0C,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC1F,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"whatsapp-text.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/whatsapp-text.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAc,MAAM,YAAY,CAAC;AAuDhF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,CA8G/E"}
1
+ {"version":3,"file":"whatsapp-text.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/whatsapp-text.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAiCpE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,CA+C/E"}
@@ -1,34 +1,8 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { basename } from "node:path";
4
+ import { scanTimestampedLines } from "./timestamp-scanner.js";
4
5
  const TIMESTAMP_PREFIX = /^\[(\d{2})\/(\d{2})\/(\d{4}|\d{2}),\s+(\d{1,2}):(\d{2})(?::(\d{2}))?\]\s*(.*)$/;
5
- const LINE_LEVEL_SYSTEM_PATTERNS = [
6
- /^Messages and calls are end-to-end encrypted/i,
7
- /'s security code changed\.?$/i,
8
- / created group ["“”]/,
9
- / added /,
10
- / removed /,
11
- / left$/,
12
- / changed the subject from /,
13
- / changed this group's icon/,
14
- / joined using this group's invite link/,
15
- /^You're now an admin$/i,
16
- /^You created group/i,
17
- ];
18
- const BODY_LEVEL_SYSTEM_PATTERNS = [
19
- /^You deleted this message\.?$/,
20
- /^This message was deleted\.?$/,
21
- ];
22
- const MEDIA_ONLY_PATTERNS = [
23
- /^<Media omitted>$/,
24
- /^IMG-\d+-\w+\.(jpg|jpeg|png|heic|gif)\s*\(file attached\)$/i,
25
- /^VID-\d+-\w+\.mp4\s*\(file attached\)$/i,
26
- /^PTT-\d+-\w+\.opus\s*\(file attached\)$/i,
27
- /^AUD-\d+-\w+\.opus\s*\(file attached\)$/i,
28
- /^STK-\d+-\w+\.webp\s*\(file attached\)$/i,
29
- /^.+\.(pdf|docx|doc|xlsx|xls|pptx|ppt|zip|csv|txt)\s*\(file attached\)$/i,
30
- /^‎.+attached:\s*.+$/,
31
- ];
32
6
  export function whatsappTextNormaliser(input) {
33
7
  const { filePath, accountId, timezone } = input;
34
8
  const opts = (input.opts ?? {});
@@ -44,74 +18,29 @@ export function whatsappTextNormaliser(input) {
44
18
  if (text.length === 0) {
45
19
  throw new Error(`whatsapp-text: file is empty — not a _chat.txt. file=${filePath}`);
46
20
  }
47
- const lines = text.split("\n");
48
- const ordering = resolveOrdering(opts.dateFormat, lines);
49
- const counters = { parsed: 0, systemSkipped: 0, mediaSkipped: 0, parseErrors: 0 };
50
- const raw = [];
51
- for (let i = 0; i < lines.length; i++) {
52
- const line = lines[i];
53
- if (line.length === 0 && i === lines.length - 1)
54
- continue;
55
- const prefixMatch = matchTimestampPrefix(line, ordering);
56
- if (prefixMatch) {
57
- raw.push({
58
- rawLineIndex: i + 1,
59
- ...prefixMatch.dateParts,
60
- remainder: prefixMatch.remainder,
61
- });
62
- }
63
- else {
64
- const last = raw[raw.length - 1];
65
- if (last)
66
- last.remainder += "\n" + line;
67
- }
68
- }
69
- const parsedLines = [];
70
- for (const r of raw) {
71
- const remainder = r.remainder;
72
- const colonIdx = remainder.indexOf(": ");
73
- if (colonIdx === -1) {
74
- const trimmed = remainder.replace(/\s+$/, "");
75
- if (matchesAny(trimmed, LINE_LEVEL_SYSTEM_PATTERNS)) {
76
- counters.systemSkipped++;
77
- continue;
78
- }
79
- counters.parseErrors++;
80
- throw new Error(`whatsapp-text: parse-error file=${filePath} line=${r.rawLineIndex} reason=no-sender-body-separator content="${trimmed.slice(0, 80)}"`);
81
- }
82
- const senderName = remainder.slice(0, colonIdx).trim();
83
- const body = remainder.slice(colonIdx + 2).replace(/\s+$/, "");
84
- if (body.length === 0) {
85
- counters.systemSkipped++;
86
- continue;
87
- }
88
- if (matchesAny(body, BODY_LEVEL_SYSTEM_PATTERNS)) {
89
- counters.systemSkipped++;
90
- continue;
91
- }
92
- if (matchesAny(body, MEDIA_ONLY_PATTERNS)) {
93
- counters.mediaSkipped++;
94
- continue;
95
- }
96
- const dateSent = isoWithOffset(r.year, r.month, r.day, r.hour, r.minute, r.second, timezone);
97
- parsedLines.push({
98
- senderName,
99
- dateSent,
100
- body,
101
- sequenceIndex: parsedLines.length,
102
- });
103
- counters.parsed++;
104
- }
105
- if (parsedLines.length === 0 && counters.systemSkipped === 0 && counters.mediaSkipped === 0) {
106
- const sample = sampleFirstNonBlankLine(lines, 100);
107
- process.stderr.write(`[conversation-archive] parse-grammar-miss source=whatsapp first-line="${sample}"\n`);
108
- throw new Error(`whatsapp-text: zero parsed lines after walking ${filePath} — not a _chat.txt or all lines failed grammar. parse-grammar-miss first-line="${sample}"`);
109
- }
21
+ const ordering = resolveOrdering(opts.dateFormat, text);
22
+ const matchPrefix = makeWhatsappPrefixMatcher(ordering);
23
+ const { parsedLines } = scanTimestampedLines({
24
+ text,
25
+ matchPrefix,
26
+ formatIso: (parts) => isoWithOffset(parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second, timezone),
27
+ });
28
+ // `parsed` is the matched-prefix count (= parsedLines.length).
29
+ // `parseErrors` is structurally zero — the scanner cannot fail on body
30
+ // shape post-Task-897. `systemSkipped` / `mediaSkipped` are zero too:
31
+ // skip detection lived in the pre-897 colon-parser path that's gone.
32
+ // The bin emits `[conversation-archive] parsed lines=N` and routes a
33
+ // zero-parsed file to the document-ingest chunker rather than failing.
110
34
  return {
111
35
  parsedLines,
112
36
  archiveSha256,
113
37
  archiveSourceFile: basename(filePath),
114
- counters,
38
+ counters: {
39
+ parsed: parsedLines.length,
40
+ systemSkipped: 0,
41
+ mediaSkipped: 0,
42
+ parseErrors: 0,
43
+ },
115
44
  };
116
45
  }
117
46
  function decodeAndNormalise(bytes) {
@@ -145,56 +74,45 @@ function decodeAndNormalise(bytes) {
145
74
  }
146
75
  return text;
147
76
  }
148
- function matchTimestampPrefix(line, ordering) {
149
- const m = line.match(TIMESTAMP_PREFIX);
150
- if (!m)
151
- return null;
152
- const a = parseInt(m[1], 10);
153
- const b = parseInt(m[2], 10);
154
- const yearRaw = m[3];
155
- const hour = parseInt(m[4], 10);
156
- const minute = parseInt(m[5], 10);
157
- const second = m[6] !== undefined ? parseInt(m[6], 10) : 0;
158
- const remainder = m[7] ?? "";
159
- const day = ordering === "MMDD" ? b : a;
160
- const month = ordering === "MMDD" ? a : b;
161
- if (month < 1 || month > 12 || day < 1 || day > 31)
162
- return null;
163
- if (hour > 23 || minute > 59 || second > 59)
164
- return null;
165
- const year = yearRaw.length === 2 ? 2000 + parseInt(yearRaw, 10) : parseInt(yearRaw, 10);
166
- return { dateParts: { year, month, day, hour, minute, second }, remainder };
77
+ function makeWhatsappPrefixMatcher(ordering) {
78
+ return (line) => {
79
+ const m = line.match(TIMESTAMP_PREFIX);
80
+ if (!m)
81
+ return null;
82
+ const a = parseInt(m[1], 10);
83
+ const b = parseInt(m[2], 10);
84
+ const yearRaw = m[3];
85
+ const hour = parseInt(m[4], 10);
86
+ const minute = parseInt(m[5], 10);
87
+ const second = m[6] !== undefined ? parseInt(m[6], 10) : 0;
88
+ const remainder = m[7] ?? "";
89
+ const day = ordering === "MMDD" ? b : a;
90
+ const month = ordering === "MMDD" ? a : b;
91
+ if (month < 1 || month > 12 || day < 1 || day > 31)
92
+ return null;
93
+ if (hour > 23 || minute > 59 || second > 59)
94
+ return null;
95
+ const year = yearRaw.length === 2 ? 2000 + parseInt(yearRaw, 10) : parseInt(yearRaw, 10);
96
+ return { year, month, day, hour, minute, second, remainder };
97
+ };
167
98
  }
168
- function resolveOrdering(explicit, lines) {
99
+ function resolveOrdering(explicit, text) {
169
100
  if (explicit === "MM/DD/YY" || explicit === "MM/DD/YYYY")
170
101
  return "MMDD";
171
102
  if (explicit === "DD/MM/YY" || explicit === "DD/MM/YYYY")
172
103
  return "DDMM";
173
- for (const line of lines) {
174
- if (matchTimestampPrefix(line, "DDMM"))
104
+ // Probe the file: first valid match wins. DD/MM precedence is consistent
105
+ // with WhatsApp's UK/EU default; explicit MM/DD is an opt-in.
106
+ const ddmm = makeWhatsappPrefixMatcher("DDMM");
107
+ const mmdd = makeWhatsappPrefixMatcher("MMDD");
108
+ for (const line of text.split("\n")) {
109
+ if (ddmm(line))
175
110
  return "DDMM";
176
- if (matchTimestampPrefix(line, "MMDD"))
111
+ if (mmdd(line))
177
112
  return "MMDD";
178
113
  }
179
114
  return "DDMM";
180
115
  }
181
- function sampleFirstNonBlankLine(lines, maxScan) {
182
- const scanLimit = Math.min(maxScan, lines.length);
183
- for (let i = 0; i < scanLimit; i++) {
184
- const trimmed = lines[i].trim();
185
- if (trimmed.length === 0)
186
- continue;
187
- const sanitised = trimmed.replace(/[\x00-\x1F\x7F]/g, "");
188
- return sanitised.slice(0, 80);
189
- }
190
- return "";
191
- }
192
- function matchesAny(text, patterns) {
193
- for (const p of patterns)
194
- if (p.test(text))
195
- return true;
196
- return false;
197
- }
198
116
  function isoWithOffset(year, month, day, hour, minute, second, timezone) {
199
117
  const guessUtcMs = Date.UTC(year, month - 1, day, hour, minute, second);
200
118
  let offMin = offsetMinutesAt(new Date(guessUtcMs), timezone);
@@ -1 +1 @@
1
- {"version":3,"file":"whatsapp-text.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/whatsapp-text.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"whatsapp-text.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/whatsapp-text.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"delta-cursor.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/delta-cursor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAqBvE,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAExB,wBAAgB,eAAe,CAC7B,WAAW,EAAE,SAAS,UAAU,EAAE,EAClC,uBAAuB,EAAE,MAAM,GAC9B,YAAY,CAiBd"}
1
+ {"version":3,"file":"delta-cursor.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/delta-cursor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAqBvE,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAExB,wBAAgB,eAAe,CAC7B,WAAW,EAAE,SAAS,UAAU,EAAE,EAClC,uBAAuB,EAAE,MAAM,GAC9B,YAAY,CAgBd"}
@@ -7,7 +7,6 @@ export function findDeltaCursor(parsedLines, lastIngestedMessageHash) {
7
7
  const line = parsedLines[i];
8
8
  const hash = deriveMessageContentHash({
9
9
  dateSent: line.dateSent,
10
- senderName: line.senderName,
11
10
  body: line.body,
12
11
  });
13
12
  if (hash === lastIngestedMessageHash) {
@@ -1 +1 @@
1
- {"version":3,"file":"delta-cursor.js","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/delta-cursor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAyB5D,MAAM,UAAU,eAAe,CAC7B,WAAkC,EAClC,uBAA+B;IAE/B,IAAI,CAAC,uBAAuB,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,wBAAwB,CAAC;YACpC,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC,CAAC;QACH,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,WAAW,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YAC3D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"delta-cursor.js","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/delta-cursor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAyB5D,MAAM,UAAU,eAAe,CAC7B,WAAkC,EAClC,uBAA+B;IAE/B,IAAI,CAAC,uBAAuB,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,wBAAwB,CAAC;YACpC,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC,CAAC;QACH,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,WAAW,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YAC3D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAC7B,CAAC"}
@@ -1,4 +1,3 @@
1
- export declare function normaliseSenderName(name: string): string;
2
1
  export declare function sha256Hex(input: string): string;
3
2
  export interface DeriveConversationIdentityInput {
4
3
  accountId: string;
@@ -9,7 +8,6 @@ export interface DeriveConversationIdentityInput {
9
8
  export declare function deriveConversationIdentity(input: DeriveConversationIdentityInput): string;
10
9
  export interface DeriveMessageContentHashInput {
11
10
  dateSent: string;
12
- senderName: string;
13
11
  body: string;
14
12
  }
15
13
  export declare function deriveMessageContentHash(input: DeriveMessageContentHashInput): string;
@@ -1 +1 @@
1
- {"version":3,"file":"derive-keys.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/derive-keys.ts"],"names":[],"mappings":"AAoBA,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB;0DACsD;IACtD,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,MAAM,CASR;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,6BAA6B,GACnC,MAAM,CAGR"}
1
+ {"version":3,"file":"derive-keys.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/derive-keys.ts"],"names":[],"mappings":"AAuBA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB;0DACsD;IACtD,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,MAAM,CASR;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,6BAA6B,GACnC,MAAM,CAER"}
@@ -1,24 +1,24 @@
1
1
  import { createHash } from "node:crypto";
2
2
  // ---------------------------------------------------------------------------
3
3
  // derive-keys — natural-key derivation for conversation-archive ingestion
4
- // (Task 894, supersedes Task 891's whatsapp-import path).
4
+ // (Task 894 / 897).
5
5
  //
6
6
  // Pure functions. No I/O. The whole point is that re-imports of the same
7
7
  // archive collapse to the same identity regardless of release-level drift in
8
8
  // chunk indices, hash widths, or arbitrary tiebreakers.
9
9
  //
10
10
  // conversationIdentity = sha256(accountId + ":" + sortedParticipantElementIds.join(","))
11
- // messageContentHash = sha256(dateSent + "|" + NFKC-trim-lower(senderName) + "|" + body)
11
+ // messageContentHash = sha256(dateSent + "|" + body)
12
12
  //
13
13
  // `conversationIdentity` is stable across re-exports — same operator + same
14
- // participant set → same identity, regardless of file bytes. DM and group
15
- // follow the same formula; the only difference is the participant array
16
- // length. `messageContentHash` is content-only (no archive sha256, no chunk
17
- // index) so cursor lookup survives a fresh re-export of the same chat.
14
+ // participant set → same identity, regardless of file bytes.
15
+ //
16
+ // `messageContentHash` (Task 897): hashes `dateSent + body`. Per-line
17
+ // senderName is gone (the scanner is body-opaque); the body string still
18
+ // carries any `Sender:` prefix verbatim, so two senders posting different
19
+ // content at the same instant stay distinguishable. The cursor survives a
20
+ // fresh re-export as long as the source preserves the prefix in the body.
18
21
  // ---------------------------------------------------------------------------
19
- export function normaliseSenderName(name) {
20
- return name.normalize("NFKC").trim().toLowerCase();
21
- }
22
22
  export function sha256Hex(input) {
23
23
  return createHash("sha256").update(input).digest("hex");
24
24
  }
@@ -33,7 +33,6 @@ export function deriveConversationIdentity(input) {
33
33
  return sha256Hex(`${input.accountId}:${sorted.join(",")}`);
34
34
  }
35
35
  export function deriveMessageContentHash(input) {
36
- const norm = normaliseSenderName(input.senderName);
37
- return sha256Hex(`${input.dateSent}|${norm}|${input.body}`);
36
+ return sha256Hex(`${input.dateSent}|${input.body}`);
38
37
  }
39
38
  //# sourceMappingURL=derive-keys.js.map