@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,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-query expansion via Haiku (Task 308).
|
|
3
|
+
*
|
|
4
|
+
* Generates 3-5 paraphrases of the user query so the hybrid pipeline runs
|
|
5
|
+
* each paraphrase against vector + BM25 in parallel and unions the
|
|
6
|
+
* candidate sets before fusion. Recovers recall on terse queries where the
|
|
7
|
+
* embedding's nearest-neighbour is unstable.
|
|
8
|
+
*
|
|
9
|
+
* The expansion runs through `@rubytech/oauth-llm` against Claude Haiku —
|
|
10
|
+
* the same OAuth-bearer path the admin classifiers use; no new SDK
|
|
11
|
+
* dependency and no Anthropic-API-key path. Cache is in-process, keyed by
|
|
12
|
+
* (accountId, normalisedQuery, retrievalClass), with a 1-hour TTL.
|
|
13
|
+
*
|
|
14
|
+
* Graceful degrade: if the LLM call fails, returns just the original
|
|
15
|
+
* query. The caller's hybrid pipeline still runs — just on one query
|
|
16
|
+
* instead of N — so missing paraphrases are a recall regression, not a
|
|
17
|
+
* fault.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { callOauthLlm } from "../../oauth-llm/dist/index.js";
|
|
21
|
+
|
|
22
|
+
const CACHE_TTL_MS = 60 * 60 * 1000;
|
|
23
|
+
const HAIKU_MODEL = "claude-haiku-4-5";
|
|
24
|
+
const MAX_PARAPHRASES = 5;
|
|
25
|
+
|
|
26
|
+
interface CacheEntry {
|
|
27
|
+
expansions: string[];
|
|
28
|
+
expiresAt: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const cache = new Map<string, CacheEntry>();
|
|
32
|
+
|
|
33
|
+
export function clearExpansionCache(): void {
|
|
34
|
+
cache.clear();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ExpandQueryParams {
|
|
38
|
+
query: string;
|
|
39
|
+
retrievalClass?: string;
|
|
40
|
+
accountId?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Override the cache or skip it entirely (useful in tests). When
|
|
43
|
+
* `false`, the cache is read but never written; when `true` (default),
|
|
44
|
+
* both reads and writes are active.
|
|
45
|
+
*/
|
|
46
|
+
useCache?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Return `[query, ...paraphrases]` (original always first). On LLM
|
|
51
|
+
* failure returns `[query]` only and writes a single stderr line so the
|
|
52
|
+
* operator can see expansion is degrading without breaking search.
|
|
53
|
+
*/
|
|
54
|
+
export async function expandQuery(
|
|
55
|
+
params: ExpandQueryParams,
|
|
56
|
+
): Promise<string[]> {
|
|
57
|
+
const { query, retrievalClass = "general", accountId = "", useCache = true } = params;
|
|
58
|
+
const normalised = query.trim().toLowerCase();
|
|
59
|
+
const cacheKey = `${accountId}::${retrievalClass}::${normalised}`;
|
|
60
|
+
|
|
61
|
+
if (useCache) {
|
|
62
|
+
const hit = cache.get(cacheKey);
|
|
63
|
+
if (hit && hit.expiresAt > Date.now()) {
|
|
64
|
+
return [query, ...hit.expansions];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const systemPrompt = [
|
|
69
|
+
"You generate paraphrases of a user query for hybrid graph search.",
|
|
70
|
+
"Return 3-5 alternative phrasings that preserve the user's intent but vary the surface form (synonyms, word order, entity-vs-attribute framing).",
|
|
71
|
+
"Do not change the entity names. Do not invent constraints the user did not state.",
|
|
72
|
+
"Output ONLY a JSON object via the provided tool; never free text.",
|
|
73
|
+
].join("\n");
|
|
74
|
+
|
|
75
|
+
const classHint = retrievalClass === "general"
|
|
76
|
+
? ""
|
|
77
|
+
: `The retrieval class is '${retrievalClass}' — tailor paraphrases accordingly.`;
|
|
78
|
+
const userMessage = [
|
|
79
|
+
`Original query: ${query}`,
|
|
80
|
+
classHint,
|
|
81
|
+
].filter((s) => s.length > 0).join("\n");
|
|
82
|
+
|
|
83
|
+
const result = await callOauthLlm({
|
|
84
|
+
model: HAIKU_MODEL,
|
|
85
|
+
system: systemPrompt,
|
|
86
|
+
userMessage,
|
|
87
|
+
maxTokens: 512,
|
|
88
|
+
timeoutMs: 15_000,
|
|
89
|
+
tools: [
|
|
90
|
+
{
|
|
91
|
+
name: "emit_paraphrases",
|
|
92
|
+
description: "Emit 3-5 paraphrases of the user query.",
|
|
93
|
+
input_schema: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: {
|
|
96
|
+
paraphrases: {
|
|
97
|
+
type: "array",
|
|
98
|
+
items: { type: "string" },
|
|
99
|
+
minItems: 3,
|
|
100
|
+
maxItems: MAX_PARAPHRASES,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
required: ["paraphrases"],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
toolChoiceName: "emit_paraphrases",
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (result.kind !== "ok-tool") {
|
|
111
|
+
process.stderr.write(
|
|
112
|
+
`[graph-search:expansion-failed] cause=${result.kind === "fallback" ? result.cause : "unexpected"} query=${query.slice(0, 60).replace(/\s+/g, " ")}\n`,
|
|
113
|
+
);
|
|
114
|
+
return [query];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const raw = (result.input as { paraphrases?: unknown }).paraphrases;
|
|
118
|
+
if (!Array.isArray(raw)) return [query];
|
|
119
|
+
const expansions = raw
|
|
120
|
+
.filter((p): p is string => typeof p === "string" && p.trim().length > 0)
|
|
121
|
+
.map((p) => p.trim())
|
|
122
|
+
.slice(0, MAX_PARAPHRASES);
|
|
123
|
+
|
|
124
|
+
if (useCache) {
|
|
125
|
+
cache.set(cacheKey, { expansions, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return [query, ...expansions];
|
|
129
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieval-class routing (Task 308).
|
|
3
|
+
*
|
|
4
|
+
* Consumes the `retrievalClass` field produced by Task 304's
|
|
5
|
+
* gateway-classifier (landed) and returns a per-class index plan: which
|
|
6
|
+
* labels to filter to, which fusion weights to favour, and whether to
|
|
7
|
+
* skip the lookup entirely.
|
|
8
|
+
*
|
|
9
|
+
* No new Haiku call here — this module is a pure deterministic mapping
|
|
10
|
+
* over the gateway's existing classification.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export type RetrievalClass =
|
|
14
|
+
| "entity"
|
|
15
|
+
| "temporal"
|
|
16
|
+
| "event"
|
|
17
|
+
| "general"
|
|
18
|
+
| "none";
|
|
19
|
+
|
|
20
|
+
export interface IndexPlan {
|
|
21
|
+
/** When true, the caller short-circuits and returns no results. */
|
|
22
|
+
skip: boolean;
|
|
23
|
+
/** Restrict vector + BM25 to these labels. Undefined means no filter. */
|
|
24
|
+
labelFilter?: string[];
|
|
25
|
+
/** Weight on the vector half of the (weighted-sum) fusion. */
|
|
26
|
+
vectorWeight: number;
|
|
27
|
+
/** Weight on the BM25 half of the (weighted-sum) fusion. */
|
|
28
|
+
bm25Weight: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Return the index plan for a retrieval class. Unknown values fall back
|
|
33
|
+
* to `general` rather than throwing — the routing is a soft hint and the
|
|
34
|
+
* caller's old behaviour is the safe default.
|
|
35
|
+
*/
|
|
36
|
+
export function pickIndexMix(retrievalClass: string | undefined): IndexPlan {
|
|
37
|
+
switch (retrievalClass) {
|
|
38
|
+
case "entity":
|
|
39
|
+
return {
|
|
40
|
+
skip: false,
|
|
41
|
+
labelFilter: ["Person", "Company", "Concept"],
|
|
42
|
+
vectorWeight: 0.85,
|
|
43
|
+
bm25Weight: 0.15,
|
|
44
|
+
};
|
|
45
|
+
case "temporal":
|
|
46
|
+
return {
|
|
47
|
+
skip: false,
|
|
48
|
+
labelFilter: ["Event"],
|
|
49
|
+
vectorWeight: 0.2,
|
|
50
|
+
bm25Weight: 0.8,
|
|
51
|
+
};
|
|
52
|
+
case "event":
|
|
53
|
+
return {
|
|
54
|
+
skip: false,
|
|
55
|
+
labelFilter: ["Event"],
|
|
56
|
+
vectorWeight: 0,
|
|
57
|
+
bm25Weight: 1,
|
|
58
|
+
};
|
|
59
|
+
case "none":
|
|
60
|
+
return { skip: true, vectorWeight: 0, bm25Weight: 0 };
|
|
61
|
+
case "general":
|
|
62
|
+
default:
|
|
63
|
+
if (retrievalClass && retrievalClass !== "general") {
|
|
64
|
+
process.stderr.write(
|
|
65
|
+
`[graph-search:unknown-class] rc=${retrievalClass} fallback=general\n`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return { skip: false, vectorWeight: 0.7, bm25Weight: 0.3 };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reciprocal Rank Fusion (Task 308).
|
|
3
|
+
*
|
|
4
|
+
* Replaces the weighted-sum fusion (`0.7 * vector + 0.3 * bm25_norm`) when
|
|
5
|
+
* the `MAXY_GS_RRF` flag is on. RRF is robust to score-distribution drift
|
|
6
|
+
* between the vector and BM25 indexes — it sums `1 / (k + rank_i)` for each
|
|
7
|
+
* node across the ranked lists it appears in. Higher rank (lower index) →
|
|
8
|
+
* larger contribution; the constant `k` (default 60, per the gbrain
|
|
9
|
+
* default) smooths the long tail.
|
|
10
|
+
*
|
|
11
|
+
* The weighted-sum baseline stays as the fallback so flag flips are
|
|
12
|
+
* reversible without code edits.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface FusionCandidate {
|
|
16
|
+
nodeId: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Fuse multiple ranked lists by Reciprocal Rank Fusion.
|
|
21
|
+
*
|
|
22
|
+
* Each input list is treated as already sorted (best first). For each
|
|
23
|
+
* candidate, score = sum over lists of `1 / (k + rank)`. Returns the union
|
|
24
|
+
* sorted by descending RRF score.
|
|
25
|
+
*
|
|
26
|
+
* Ties on RRF score are broken by the candidate's earliest appearance
|
|
27
|
+
* across the input lists (first-seen wins), which is deterministic given
|
|
28
|
+
* a deterministic input order.
|
|
29
|
+
*/
|
|
30
|
+
export function fuseRrf<T extends FusionCandidate>(
|
|
31
|
+
rankedLists: T[][],
|
|
32
|
+
k = 60,
|
|
33
|
+
): Array<T & { rrfScore: number }> {
|
|
34
|
+
const scores = new Map<string, number>();
|
|
35
|
+
const representatives = new Map<string, T>();
|
|
36
|
+
const firstSeen = new Map<string, number>();
|
|
37
|
+
let order = 0;
|
|
38
|
+
|
|
39
|
+
for (const list of rankedLists) {
|
|
40
|
+
for (let rank = 0; rank < list.length; rank++) {
|
|
41
|
+
const candidate = list[rank];
|
|
42
|
+
const contribution = 1 / (k + rank);
|
|
43
|
+
const prior = scores.get(candidate.nodeId) ?? 0;
|
|
44
|
+
scores.set(candidate.nodeId, prior + contribution);
|
|
45
|
+
if (!representatives.has(candidate.nodeId)) {
|
|
46
|
+
representatives.set(candidate.nodeId, candidate);
|
|
47
|
+
firstSeen.set(candidate.nodeId, order++);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const out: Array<T & { rrfScore: number }> = [];
|
|
53
|
+
for (const [nodeId, rrfScore] of scores) {
|
|
54
|
+
const rep = representatives.get(nodeId)!;
|
|
55
|
+
out.push({ ...rep, rrfScore });
|
|
56
|
+
}
|
|
57
|
+
out.sort((a, b) => {
|
|
58
|
+
if (b.rrfScore !== a.rrfScore) return b.rrfScore - a.rrfScore;
|
|
59
|
+
return (firstSeen.get(a.nodeId)! - firstSeen.get(b.nodeId)!);
|
|
60
|
+
});
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* obsidian-parser — pure functions for parsing an Obsidian vault on disk.
|
|
3
|
+
*
|
|
4
|
+
* No DB access, no MCP, no I/O side effects other than fs reads. Composed by
|
|
5
|
+
* the `obsidian-vault-import` MCP tool in the memory plugin.
|
|
6
|
+
*
|
|
7
|
+
* Doctrine: every regex is linear-time (no catastrophic backtracking), every
|
|
8
|
+
* function is deterministic. The agent never calls this code; the server does.
|
|
9
|
+
*/
|
|
10
|
+
export interface ParsedPage {
|
|
11
|
+
/** Path relative to the vault root, with forward slashes. The MERGE natural key. */
|
|
12
|
+
obsidianPath: string;
|
|
13
|
+
/** Filename without `.md` — the default wikilink target form. */
|
|
14
|
+
title: string;
|
|
15
|
+
/** Aliases declared in frontmatter (`aliases:` array). */
|
|
16
|
+
aliases: string[];
|
|
17
|
+
/** Tag tokens (without `#`), unioned from frontmatter `tags:` and inline `#tag` body matches. */
|
|
18
|
+
tags: string[];
|
|
19
|
+
/** Body text (frontmatter stripped). Stored verbatim. */
|
|
20
|
+
body: string;
|
|
21
|
+
/** SHA-256 of the on-disk file bytes — idempotency key. */
|
|
22
|
+
contentHash: string;
|
|
23
|
+
/** Detected daily-note date (YYYY-MM-DD) or null. */
|
|
24
|
+
noteDate: string | null;
|
|
25
|
+
/** Wikilinks emitted by this page. */
|
|
26
|
+
wikilinks: WikilinkOccurrence[];
|
|
27
|
+
/** Embedded attachments declared via `![[file.png]]` or ``. */
|
|
28
|
+
attachments: AttachmentOccurrence[];
|
|
29
|
+
/** Last-modified timestamp from fs stat — used for modified-since filtering. */
|
|
30
|
+
mtimeMs: number;
|
|
31
|
+
}
|
|
32
|
+
export interface WikilinkOccurrence {
|
|
33
|
+
/** The exact text inside `[[…]]` before any alias `|`. May contain `#anchor`. */
|
|
34
|
+
rawTarget: string;
|
|
35
|
+
/** Target after stripping `#anchor`. */
|
|
36
|
+
target: string;
|
|
37
|
+
/** Anchor portion (after `#`) or null. */
|
|
38
|
+
anchor: string | null;
|
|
39
|
+
/** Alias text after `|`, or null when absent. The display text. */
|
|
40
|
+
alias: string | null;
|
|
41
|
+
}
|
|
42
|
+
export interface AttachmentOccurrence {
|
|
43
|
+
/** Path as written in the markdown. May be relative to the page or to the vault root. */
|
|
44
|
+
rawPath: string;
|
|
45
|
+
/** Resolved path relative to vault root, normalised. May not exist on disk yet. */
|
|
46
|
+
resolvedPath: string;
|
|
47
|
+
/** Display alt text (from `` form) or null for embed-syntax `![[file]]`. */
|
|
48
|
+
alt: string | null;
|
|
49
|
+
}
|
|
50
|
+
export interface VaultWalkOptions {
|
|
51
|
+
/** Filter: only pages whose path starts with one of these folder prefixes. */
|
|
52
|
+
folderPrefixes?: string[];
|
|
53
|
+
/** Filter: only pages whose tags intersect with this set. */
|
|
54
|
+
tagFilter?: string[];
|
|
55
|
+
/** Filter: only pages modified at or after this epoch-ms. */
|
|
56
|
+
modifiedSinceMs?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Walk the vault directory and return one ParsedPage per `.md` file that
|
|
60
|
+
* survives the optional filter set.
|
|
61
|
+
*
|
|
62
|
+
* Path-traversal defence: paths containing `..` segments are rejected at
|
|
63
|
+
* entry. The `vaultRoot` passed in is assumed to have already been verified
|
|
64
|
+
* by the caller as living under the account's archive directory.
|
|
65
|
+
*/
|
|
66
|
+
export declare function walkVault(vaultRoot: string, opts?: VaultWalkOptions): Promise<ParsedPage[]>;
|
|
67
|
+
export declare function parsePage(obsidianPath: string, bytes: Buffer, mtimeMs: number): ParsedPage;
|
|
68
|
+
interface ParsedFrontmatter {
|
|
69
|
+
raw: string;
|
|
70
|
+
/** Map of top-level key → raw value string. Arrays are returned as the
|
|
71
|
+
* block following the key (caller parses per-key). */
|
|
72
|
+
fields: Map<string, string>;
|
|
73
|
+
}
|
|
74
|
+
export declare function splitFrontmatter(text: string): {
|
|
75
|
+
frontmatter: ParsedFrontmatter;
|
|
76
|
+
body: string;
|
|
77
|
+
};
|
|
78
|
+
export declare function normalizeTag(tag: string): string;
|
|
79
|
+
export declare function detectDailyNote(obsidianPath: string, fm: ParsedFrontmatter): string | null;
|
|
80
|
+
export interface VaultIndex {
|
|
81
|
+
/** Lowercased title (without `.md`) → set of `obsidianPath` candidates. */
|
|
82
|
+
byTitle: Map<string, string[]>;
|
|
83
|
+
/** Lowercased alias → set of `obsidianPath` candidates. */
|
|
84
|
+
byAlias: Map<string, string[]>;
|
|
85
|
+
}
|
|
86
|
+
export declare function buildVaultIndex(pages: ParsedPage[]): VaultIndex;
|
|
87
|
+
/**
|
|
88
|
+
* Look up a wikilink target inside the vault, returning candidate page paths.
|
|
89
|
+
*
|
|
90
|
+
* The target is matched against (a) page titles and (b) declared aliases.
|
|
91
|
+
* Path-form targets (`Folder/Page`) match directly. Returns an empty array
|
|
92
|
+
* when no intra-vault candidate exists — the caller falls through to entity
|
|
93
|
+
* resolution.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resolveWikilinkInVault(target: string, index: VaultIndex, knownPaths: Set<string>): string[];
|
|
96
|
+
export declare function makeImportId(): string;
|
|
97
|
+
export {};
|
|
98
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,MAAM,WAAW,UAAU;IACzB,oFAAoF;IACpF,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iGAAiG;IACjG,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,sCAAsC;IACtC,SAAS,EAAE,kBAAkB,EAAE,CAAC;IAChC,+EAA+E;IAC/E,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,mEAAmE;IACnE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,YAAY,EAAE,MAAM,CAAC;IACrB,wFAAwF;IACxF,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAC7B,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,UAAU,EAAE,CAAC,CA6EvB;AAoBD,wBAAgB,SAAS,CACvB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,UAAU,CA6BZ;AAMD,UAAU,iBAAiB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ;2DACuD;IACvD,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAID,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,GACX;IAAE,WAAW,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAoDlD;AAiKD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMhD;AAmBD,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,EAAE,EAAE,iBAAiB,GACpB,MAAM,GAAG,IAAI,CAyBf;AAMD,MAAM,WAAW,UAAU;IACzB,2EAA2E;IAC3E,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/B,2DAA2D;IAC3D,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAChC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,UAAU,CAU/D;AAWD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACtB,MAAM,EAAE,CAmBV;AAMD,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
|