@rubytech/create-realagent 1.0.832 → 1.0.834

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 (97) hide show
  1. package/dist/index.js +131 -9
  2. package/package.json +1 -1
  3. package/payload/platform/lib/admins-write/dist/index.d.ts +87 -0
  4. package/payload/platform/lib/admins-write/dist/index.d.ts.map +1 -0
  5. package/payload/platform/lib/admins-write/dist/index.js +248 -0
  6. package/payload/platform/lib/admins-write/dist/index.js.map +1 -0
  7. package/payload/platform/lib/admins-write/src/index.ts +311 -0
  8. package/payload/platform/lib/admins-write/tsconfig.json +8 -0
  9. package/payload/platform/neo4j/migrations/009-conversation-archive-title.ts +197 -0
  10. package/payload/platform/neo4j/schema.cypher +1 -1
  11. package/payload/platform/package.json +2 -2
  12. package/payload/platform/plugins/admin/PLUGIN.md +1 -1
  13. package/payload/platform/plugins/admin/mcp/dist/index.js +37 -44
  14. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  15. package/payload/platform/plugins/docs/references/internals.md +4 -3
  16. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +215 -43
  17. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +7 -2
  18. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +75 -0
  19. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +1 -1
  20. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +16 -10
  21. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -1
  22. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +155 -100
  23. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
  24. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts +13 -5
  25. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +1 -1
  26. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +53 -59
  27. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +1 -1
  28. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +9 -0
  29. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -1
  30. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +24 -7
  31. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +47 -11
  33. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  34. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +45 -8
  35. package/payload/platform/scripts/lib/resolve-account-dir.sh +3 -1
  36. package/payload/platform/scripts/migrate-import.sh +3 -1
  37. package/payload/platform/scripts/seed-neo4j.sh +13 -3
  38. package/payload/server/chunk-CRAIGEXY.js +654 -0
  39. package/payload/server/chunk-GK4WHM3H.js +9961 -0
  40. package/payload/server/chunk-I2NOLBQA.js +2123 -0
  41. package/payload/server/chunk-IVTESKFR.js +9961 -0
  42. package/payload/server/chunk-KD3XP4IK.js +1116 -0
  43. package/payload/server/chunk-KKGGT5RH.js +654 -0
  44. package/payload/server/chunk-MRJGG6CS.js +2124 -0
  45. package/payload/server/chunk-OJZPS4BL.js +367 -0
  46. package/payload/server/chunk-ZVW5XKPU.js +1116 -0
  47. package/payload/server/client-pool-FM3YJWV5.js +32 -0
  48. package/payload/server/client-pool-J5BCVVI2.js +32 -0
  49. package/payload/server/cloudflare-task-tracker-FSPEJOTH.js +19 -0
  50. package/payload/server/cloudflare-task-tracker-XCUO4N74.js +19 -0
  51. package/payload/server/maxy-edge.js +6 -5
  52. package/payload/server/neo4j-migrations-5AN2U3YO.js +664 -0
  53. package/payload/server/neo4j-migrations-XP7XDVPX.js +664 -0
  54. package/payload/server/public/assets/{Checkbox-CTGhpDKq.js → Checkbox-Bq6ORjz2.js} +1 -1
  55. package/payload/server/public/assets/admin-CstEkw-G.js +352 -0
  56. package/payload/server/public/assets/data-DwZZ7qbH.js +1 -0
  57. package/payload/server/public/assets/graph-DceEv42K.js +1 -0
  58. package/payload/server/public/assets/{jsx-runtime-D4WovFYk.css → jsx-runtime-DidQeNoZ.css} +1 -1
  59. package/payload/server/public/assets/page-Bpi_jPw6.js +50 -0
  60. package/payload/server/public/assets/{page-DkBfWy4C.js → page-CFWoVkgV.js} +1 -1
  61. package/payload/server/public/assets/{public-BdVIVpv8.js → public-BWMwq5Jj.js} +1 -1
  62. package/payload/server/public/assets/{useAdminFetch-DmHu0oCx.js → useAdminFetch-B93ig7ef.js} +1 -1
  63. package/payload/server/public/assets/{useVoiceRecorder-CSc_hxjV.js → useVoiceRecorder-Cb0nAtOo.js} +1 -1
  64. package/payload/server/public/data.html +5 -5
  65. package/payload/server/public/graph.html +6 -6
  66. package/payload/server/public/index.html +8 -8
  67. package/payload/server/public/public.html +5 -5
  68. package/payload/server/server.js +376 -167
  69. package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.d.ts +0 -31
  70. package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.d.ts.map +0 -1
  71. package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.js +0 -666
  72. package/payload/platform/plugins/admin/mcp/dist/lib/review-tools.js.map +0 -1
  73. package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.d.ts +0 -61
  74. package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.d.ts.map +0 -1
  75. package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.js +0 -266
  76. package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.js.map +0 -1
  77. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts +0 -27
  78. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts.map +0 -1
  79. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js +0 -477
  80. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js.map +0 -1
  81. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-write.d.ts +0 -27
  82. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-write.d.ts.map +0 -1
  83. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-write.js +0 -160
  84. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-write.js.map +0 -1
  85. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.d.ts +0 -10
  86. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.d.ts.map +0 -1
  87. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.js +0 -29
  88. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-parse.js.map +0 -1
  89. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.d.ts +0 -28
  90. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.d.ts.map +0 -1
  91. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.js +0 -34
  92. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.js.map +0 -1
  93. package/payload/server/public/assets/admin-BNwPsMhJ.js +0 -352
  94. package/payload/server/public/assets/data-Y77FLKjs.js +0 -1
  95. package/payload/server/public/assets/graph-N_Bw-8oT.js +0 -1
  96. package/payload/server/public/assets/page-BKLGP-th.js +0 -50
  97. /package/payload/server/public/assets/{jsx-runtime-DkaAusaX.js → jsx-runtime-DH5S-MwB.js} +0 -0
@@ -24,11 +24,16 @@
24
24
  // Argv (flags): --source <enum>
25
25
  // --participant-person-ids <csv>
26
26
  // --scope <admin|public>
27
- // [--session-gap-hours <N>] (default 12)
27
+ // [--rebuild] (Task 902 — destructive rebuild)
28
28
  // [--timezone <iana>]
29
29
  // [--date-format <DD/MM/YY|MM/DD/YY|DD/MM/YYYY|MM/DD/YYYY>]
30
30
  // [--session-id <id>]
31
31
  //
32
+ // Sessions split at SESSION_GAP_HOURS=8 (fixed constant — Task 902). The flag
33
+ // `--session-gap-hours` is REMOVED; passing it FAILs at phase=argv. The
34
+ // chunked chat-mode classify path (Task 902 sub-scope C) absorbs oversize
35
+ // sessions internally — no operator lever required.
36
+ //
32
37
  // Stdout (success): one JSON line — counters the skill needs to formulate
33
38
  // the three operator-facing messages. See SKILL.md for the shape.
34
39
  // Stderr + progress file: `[conversation-archive] ...` lines, fsync per write.
@@ -56,6 +61,16 @@ import { fileURLToPath } from "node:url";
56
61
 
57
62
  const __dirname = dirname(fileURLToPath(import.meta.url));
58
63
 
64
+ // Task 902 sub-scope D — fixed session gap. Pre-902 this was a CLI flag with a
65
+ // 12h default; oversize sessions on dense archives drove operators to re-issue
66
+ // with a smaller gap, which combined with cleanup-by-archiveSha256 to silently
67
+ // destroy 138 chunks (Adam Mackay incident, 2026-05-04). The flag is gone;
68
+ // chunked chat-mode classify (sub-scope C) absorbs oversize sessions
69
+ // internally so the gap value is no longer an operational lever. 8h is a
70
+ // tighter default than 12h and matches the operator-decided post-incident
71
+ // constant.
72
+ const SESSION_GAP_HOURS = 8;
73
+
59
74
  // ---------------------------------------------------------------------------
60
75
  // 1. Resolve dist paths.
61
76
  // ---------------------------------------------------------------------------
@@ -190,6 +205,9 @@ const VALID_SOURCES = new Set([
190
205
  "other",
191
206
  ]);
192
207
 
208
+ // Boolean flags carry no value; every other flag consumes the following arg.
209
+ const BOOLEAN_FLAGS = new Set(["rebuild"]);
210
+
193
211
  function parseArgv(argv) {
194
212
  const args = argv.slice(2);
195
213
  let archive = null;
@@ -202,6 +220,18 @@ function parseArgv(argv) {
202
220
  continue;
203
221
  }
204
222
  const key = a.slice(2);
223
+ // Task 902 sub-scope D — the flag re-introduced the surface that enabled
224
+ // the silent-partial-wipe failure (Defect B). Reject loudly with the
225
+ // remediation the operator should use instead.
226
+ if (key === "session-gap-hours") {
227
+ fail("argv", {
228
+ reason: `--session-gap-hours is no longer supported; gap is fixed at 8h. Use --rebuild on the prior export bytes if you need to re-classify.`,
229
+ });
230
+ }
231
+ if (BOOLEAN_FLAGS.has(camelCase(key))) {
232
+ flags[camelCase(key)] = true;
233
+ continue;
234
+ }
205
235
  const v = args[++i];
206
236
  if (v == null) fail("argv", { reason: `flag --${key} requires a value` });
207
237
  flags[camelCase(key)] = v;
@@ -281,6 +311,87 @@ function findChatTxt(dir) {
281
311
  return null;
282
312
  }
283
313
 
314
+ // ---------------------------------------------------------------------------
315
+ // 4b. Stable archive title (Task 902 sub-scope A).
316
+ //
317
+ // Format: `<source> · <owner> ↔ <other1>, <other2>, … · <YYYY-MM-DD>→<YYYY-MM-DD>`.
318
+ //
319
+ // The title is computed once per ingest from inputs that are immutable for
320
+ // the conversation (source, accountId, sortedParticipantElementIds, archive
321
+ // dateRange) so re-ingests produce the same string. Memory-ingest writes it
322
+ // to `:ConversationArchive.title` ON CREATE and COALESCE-on-MATCH so the UI
323
+ // (display-helpers.ts) reads a stable label without ever falling through to
324
+ // `summary`. Per-session counter strings stay in the progress file only.
325
+ //
326
+ // Names: AdminUser uses `displayName` then `slug`; Person composes
327
+ // `givenName + familyName`. When neither resolves, fall back to a short
328
+ // elementId prefix — same degraded format the migration uses on backfill,
329
+ // so a re-ingest after the migration produces the same title shape that
330
+ // already populated the node.
331
+ // ---------------------------------------------------------------------------
332
+
333
+ function isoToYmd(iso) {
334
+ // Read the YYYY-MM-DD prefix verbatim — same tz-stable contract as
335
+ // formatRunDate in display-helpers.ts. Parsing to Date + reformatting
336
+ // would flip the day between operator timezones.
337
+ const m = typeof iso === "string" ? iso.match(/^(\d{4}-\d{2}-\d{2})/) : null;
338
+ return m ? m[1] : "?";
339
+ }
340
+
341
+ function pickNameForRow(labels, props) {
342
+ if (Array.isArray(labels) && labels.includes("AdminUser")) {
343
+ const dn = typeof props.displayName === "string" ? props.displayName.trim() : "";
344
+ if (dn) return dn;
345
+ const slug = typeof props.slug === "string" ? props.slug.trim() : "";
346
+ if (slug) return slug;
347
+ }
348
+ if (Array.isArray(labels) && labels.includes("Person")) {
349
+ const given = typeof props.givenName === "string" ? props.givenName.trim() : "";
350
+ const family = typeof props.familyName === "string" ? props.familyName.trim() : "";
351
+ const full = [given, family].filter((s) => s.length > 0).join(" ");
352
+ if (full) return full;
353
+ }
354
+ return "";
355
+ }
356
+
357
+ async function computeArchiveTitle({
358
+ source,
359
+ accountId,
360
+ ownerElementId,
361
+ participantElementIds,
362
+ firstMessageAt,
363
+ lastMessageAt,
364
+ getSession,
365
+ }) {
366
+ const session = getSession();
367
+ let nameByElementId = new Map();
368
+ try {
369
+ const ids = [ownerElementId, ...participantElementIds];
370
+ const r = await session.run(
371
+ `UNWIND $ids AS id
372
+ MATCH (n) WHERE elementId(n) = id AND n.accountId = $accountId
373
+ RETURN elementId(n) AS elemId, labels(n) AS labels, properties(n) AS props`,
374
+ { ids, accountId },
375
+ );
376
+ for (const rec of r.records) {
377
+ const elemId = rec.get("elemId");
378
+ const labels = rec.get("labels");
379
+ const props = rec.get("props") || {};
380
+ const name = pickNameForRow(labels, props);
381
+ if (name) nameByElementId.set(elemId, name);
382
+ }
383
+ } finally {
384
+ await session.close().catch(() => {});
385
+ }
386
+ const ownerName = nameByElementId.get(ownerElementId) || ownerElementId.slice(0, 8);
387
+ const otherNames = participantElementIds
388
+ .map((id) => nameByElementId.get(id) || id.slice(0, 8))
389
+ .join(", ");
390
+ const firstYmd = isoToYmd(firstMessageAt);
391
+ const lastYmd = isoToYmd(lastMessageAt);
392
+ return `${source} · ${ownerName} ↔ ${otherNames} · ${firstYmd}→${lastYmd}`;
393
+ }
394
+
284
395
  // ---------------------------------------------------------------------------
285
396
  // 5. Main
286
397
  // ---------------------------------------------------------------------------
@@ -327,12 +438,12 @@ async function main() {
327
438
  const scope = flags.scope;
328
439
  const timezone = flags.timezone || "Europe/London";
329
440
  const dateFormat = flags.dateFormat;
330
- const sessionGapHours = flags.sessionGapHours
331
- ? parseFloat(flags.sessionGapHours)
332
- : 12;
333
- if (!Number.isFinite(sessionGapHours) || sessionGapHours <= 0) {
334
- fail("argv", { reason: `invalid --session-gap-hours "${flags.sessionGapHours}" (must be positive number)` });
335
- }
441
+ // Task 902 sub-scope B — `--rebuild` is the single opt-in for destructive
442
+ // re-run-with-same-bytes. Without it, re-issuing the bin on the same export
443
+ // is a no-op (chunk-count summary, exit 0). With it, the bin treats the run
444
+ // as first-ingest (skips prior-cursor lookup) and the FIRST session's
445
+ // memoryIngest call cleans prior chunks for the archiveSha256.
446
+ const rebuild = flags.rebuild === true;
336
447
  const sessionId =
337
448
  flags.sessionId ||
338
449
  `conversation-archive:${source}:${Date.now()}:${Math.random().toString(36).slice(2, 10)}`;
@@ -426,7 +537,7 @@ async function main() {
426
537
  const archiveSha256 = normaliserResult.archiveSha256;
427
538
  const archiveSourceFile = normaliserResult.archiveSourceFile;
428
539
  log(
429
- `source=${source} file=${archiveSourceFile} owner=${ownerElementId} participants=${participantElementIds.length} scope=${scope} accountId=${accountId} archiveSha256=${archiveSha256.slice(0, 12)} session-gap-hours=${sessionGapHours}`,
540
+ `source=${source} file=${archiveSourceFile} owner=${ownerElementId} participants=${participantElementIds.length} scope=${scope} accountId=${accountId} archiveSha256=${archiveSha256.slice(0, 12)} gap=${SESSION_GAP_HOURS} rebuild=${rebuild}`,
430
541
  );
431
542
  log(
432
543
  `parsed lines=${normaliserResult.counters.parsed} media-skipped=${normaliserResult.counters.mediaSkipped} system-skipped=${normaliserResult.counters.systemSkipped}`,
@@ -463,38 +574,56 @@ async function main() {
463
574
  fail("argv", { reason: err instanceof Error ? err.message : String(err) });
464
575
  }
465
576
 
466
- // 6d. Derive conversationIdentity and look up prior :ConversationArchive
577
+ // 6d. Derive conversationIdentity and look up prior :ConversationArchive.
578
+ // Task 902 sub-scope B — under `--rebuild` we skip the prior-cursor lookup
579
+ // entirely; the run is treated as first-ingest, the first session cleans
580
+ // prior chunks (cleanupPriorChunks=true), and the cursor is overwritten
581
+ // by that session's MERGE. Without `--rebuild`, the prior cursor drives
582
+ // delta-append OR (Task 902 sub-scope B) the same-bytes no-op exit when
583
+ // chunks already exist for this archiveSha256.
467
584
  const conversationIdentity = deriveConversationIdentity({
468
585
  accountId,
469
586
  participantElementIds: [ownerElementId, ...participantElementIds],
470
587
  });
471
588
  let priorArchive = null;
472
- try {
473
- const r = await session.run(
474
- `MATCH (a:ConversationArchive { conversationIdentity: $cid })
475
- RETURN elementId(a) AS elemId,
476
- a.lastIngestedMessageHash AS lastHash,
477
- a.lastIngestedMessageAt AS lastAt LIMIT 1`,
478
- { cid: conversationIdentity },
479
- );
480
- if (r.records[0]) {
481
- priorArchive = {
482
- elemId: r.records[0].get("elemId"),
483
- lastHash: r.records[0].get("lastHash"),
484
- lastAt: r.records[0].get("lastAt"),
485
- };
589
+ let priorChunkCountForSha = 0;
590
+ if (!rebuild) {
591
+ try {
592
+ const r = await session.run(
593
+ `MATCH (a:ConversationArchive { conversationIdentity: $cid })
594
+ OPTIONAL MATCH (a)-[:HAS_SECTION]->(c:Section:Conversation { archiveSha256: $sha })
595
+ RETURN elementId(a) AS elemId,
596
+ a.lastIngestedMessageHash AS lastHash,
597
+ a.lastIngestedMessageAt AS lastAt,
598
+ count(c) AS chunksForSha LIMIT 1`,
599
+ { cid: conversationIdentity, sha: archiveSha256 },
600
+ );
601
+ if (r.records[0] && r.records[0].get("elemId")) {
602
+ priorArchive = {
603
+ elemId: r.records[0].get("elemId"),
604
+ lastHash: r.records[0].get("lastHash"),
605
+ lastAt: r.records[0].get("lastAt"),
606
+ };
607
+ const cs = r.records[0].get("chunksForSha");
608
+ priorChunkCountForSha =
609
+ typeof cs === "number" ? cs : cs?.toNumber?.() ?? 0;
610
+ }
611
+ } catch (err) {
612
+ await session.close().catch(() => {});
613
+ cleanup();
614
+ fail("delta-cursor-missing", { reason: `conversationArchive lookup failed: ${err instanceof Error ? err.message : String(err)}` });
486
615
  }
487
- } catch (err) {
488
- await session.close().catch(() => {});
489
- cleanup();
490
- fail("delta-cursor-missing", { reason: `conversationArchive lookup failed: ${err instanceof Error ? err.message : String(err)}` });
491
616
  }
492
617
  await session.close().catch(() => {});
493
618
 
494
- // 6e. Compute deltaStart
619
+ // 6e. Compute deltaStart. Three branches:
620
+ // (i) --rebuild: deltaStart=0, kind=rebuild, classify entire archive.
621
+ // (ii) no prior archive (or no cursor): first-ingest, deltaStart=0.
622
+ // (iii) prior archive + cursor: standard delta-append. Same-bytes (no
623
+ // cursor advance possible) AND chunks exist for this sha → no-op.
495
624
  let deltaStart = 0;
496
- let deltaKind = "first-ingest";
497
- if (priorArchive && priorArchive.lastHash) {
625
+ let deltaKind = rebuild ? "rebuild" : "first-ingest";
626
+ if (!rebuild && priorArchive && priorArchive.lastHash) {
498
627
  const cursor = findDeltaCursor(allLines, priorArchive.lastHash);
499
628
  if (cursor.kind === "missing") {
500
629
  cleanup();
@@ -505,7 +634,7 @@ async function main() {
505
634
  });
506
635
  }
507
636
  if (cursor.kind === "empty") {
508
- log(`noop reason="no new messages since ${priorArchive.lastAt}"`);
637
+ log(`noop reason="no new messages since ${priorArchive.lastAt}" prior-chunks=${priorChunkCountForSha}`);
509
638
  cleanup();
510
639
  const totalMs = Date.now() - startedMs;
511
640
  process.stdout.write(JSON.stringify({
@@ -519,7 +648,7 @@ async function main() {
519
648
  systemSkipped: normaliserResult.counters.systemSkipped,
520
649
  delta: { kind: "empty-delta", deltaStart: allLines.length, deltaMessages: 0 },
521
650
  sessions: 0,
522
- chunks: 0,
651
+ chunks: priorChunkCountForSha,
523
652
  nextEdgesCreated: 0,
524
653
  participantsLinked: 0,
525
654
  dateRange: { first: allLines[0].dateSent, last: allLines[allLines.length - 1].dateSent },
@@ -536,15 +665,31 @@ async function main() {
536
665
  }
537
666
  const deltaLines = allLines.slice(deltaStart);
538
667
  log(
539
- `delta cursor=${priorArchive ? priorArchive.lastHash.slice(0, 12) : "(first-ingest)"} cursor-line=${deltaStart} delta-messages=${deltaLines.length}`,
668
+ `delta kind=${deltaKind} cursor=${priorArchive && priorArchive.lastHash && !rebuild ? priorArchive.lastHash.slice(0, 12) : "(first-ingest)"} cursor-line=${deltaStart} delta-messages=${deltaLines.length}`,
540
669
  );
541
670
 
542
- // 6f. Sessionize delta
543
- const sessions = sessionize(deltaLines, sessionGapHours);
671
+ // 6f. Sessionize delta at the fixed 8h gap (Task 902 sub-scope D).
672
+ const sessions = sessionize(deltaLines, SESSION_GAP_HOURS);
544
673
  log(
545
- `sessionize source=${source} archiveSha256=${archiveSha256.slice(0, 12)} messages=${deltaLines.length} sessions=${sessions.length} gap-hours=${sessionGapHours}`,
674
+ `sessionize source=${source} archiveSha256=${archiveSha256.slice(0, 12)} messages=${deltaLines.length} sessions=${sessions.length} gap-hours=${SESSION_GAP_HOURS}`,
546
675
  );
547
676
 
677
+ // Task 902 sub-scope A — compute the stable archive title once, before
678
+ // the per-session loop. The title is `<source> · <owner> ↔ <others> ·
679
+ // <YYYY-MM-DD>→<YYYY-MM-DD>`. Operator-confirmed participant names come
680
+ // from one Cypher hop; absent canonical names fall back to elementId
681
+ // prefixes so the migration's degraded-title path mirrors live behaviour.
682
+ const archiveTitle = await computeArchiveTitle({
683
+ source,
684
+ accountId,
685
+ ownerElementId,
686
+ participantElementIds,
687
+ firstMessageAt: allLines[0].dateSent,
688
+ lastMessageAt: allLines[allLines.length - 1].dateSent,
689
+ getSession,
690
+ });
691
+ log(`archive-title=${archiveTitle}`);
692
+
548
693
  // 6g. Per-session classify + immediate memoryIngest (Task 900 sub-scope E).
549
694
  // Each session commits atomically: chunks + cursor advance happen together
550
695
  // via memoryIngest. A kill mid-loop leaves a partial archive whose cursor
@@ -621,7 +766,6 @@ async function main() {
621
766
  body: lastSessionLine.body,
622
767
  });
623
768
  const sessionLastAt = lastSessionLine.dateSent;
624
- const sessionDocumentSummary = `Session ${s.index + 1}/${sessions.length}: ${s.messages.length} messages, ${sessionChunks.length} chunks.`;
625
769
 
626
770
  let ingestResult;
627
771
  const ingestStart = Date.now();
@@ -631,7 +775,13 @@ async function main() {
631
775
  attachmentId: conversationIdentity,
632
776
  parentLabel: "ConversationArchive",
633
777
  source,
634
- documentSummary: sessionDocumentSummary,
778
+ // Task 902 sub-scope A — `archiveTitle` is the stable label written
779
+ // to `:ConversationArchive.title` (UI prefers it over `summary`).
780
+ // `documentSummary` is also the stable title rather than the
781
+ // per-session counter that previously locked the parent's first
782
+ // checkpoint into "Session 1/N: …".
783
+ archiveTitle,
784
+ documentSummary: archiveTitle,
635
785
  anchorNodeId: ownerElementId,
636
786
  anchorLabel: "AdminUser",
637
787
  sections: sessionChunks,
@@ -643,11 +793,11 @@ async function main() {
643
793
  lastIngestedMessageHash: sessionLastHash,
644
794
  lastIngestedMessageAt: sessionLastAt,
645
795
  participantElementIds: [ownerElementId, ...participantElementIds],
646
- // First session of THIS run cleans prior chunks for matching
647
- // archiveSha256 (re-run-with-same-bytes idempotency); subsequent
648
- // sessions in the same run skip cleanup or they would delete the
649
- // chunks just written by sessions above them.
650
- cleanupPriorChunks: isFirstSessionInRun,
796
+ // Task 902 sub-scope B cleanup-by-archiveSha256 runs ONLY under
797
+ // `--rebuild`, AND only on the first session of the run (subsequent
798
+ // sessions skip cleanup or they would delete chunks the earlier
799
+ // sessions in the same run just wrote).
800
+ cleanupPriorChunks: rebuild && isFirstSessionInRun,
651
801
  });
652
802
  } catch (err) {
653
803
  cleanup();
@@ -663,10 +813,32 @@ async function main() {
663
813
  totalNextEdges += ingestResult.edgeBreakdown.NEXT ?? 0;
664
814
  if (isFirstSessionInRun) {
665
815
  participantsLinked = ingestResult.edgeBreakdown.PARTICIPANT_IN ?? 0;
816
+ // Task 902 sub-scope E — surface destructive cleanup as a WARN line in
817
+ // the progress sink (not just memory-ingest's stderr). The agent's
818
+ // heartbeat grep-matches this exact prefix and elevates to operator
819
+ // chat. Cleanup only ever fires under `--rebuild` per sub-scope B,
820
+ // so seeing the line outside of an operator-issued `--rebuild` is a
821
+ // doctrine violation worth surfacing loudly.
822
+ const cleaned = ingestResult.cleanedPriorChunks ?? 0;
823
+ if (cleaned > 0) {
824
+ log(
825
+ `WARN cleanup-dropped chunks=${cleaned} archiveSha256=${archiveSha256.slice(0, 12)} — prior data deleted, expected only under --rebuild`,
826
+ );
827
+ }
666
828
  }
667
829
  log(
668
830
  `session-committed sessionIndex=${s.index + 1}/${sessions.length} chunks-written=${ingestResult.sectionCount} cursor-at="${sessionLastAt}" ms=${Date.now() - ingestStart}`,
669
831
  );
832
+ // Task 902 sub-scope F — derived progress line. The agent's heartbeat
833
+ // surfaces only `progress` lines as chat tokens; raw `classify-session`
834
+ // and `session-committed` lines remain on disk for diagnosis. The render
835
+ // is computed in the bin (deterministic) instead of in SKILL.md prose
836
+ // (LLM drift) — sessionIndex/N → P% with running totals comes from one
837
+ // place, not from per-tick agent arithmetic.
838
+ const pct = Math.round((100 * (s.index + 1)) / sessions.length);
839
+ log(
840
+ `progress sessionIndex=${s.index + 1}/${sessions.length} pct=${pct} chunks-so-far=${totalChunksWritten} elapsed-ms=${Date.now() - startedMs}`,
841
+ );
670
842
  }
671
843
 
672
844
  if (sessionsUnenriched > 0) {
@@ -18,11 +18,14 @@
18
18
  # --source <whatsapp|telegram|signal|linkedin-messages|zoom-transcript|meeting-minutes|imessage|slack|other>
19
19
  # --participant-person-ids <csv>
20
20
  # --scope <admin|public>
21
- # [--session-gap-hours <N>] (default 12)
21
+ # [--rebuild] (Task 902 — opt-in destructive rebuild)
22
22
  # [--timezone <iana-zone>]
23
23
  # [--date-format <DD/MM/YY|MM/DD/YY|DD/MM/YYYY|MM/DD/YYYY>] (whatsapp only)
24
24
  # [--session-id <id>]
25
25
  #
26
+ # Sessions split deterministically at 8h gap (fixed code constant — Task 902).
27
+ # `--session-gap-hours` is REMOVED; passing it FAILs at phase=argv.
28
+ #
26
29
  # Identity (Task 900): `ACCOUNT_ID` + `USER_ID` come from the caller's env
27
30
  # (plumbed by `spawn-env.ts` into every Bash subprocess). The owner :AdminUser
28
31
  # elementId is derived from (accountId, userId) inside the bin via Cypher.
@@ -82,7 +85,9 @@ while [ $i -lt ${#ARGS[@]} ]; do
82
85
  --participant-person-ids) HAS_PARTICIPANTS=1; PARTICIPANTS_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
83
86
  --scope) HAS_SCOPE=1; SCOPE_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
84
87
  --session-id) SESSION_ID_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
85
- --session-gap-hours|--timezone|--date-format) i=$((i + 2)); continue ;;
88
+ --rebuild) i=$((i + 1)); continue ;;
89
+ --session-gap-hours) arg_fail "--session-gap-hours is no longer supported; gap is fixed at 8h. Use --rebuild on the prior export bytes if you need to re-classify." ;;
90
+ --timezone|--date-format) i=$((i + 2)); continue ;;
86
91
  --*) i=$((i + 2)); continue ;;
87
92
  *)
88
93
  if [ -z "$ARCHIVE" ]; then ARCHIVE="$a"; fi
@@ -139,6 +139,81 @@ test("Task 891: classifyDocument logs `mode=` for grep parity", () => {
139
139
  const source = readClassifier();
140
140
  assert.match(source, /\[memory-classify\][^\n]*calling haiku[^\n]*mode=/, "calling-haiku log line must include `mode=<document|chat>` for grep parity");
141
141
  });
142
+ // ---------------------------------------------------------------------------
143
+ // Task 898 — classifier + ranker structured tool-use migration.
144
+ //
145
+ // The classifier and ranker now force Haiku to emit `tool_use.input` matching
146
+ // a typed `input_schema`. The migration deletes `extractJson` (used to strip
147
+ // markdown fences from a free-form text response) and the malformed-JSON
148
+ // fallback path entirely — Anthropic enforces the schema at the API boundary,
149
+ // so structural failures (missing fields, wrong types, truncation mid-tool-input)
150
+ // surface as fallback responses or as the existing missing-offsets loud-fail.
151
+ //
152
+ // These tests grep source so a future refactor that reverts to free-form text
153
+ // re-fails the test suite on `main` pre-fix.
154
+ // ---------------------------------------------------------------------------
155
+ const RANKER_PATH = resolve(PACKAGE_ROOT, "src/lib/llm-ranker.ts");
156
+ function readRanker() {
157
+ return readFileSync(RANKER_PATH, "utf-8");
158
+ }
159
+ test("Task 898: classifier defines a ClassifierOutputTool with input_schema", () => {
160
+ const source = readClassifier();
161
+ assert.match(source, /ClassifierOutputTool[\s\S]{0,200}name:\s*["']emit_classifier_output["']/, "classifier must export a ClassifierOutputTool whose name is 'emit_classifier_output'");
162
+ assert.match(source, /ClassifierOutputTool[\s\S]*?input_schema/, "ClassifierOutputTool must declare an input_schema");
163
+ });
164
+ test("Task 898: classifier call site forces tool selection", () => {
165
+ const source = readClassifier();
166
+ const callMatch = source.match(/callOauthLlm\(\{[\s\S]*?\}\)/);
167
+ assert.ok(callMatch, "expected to find callOauthLlm({...}) call site");
168
+ assert.match(callMatch[0], /tools:\s*\[ClassifierOutputTool\]/, "classifier MUST pass `tools: [ClassifierOutputTool]` so Haiku emits structured output");
169
+ assert.match(callMatch[0], /toolChoiceName:\s*ClassifierOutputTool\.name/, "classifier MUST force the tool by name — Anthropic only validates the schema when forced");
170
+ });
171
+ test("Task 898: classifier `kind` schema is enforced as an enum (closed enumeration)", () => {
172
+ const source = readClassifier();
173
+ // The schema must declare `kind` with an `enum` whose membership is sourced
174
+ // from ALL_SECTION_KINDS. We check the structural shape rather than the
175
+ // exact list — the list is exported and tested elsewhere.
176
+ assert.match(source, /kind:\s*\{\s*type:\s*["']string["'],\s*enum:\s*ALL_SECTION_KINDS_LIST/, "ClassifierOutputTool.input_schema must enforce `kind` as an enum sourced from ALL_SECTION_KINDS — schema-level enforcement is what makes hallucinated kinds impossible");
177
+ });
178
+ test("Task 898: classifier no longer references extractJson (dead code removed)", () => {
179
+ const source = readClassifier();
180
+ assert.doesNotMatch(source, /extractJson/, "extractJson is unreachable on the structured-output path and was deleted; reintroducing it is the regression");
181
+ });
182
+ test("Task 898: classifier no longer collapses unknown kinds to Other (unreachable on structured path)", () => {
183
+ const source = readClassifier();
184
+ // The pre-Task-898 collapse used `isKnownKind ? rawKind : SECTION_KIND_OTHER`.
185
+ // With a strict schema enum on `kind`, the collapse is unreachable. The
186
+ // variable name is the load-bearing fingerprint of the dead branch.
187
+ assert.doesNotMatch(source, /isKnownKind/, "the `isKnownKind` collapse is dead code on the structured path; reintroducing it weakens the schema-as-enforcement contract");
188
+ });
189
+ test("Task 898: classifier emits `structured-output ok` log line per call", () => {
190
+ const source = readClassifier();
191
+ assert.match(source, /\[memory-classify\][^\n]*structured-output ok[^\n]*stop-reason=/, "classifier must log `structured-output ok stop-reason=...` per call so observability names the structured path");
192
+ });
193
+ test("Task 898: ranker defines a RankerOutputTool with input_schema", () => {
194
+ const source = readRanker();
195
+ assert.match(source, /RankerOutputTool[\s\S]{0,200}name:\s*["']emit_ranker_output["']/, "ranker must export a RankerOutputTool whose name is 'emit_ranker_output'");
196
+ assert.match(source, /RankerOutputTool[\s\S]*?input_schema/, "RankerOutputTool must declare an input_schema");
197
+ });
198
+ test("Task 898: ranker call site forces tool selection", () => {
199
+ const source = readRanker();
200
+ const callMatch = source.match(/callOauthLlm\(\{[\s\S]*?\}\)/);
201
+ assert.ok(callMatch, "expected to find callOauthLlm({...}) call site");
202
+ assert.match(callMatch[0], /tools:\s*\[RankerOutputTool\]/, "ranker MUST pass `tools: [RankerOutputTool]`");
203
+ assert.match(callMatch[0], /toolChoiceName:\s*RankerOutputTool\.name/, "ranker MUST force the tool by name");
204
+ });
205
+ test("Task 898: ranker no longer references extractJson", () => {
206
+ const source = readRanker();
207
+ assert.doesNotMatch(source, /extractJson/, "extractJson is unreachable on the structured-output path and was deleted");
208
+ });
209
+ test("Task 898: ranker no longer parses JSON.parse on response text", () => {
210
+ const source = readRanker();
211
+ assert.doesNotMatch(source, /JSON\.parse\(jsonText\)/, "structured tool-use returns a typed object — JSON.parse on response text is dead code");
212
+ });
213
+ test("Task 898: ranker emits `structured-output ok` log line per call", () => {
214
+ const source = readRanker();
215
+ assert.match(source, /\[memory-rank\][^\n]*structured-output ok[^\n]*stop-reason=/, "ranker must log `structured-output ok stop-reason=...` per call");
216
+ });
142
217
  test("Task 781 fixture is still >=10K chars (regression anchor)", () => {
143
218
  // The fixture is named in the brief so the same evidence shape can be
144
219
  // re-run on demand. If it ever shrinks below 10K, this test fails loud
@@ -1 +1 @@
1
- {"version":3,"file":"llm-classifier.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/llm-classifier.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"llm-classifier.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/llm-classifier.test.ts"],"names":[],"mappings":"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"}
@@ -14,14 +14,18 @@
14
14
  * the Anthropic API key. The API-key path is reserved for the public
15
15
  * agent.
16
16
  *
17
- * Hallucination defence: every returned `kind` is verified against the
18
- * loaded ontology label set. Sections whose `kind` is not a real label
19
- * are tagged `UNMAPPED`. Failure of the LLM call (missing creds, network,
20
- * malformed JSON) returns `{kind: "error", reason}` (Task 897 vocabulary
21
- * was `kind: "fallback"`). The caller decides whether to abort the
22
- * ingest or degrade-on-error per session; classifier never silently
23
- * substitutes a degraded write.
17
+ * Hallucination defence: section `kind` is enforced as a closed enum in
18
+ * `ClassifierOutputTool.input_schema`, so a hallucinated kind cannot reach
19
+ * this module Anthropic rejects the tool_use input at the API boundary.
20
+ * For `related[].kind` and `documentEdges[].targetKind` (which the static
21
+ * schema cannot constrain because the live ontology label set is a runtime
22
+ * input), the post-validator drops any label not in `ontologyLabels`.
23
+ * Failure of the LLM call (missing creds, network, schema-violating
24
+ * `tool_use.input`) returns `{kind: "error", reason}`. The caller decides
25
+ * whether to abort the ingest or degrade-on-error per session; classifier
26
+ * never silently substitutes a degraded write.
24
27
  */
28
+ import { type OauthLlmTool } from "../../../../../lib/oauth-llm/dist/index.js";
25
29
  /** Direction of the anchor edge relative to the typed node. */
26
30
  export type AnchorEdgeDirection = "from-anchor" | "to-anchor";
27
31
  /** Direction of a related-node edge relative to the typed node. */
@@ -166,9 +170,10 @@ export type ClassifyResult = {
166
170
  };
167
171
  /**
168
172
  * Closed enumeration of section `kind` values. Each becomes a secondary
169
- * label on the `:Section` node (e.g. `:Section:Position`). Anything outside
170
- * this list collapses to `Other` (the writer stamps the classifier's
171
- * one-line reason on `:Section:Other.classifierReason` for ontology growth).
173
+ * label on the `:Section` node (e.g. `:Section:Position`). The `kind` field
174
+ * is enforced as an `enum` in `ClassifierOutputTool.input_schema`, so a
175
+ * hallucinated kind cannot reach the post-validator — Anthropic rejects the
176
+ * tool_use input at the API boundary (Task 898).
172
177
  *
173
178
  * Source of truth: schema-base.md "Section kinds" table. Changes here MUST
174
179
  * be mirrored there or the validator will reject the secondary label.
@@ -180,6 +185,7 @@ export declare const CONTRACT_SECTION_KINDS: readonly ["Parties", "Recitals", "D
180
185
  /** Standalone (non-Section) node kind the classifier may emit per section. */
181
186
  export declare const STANDALONE_NODE_KINDS: readonly ["Project"];
182
187
  export declare const ALL_SECTION_KINDS: ReadonlySet<string>;
188
+ export declare const ClassifierOutputTool: OauthLlmTool;
183
189
  export interface ClassifyParams {
184
190
  /** Account scope, for log prefixing. */
185
191
  accountId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"llm-classifier.d.ts","sourceRoot":"","sources":["../../src/lib/llm-classifier.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"llm-classifier.d.ts","sourceRoot":"","sources":["../../src/lib/llm-classifier.ts"],"names":[],"mappings":"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"}