@rubytech/create-maxy-code 0.1.132 → 0.1.140

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 (161) hide show
  1. package/dist/__tests__/installer-specialist-registration.test.js +5 -40
  2. package/dist/index.js +24 -28
  3. package/dist/specialist-registration.js +0 -15
  4. package/package.json +1 -1
  5. package/payload/platform/config/brand.json +1 -1
  6. package/payload/platform/lib/graph-search/dist/boosts.d.ts +39 -0
  7. package/payload/platform/lib/graph-search/dist/boosts.d.ts.map +1 -0
  8. package/payload/platform/lib/graph-search/dist/boosts.js +57 -0
  9. package/payload/platform/lib/graph-search/dist/boosts.js.map +1 -0
  10. package/payload/platform/lib/graph-search/dist/dedup.d.ts +29 -0
  11. package/payload/platform/lib/graph-search/dist/dedup.d.ts.map +1 -0
  12. package/payload/platform/lib/graph-search/dist/dedup.js +97 -0
  13. package/payload/platform/lib/graph-search/dist/dedup.js.map +1 -0
  14. package/payload/platform/lib/graph-search/dist/index.d.ts +70 -8
  15. package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
  16. package/payload/platform/lib/graph-search/dist/index.js +323 -62
  17. package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
  18. package/payload/platform/lib/graph-search/dist/query-expansion.d.ts +37 -0
  19. package/payload/platform/lib/graph-search/dist/query-expansion.d.ts.map +1 -0
  20. package/payload/platform/lib/graph-search/dist/query-expansion.js +101 -0
  21. package/payload/platform/lib/graph-search/dist/query-expansion.js.map +1 -0
  22. package/payload/platform/lib/graph-search/dist/route.d.ts +29 -0
  23. package/payload/platform/lib/graph-search/dist/route.d.ts.map +1 -0
  24. package/payload/platform/lib/graph-search/dist/route.js +53 -0
  25. package/payload/platform/lib/graph-search/dist/route.js.map +1 -0
  26. package/payload/platform/lib/graph-search/dist/rrf-fusion.d.ts +31 -0
  27. package/payload/platform/lib/graph-search/dist/rrf-fusion.d.ts.map +1 -0
  28. package/payload/platform/lib/graph-search/dist/rrf-fusion.js +57 -0
  29. package/payload/platform/lib/graph-search/dist/rrf-fusion.js.map +1 -0
  30. package/payload/platform/lib/graph-search/src/__tests__/boosts.test.ts +59 -0
  31. package/payload/platform/lib/graph-search/src/__tests__/dedup.test.ts +83 -0
  32. package/payload/platform/lib/graph-search/src/__tests__/query-expansion.test.ts +96 -0
  33. package/payload/platform/lib/graph-search/src/__tests__/route.test.ts +62 -0
  34. package/payload/platform/lib/graph-search/src/__tests__/rrf-fusion.test.ts +39 -0
  35. package/payload/platform/lib/graph-search/src/boosts.ts +61 -0
  36. package/payload/platform/lib/graph-search/src/dedup.ts +108 -0
  37. package/payload/platform/lib/graph-search/src/index.ts +407 -65
  38. package/payload/platform/lib/graph-search/src/query-expansion.ts +129 -0
  39. package/payload/platform/lib/graph-search/src/route.ts +70 -0
  40. package/payload/platform/lib/graph-search/src/rrf-fusion.ts +62 -0
  41. package/payload/platform/lib/obsidian-parser/dist/index.d.ts +98 -0
  42. package/payload/platform/lib/obsidian-parser/dist/index.d.ts.map +1 -0
  43. package/payload/platform/lib/obsidian-parser/dist/index.js +480 -0
  44. package/payload/platform/lib/obsidian-parser/dist/index.js.map +1 -0
  45. package/payload/platform/lib/obsidian-parser/src/index.ts +572 -0
  46. package/payload/platform/lib/obsidian-parser/tsconfig.json +8 -0
  47. package/payload/platform/neo4j/schema.cypher +86 -1
  48. package/payload/platform/package.json +2 -2
  49. package/payload/platform/plugins/.claude-plugin/marketplace.json +10 -0
  50. package/payload/platform/plugins/admin/hooks/session-end-retrospective.sh +4 -2
  51. package/payload/platform/plugins/admin/mcp/dist/index.js +6 -4
  52. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  53. package/payload/platform/plugins/docs/references/internals.md +36 -10
  54. package/payload/platform/plugins/docs/references/neo4j.md +63 -0
  55. package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
  56. package/payload/platform/plugins/docs/references/session-retrospective.md +11 -4
  57. package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js +2 -2
  58. package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js.map +1 -1
  59. package/payload/platform/plugins/memory/PLUGIN.md +3 -0
  60. package/payload/platform/plugins/memory/mcp/dist/index.js +203 -7
  61. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  62. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.d.ts +2 -0
  63. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.d.ts.map +1 -0
  64. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.js +25 -0
  65. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.js.map +1 -0
  66. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.d.ts +2 -0
  67. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.d.ts.map +1 -0
  68. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.js +70 -0
  69. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.js.map +1 -0
  70. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.d.ts +2 -0
  71. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.d.ts.map +1 -0
  72. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +50 -0
  73. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -0
  74. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts +33 -0
  75. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts.map +1 -0
  76. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js +99 -0
  77. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js.map +1 -0
  78. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts +21 -0
  79. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts.map +1 -1
  80. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js +24 -0
  81. package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js.map +1 -1
  82. package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.d.ts +13 -0
  83. package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.d.ts.map +1 -0
  84. package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.js +151 -0
  85. package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.js.map +1 -0
  86. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts +55 -0
  87. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts.map +1 -0
  88. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js +290 -0
  89. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js.map +1 -0
  90. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts +19 -0
  91. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts.map +1 -0
  92. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js +102 -0
  93. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js.map +1 -0
  94. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js +18 -1
  95. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js.map +1 -1
  96. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js +5 -0
  97. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js.map +1 -1
  98. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js +2 -0
  99. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js.map +1 -1
  100. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +64 -1
  101. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  102. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +258 -0
  103. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  104. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -1
  105. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +14 -0
  106. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -1
  107. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +29 -0
  108. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts.map +1 -0
  109. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +22 -0
  110. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js.map +1 -0
  111. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts +31 -1
  112. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -1
  113. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +182 -50
  114. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
  115. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  116. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +76 -1
  117. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  118. package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.d.ts +127 -0
  119. package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.d.ts.map +1 -0
  120. package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.js +477 -0
  121. package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.js.map +1 -0
  122. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts +30 -0
  123. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts.map +1 -0
  124. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js +129 -0
  125. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js.map +1 -0
  126. package/payload/platform/plugins/memory/mcp/scripts/backfill-typed-edges.ts +72 -0
  127. package/payload/platform/plugins/memory/mcp/scripts/generate-edge-docs.ts +75 -0
  128. package/payload/platform/plugins/memory/mcp/vitest.config.ts +5 -0
  129. package/payload/platform/plugins/memory/references/schema-base.md +44 -0
  130. package/payload/platform/plugins/notion-import/.claude-plugin/plugin.json +8 -0
  131. package/payload/platform/plugins/notion-import/PLUGIN.md +27 -0
  132. package/payload/platform/plugins/notion-import/skills/notion-import/SKILL.md +110 -0
  133. package/payload/platform/plugins/notion-import/skills/notion-import/references/attachments.md +55 -0
  134. package/payload/platform/plugins/notion-import/skills/notion-import/references/databases.md +81 -0
  135. package/payload/platform/plugins/notion-import/skills/notion-import/references/page-tree.md +61 -0
  136. package/payload/platform/plugins/notion-import/skills/notion-import/references/workspace-export.md +41 -0
  137. package/payload/platform/plugins/obsidian-import/.claude-plugin/plugin.json +8 -0
  138. package/payload/platform/plugins/obsidian-import/PLUGIN.md +39 -0
  139. package/payload/platform/plugins/obsidian-import/skills/obsidian-import/SKILL.md +92 -0
  140. package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/attachments.md +80 -0
  141. package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/daily-notes.md +31 -0
  142. package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/vault-structure.md +46 -0
  143. package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/wikilinks.md +70 -0
  144. package/payload/platform/scripts/seed-neo4j.sh +49 -0
  145. package/payload/platform/templates/agents/admin/IDENTITY.md +4 -3
  146. package/payload/platform/templates/specialists/agents/database-operator.md +21 -1
  147. package/payload/server/{adminuser-self-heal-QAWOZ3JV.js → adminuser-self-heal-YC47O34W.js} +2 -1
  148. package/payload/server/{chunk-JSBRDJBE.js → chunk-HYQNUVGO.js} +9 -1
  149. package/payload/server/{chunk-HCYM5FLU.js → chunk-T3DJD5QR.js} +3 -1
  150. package/payload/server/maxy-edge.js +3 -2
  151. package/payload/server/public/assets/{admin-FcRHAL-3.js → admin-BM9aXUUh.js} +1 -1
  152. package/payload/server/public/assets/{data-Ds37mflX.js → data-Ds2ECtfP.js} +1 -1
  153. package/payload/server/public/assets/{graph-CmWRhaiS.js → graph-BzxObKMM.js} +1 -1
  154. package/payload/server/public/assets/{graph-labels-Ch2r00Gt.js → graph-labels-Dwgep_nr.js} +1 -1
  155. package/payload/server/public/assets/{page-BcHhJXUt.js → page-BlkBsjP2.js} +1 -1
  156. package/payload/server/public/assets/page-h_SA5SVQ.js +51 -0
  157. package/payload/server/public/data.html +3 -3
  158. package/payload/server/public/graph.html +3 -3
  159. package/payload/server/public/index.html +4 -4
  160. package/payload/server/server.js +803 -76
  161. package/payload/server/public/assets/page-BOtNny_4.js +0 -51
@@ -0,0 +1,477 @@
1
+ import { promises as fs } from "node:fs";
2
+ import * as path from "node:path";
3
+ import { walkVault, buildVaultIndex, resolveWikilinkInVault, normalizeTag, makeImportId, } from "../../../../../lib/obsidian-parser/dist/index.js";
4
+ import { levenshtein } from "../../../../../lib/graph-mcp/dist/schema-cypher-parser.js";
5
+ import { getSession } from "../lib/neo4j.js";
6
+ import { memoryArchiveWrite, } from "./memory-archive-write.js";
7
+ const FUZZY_MATCH_THRESHOLD = 2;
8
+ const SUPPORTED_ATTACHMENT_MIME = {
9
+ ".png": "image/png",
10
+ ".jpg": "image/jpeg",
11
+ ".jpeg": "image/jpeg",
12
+ ".gif": "image/gif",
13
+ ".webp": "image/webp",
14
+ ".svg": "image/svg+xml",
15
+ ".pdf": "application/pdf",
16
+ ".mp3": "audio/mpeg",
17
+ ".mp4": "video/mp4",
18
+ ".mov": "video/quicktime",
19
+ ".wav": "audio/wav",
20
+ ".m4a": "audio/mp4",
21
+ ".webm": "video/webm",
22
+ };
23
+ export async function obsidianVaultImport(params) {
24
+ const importId = params.importId ?? makeImportId();
25
+ const threshold = params.selectiveIngestThreshold ?? 100;
26
+ const startedMs = Date.now();
27
+ await assertExtractedDir(params.extractedDir);
28
+ const walkOpts = {
29
+ folderPrefixes: params.filters?.folderPrefixes,
30
+ tagFilter: params.filters?.tags?.map(normalizeTag),
31
+ modifiedSinceMs: toEpochMs(params.filters?.modifiedSince),
32
+ };
33
+ const allPages = await walkVault(params.extractedDir, {});
34
+ const pagesDiscovered = allPages.length;
35
+ // Re-walk with filters for the "eligible" set so the plan honours the
36
+ // operator's selective-ingest choices.
37
+ const eligiblePages = walkOpts.folderPrefixes || walkOpts.tagFilter || walkOpts.modifiedSinceMs
38
+ ? await walkVault(params.extractedDir, walkOpts)
39
+ : allPages;
40
+ const vaultIndex = buildVaultIndex(eligiblePages);
41
+ const knownPaths = new Set(eligiblePages.map((p) => p.obsidianPath));
42
+ const session = getSession();
43
+ let resolved;
44
+ try {
45
+ resolved = await resolveAllWikilinks(eligiblePages, vaultIndex, knownPaths, params.accountId, session);
46
+ }
47
+ finally {
48
+ await session.close();
49
+ }
50
+ const tagCount = countDistinct(eligiblePages.flatMap((p) => p.tags.map(normalizeTag)));
51
+ const attachmentCount = countDistinct(eligiblePages.flatMap((p) => p.attachments.map((a) => a.resolvedPath)));
52
+ // Idempotency: query existing contentHashes for these paths and mark
53
+ // unchanged. Only the dry-run plan reports the count; the commit phase
54
+ // uses the same map to filter rows before write.
55
+ const idemSession = getSession();
56
+ let unchangedSet;
57
+ try {
58
+ unchangedSet = await findUnchangedPages(idemSession, params.accountId, eligiblePages);
59
+ }
60
+ finally {
61
+ await idemSession.close();
62
+ }
63
+ const ambiguities = resolved.ambiguities;
64
+ const plan = {
65
+ importId,
66
+ mode: params.mode,
67
+ pagesDiscovered,
68
+ pagesEligible: eligiblePages.length,
69
+ pagesUnchanged: unchangedSet.size,
70
+ wikilinks: {
71
+ page: resolved.counts.page,
72
+ entity: resolved.counts.entity,
73
+ stub: resolved.counts.stub,
74
+ ambiguous: ambiguities.length,
75
+ },
76
+ tagCount,
77
+ attachmentCount,
78
+ ambiguities,
79
+ selectiveIngestSuggested: eligiblePages.length > threshold &&
80
+ !walkOpts.folderPrefixes &&
81
+ !walkOpts.tagFilter &&
82
+ !walkOpts.modifiedSinceMs,
83
+ };
84
+ process.stderr.write(`[obsidian-import] event=dry-run-complete importId=${importId}` +
85
+ ` pages=${eligiblePages.length} unchanged=${unchangedSet.size}` +
86
+ ` wikilinks-page=${resolved.counts.page} wikilinks-entity=${resolved.counts.entity}` +
87
+ ` wikilinks-stub=${resolved.counts.stub} wikilinks-ambiguous=${ambiguities.length}` +
88
+ ` tags=${tagCount} attachments=${attachmentCount}` +
89
+ ` ms=${Date.now() - startedMs}\n`);
90
+ if (params.mode === "dry-run")
91
+ return plan;
92
+ // Commit phase — refuse if unresolved ambiguities remain.
93
+ const resolutions = params.resolutions ?? {};
94
+ const missing = ambiguities.filter((a) => !(a.id in resolutions));
95
+ if (missing.length > 0) {
96
+ throw new Error(`obsidian-vault-import commit refused: ${missing.length} unresolved ambiguities. Re-run dry-run, surface to operator, supply resolutions.`);
97
+ }
98
+ const accountDir = resolveAccountDir(params.accountId);
99
+ const importArchiveDir = path.join(accountDir, "archive", "obsidian", importId);
100
+ await fs.mkdir(importArchiveDir, { recursive: true });
101
+ let attachmentsCopied = 0;
102
+ const rows = [];
103
+ for (const page of eligiblePages) {
104
+ if (unchangedSet.has(page.obsidianPath))
105
+ continue;
106
+ const pageWikilinks = resolveWithOverrides(page, resolved.byPage.get(page.obsidianPath) ?? [], resolutions);
107
+ const pageAttachments = await stageAttachments(page, params.extractedDir, importArchiveDir, (n) => {
108
+ attachmentsCopied += n;
109
+ });
110
+ rows.push({
111
+ obsidianPath: page.obsidianPath,
112
+ title: page.title,
113
+ aliases: page.aliases,
114
+ tags: page.tags.map(normalizeTag),
115
+ body: page.body,
116
+ contentHash: page.contentHash,
117
+ noteDate: page.noteDate,
118
+ mtimeMs: page.mtimeMs,
119
+ wikilinks: pageWikilinks,
120
+ attachments: pageAttachments,
121
+ });
122
+ }
123
+ let written = {
124
+ createdPages: 0,
125
+ mergedPages: 0,
126
+ createdTags: 0,
127
+ createdTagEdges: 0,
128
+ createdAttachments: 0,
129
+ createdAttachmentEdges: 0,
130
+ createdWikilinkEdges: 0,
131
+ };
132
+ const errors = [];
133
+ if (rows.length > 0) {
134
+ const result = await memoryArchiveWrite({
135
+ archiveType: "obsidian-vault",
136
+ ownerNodeId: params.ownerNodeId,
137
+ accountId: params.accountId,
138
+ rows,
139
+ sessionId: params.sessionId,
140
+ });
141
+ written = {
142
+ createdPages: result.counters.createdPages ?? 0,
143
+ mergedPages: result.counters.mergedPages ?? 0,
144
+ createdTags: result.counters.createdTags ?? 0,
145
+ createdTagEdges: result.counters.createdTagEdges ?? 0,
146
+ createdAttachments: result.counters.createdAttachments ?? 0,
147
+ createdAttachmentEdges: result.counters.createdAttachmentEdges ?? 0,
148
+ createdWikilinkEdges: result.counters.createdWikilinkEdges ?? 0,
149
+ };
150
+ errors.push(...result.errors);
151
+ }
152
+ const elapsedMs = Date.now() - startedMs;
153
+ const observabilityTag = `[obsidian-import] event=commit-complete importId=${importId}` +
154
+ ` pages=${eligiblePages.length} written=${rows.length} skipped=${unchangedSet.size}` +
155
+ ` wikilinks=${written.createdWikilinkEdges} tags=${written.createdTags}` +
156
+ ` attachments=${attachmentsCopied} errors=${errors.length} ms=${elapsedMs}`;
157
+ process.stderr.write(observabilityTag + "\n");
158
+ return {
159
+ ...plan,
160
+ written,
161
+ attachmentsCopied,
162
+ errors,
163
+ observabilityTag,
164
+ };
165
+ }
166
+ // ---------------------------------------------------------------------------
167
+ // Helpers
168
+ // ---------------------------------------------------------------------------
169
+ async function assertExtractedDir(extractedDir) {
170
+ if (!extractedDir || !path.isAbsolute(extractedDir)) {
171
+ throw new Error(`obsidian-vault-import: extractedDir must be an absolute path; got "${extractedDir}".`);
172
+ }
173
+ const stat = await fs.stat(extractedDir).catch(() => null);
174
+ if (!stat || !stat.isDirectory()) {
175
+ throw new Error(`obsidian-vault-import: extractedDir does not exist or is not a directory: ${extractedDir}`);
176
+ }
177
+ }
178
+ function toEpochMs(modifiedSince) {
179
+ if (!modifiedSince)
180
+ return undefined;
181
+ if (/^\d+$/.test(modifiedSince))
182
+ return Number.parseInt(modifiedSince, 10);
183
+ const t = Date.parse(modifiedSince);
184
+ return Number.isFinite(t) ? t : undefined;
185
+ }
186
+ function countDistinct(values) {
187
+ return new Set(values.filter(Boolean)).size;
188
+ }
189
+ function resolveAccountDir(accountId) {
190
+ // Same convention as memory plugin's attachment-store: lives under the
191
+ // configured DATA_ROOT/accounts/<accountId>/. If neither env is set we
192
+ // throw — fail loud rather than write attachments to an unexpected path.
193
+ const root = process.env.MAXY_DATA_ROOT ??
194
+ process.env.DATA_ROOT ??
195
+ process.env.PLATFORM_DATA_ROOT;
196
+ if (!root) {
197
+ throw new Error("obsidian-vault-import: data root env (MAXY_DATA_ROOT / DATA_ROOT / PLATFORM_DATA_ROOT) is not set; cannot resolve attachment archive directory.");
198
+ }
199
+ return path.join(root, "accounts", accountId);
200
+ }
201
+ /**
202
+ * Build the resolution plan for every wikilink in every eligible page.
203
+ *
204
+ * Resolution order:
205
+ * 1. Intra-vault page (title or alias) — if unique, page target.
206
+ * 2. Existing entity fuzzy match against :Person/:Organization/:Concept
207
+ * with name distance ≤ FUZZY_MATCH_THRESHOLD — if unique, entity target.
208
+ * 3. Otherwise, stub target (the handler creates a :Concept stub at write).
209
+ *
210
+ * Ambiguity: both an intra-vault page AND an existing entity match; or
211
+ * multiple intra-vault candidates; or ≥2 entity candidates within
212
+ * threshold. Stub-only matches are NOT ambiguities — we simply create the
213
+ * stub.
214
+ */
215
+ async function resolveAllWikilinks(pages, index, knownPaths, accountId, session) {
216
+ const distinctTargets = new Set();
217
+ for (const p of pages) {
218
+ for (const wl of p.wikilinks)
219
+ distinctTargets.add(wl.target);
220
+ }
221
+ const entityIndex = await loadEntityIndex(session, accountId, distinctTargets);
222
+ const byPage = new Map();
223
+ const ambiguities = [];
224
+ const counts = { page: 0, entity: 0, stub: 0 };
225
+ let ambiguityCounter = 0;
226
+ for (const page of pages) {
227
+ const resolved = [];
228
+ for (const wl of page.wikilinks) {
229
+ const pageHits = resolveWikilinkInVault(wl.target, index, knownPaths);
230
+ const entityHits = lookupEntities(wl.target, entityIndex);
231
+ const decision = decideResolution(wl, pageHits, entityHits);
232
+ if (decision.kind === "ambiguous") {
233
+ ambiguityCounter++;
234
+ const id = `amb-${ambiguityCounter}`;
235
+ ambiguities.push({
236
+ id,
237
+ fromPath: page.obsidianPath,
238
+ target: wl.target,
239
+ pageCandidates: decision.pageCandidates,
240
+ entityCandidates: decision.entityCandidates,
241
+ });
242
+ // Provisional resolution for the dry-run summary: stub, until
243
+ // the operator decides. The commit phase overrides via resolutions.
244
+ resolved.push(toResolvedWikilink(wl, { kind: "stub", name: wl.target }, id));
245
+ counts.stub++;
246
+ continue;
247
+ }
248
+ resolved.push(toResolvedWikilink(wl, decision.target));
249
+ counts[decision.target.kind]++;
250
+ }
251
+ byPage.set(page.obsidianPath, resolved);
252
+ }
253
+ return { byPage, ambiguities, counts };
254
+ }
255
+ async function loadEntityIndex(session, accountId, distinctTargets) {
256
+ if (distinctTargets.size === 0) {
257
+ return { byName: new Map(), allNames: [] };
258
+ }
259
+ const result = await session.run(`MATCH (n)
260
+ WHERE n.accountId = $accountId
261
+ AND (n:Person OR n:Organization OR n:Concept)
262
+ AND NOT n:Trashed
263
+ AND n.name IS NOT NULL
264
+ RETURN elementId(n) AS nodeId, labels(n) AS labels, n.name AS name
265
+ LIMIT 50000`, { accountId });
266
+ const allNames = [];
267
+ const byName = new Map();
268
+ for (const rec of result.records) {
269
+ const labels = rec.get("labels");
270
+ const primary = labels.find((l) => l === "Person" || l === "Organization" || l === "Concept") ?? labels[0];
271
+ const entry = {
272
+ nodeId: rec.get("nodeId"),
273
+ label: primary,
274
+ name: rec.get("name"),
275
+ };
276
+ allNames.push(entry);
277
+ const key = entry.name.toLowerCase();
278
+ const arr = byName.get(key);
279
+ if (arr)
280
+ arr.push(entry);
281
+ else
282
+ byName.set(key, [entry]);
283
+ }
284
+ return { byName, allNames };
285
+ }
286
+ function lookupEntities(target, index) {
287
+ const t = target.toLowerCase().trim();
288
+ if (!t)
289
+ return [];
290
+ const exact = index.byName.get(t);
291
+ if (exact && exact.length > 0) {
292
+ return exact.map((e) => ({ ...e, distance: 0 }));
293
+ }
294
+ const out = [];
295
+ for (const entry of index.allNames) {
296
+ const d = levenshtein(t, entry.name.toLowerCase());
297
+ if (d <= FUZZY_MATCH_THRESHOLD) {
298
+ out.push({ ...entry, distance: d });
299
+ }
300
+ }
301
+ out.sort((a, b) => a.distance - b.distance);
302
+ return out;
303
+ }
304
+ function decideResolution(wl, pageHits, entityHits) {
305
+ // Exact entity (distance 0) is treated as a strong hit alongside any page hit.
306
+ const exactEntities = entityHits.filter((e) => e.distance === 0);
307
+ if (pageHits.length === 1 && exactEntities.length === 0 && entityHits.length === 0) {
308
+ return { kind: "resolved", target: { kind: "page", obsidianPath: pageHits[0] } };
309
+ }
310
+ if (pageHits.length === 0 && exactEntities.length === 1) {
311
+ return {
312
+ kind: "resolved",
313
+ target: { kind: "entity", nodeId: exactEntities[0].nodeId, nodeLabel: exactEntities[0].label },
314
+ };
315
+ }
316
+ // Two or more competing categories or candidates → ambiguous.
317
+ if (pageHits.length >= 1 && exactEntities.length >= 1) {
318
+ return { kind: "ambiguous", pageCandidates: pageHits, entityCandidates: entityHits };
319
+ }
320
+ if (pageHits.length >= 2) {
321
+ return { kind: "ambiguous", pageCandidates: pageHits, entityCandidates: [] };
322
+ }
323
+ if (exactEntities.length >= 2) {
324
+ return { kind: "ambiguous", pageCandidates: [], entityCandidates: exactEntities };
325
+ }
326
+ // Only fuzzy entity matches (distance > 0). Single fuzzy hit → resolve.
327
+ // Multiple fuzzy hits → ambiguous.
328
+ if (pageHits.length === 0 && entityHits.length === 1) {
329
+ return {
330
+ kind: "resolved",
331
+ target: { kind: "entity", nodeId: entityHits[0].nodeId, nodeLabel: entityHits[0].label },
332
+ };
333
+ }
334
+ if (pageHits.length === 0 && entityHits.length >= 2) {
335
+ return { kind: "ambiguous", pageCandidates: [], entityCandidates: entityHits };
336
+ }
337
+ // No hits anywhere → stub.
338
+ return { kind: "resolved", target: { kind: "stub", name: wl.target } };
339
+ }
340
+ function toResolvedWikilink(wl, target, ambiguityId) {
341
+ return {
342
+ linkText: wl.target,
343
+ alias: wl.alias,
344
+ anchor: wl.anchor,
345
+ target,
346
+ ...(ambiguityId ? { _ambiguityId: ambiguityId } : {}),
347
+ };
348
+ }
349
+ function resolveWithOverrides(page, resolvedForPage, resolutions) {
350
+ return resolvedForPage.map((wl) => {
351
+ const tagged = wl;
352
+ if (!tagged._ambiguityId)
353
+ return wl;
354
+ const decision = resolutions[tagged._ambiguityId];
355
+ if (!decision)
356
+ return wl;
357
+ if (decision.kind === "page") {
358
+ return { ...wl, target: { kind: "page", obsidianPath: decision.obsidianPath } };
359
+ }
360
+ if (decision.kind === "entity") {
361
+ // nodeLabel is not strictly required by the Cypher (elementId scopes
362
+ // by accountId guard); pass empty string when not known.
363
+ return { ...wl, target: { kind: "entity", nodeId: decision.nodeId, nodeLabel: "" } };
364
+ }
365
+ return { ...wl, target: { kind: "stub", name: wl.linkText } };
366
+ });
367
+ }
368
+ async function findUnchangedPages(session, accountId, pages) {
369
+ if (pages.length === 0)
370
+ return new Set();
371
+ const payload = pages.map((p) => ({
372
+ obsidianPath: p.obsidianPath,
373
+ contentHash: p.contentHash,
374
+ }));
375
+ const result = await session.run(`UNWIND $payload AS row
376
+ MATCH (k:KnowledgeDocument {accountId: $accountId, obsidianPath: row.obsidianPath})
377
+ WHERE k.contentHash = row.contentHash AND NOT k:Trashed
378
+ RETURN k.obsidianPath AS p`, { accountId, payload });
379
+ const out = new Set();
380
+ for (const rec of result.records)
381
+ out.add(rec.get("p"));
382
+ return out;
383
+ }
384
+ async function stageAttachments(page, vaultRoot, importArchiveDir, onCopied) {
385
+ const staged = [];
386
+ const seen = new Set();
387
+ for (const att of page.attachments) {
388
+ if (seen.has(att.resolvedPath))
389
+ continue;
390
+ seen.add(att.resolvedPath);
391
+ const src = await findAttachmentSource(vaultRoot, page.obsidianPath, att);
392
+ if (!src) {
393
+ process.stderr.write(`[obsidian-import] event=attachment-missing page=${page.obsidianPath} path=${att.resolvedPath}\n`);
394
+ continue;
395
+ }
396
+ const safeName = sanitizeArchiveName(src.relPath);
397
+ const dest = path.join(importArchiveDir, safeName);
398
+ const destDir = path.dirname(dest);
399
+ await fs.mkdir(destDir, { recursive: true });
400
+ try {
401
+ await fs.copyFile(src.absPath, dest);
402
+ onCopied(1);
403
+ }
404
+ catch (err) {
405
+ process.stderr.write(`[obsidian-import] event=attachment-copy-failed page=${page.obsidianPath} path=${att.resolvedPath} reason=${err instanceof Error ? err.message : String(err)}\n`);
406
+ continue;
407
+ }
408
+ const ext = path.extname(src.relPath).toLowerCase();
409
+ staged.push({
410
+ rawPath: att.rawPath,
411
+ archivePath: dest,
412
+ encodingFormat: SUPPORTED_ATTACHMENT_MIME[ext] ?? "application/octet-stream",
413
+ alt: att.alt,
414
+ });
415
+ }
416
+ return staged;
417
+ }
418
+ async function findAttachmentSource(vaultRoot, pagePath, att) {
419
+ // 1. Vault-root-relative as written.
420
+ const candidates = [att.resolvedPath];
421
+ // 2. Page-relative.
422
+ candidates.push(path.posix.join(path.posix.dirname(pagePath), att.resolvedPath));
423
+ for (const rel of candidates) {
424
+ const abs = path.join(vaultRoot, rel.split("/").join(path.sep));
425
+ try {
426
+ const stat = await fs.stat(abs);
427
+ if (stat.isFile())
428
+ return { absPath: abs, relPath: rel };
429
+ }
430
+ catch {
431
+ // try next candidate
432
+ }
433
+ }
434
+ // 3. Bare filename — search the vault for a single match (typical when
435
+ // operator stores attachments in a default folder Obsidian inserts by name).
436
+ const basename = path.posix.basename(att.resolvedPath);
437
+ if (!basename.includes("/")) {
438
+ const hit = await searchByBasename(vaultRoot, basename);
439
+ if (hit)
440
+ return hit;
441
+ }
442
+ return null;
443
+ }
444
+ async function searchByBasename(root, basename) {
445
+ const queue = [root];
446
+ while (queue.length > 0) {
447
+ const dir = queue.shift();
448
+ let entries;
449
+ try {
450
+ entries = await fs.readdir(dir, { withFileTypes: true });
451
+ }
452
+ catch {
453
+ continue;
454
+ }
455
+ for (const entry of entries) {
456
+ if (entry.name.startsWith("."))
457
+ continue;
458
+ const abs = path.join(dir, entry.name);
459
+ if (entry.isDirectory()) {
460
+ queue.push(abs);
461
+ continue;
462
+ }
463
+ if (entry.isFile() && entry.name === basename) {
464
+ const rel = path.relative(root, abs).split(path.sep).join("/");
465
+ return { absPath: abs, relPath: rel };
466
+ }
467
+ }
468
+ }
469
+ return null;
470
+ }
471
+ function sanitizeArchiveName(rel) {
472
+ return rel
473
+ .split("/")
474
+ .filter((seg) => seg && seg !== "." && seg !== "..")
475
+ .join("/");
476
+ }
477
+ //# sourceMappingURL=obsidian-vault-import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obsidian-vault-import.js","sourceRoot":"","sources":["../../src/tools/obsidian-vault-import.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Session-retrospective skip-rate visibility tool.
3
+ *
4
+ * Called by the admin agent at session start. Returns the ratio of recent
5
+ * sessions that closed without calling `session-retrospective-mark-complete`
6
+ * — sessions where the typed-edge pass, learnings pass, and SOUL.md pass
7
+ * never ran. The admin agent surfaces the result to the operator if the
8
+ * rate is non-zero so closing via Sidebar Archive / tab-close becomes a
9
+ * visible cost rather than a silent one.
10
+ *
11
+ * Mechanism: count `:Conversation` nodes for this account in the recent
12
+ * window; count those whose corresponding session log contains the
13
+ * sentinel tool-use of `session-retrospective-mark-complete`. The ratio
14
+ * is the skip rate. Pending nodes (prose with `updatedAt` later than the
15
+ * most-recent completed retrospective) carry the deferred typed-edge work
16
+ * that will land at the next /end.
17
+ */
18
+ import type { Session } from "neo4j-driver";
19
+ export interface SessionSkipRateArgs {
20
+ /** Window size — number of most-recent sessions to inspect. Default 10. */
21
+ windowSize?: number;
22
+ }
23
+ export interface SessionSkipRateResult {
24
+ windowSessions: number;
25
+ completedRetrospectives: number;
26
+ skipRate: number;
27
+ oldestPendingSinceIso: string | null;
28
+ }
29
+ export declare function sessionRetrospectiveSkipRate(args: SessionSkipRateArgs, session: Session, accountId: string): Promise<SessionSkipRateResult>;
30
+ //# sourceMappingURL=session-retrospective-skip-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-retrospective-skip-rate.d.ts","sourceRoot":"","sources":["../../src/tools/session-retrospective-skip-rate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,uBAAuB,EAAE,MAAM,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,qBAAqB,CAAC,CAuHhC"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Session-retrospective skip-rate visibility tool.
3
+ *
4
+ * Called by the admin agent at session start. Returns the ratio of recent
5
+ * sessions that closed without calling `session-retrospective-mark-complete`
6
+ * — sessions where the typed-edge pass, learnings pass, and SOUL.md pass
7
+ * never ran. The admin agent surfaces the result to the operator if the
8
+ * rate is non-zero so closing via Sidebar Archive / tab-close becomes a
9
+ * visible cost rather than a silent one.
10
+ *
11
+ * Mechanism: count `:Conversation` nodes for this account in the recent
12
+ * window; count those whose corresponding session log contains the
13
+ * sentinel tool-use of `session-retrospective-mark-complete`. The ratio
14
+ * is the skip rate. Pending nodes (prose with `updatedAt` later than the
15
+ * most-recent completed retrospective) carry the deferred typed-edge work
16
+ * that will land at the next /end.
17
+ */
18
+ export async function sessionRetrospectiveSkipRate(args, session, accountId) {
19
+ const windowSize = Math.max(1, Math.min(args.windowSize ?? 10, 100));
20
+ // Conversations carry `retrospectiveCompletedAt` when the sentinel tool
21
+ // fired (set by the admin mark-complete tool through log-ingest +
22
+ // platform/ui session writer). When the property is null/missing, the
23
+ // retrospective was skipped.
24
+ // The oldest-pending bound is the earliest updatedAt across prose nodes
25
+ // for this account whose updatedAt is *after* the most-recent completed
26
+ // retrospective — i.e. nodes the next session's pass will pick up.
27
+ const cypher = `
28
+ CALL {
29
+ MATCH (c:Conversation {accountId: $accountId})
30
+ RETURN c
31
+ ORDER BY c.updatedAt DESC
32
+ LIMIT $windowSize
33
+ }
34
+ WITH collect(c) AS recent
35
+ WITH recent,
36
+ [c IN recent WHERE c.retrospectiveCompletedAt IS NOT NULL] AS done,
37
+ [c IN recent WHERE c.retrospectiveCompletedAt IS NOT NULL | c.retrospectiveCompletedAt]
38
+ AS doneTimestamps
39
+ WITH size(recent) AS windowSessions,
40
+ size(done) AS completedRetrospectives,
41
+ CASE WHEN size(doneTimestamps) > 0
42
+ THEN apoc.coll.max(doneTimestamps)
43
+ ELSE null END AS lastCompletedAt
44
+ OPTIONAL MATCH (n)
45
+ WHERE n.accountId = $accountId
46
+ AND ANY(l IN labels(n) WHERE l IN $proseLabels)
47
+ AND (lastCompletedAt IS NULL OR n.updatedAt > lastCompletedAt)
48
+ WITH windowSessions, completedRetrospectives, min(n.updatedAt) AS oldestPending
49
+ RETURN windowSessions, completedRetrospectives, oldestPending
50
+ `;
51
+ // APOC is bundled with the Maxy Neo4j install. If unavailable, fall back to
52
+ // a pure-Cypher variant that uses reduce() for max.
53
+ const fallbackCypher = `
54
+ CALL {
55
+ MATCH (c:Conversation {accountId: $accountId})
56
+ RETURN c
57
+ ORDER BY c.updatedAt DESC
58
+ LIMIT $windowSize
59
+ }
60
+ WITH collect(c) AS recent
61
+ WITH recent,
62
+ [c IN recent WHERE c.retrospectiveCompletedAt IS NOT NULL] AS done,
63
+ [c IN recent WHERE c.retrospectiveCompletedAt IS NOT NULL | c.retrospectiveCompletedAt]
64
+ AS doneTimestamps
65
+ WITH size(recent) AS windowSessions,
66
+ size(done) AS completedRetrospectives,
67
+ reduce(acc = null, ts IN doneTimestamps |
68
+ CASE WHEN acc IS NULL OR ts > acc THEN ts ELSE acc END)
69
+ AS lastCompletedAt
70
+ OPTIONAL MATCH (n)
71
+ WHERE n.accountId = $accountId
72
+ AND ANY(l IN labels(n) WHERE l IN $proseLabels)
73
+ AND (lastCompletedAt IS NULL OR n.updatedAt > lastCompletedAt)
74
+ WITH windowSessions, completedRetrospectives, min(n.updatedAt) AS oldestPending
75
+ RETURN windowSessions, completedRetrospectives, oldestPending
76
+ `;
77
+ const proseLabels = [
78
+ "Message",
79
+ "Page",
80
+ "Meeting",
81
+ "Email",
82
+ "Idea",
83
+ "Note",
84
+ "Post",
85
+ "Report",
86
+ ];
87
+ let records;
88
+ try {
89
+ const result = await session.run(cypher, {
90
+ accountId,
91
+ windowSize,
92
+ proseLabels,
93
+ });
94
+ records = result.records;
95
+ }
96
+ catch {
97
+ const result = await session.run(fallbackCypher, {
98
+ accountId,
99
+ windowSize,
100
+ proseLabels,
101
+ });
102
+ records = result.records;
103
+ }
104
+ if (records.length === 0) {
105
+ return {
106
+ windowSessions: 0,
107
+ completedRetrospectives: 0,
108
+ skipRate: 0,
109
+ oldestPendingSinceIso: null,
110
+ };
111
+ }
112
+ const r = records[0];
113
+ const windowSessions = Number(r.get("windowSessions") ?? 0);
114
+ const completedRetrospectives = Number(r.get("completedRetrospectives") ?? 0);
115
+ const oldestPending = r.get("oldestPending");
116
+ const skipRate = windowSessions > 0
117
+ ? Number(((windowSessions - completedRetrospectives) / windowSessions).toFixed(3))
118
+ : 0;
119
+ const accountShort = accountId.slice(0, 8);
120
+ const oldestPendingIso = oldestPending == null ? null : String(oldestPending);
121
+ console.log(`[session-retrospective-skip-rate] accountId=${accountShort}… window=${windowSessions} completed=${completedRetrospectives} skip-rate=${skipRate} oldest-pending=${oldestPendingIso ?? "none"}`);
122
+ return {
123
+ windowSessions,
124
+ completedRetrospectives,
125
+ skipRate,
126
+ oldestPendingSinceIso: oldestPendingIso,
127
+ };
128
+ }
129
+ //# sourceMappingURL=session-retrospective-skip-rate.js.map