@rubytech/create-realagent 1.0.829 → 1.0.830

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 (103) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/config/brand.json +1 -1
  3. package/payload/platform/lib/oauth-llm/dist/index.d.ts +1 -1
  4. package/payload/platform/lib/oauth-llm/dist/index.d.ts.map +1 -1
  5. package/payload/platform/lib/oauth-llm/dist/index.js +21 -0
  6. package/payload/platform/lib/oauth-llm/dist/index.js.map +1 -1
  7. package/payload/platform/lib/oauth-llm/src/index.ts +24 -0
  8. package/payload/platform/neo4j/migrations/007-conversation-archive-source.ts +116 -0
  9. package/payload/platform/neo4j/schema.cypher +12 -3
  10. package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +54 -39
  11. package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +64 -26
  12. package/payload/platform/plugins/contacts/mcp/dist/index.js +5 -5
  13. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  14. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +1 -1
  15. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
  16. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +29 -23
  17. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
  18. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  19. package/payload/platform/plugins/memory/PLUGIN.md +2 -1
  20. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +541 -0
  21. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +106 -0
  22. package/payload/platform/plugins/memory/mcp/dist/index.js +30 -16
  23. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  24. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +4 -3
  25. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +1 -1
  26. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +11 -6
  27. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -1
  28. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +5 -0
  29. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts.map +1 -0
  30. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js +30 -0
  31. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js.map +1 -0
  32. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +48 -0
  33. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -0
  34. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +23 -0
  35. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -0
  36. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts +3 -0
  37. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts.map +1 -0
  38. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js +237 -0
  39. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js.map +1 -0
  40. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts +11 -0
  41. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts.map +1 -0
  42. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js +21 -0
  43. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js.map +1 -0
  44. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts +16 -0
  45. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts.map +1 -0
  46. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js +39 -0
  47. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js.map +1 -0
  48. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts +17 -0
  49. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts.map +1 -0
  50. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js +90 -0
  51. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js.map +1 -0
  52. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.d.ts +9 -0
  53. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.d.ts.map +1 -0
  54. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.js +32 -0
  55. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.js.map +1 -0
  56. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts +3 -0
  57. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts.map +1 -0
  58. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js +27 -0
  59. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js.map +1 -0
  60. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.d.ts +45 -0
  61. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.d.ts.map +1 -0
  62. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.js +125 -0
  63. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.js.map +1 -0
  64. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +24 -1
  65. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -1
  66. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +266 -16
  67. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
  68. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +1 -1
  69. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +9 -2
  70. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +1 -1
  71. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.d.ts +2 -0
  72. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.d.ts.map +1 -0
  73. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js +75 -0
  74. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js.map +1 -0
  75. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.d.ts +2 -0
  76. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.d.ts.map +1 -0
  77. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js +67 -0
  78. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js.map +1 -0
  79. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +34 -3
  80. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -1
  81. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +17 -0
  82. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  83. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +34 -13
  84. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  85. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +18 -7
  86. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  87. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +24 -8
  88. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  89. package/payload/platform/plugins/memory/references/schema-base.md +2 -2
  90. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +133 -0
  91. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +5 -2
  92. package/payload/platform/plugins/whatsapp/PLUGIN.md +1 -1
  93. package/payload/platform/scripts/seed-neo4j.sh +15 -15
  94. package/payload/platform/templates/specialists/agents/database-operator.md +8 -9
  95. package/payload/server/chunk-7BO5HDJC.js +10093 -0
  96. package/payload/server/chunk-EL4DZ56X.js +1116 -0
  97. package/payload/server/chunk-QOJ2D26Z.js +654 -0
  98. package/payload/server/chunk-RC46ZYGT.js +2305 -0
  99. package/payload/server/client-pool-7NTEFNVQ.js +32 -0
  100. package/payload/server/cloudflare-task-tracker-WE77WXSI.js +19 -0
  101. package/payload/server/maxy-edge.js +3 -3
  102. package/payload/server/neo4j-migrations-4XPNJNM6.js +490 -0
  103. package/payload/server/server.js +6 -6
@@ -0,0 +1,23 @@
1
+ // Conversation-archive ingest contracts (Task 894).
2
+ //
3
+ // One skill, one writer, one bash entry — source becomes a property on
4
+ // :ConversationArchive and a pluggable normaliser, never a separate skill.
5
+ //
6
+ // `ConversationSource` is the closed enum stamped on every :ConversationArchive
7
+ // and :Section:Conversation. Adding a new source = adding a normaliser
8
+ // implementation here + extending this enum, never a new skill or plugin.
9
+ export const CONVERSATION_SOURCES = [
10
+ "whatsapp",
11
+ "telegram",
12
+ "signal",
13
+ "linkedin-messages",
14
+ "zoom-transcript",
15
+ "meeting-minutes",
16
+ "imessage",
17
+ "slack",
18
+ "other",
19
+ ];
20
+ export function isConversationSource(s) {
21
+ return typeof s === "string" && CONVERSATION_SOURCES.includes(s);
22
+ }
23
+ //# sourceMappingURL=types.js.map
@@ -0,0 +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"}
@@ -0,0 +1,3 @@
1
+ import type { NormaliserInput, NormaliserResult } from "./types.js";
2
+ export declare function whatsappTextNormaliser(input: NormaliserInput): NormaliserResult;
3
+ //# sourceMappingURL=whatsapp-text.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,237 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import { basename } from "node:path";
4
+ 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
+ export function whatsappTextNormaliser(input) {
33
+ const { filePath, accountId, timezone } = input;
34
+ const opts = (input.opts ?? {});
35
+ if (!accountId || !accountId.trim()) {
36
+ throw new Error("whatsapp-text: accountId is required.");
37
+ }
38
+ if (!timezone || !timezone.trim()) {
39
+ throw new Error("whatsapp-text: timezone is required (e.g. 'Europe/London').");
40
+ }
41
+ const rawBytes = readFileSync(filePath);
42
+ const archiveSha256 = createHash("sha256").update(rawBytes).digest("hex");
43
+ const text = decodeAndNormalise(rawBytes);
44
+ if (text.length === 0) {
45
+ throw new Error(`whatsapp-text: file is empty — not a _chat.txt. file=${filePath}`);
46
+ }
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
+ }
110
+ return {
111
+ parsedLines,
112
+ archiveSha256,
113
+ archiveSourceFile: basename(filePath),
114
+ counters,
115
+ };
116
+ }
117
+ function decodeAndNormalise(bytes) {
118
+ let text;
119
+ try {
120
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
121
+ }
122
+ catch (err) {
123
+ throw new Error(`whatsapp-text: UTF-8 decode failed — ${err instanceof Error ? err.message : String(err)}. The file is not valid UTF-8; re-export from WhatsApp.`);
124
+ }
125
+ if (text.charCodeAt(0) === 0xfeff)
126
+ text = text.slice(1);
127
+ text = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
128
+ // Strip leading bidi marks (U+200E LRM, U+200F RLM) only at line-start —
129
+ // some WhatsApp builds prefix the timestamp header. Body-internal bidi
130
+ // marks are preserved (semantic). See Task 887.
131
+ const leadingBidiMatches = text.match(/(?:^|\n)[‎‏]+/g) || [];
132
+ let lrmStripped = 0;
133
+ let rlmStripped = 0;
134
+ for (const m of leadingBidiMatches) {
135
+ for (const ch of m) {
136
+ if (ch === "‎")
137
+ lrmStripped++;
138
+ else if (ch === "‏")
139
+ rlmStripped++;
140
+ }
141
+ }
142
+ if (leadingBidiMatches.length > 0) {
143
+ text = text.replace(/(^|\n)[‎‏]+/g, "$1");
144
+ process.stderr.write(`[conversation-archive] source=whatsapp decoded normalised lrm-stripped=${lrmStripped} rlm-stripped=${rlmStripped}\n`);
145
+ }
146
+ return text;
147
+ }
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 };
167
+ }
168
+ function resolveOrdering(explicit, lines) {
169
+ if (explicit === "MM/DD/YY" || explicit === "MM/DD/YYYY")
170
+ return "MMDD";
171
+ if (explicit === "DD/MM/YY" || explicit === "DD/MM/YYYY")
172
+ return "DDMM";
173
+ for (const line of lines) {
174
+ if (matchTimestampPrefix(line, "DDMM"))
175
+ return "DDMM";
176
+ if (matchTimestampPrefix(line, "MMDD"))
177
+ return "MMDD";
178
+ }
179
+ return "DDMM";
180
+ }
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
+ function isoWithOffset(year, month, day, hour, minute, second, timezone) {
199
+ const guessUtcMs = Date.UTC(year, month - 1, day, hour, minute, second);
200
+ let offMin = offsetMinutesAt(new Date(guessUtcMs), timezone);
201
+ const refinedUtcMs = guessUtcMs - offMin * 60_000;
202
+ offMin = offsetMinutesAt(new Date(refinedUtcMs), timezone);
203
+ const sign = offMin >= 0 ? "+" : "-";
204
+ const absOff = Math.abs(offMin);
205
+ const offHH = String(Math.floor(absOff / 60)).padStart(2, "0");
206
+ const offMM = String(absOff % 60).padStart(2, "0");
207
+ const Y = String(year).padStart(4, "0");
208
+ const M = String(month).padStart(2, "0");
209
+ const D = String(day).padStart(2, "0");
210
+ const H = String(hour).padStart(2, "0");
211
+ const Mi = String(minute).padStart(2, "0");
212
+ const S = String(second).padStart(2, "0");
213
+ return `${Y}-${M}-${D}T${H}:${Mi}:${S}${sign}${offHH}:${offMM}`;
214
+ }
215
+ function offsetMinutesAt(date, timezone) {
216
+ const formatter = new Intl.DateTimeFormat("en-US", {
217
+ timeZone: timezone,
218
+ timeZoneName: "longOffset",
219
+ });
220
+ const parts = formatter.formatToParts(date);
221
+ const tzPart = parts.find((p) => p.type === "timeZoneName");
222
+ if (!tzPart) {
223
+ throw new Error(`whatsapp-text: unable to read offset for timezone "${timezone}".`);
224
+ }
225
+ const value = tzPart.value;
226
+ if (value === "GMT" || value === "UTC")
227
+ return 0;
228
+ const m = value.match(/^(?:GMT|UTC)([+-])(\d{1,2}):?(\d{2})?$/);
229
+ if (!m) {
230
+ throw new Error(`whatsapp-text: cannot parse timezone offset "${value}" for IANA zone "${timezone}".`);
231
+ }
232
+ const sign = m[1] === "+" ? 1 : -1;
233
+ const hh = parseInt(m[2], 10);
234
+ const mm = m[3] ? parseInt(m[3], 10) : 0;
235
+ return sign * (hh * 60 + mm);
236
+ }
237
+ //# sourceMappingURL=whatsapp-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp-text.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/whatsapp-text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ParsedLine } from "../conversation-normalisers/types.js";
2
+ export type CursorResult = {
3
+ kind: "found";
4
+ deltaStart: number;
5
+ } | {
6
+ kind: "empty";
7
+ } | {
8
+ kind: "missing";
9
+ };
10
+ export declare function findDeltaCursor(parsedLines: readonly ParsedLine[], lastIngestedMessageHash: string): CursorResult;
11
+ //# sourceMappingURL=delta-cursor.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,21 @@
1
+ import { deriveMessageContentHash } from "./derive-keys.js";
2
+ export function findDeltaCursor(parsedLines, lastIngestedMessageHash) {
3
+ if (!lastIngestedMessageHash || !lastIngestedMessageHash.trim()) {
4
+ throw new Error("findDeltaCursor: lastIngestedMessageHash must be non-empty");
5
+ }
6
+ for (let i = 0; i < parsedLines.length; i++) {
7
+ const line = parsedLines[i];
8
+ const hash = deriveMessageContentHash({
9
+ dateSent: line.dateSent,
10
+ senderName: line.senderName,
11
+ body: line.body,
12
+ });
13
+ if (hash === lastIngestedMessageHash) {
14
+ if (i === parsedLines.length - 1)
15
+ return { kind: "empty" };
16
+ return { kind: "found", deltaStart: i + 1 };
17
+ }
18
+ }
19
+ return { kind: "missing" };
20
+ }
21
+ //# sourceMappingURL=delta-cursor.js.map
@@ -0,0 +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"}
@@ -0,0 +1,16 @@
1
+ export declare function normaliseSenderName(name: string): string;
2
+ export declare function sha256Hex(input: string): string;
3
+ export interface DeriveConversationIdentityInput {
4
+ accountId: string;
5
+ /** Element IDs of every confirmed participant (owner + others). Order is
6
+ * not significant; the function sorts internally. */
7
+ participantElementIds: readonly string[];
8
+ }
9
+ export declare function deriveConversationIdentity(input: DeriveConversationIdentityInput): string;
10
+ export interface DeriveMessageContentHashInput {
11
+ dateSent: string;
12
+ senderName: string;
13
+ body: string;
14
+ }
15
+ export declare function deriveMessageContentHash(input: DeriveMessageContentHashInput): string;
16
+ //# sourceMappingURL=derive-keys.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,39 @@
1
+ import { createHash } from "node:crypto";
2
+ // ---------------------------------------------------------------------------
3
+ // derive-keys — natural-key derivation for conversation-archive ingestion
4
+ // (Task 894, supersedes Task 891's whatsapp-import path).
5
+ //
6
+ // Pure functions. No I/O. The whole point is that re-imports of the same
7
+ // archive collapse to the same identity regardless of release-level drift in
8
+ // chunk indices, hash widths, or arbitrary tiebreakers.
9
+ //
10
+ // conversationIdentity = sha256(accountId + ":" + sortedParticipantElementIds.join(","))
11
+ // messageContentHash = sha256(dateSent + "|" + NFKC-trim-lower(senderName) + "|" + body)
12
+ //
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.
18
+ // ---------------------------------------------------------------------------
19
+ export function normaliseSenderName(name) {
20
+ return name.normalize("NFKC").trim().toLowerCase();
21
+ }
22
+ export function sha256Hex(input) {
23
+ return createHash("sha256").update(input).digest("hex");
24
+ }
25
+ export function deriveConversationIdentity(input) {
26
+ if (!input.accountId || !input.accountId.trim()) {
27
+ throw new Error("deriveConversationIdentity: accountId is required");
28
+ }
29
+ if (input.participantElementIds.length === 0) {
30
+ throw new Error("deriveConversationIdentity: participantElementIds must be non-empty");
31
+ }
32
+ const sorted = [...input.participantElementIds].sort();
33
+ return sha256Hex(`${input.accountId}:${sorted.join(",")}`);
34
+ }
35
+ export function deriveMessageContentHash(input) {
36
+ const norm = normaliseSenderName(input.senderName);
37
+ return sha256Hex(`${input.dateSent}|${norm}|${input.body}`);
38
+ }
39
+ //# sourceMappingURL=derive-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-keys.js","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/derive-keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,8EAA8E;AAC9E,0EAA0E;AAC1E,0DAA0D;AAC1D,EAAE;AACF,yEAAyE;AACzE,6EAA6E;AAC7E,wDAAwD;AACxD,EAAE;AACF,2FAA2F;AAC3F,6FAA6F;AAC7F,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,wEAAwE;AACxE,4EAA4E;AAC5E,uEAAuE;AACvE,8EAA8E;AAE9E,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AASD,MAAM,UAAU,0BAA0B,CACxC,KAAsC;IAEtC,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,KAAK,CAAC,qBAAqB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,qBAAqB,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,OAAO,SAAS,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC7D,CAAC;AAQD,MAAM,UAAU,wBAAwB,CACtC,KAAoC;IAEpC,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACnD,OAAO,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Session as Neo4jSession } from "neo4j-driver";
2
+ export declare class IngestUserFacingError extends Error {
3
+ readonly userFacing = true;
4
+ constructor(message: string);
5
+ }
6
+ export interface BindCanonicalSendersInput {
7
+ session: Neo4jSession;
8
+ accountId: string;
9
+ ownerElementId: string;
10
+ participantElementIds: readonly string[];
11
+ senderNames: readonly string[];
12
+ }
13
+ export interface BindCanonicalSendersResult {
14
+ participantsResolved: number;
15
+ }
16
+ export declare function bindCanonicalSenders(input: BindCanonicalSendersInput): Promise<BindCanonicalSendersResult>;
17
+ //# sourceMappingURL=sender-bind.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sender-bind.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/sender-bind.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,cAAc,CAAC;AAkB5D,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,UAAU,QAAQ;gBACf,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAaD,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,0BAA0B,CAAC,CAsDrC"}
@@ -0,0 +1,90 @@
1
+ import { normaliseSenderName } from "./derive-keys.js";
2
+ // ---------------------------------------------------------------------------
3
+ // sender-bind — closed-set sender resolution against operator-confirmed
4
+ // :Person / :AdminUser elementIds (Task 894, replaces Task 887 §A0 path).
5
+ //
6
+ // Every distinct senderName parsed from the source must resolve to one of
7
+ // the supplied elementIds. Any miss LOUD-FAILs with a `parser-miss` shape
8
+ // preserved verbatim from the prior whatsapp-import contract.
9
+ //
10
+ // No auto-create. The operator confirms canonical participants before this
11
+ // function ever runs; if a senderName falls outside the closed set, the
12
+ // operator either re-runs with the missing elementId added or reports a
13
+ // parser bug. This guards `feedback_archives_are_not_documents.md` and the
14
+ // prior Task-887 leak fingerprint.
15
+ // ---------------------------------------------------------------------------
16
+ export class IngestUserFacingError extends Error {
17
+ userFacing = true;
18
+ constructor(message) {
19
+ super(message);
20
+ this.name = "IngestUserFacingError";
21
+ }
22
+ }
23
+ const CANONICAL_FETCH_CYPHER = `
24
+ UNWIND $ids AS id
25
+ MATCH (n) WHERE elementId(n) = id
26
+ RETURN elementId(n) AS elemId,
27
+ labels(n) AS labels,
28
+ coalesce(n.name, '') AS name,
29
+ coalesce(n.givenName, '') AS givenName,
30
+ coalesce(n.familyName, '') AS familyName,
31
+ coalesce(n.accountId, '') AS accountId
32
+ `;
33
+ export async function bindCanonicalSenders(input) {
34
+ const { session, accountId, ownerElementId, participantElementIds, senderNames } = input;
35
+ const allIds = [ownerElementId, ...participantElementIds];
36
+ const distinctIds = Array.from(new Set(allIds));
37
+ if (distinctIds.length !== allIds.length) {
38
+ throw new IngestUserFacingError(`participant id list contains duplicates (owner appears in --participant-person-ids?)`);
39
+ }
40
+ const res = await session.executeRead(async (tx) => tx.run(CANONICAL_FETCH_CYPHER, { ids: distinctIds }));
41
+ const seenIds = new Set();
42
+ const index = new Map();
43
+ for (const r of res.records) {
44
+ const elemId = r.get("elemId");
45
+ const labels = r.get("labels") || [];
46
+ const acct = r.get("accountId") || "";
47
+ if (!acct) {
48
+ throw new IngestUserFacingError(`node ${elemId} has no accountId — corrupt canonical Person/AdminUser`);
49
+ }
50
+ if (acct !== accountId) {
51
+ throw new IngestUserFacingError(`node ${elemId} belongs to account ${acct}, not ${accountId}`);
52
+ }
53
+ if (!labels.includes("Person") && !labels.includes("AdminUser")) {
54
+ throw new IngestUserFacingError(`node ${elemId} has labels [${labels.join(",")}]; expected :Person or :AdminUser`);
55
+ }
56
+ seenIds.add(elemId);
57
+ const candidates = [];
58
+ const name = r.get("name") || "";
59
+ const given = r.get("givenName") || "";
60
+ const family = r.get("familyName") || "";
61
+ if (name)
62
+ candidates.push(name);
63
+ if (given && family)
64
+ candidates.push(`${given} ${family}`);
65
+ if (given)
66
+ candidates.push(given);
67
+ if (family)
68
+ candidates.push(family);
69
+ for (const c of candidates) {
70
+ const norm = normaliseSenderName(c);
71
+ if (!norm)
72
+ continue;
73
+ if (!index.has(norm))
74
+ index.set(norm, elemId);
75
+ }
76
+ }
77
+ for (const id of distinctIds) {
78
+ if (!seenIds.has(id)) {
79
+ throw new IngestUserFacingError(`elementId ${id} not found in graph`);
80
+ }
81
+ }
82
+ for (const senderName of senderNames) {
83
+ const norm = normaliseSenderName(senderName);
84
+ if (!index.has(norm)) {
85
+ throw new IngestUserFacingError(`parser-miss reason="senderName=${senderName} not in confirmed participant set (${distinctIds.length} confirmed elementIds; re-run with the missing :Person elementId in --participant-person-ids)"`);
86
+ }
87
+ }
88
+ return { participantsResolved: seenIds.size };
89
+ }
90
+ //# sourceMappingURL=sender-bind.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sender-bind.js","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/sender-bind.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ParsedLine } from "../conversation-normalisers/types.js";
2
+ export interface ConversationSession {
3
+ index: number;
4
+ firstMessageAt: string;
5
+ lastMessageAt: string;
6
+ messages: ParsedLine[];
7
+ }
8
+ export declare function sessionize(messages: readonly ParsedLine[], gapHours: number): ConversationSession[];
9
+ //# sourceMappingURL=sessionize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionize.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/sessionize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAcvE,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED,wBAAgB,UAAU,CACxB,QAAQ,EAAE,SAAS,UAAU,EAAE,EAC/B,QAAQ,EAAE,MAAM,GACf,mBAAmB,EAAE,CA+BvB"}
@@ -0,0 +1,32 @@
1
+ export function sessionize(messages, gapHours) {
2
+ if (gapHours <= 0) {
3
+ throw new Error(`sessionize: gapHours must be positive, got ${gapHours}`);
4
+ }
5
+ if (messages.length === 0)
6
+ return [];
7
+ const gapMs = gapHours * 60 * 60 * 1000;
8
+ const sessions = [];
9
+ let currentMessages = [messages[0]];
10
+ const flush = () => {
11
+ sessions.push({
12
+ index: sessions.length,
13
+ firstMessageAt: currentMessages[0].dateSent,
14
+ lastMessageAt: currentMessages[currentMessages.length - 1].dateSent,
15
+ messages: currentMessages,
16
+ });
17
+ };
18
+ for (let i = 1; i < messages.length; i++) {
19
+ const prevMs = Date.parse(messages[i - 1].dateSent);
20
+ const currMs = Date.parse(messages[i].dateSent);
21
+ if (currMs - prevMs >= gapMs) {
22
+ flush();
23
+ currentMessages = [messages[i]];
24
+ }
25
+ else {
26
+ currentMessages.push(messages[i]);
27
+ }
28
+ }
29
+ flush();
30
+ return sessions;
31
+ }
32
+ //# sourceMappingURL=sessionize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionize.js","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/sessionize.ts"],"names":[],"mappings":"AAqBA,MAAM,UAAU,UAAU,CACxB,QAA+B,EAC/B,QAAgB;IAEhB,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,8CAA8C,QAAQ,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAErC,MAAM,KAAK,GAAG,QAAQ,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACxC,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAC3C,IAAI,eAAe,GAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAElD,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,QAAQ,CAAC,IAAI,CAAC;YACZ,KAAK,EAAE,QAAQ,CAAC,MAAM;YACtB,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,QAAQ;YAC3C,aAAa,EAAE,eAAe,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ;YACnE,QAAQ,EAAE,eAAe;SAC1B,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAI,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,CAAC;YAC7B,KAAK,EAAE,CAAC;YACR,eAAe,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IACD,KAAK,EAAE,CAAC;IACR,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ConversationSession } from "./sessionize.js";
2
+ export declare function toTurnText(session: ConversationSession): string;
3
+ //# sourceMappingURL=to-turn-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-turn-text.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/to-turn-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAc3D,wBAAgB,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAM/D"}
@@ -0,0 +1,27 @@
1
+ // ---------------------------------------------------------------------------
2
+ // to-turn-text — render one ConversationSession as a turn-attributed text
3
+ // block ready for memory-classify (mode='chat'). Source-agnostic: every
4
+ // normaliser feeds ParsedLine[] of the same shape.
5
+ //
6
+ // [YYYY-MM-DD HH:MM:SS ±HH:MM] <Sender>: <body>
7
+ // [YYYY-MM-DD HH:MM:SS ±HH:MM] <Sender>: <body>
8
+ // ...
9
+ //
10
+ // The classifier never sees the source format — only this rendered text.
11
+ // ---------------------------------------------------------------------------
12
+ export function toTurnText(session) {
13
+ const lines = [];
14
+ for (const m of session.messages) {
15
+ lines.push(`[${formatWallClock(m.dateSent)}] ${m.senderName}: ${m.body}`);
16
+ }
17
+ return lines.join("\n");
18
+ }
19
+ function formatWallClock(iso) {
20
+ const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(Z|[+-]\d{2}:?\d{2})$/);
21
+ if (!m)
22
+ return iso;
23
+ const [, y, mo, d, h, mi, s, off] = m;
24
+ const offsetLabel = off === "Z" ? "+00:00" : off;
25
+ return `${y}-${mo}-${d} ${h}:${mi}:${s} ${offsetLabel}`;
26
+ }
27
+ //# sourceMappingURL=to-turn-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-turn-text.js","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/to-turn-text.ts"],"names":[],"mappings":"AAEA,8EAA8E;AAC9E,0EAA0E;AAC1E,wEAAwE;AACxE,mDAAmD;AACnD,EAAE;AACF,kDAAkD;AAClD,kDAAkD;AAClD,QAAQ;AACR,EAAE;AACF,yEAAyE;AACzE,8EAA8E;AAE9E,MAAM,UAAU,UAAU,CAAC,OAA4B;IACrD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,eAAe,CAAC,GAAW;IAClC,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CACjB,iFAAiF,CAClF,CAAC;IACF,IAAI,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,WAAW,GAAG,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;IACjD,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC;AAC1D,CAAC"}