@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.
- package/dist/__tests__/installer-specialist-registration.test.js +5 -40
- package/dist/index.js +24 -28
- package/dist/specialist-registration.js +0 -15
- package/package.json +1 -1
- package/payload/platform/config/brand.json +1 -1
- package/payload/platform/lib/graph-search/dist/boosts.d.ts +39 -0
- package/payload/platform/lib/graph-search/dist/boosts.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/boosts.js +57 -0
- package/payload/platform/lib/graph-search/dist/boosts.js.map +1 -0
- package/payload/platform/lib/graph-search/dist/dedup.d.ts +29 -0
- package/payload/platform/lib/graph-search/dist/dedup.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/dedup.js +97 -0
- package/payload/platform/lib/graph-search/dist/dedup.js.map +1 -0
- package/payload/platform/lib/graph-search/dist/index.d.ts +70 -8
- package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-search/dist/index.js +323 -62
- package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-search/dist/query-expansion.d.ts +37 -0
- package/payload/platform/lib/graph-search/dist/query-expansion.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/query-expansion.js +101 -0
- package/payload/platform/lib/graph-search/dist/query-expansion.js.map +1 -0
- package/payload/platform/lib/graph-search/dist/route.d.ts +29 -0
- package/payload/platform/lib/graph-search/dist/route.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/route.js +53 -0
- package/payload/platform/lib/graph-search/dist/route.js.map +1 -0
- package/payload/platform/lib/graph-search/dist/rrf-fusion.d.ts +31 -0
- package/payload/platform/lib/graph-search/dist/rrf-fusion.d.ts.map +1 -0
- package/payload/platform/lib/graph-search/dist/rrf-fusion.js +57 -0
- package/payload/platform/lib/graph-search/dist/rrf-fusion.js.map +1 -0
- package/payload/platform/lib/graph-search/src/__tests__/boosts.test.ts +59 -0
- package/payload/platform/lib/graph-search/src/__tests__/dedup.test.ts +83 -0
- package/payload/platform/lib/graph-search/src/__tests__/query-expansion.test.ts +96 -0
- package/payload/platform/lib/graph-search/src/__tests__/route.test.ts +62 -0
- package/payload/platform/lib/graph-search/src/__tests__/rrf-fusion.test.ts +39 -0
- package/payload/platform/lib/graph-search/src/boosts.ts +61 -0
- package/payload/platform/lib/graph-search/src/dedup.ts +108 -0
- package/payload/platform/lib/graph-search/src/index.ts +407 -65
- package/payload/platform/lib/graph-search/src/query-expansion.ts +129 -0
- package/payload/platform/lib/graph-search/src/route.ts +70 -0
- package/payload/platform/lib/graph-search/src/rrf-fusion.ts +62 -0
- package/payload/platform/lib/obsidian-parser/dist/index.d.ts +98 -0
- package/payload/platform/lib/obsidian-parser/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/obsidian-parser/dist/index.js +480 -0
- package/payload/platform/lib/obsidian-parser/dist/index.js.map +1 -0
- package/payload/platform/lib/obsidian-parser/src/index.ts +572 -0
- package/payload/platform/lib/obsidian-parser/tsconfig.json +8 -0
- package/payload/platform/neo4j/schema.cypher +86 -1
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/.claude-plugin/marketplace.json +10 -0
- package/payload/platform/plugins/admin/hooks/session-end-retrospective.sh +4 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js +6 -4
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/internals.md +36 -10
- package/payload/platform/plugins/docs/references/neo4j.md +63 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
- package/payload/platform/plugins/docs/references/session-retrospective.md +11 -4
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js +2 -2
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js.map +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +3 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js +203 -7
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.js +25 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-property.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.js +70 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +50 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts +33 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js +99 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts +21 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js +24 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.d.ts +13 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.js +151 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts +55 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js +290 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-pass.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts +19 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js +102 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js +18 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js +5 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +64 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +258 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +14 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +29 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +22 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts +31 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +182 -50
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +76 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.d.ts +127 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.js +477 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts +30 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js +129 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/scripts/backfill-typed-edges.ts +72 -0
- package/payload/platform/plugins/memory/mcp/scripts/generate-edge-docs.ts +75 -0
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +5 -0
- package/payload/platform/plugins/memory/references/schema-base.md +44 -0
- package/payload/platform/plugins/notion-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/notion-import/PLUGIN.md +27 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/SKILL.md +110 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/attachments.md +55 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/databases.md +81 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/page-tree.md +61 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/workspace-export.md +41 -0
- package/payload/platform/plugins/obsidian-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/obsidian-import/PLUGIN.md +39 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/SKILL.md +92 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/attachments.md +80 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/daily-notes.md +31 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/vault-structure.md +46 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/wikilinks.md +70 -0
- package/payload/platform/scripts/seed-neo4j.sh +49 -0
- package/payload/platform/templates/agents/admin/IDENTITY.md +4 -3
- package/payload/platform/templates/specialists/agents/database-operator.md +21 -1
- package/payload/server/{adminuser-self-heal-QAWOZ3JV.js → adminuser-self-heal-YC47O34W.js} +2 -1
- package/payload/server/{chunk-JSBRDJBE.js → chunk-HYQNUVGO.js} +9 -1
- package/payload/server/{chunk-HCYM5FLU.js → chunk-T3DJD5QR.js} +3 -1
- package/payload/server/maxy-edge.js +3 -2
- package/payload/server/public/assets/{admin-FcRHAL-3.js → admin-BM9aXUUh.js} +1 -1
- package/payload/server/public/assets/{data-Ds37mflX.js → data-Ds2ECtfP.js} +1 -1
- package/payload/server/public/assets/{graph-CmWRhaiS.js → graph-BzxObKMM.js} +1 -1
- package/payload/server/public/assets/{graph-labels-Ch2r00Gt.js → graph-labels-Dwgep_nr.js} +1 -1
- package/payload/server/public/assets/{page-BcHhJXUt.js → page-BlkBsjP2.js} +1 -1
- package/payload/server/public/assets/page-h_SA5SVQ.js +51 -0
- package/payload/server/public/data.html +3 -3
- package/payload/server/public/graph.html +3 -3
- package/payload/server/public/index.html +4 -4
- package/payload/server/server.js +803 -76
- 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
|
package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts.map
ADDED
|
@@ -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
|