@rubytech/create-maxy-code 0.1.159 → 0.1.161

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 (146) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +48 -17
  3. package/payload/platform/neo4j/schema.cypher +59 -36
  4. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  5. package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +2 -2
  6. package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +1 -1
  7. package/payload/platform/plugins/docs/references/plugins-guide.md +3 -2
  8. package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
  9. package/payload/platform/plugins/email/PLUGIN.md +5 -1
  10. package/payload/platform/plugins/email/mcp/dist/index.js +25 -0
  11. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  12. package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.d.ts +8 -0
  13. package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.d.ts.map +1 -0
  14. package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.js +177 -0
  15. package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.js.map +1 -0
  16. package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts +6 -54
  17. package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts.map +1 -1
  18. package/payload/platform/plugins/email/mcp/dist/lib/graph.js +12 -300
  19. package/payload/platform/plugins/email/mcp/dist/lib/graph.js.map +1 -1
  20. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts +6 -0
  21. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts.map +1 -0
  22. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js +89 -0
  23. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js.map +1 -0
  24. package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.d.ts.map +1 -1
  25. package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js +11 -6
  26. package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js.map +1 -1
  27. package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +10 -37
  28. package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/connections.md +1 -0
  29. package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
  30. package/payload/platform/plugins/memory/PLUGIN.md +10 -1
  31. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +139 -439
  32. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +15 -47
  33. package/payload/platform/plugins/memory/mcp/dist/index.js +168 -29
  34. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  35. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js +43 -27
  36. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js.map +1 -1
  37. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.d.ts +2 -0
  38. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.d.ts.map +1 -0
  39. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.js +62 -0
  40. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.js.map +1 -0
  41. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js +5 -0
  42. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js.map +1 -1
  43. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +1 -1
  44. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -1
  45. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts +42 -25
  46. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts.map +1 -1
  47. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js +23 -17
  48. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js.map +1 -1
  49. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.d.ts +3 -0
  50. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.d.ts.map +1 -0
  51. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.js +61 -0
  52. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.js.map +1 -0
  53. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +1 -0
  54. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js +3 -0
  56. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js.map +1 -1
  57. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +1 -1
  58. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -1
  59. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +1 -0
  60. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -1
  61. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts +2 -0
  62. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts.map +1 -1
  63. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js +9 -3
  64. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js.map +1 -1
  65. package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.d.ts +41 -0
  66. package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.d.ts.map +1 -0
  67. package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.js +71 -0
  68. package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.js.map +1 -0
  69. package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.d.ts +18 -0
  70. package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.d.ts.map +1 -0
  71. package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.js +31 -0
  72. package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.js.map +1 -0
  73. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts +5 -0
  74. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts.map +1 -1
  75. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js +162 -78
  76. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js.map +1 -1
  77. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js +1 -1
  78. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js.map +1 -1
  79. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js +2 -0
  80. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js.map +1 -1
  81. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js +2 -0
  82. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js.map +1 -1
  83. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js +6 -0
  84. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js.map +1 -1
  85. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.d.ts +2 -0
  86. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.d.ts.map +1 -0
  87. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.js +141 -0
  88. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.js.map +1 -0
  89. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.d.ts +2 -0
  90. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.d.ts.map +1 -0
  91. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.js +86 -0
  92. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.js.map +1 -0
  93. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts +33 -34
  94. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -1
  95. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +94 -273
  96. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -1
  97. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.d.ts +43 -0
  98. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.d.ts.map +1 -0
  99. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.js +96 -0
  100. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.js.map +1 -0
  101. package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.d.ts +6 -0
  102. package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.d.ts.map +1 -0
  103. package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.js +73 -0
  104. package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.js.map +1 -0
  105. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  106. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +0 -1
  107. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  108. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts +6 -0
  109. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts.map +1 -1
  110. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js +2 -0
  111. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js.map +1 -1
  112. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +8 -0
  113. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts.map +1 -1
  114. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +40 -5
  115. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -1
  116. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts +7 -0
  117. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts.map +1 -1
  118. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js +17 -1
  119. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js.map +1 -1
  120. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +11 -0
  121. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  122. package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.d.ts +28 -0
  123. package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.d.ts.map +1 -0
  124. package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.js +46 -0
  125. package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.js.map +1 -0
  126. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -1
  127. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +26 -0
  128. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
  129. package/payload/platform/plugins/memory/mcp/vitest.config.ts +5 -4
  130. package/payload/platform/plugins/memory/references/schema-base.md +4 -6
  131. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +100 -116
  132. package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +74 -24
  133. package/payload/platform/plugins/notion-import/skills/notion-import/SKILL.md +1 -1
  134. package/payload/platform/plugins/obsidian-import/skills/obsidian-import/SKILL.md +1 -1
  135. package/payload/platform/plugins/substack-import/.claude-plugin/plugin.json +8 -0
  136. package/payload/platform/plugins/substack-import/PLUGIN.md +34 -0
  137. package/payload/platform/plugins/substack-import/skills/substack-import/SKILL.md +182 -0
  138. package/payload/platform/plugins/substack-import/skills/substack-import/references/archive-shape.md +68 -0
  139. package/payload/platform/plugins/substack-import/skills/substack-import/references/attachments.md +72 -0
  140. package/payload/platform/plugins/substack-import/skills/substack-import/references/engagement.md +61 -0
  141. package/payload/platform/plugins/substack-import/skills/substack-import/references/posts.md +80 -0
  142. package/payload/platform/plugins/substack-import/skills/substack-import/references/subscribers.md +74 -0
  143. package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +1 -1
  144. package/payload/platform/templates/specialists/agents/archive-ingest-operator.md +45 -0
  145. package/payload/platform/templates/specialists/agents/database-operator.md +5 -17
  146. package/payload/platform/templates/specialists/agents/librarian.md +1 -1
@@ -0,0 +1,89 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ import { getSession } from "../lib/neo4j.js";
5
+ // ---------------------------------------------------------------------------
6
+ // email-classify — operator-solicited reclassify for one email-thread
7
+ // :KnowledgeDocument keyed by its root Message-ID. Thin wrapper over the
8
+ // generic kd-classify primitive (memory plugin): derives the
9
+ // account-scoped attachmentId, runs the same body-growth gate, stages the
10
+ // thread body to a temp file, and returns a dispatch envelope the admin
11
+ // agent uses to hand off to the `librarian` specialist.
12
+ //
13
+ // The gate threshold and envelope shape mirror
14
+ // `platform/plugins/memory/mcp/src/lib/kd-classify-gate.ts`; the two copies
15
+ // are duplicated rather than cross-plugin-imported so each MCP process boots
16
+ // independently. Keep both aligned when the contract changes. Task 401.
17
+ // ---------------------------------------------------------------------------
18
+ const KD_CLASSIFY_GROWTH_THRESHOLD = 0.25;
19
+ function deriveThreadAttachmentId(accountId, rootMessageId) {
20
+ return createHash("sha256")
21
+ .update(`email:${accountId}:${rootMessageId}`)
22
+ .digest("hex");
23
+ }
24
+ async function readThread(accountId, attachmentId) {
25
+ const session = getSession();
26
+ try {
27
+ const result = await session.run(`MATCH (kd:KnowledgeDocument { source: 'email', accountId: $accountId, attachmentId: $attachmentId })
28
+ RETURN coalesce(size(kd.body), 0) AS bodyLen,
29
+ coalesce(kd.lastClassifiedBodyLength, 0) AS lastLen,
30
+ kd.body AS body`, { accountId, attachmentId });
31
+ if (result.records.length === 0)
32
+ return null;
33
+ const r = result.records[0];
34
+ return {
35
+ bodyLen: Number(r.get("bodyLen")),
36
+ lastClassifiedBodyLength: Number(r.get("lastLen")),
37
+ body: r.get("body") ?? "",
38
+ };
39
+ }
40
+ finally {
41
+ await session.close();
42
+ }
43
+ }
44
+ export async function emailClassify(params) {
45
+ const { accountId, rootMessageId } = params;
46
+ if (!rootMessageId.trim()) {
47
+ throw new Error("email-classify: rootMessageId is required.");
48
+ }
49
+ const attachmentId = deriveThreadAttachmentId(accountId, rootMessageId);
50
+ const meta = await readThread(accountId, attachmentId);
51
+ if (!meta) {
52
+ throw new Error(`email-classify: no email-thread :KnowledgeDocument for rootMessageId=${rootMessageId} in account ${accountId}. Have you run email-fetch + email-ingest first?`);
53
+ }
54
+ if (meta.bodyLen === 0) {
55
+ throw new Error(`email-classify: thread KD for rootMessageId=${rootMessageId} has empty body; cannot classify.`);
56
+ }
57
+ const lastLen = meta.lastClassifiedBodyLength;
58
+ const growthPct = lastLen > 0 ? (meta.bodyLen - lastLen) / lastLen : 1;
59
+ const isFirstClassify = lastLen <= 0;
60
+ const shouldProceed = isFirstClassify || growthPct >= KD_CLASSIFY_GROWTH_THRESHOLD;
61
+ process.stderr.write(`[email-classify] attachmentId=${attachmentId.slice(0, 12)} bodyLen=${meta.bodyLen} lastLen=${lastLen} growthPct=${growthPct.toFixed(3)} action=${shouldProceed ? "proceed" : "skip"}\n`);
62
+ if (!shouldProceed) {
63
+ const pctRounded = Math.round(growthPct * 100);
64
+ return `no classification needed (body grew ${pctRounded}% since last ingest; threshold ${Math.round(KD_CLASSIFY_GROWTH_THRESHOLD * 100)}%)`;
65
+ }
66
+ const accountDir = process.env.ACCOUNT_DIR;
67
+ if (!accountDir) {
68
+ throw new Error(`email-classify: ACCOUNT_DIR env not set; cannot stage thread body for librarian dispatch.`);
69
+ }
70
+ const tempFilePath = `${accountDir}/tmp/kd-classify-${attachmentId}.txt`;
71
+ await mkdir(dirname(tempFilePath), { recursive: true });
72
+ await writeFile(tempFilePath, meta.body, "utf-8");
73
+ const growthLabel = isFirstClassify
74
+ ? "first classify"
75
+ : `${Math.round(growthPct * 100)}% growth since last ingest`;
76
+ return [
77
+ `Ready to classify email :KnowledgeDocument into sections.`,
78
+ `attachmentId: ${attachmentId}`,
79
+ `rootMessageId: ${rootMessageId}`,
80
+ `bodyLen: ${meta.bodyLen} chars (${growthLabel})`,
81
+ `tempFilePath: ${tempFilePath}`,
82
+ ``,
83
+ `Next: dispatch the \`librarian\` specialist via the Task tool with this brief —`,
84
+ ` "Ingest the document at ${tempFilePath} using mode=document, attachmentId=${attachmentId},`,
85
+ ` anchor=LocalBusiness. The :KnowledgeDocument already exists and has source=email;`,
86
+ ` memory-ingest will MERGE by attachmentId and write :Section children, replacing any prior."`,
87
+ ].join("\n");
88
+ }
89
+ //# sourceMappingURL=email-classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-classify.js","sourceRoot":"","sources":["../../src/tools/email-classify.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"email-ingest.d.ts","sourceRoot":"","sources":["../../src/tools/email-ingest.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,QAAQ,GAAG,SAAS,CAAC;CACnC;AAED,UAAU,YAAY;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAMD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAgFvE"}
1
+ {"version":3,"file":"email-ingest.d.ts","sourceRoot":"","sources":["../../src/tools/email-ingest.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,QAAQ,GAAG,SAAS,CAAC;CACnC;AAED,UAAU,YAAY;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAMD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAuFvE"}
@@ -1,5 +1,6 @@
1
1
  import { updatePollState } from "../lib/credentials.js";
2
- import { storeEmailMessages, stripAngleBrackets } from "../lib/graph.js";
2
+ import { stripAngleBrackets } from "../lib/graph.js";
3
+ import { dispatchEmailMessagesToConversationArchive } from "../lib/conversation-archive-dispatch.js";
3
4
  import { takeBatch, peekBatch } from "../lib/ingest-batch.js";
4
5
  function normaliseId(id) {
5
6
  return stripAngleBrackets(id)?.toLowerCase() ?? null;
@@ -71,16 +72,20 @@ export async function emailIngest(params) {
71
72
  }
72
73
  }
73
74
  toIngest.sort((a, b) => a.receivedAt.localeCompare(b.receivedAt));
74
- const { threadsCreated, messagesAppended, duplicates } = await storeEmailMessages(toIngest);
75
+ const { threadsDispatched, threadsSkipped, unresolvedAddresses } = await dispatchEmailMessagesToConversationArchive(toIngest);
75
76
  // Advance the high-water mark so the next email-fetch starts after this batch.
76
77
  await updatePollState(params.accountId, batch.maxUid, batch.uidValidity);
77
78
  // Consume the cached batch only after a successful write + state advance.
78
79
  takeBatch(params.accountId);
79
- console.error(`[email-ingest] tag=email-ingest ingested=${toIngest.length} discarded=${discarded}`);
80
- return [
80
+ console.error(`[email-ingest] tag=email-ingest ingested=${toIngest.length} discarded=${discarded} threadsDispatched=${threadsDispatched} threadsSkipped=${threadsSkipped} unresolved=${unresolvedAddresses.length}`);
81
+ const lines = [
81
82
  `Ingested ${toIngest.length} message(s); discarded ${discarded}.`,
82
- `Graph: ${threadsCreated} new thread(s), ${messagesAppended} appended, ${duplicates} duplicate(s) skipped.`,
83
+ `Graph: ${threadsDispatched} thread(s) dispatched to conversation-archive, ${threadsSkipped} skipped.`,
83
84
  `Inbox high-water mark advanced to UID ${batch.maxUid}.`,
84
- ].join("\n");
85
+ ];
86
+ if (unresolvedAddresses.length > 0) {
87
+ lines.push(`Unresolved sender/recipient addresses (no :Person or :AdminUser carries these emails — add them to the graph and re-run email-ingest to land their threads): ${unresolvedAddresses.join(", ")}`);
88
+ }
89
+ return lines.join("\n");
85
90
  }
86
91
  //# sourceMappingURL=email-ingest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"email-ingest.js","sourceRoot":"","sources":["../../src/tools/email-ingest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"email-ingest.js","sourceRoot":"","sources":["../../src/tools/email-ingest.ts"],"names":[],"mappings":"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"}
@@ -72,7 +72,7 @@ When the owner is an external Person (non-operator archive), the anchor is the c
72
72
 
73
73
  **Doctrine:** raw Cypher and `cypher-shell` invocations are forbidden in this skill and its references. Writes route through `mcp__plugin_memory_memory__memory-archive-write` (bulk archives) or `mcp__plugin_memory_memory__memory-write` / `mcp__plugin_memory_memory__memory-update` (single-node enrichments like `profile.md`). If a CSV needs a write shape no current MCP tool supports, file a task to extend `memory-archive-write` with a new `archiveType` handler — never improvise via Bash. See [librarian's LOUD-FAIL prerogative](../../../../templates/specialists/agents/librarian.md#prerogatives).
74
74
 
75
- **LOUD-FAIL on parse errors (structurally enforced).** When LinkedIn parser tools land that follow the `mcp__*__*-import-parse` naming convention, the harness-level `platform/plugins/admin/hooks/archive-ingest-gate.sh` will record an `isError: true` response and block every subsequent tool call this turn until the operator submits the next prompt. The hook also denies edits to `platform/plugins/*/lib/*` and JavaScript test runners (`vitest`, `bun test`, `npm test`, `npx jest`) unconditionally. The skill's "no Bash improvisation" doctrine above is the contract; the hook is the enforcement. See [.docs/hooks.md](../../../../../.docs/hooks.md) for the full gate surface.
75
+ **LOUD-FAIL on parse errors (structurally enforced).** When LinkedIn parser tools land that follow the `mcp__*__*-import-parse` naming convention, the harness-level `platform/plugins/admin/hooks/archive-ingest-surface-gate.sh` will record an `isError: true` response and block every subsequent tool call this turn until the operator submits the next prompt. The hook also denies edits to `platform/plugins/*/lib/*` and JavaScript test runners (`vitest`, `bun test`, `npm test`, `npx jest`) unconditionally. The skill's "no Bash improvisation" doctrine above is the contract; the hook is the enforcement. See [.docs/hooks.md](../../../../../.docs/hooks.md) for the full gate surface.
76
76
 
77
77
  ## Selective-ingest threshold (bulk archives)
78
78
 
@@ -94,42 +94,15 @@ The operator picks one axis or a combination. The agent applies the filter to `r
94
94
 
95
95
  ## File roster
96
96
 
97
- Each entry maps to exactly one reference. Order reflects when a reference's edges first become meaningful — `profile.md` enriches the owner's UserProfile and runs before any file that queries it.
98
-
99
- | Order | File | Reference | Edges produced |
100
- |-------|------|-----------|----------------|
101
- | 1 | `Profile.csv` | [profile.md](references/profile.md) | enrichment on `:UserProfile` (no new nodes) |
102
- | 2 | `Email Addresses.csv` | _pending_ | `:UserProfile` ← `.email` / `:AdminUser.emails` array enrichment |
103
- | 2 | `PhoneNumbers.csv` | _pending_ | `:UserProfile.telephone` enrichment |
104
- | 2 | `Whatsapp Phone Numbers.csv` | _pending_ | `:UserProfile.whatsappTelephone` enrichment |
105
- | 3 | `Positions.csv` | _pending_ | `(:UserProfile)-[:WORKS_FOR {title,startDate,endDate}]->(:Organization)` |
106
- | 3 | `Education.csv` | _pending_ | `(:UserProfile)-[:ATTENDED {degree,startDate,endDate}]->(:Organization {organizationCategory:'educational'})` |
107
- | 3 | `Certifications.csv` | _pending_ | `(:UserProfile)-[:HOLDS]->(:Credential)` |
108
- | 3 | `Languages.csv` | _pending_ | `(:UserProfile)-[:SPEAKS {proficiency}]->(:DefinedTerm {category:'language'})` |
109
- | 3 | `Skills.csv` | _pending_ | `(:UserProfile)-[:HAS_SKILL]->(:DefinedTerm {category:'linkedin-skill'})` |
110
- | 4 | `Connections.csv` | [connections.md](references/connections.md) | `(:AdminUser)-[:CONNECTED_ON_LINKEDIN {connectedOn}]->(:Person)` + `(:Person)-[:WORKS_FOR {title}]->(:Organization)` |
111
- | 5 | `Invitations.csv` | _pending_ | `(:AdminUser)-[:INVITED / :INVITED_BY]->(:Person)` |
112
- | 5 | `Endorsement_Given_Info.csv` | _pending_ | `(:AdminUser)-[:ENDORSED {date}]->(:Person)-[:FOR_SKILL]->(:DefinedTerm)` |
113
- | 5 | `Endorsement_Received_Info.csv` | _pending_ | `(:Person)-[:ENDORSED {date}]->(:AdminUser)-[:FOR_SKILL]->(:DefinedTerm)` |
114
- | 5 | `Recommendations_Given.csv` | _pending_ | `(:Review)-[:ABOUT]->(:Person)`, `(:AdminUser)-[:AUTHORED]->(:Review)` |
115
- | 5 | `Recommendations_Received.csv` | _pending_ | `(:Review)-[:ABOUT]->(:AdminUser)`, `(:Person)-[:AUTHORED]->(:Review)` |
116
- | 6 | `Company Follows.csv` | _pending_ | `(:AdminUser)-[:FOLLOWS {since}]->(:Organization)` |
117
- | 6 | `Causes You Care About.csv` | _pending_ | `(:UserProfile)-[:SUPPORTS]->(:DefinedTerm {category:'cause'})` |
118
- | 6 | `Events.csv` | _pending_ | `(:AdminUser)-[:ATTENDED_EVENT]->(:Event)` |
119
- | 7 | `messages.csv` | conversation-archive (Task 397) | dispatched to `conversation-archive` skill with `--source linkedin-messages`; one `:ConversationArchive` per DM thread + `:Section` chunks + `(:Person)-[:PARTICIPANT_IN]->(archive)` |
120
- | 7 | `guide_messages.csv` | same | same dispatch (likely empty) |
121
- | 7 | `learning_coach_messages.csv` | same | same dispatch (likely empty) |
122
- | 7 | `learning_role_play_messages.csv` | same | same dispatch (likely empty) |
123
- | 8 | `Articles/` | _pending_ | `(:AdminUser)-[:AUTHORED]->(:CreativeWork {category:'linkedin-article'})` |
124
- | 8 | `Learning.csv` | _pending_ | `(:UserProfile)-[:COMPLETED]->(:Credential)` (LinkedIn Learning courses) |
125
- | 9 | `Jobs/Job Applications.csv` | _pending_ | `(:AdminUser)-[:APPLIED_TO {date}]->(:Organization)` via job-posting text on the edge |
126
- | 9 | `Jobs/Saved Jobs.csv` | _pending_ | `(:AdminUser)-[:SAVED_JOB]->(:Organization)` |
127
- | 9 | `Jobs/Job Seeker Preferences.csv` | _pending_ | `:UserProfile.jobSeekerPreferences` property blob |
128
- | 9 | `Jobs/Online Job Postings.csv` | _pending_ | `(:AdminUser)-[:POSTED]->(:JobPosting)` (only rows where the operator authored the posting) |
129
- | 9 | `SavedJobAlerts.csv` | _pending_ | `:UserProfile.savedJobAlerts` property blob |
130
- | 10 | `Services Marketplace/Providers.csv` | _pending_ | `:UserProfile.servicesOffered` property blob |
131
- | 10 | `Receipts_v2.csv` | _pending_ | `(:AdminUser)-[:PAID]->(:Invoice)` (LinkedIn subscriptions) |
132
- | 10 | `Registration.csv` | _pending_ | `:UserProfile.linkedinRegisteredAt` property enrichment |
97
+ What's shipped today:
98
+
99
+ | File | Reference | Edges produced |
100
+ |------|-----------|----------------|
101
+ | `Profile.csv` | [profile.md](references/profile.md) | enrichment on `:UserProfile` (no new nodes) |
102
+ | `Connections.csv` | [connections.md](references/connections.md) | `(:AdminUser)-[:CONNECTED_ON_LINKEDIN {connectedOn}]->(:Person)` + `(:Person)-[:WORKS_FOR {title}]->(:Organization)` |
103
+ | `messages.csv` (and the three other DM CSVs) | conversation-archive (Task 397) | dispatched to `conversation-archive` skill with `--source linkedin-messages`; one `:ConversationArchive` per DM thread + `:Section` chunks + `(:Person)-[:PARTICIPANT_IN]->(archive)` |
104
+
105
+ Additional CSVs in the export (Positions, Education, Skills, Endorsements, Recommendations, Company Follows, Articles, Jobs, etc.) land as new references when the operator asks for them — each is its own follow-up.
133
106
 
134
107
  ## Explicitly out of scope
135
108
 
@@ -130,5 +130,6 @@ These are **read queries**, not writes. Cypher writes from the agent are forbidd
130
130
  | Every row parsed as "Notes:,NaN,…" | Header preamble not skipped | Skip first 3 lines before the CSV parser |
131
131
  | Tool error "row connectedOn is not ISO 8601" | Parser left `Connected On` in `"23 Apr 2026"` form | Convert to `YYYY-MM-DD` before passing to the tool |
132
132
  | Tool error "ownerNodeId not found" | Owner-confirmation flow not run, or operator typed the wrong id | Re-run owner confirmation; pass the resulting `elementId` as `ownerNodeId` |
133
+ | Tool error `ownerNodeId <id> belongs to account <X>, not <Y>. Cross-account archive ingest refused.` | The confirmed owner lives in a different account than the calling session — typically the operator confirmed a Person/AdminUser from a different brand, or `accountId` was passed wrong | Re-run owner confirmation inside the correct account, or pass the calling session's `accountId` |
133
134
  | `WORKS_FOR` count « connection count | Many rows have blank company | Expected — LinkedIn doesn't force connections to list a current employer |
134
135
  | Tool not present in `init` frame | `specialists:librarian` spawned without the `mcp__plugin_memory_memory__memory-archive-write` token | Loud-fail per librarian's prerogatives. Do not improvise via Bash. Operator must remediate (re-seed specialist templates) |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memory",
3
- "description": "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-write, memory-update, and the :Report surface (memory-report-write / memory-report-read-latest / memory-report-list) for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 (`conversation-archive-derive-insights`) walks chunks of one named archive and emits per-row claim proposals for operator-gated wiring; `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships four skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks).",
3
+ "description": "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-write, memory-update, and the :Report surface (memory-report-write / memory-report-read-latest / memory-report-list) for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Operator-solicited reclassify of an already-stored thread-shaped KD (today: email-thread KDs) is exposed as `kd-classify <attachmentId>` — runs a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under `$ACCOUNT_DIR/tmp/`, and returns a dispatch envelope for the `librarian`; on success `memory-ingest` replaces the prior :Section children and stamps `lastClassifiedAt` + `lastClassifiedBodyLength` on the parent KD. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 splits across two read-only tools (Task 433): `conversation-archive-list-chunks` pages chunk bodies for the dispatched specialist to read in-turn; `conversation-archive-derive-insights` converts the specialist's per-chunk claims into operator-facing proposals with per-kind cypher. `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships four skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks).",
4
4
  "version": "0.1.0",
5
5
  "author": {
6
6
  "name": "Rubytech LLC"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: memory
3
- description: "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-write, memory-update, and the :Report surface (memory-report-write / memory-report-read-latest / memory-report-list) for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 (`conversation-archive-derive-insights`) walks chunks of one named archive and emits per-row claim proposals for operator-gated wiring; `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships four skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks)."
3
+ description: "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-write, memory-update, and the :Report surface (memory-report-write / memory-report-read-latest / memory-report-list) for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Operator-solicited reclassify of an already-stored thread-shaped KD (today: email-thread KDs) is exposed as `kd-classify <attachmentId>` — runs a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under `$ACCOUNT_DIR/tmp/`, and returns a dispatch envelope for the `librarian`; on success `memory-ingest` replaces the prior :Section children and stamps `lastClassifiedAt` + `lastClassifiedBodyLength` on the parent KD. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 splits across two read-only tools (Task 433): `conversation-archive-list-chunks` pages chunk bodies for the dispatched specialist to read in-turn; `conversation-archive-derive-insights` converts the specialist's per-chunk claims into operator-facing proposals with per-kind cypher. `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships four skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks)."
4
4
  tools:
5
5
  - name: memory-search
6
6
  publicAllowlist: true
@@ -17,6 +17,9 @@ tools:
17
17
  - name: memory-ingest
18
18
  publicAllowlist: false
19
19
  adminAllowlist: false
20
+ - name: kd-classify
21
+ publicAllowlist: false
22
+ adminAllowlist: false
20
23
  - name: memory-ingest-extract
21
24
  publicAllowlist: false
22
25
  adminAllowlist: false
@@ -26,6 +29,9 @@ tools:
26
29
  - name: memory-find-candidates
27
30
  publicAllowlist: false
28
31
  adminAllowlist: true
32
+ - name: memory-node-exists
33
+ publicAllowlist: false
34
+ adminAllowlist: true
29
35
  - name: memory-delete
30
36
  publicAllowlist: false
31
37
  adminAllowlist: true
@@ -53,6 +59,9 @@ tools:
53
59
  - name: obsidian-vault-import
54
60
  publicAllowlist: false
55
61
  adminAllowlist: false
62
+ - name: conversation-archive-list-chunks
63
+ publicAllowlist: false
64
+ adminAllowlist: false
56
65
  - name: conversation-archive-derive-insights
57
66
  publicAllowlist: false
58
67
  adminAllowlist: false