@gethmy/mcp 2.2.2 → 2.2.4
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/cli.js +201 -18
- package/dist/index.js +165 -17
- package/dist/lib/context-assembly.js +218 -26
- package/dist/lib/skills.js +25 -1
- package/dist/lib/tui/setup.js +11 -0
- package/package.json +2 -2
- package/src/context-assembly.ts +267 -26
- package/src/skills.ts +25 -1
- package/src/tui/setup.ts +11 -0
package/dist/cli.js
CHANGED
|
@@ -9163,6 +9163,7 @@ __export(exports_context_assembly, {
|
|
|
9163
9163
|
mapToContextEntity: () => mapToContextEntity,
|
|
9164
9164
|
getSessionAssemblyId: () => getSessionAssemblyId,
|
|
9165
9165
|
getCachedManifest: () => getCachedManifest,
|
|
9166
|
+
expandQuery: () => expandQuery,
|
|
9166
9167
|
computeRelevanceScore: () => computeRelevanceScore,
|
|
9167
9168
|
cacheManifest: () => cacheManifest,
|
|
9168
9169
|
assembleContext: () => assembleContext
|
|
@@ -9201,7 +9202,45 @@ function truncateContent(content, maxTokens) {
|
|
|
9201
9202
|
}
|
|
9202
9203
|
return { text: result, truncated: true };
|
|
9203
9204
|
}
|
|
9204
|
-
function
|
|
9205
|
+
function escapeRegex2(str) {
|
|
9206
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9207
|
+
}
|
|
9208
|
+
function expandQuery(taskContext) {
|
|
9209
|
+
const queries = [taskContext];
|
|
9210
|
+
const lowerQueries = [taskContext.toLowerCase()];
|
|
9211
|
+
const words = taskContext.toLowerCase().split(/\W+/).filter((w) => w.length > 2);
|
|
9212
|
+
const expandableWords = words.filter((w) => QUERY_SYNONYMS[w]);
|
|
9213
|
+
for (const word of expandableWords) {
|
|
9214
|
+
const synonyms = QUERY_SYNONYMS[word];
|
|
9215
|
+
if (!synonyms)
|
|
9216
|
+
continue;
|
|
9217
|
+
const variation = taskContext.replace(new RegExp(`\\b${escapeRegex2(word)}\\b`, "gi"), synonyms[0]);
|
|
9218
|
+
const lowerVariation = variation.toLowerCase();
|
|
9219
|
+
if (lowerVariation !== taskContext.toLowerCase() && !lowerQueries.includes(lowerVariation)) {
|
|
9220
|
+
queries.push(variation);
|
|
9221
|
+
lowerQueries.push(lowerVariation);
|
|
9222
|
+
}
|
|
9223
|
+
if (queries.length >= MAX_QUERY_VARIATIONS)
|
|
9224
|
+
break;
|
|
9225
|
+
}
|
|
9226
|
+
if (words.length >= 3) {
|
|
9227
|
+
const keyPhrases = words.filter((w) => ![
|
|
9228
|
+
"the",
|
|
9229
|
+
"and",
|
|
9230
|
+
"for",
|
|
9231
|
+
"with",
|
|
9232
|
+
"this",
|
|
9233
|
+
"that",
|
|
9234
|
+
"from",
|
|
9235
|
+
"into"
|
|
9236
|
+
].includes(w)).slice(0, 4).join(" ");
|
|
9237
|
+
if (!lowerQueries.includes(keyPhrases)) {
|
|
9238
|
+
queries.push(keyPhrases);
|
|
9239
|
+
}
|
|
9240
|
+
}
|
|
9241
|
+
return queries.slice(0, MAX_QUERY_VARIATIONS);
|
|
9242
|
+
}
|
|
9243
|
+
function computeRelevanceScore(entity, taskContext, cardLabels, graphRelations) {
|
|
9205
9244
|
const reasons = [];
|
|
9206
9245
|
let score = 0;
|
|
9207
9246
|
const hasRrfScore = entity.rrf_score !== undefined && entity.rrf_score > 0;
|
|
@@ -9260,7 +9299,23 @@ function computeRelevanceScore(entity, taskContext, cardLabels) {
|
|
|
9260
9299
|
score += 0.1;
|
|
9261
9300
|
reasons.push("procedure_boost");
|
|
9262
9301
|
}
|
|
9263
|
-
|
|
9302
|
+
if (graphRelations && graphRelations.length > 0) {
|
|
9303
|
+
const entityRelations = graphRelations.filter((r) => r.source_id === entity.id || r.target_id === entity.id);
|
|
9304
|
+
if (entityRelations.length > 0) {
|
|
9305
|
+
let bestBonus = 0;
|
|
9306
|
+
let bestRelType = "";
|
|
9307
|
+
for (const rel of entityRelations) {
|
|
9308
|
+
const bonus = RELATION_BONUSES[rel.relation_type] ?? 0.1;
|
|
9309
|
+
if (bonus > bestBonus) {
|
|
9310
|
+
bestBonus = bonus;
|
|
9311
|
+
bestRelType = rel.relation_type;
|
|
9312
|
+
}
|
|
9313
|
+
}
|
|
9314
|
+
score += bestBonus;
|
|
9315
|
+
reasons.push(`graph_walk(${bestRelType})`);
|
|
9316
|
+
}
|
|
9317
|
+
}
|
|
9318
|
+
score = Math.max(0, Math.min(score, 1));
|
|
9264
9319
|
const tierWeight = TIER_WEIGHTS[entity.memory_tier];
|
|
9265
9320
|
score *= tierWeight;
|
|
9266
9321
|
return { score, reasons };
|
|
@@ -9272,7 +9327,11 @@ async function assembleContext(options) {
|
|
|
9272
9327
|
taskContext,
|
|
9273
9328
|
cardLabels = [],
|
|
9274
9329
|
tokenBudget = DEFAULT_TOKEN_BUDGET,
|
|
9275
|
-
client: client2
|
|
9330
|
+
client: client2,
|
|
9331
|
+
graphWalkEnabled = true,
|
|
9332
|
+
queryExpansionEnabled = true,
|
|
9333
|
+
enableLlmReranking = false,
|
|
9334
|
+
rerankFn
|
|
9276
9335
|
} = options;
|
|
9277
9336
|
const assemblyId = generateAssemblyId();
|
|
9278
9337
|
const manifest = {
|
|
@@ -9288,13 +9347,26 @@ async function assembleContext(options) {
|
|
|
9288
9347
|
reference: { count: 0, tokens: 0 }
|
|
9289
9348
|
}
|
|
9290
9349
|
};
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9350
|
+
const candidates = [];
|
|
9351
|
+
const queries = queryExpansionEnabled ? expandQuery(taskContext) : [taskContext];
|
|
9352
|
+
const searchResults = await Promise.allSettled(queries.map((query) => client2.searchMemoryEntities(workspaceId, query, {
|
|
9353
|
+
project_id: projectId,
|
|
9354
|
+
limit: 30
|
|
9355
|
+
})));
|
|
9356
|
+
const candidateIds = new Set;
|
|
9357
|
+
for (const result of searchResults) {
|
|
9358
|
+
if (result.status !== "fulfilled")
|
|
9359
|
+
continue;
|
|
9360
|
+
if (result.value.entities?.length > 0) {
|
|
9361
|
+
for (const raw of result.value.entities) {
|
|
9362
|
+
const entity = mapToContextEntity(raw);
|
|
9363
|
+
if (!candidateIds.has(entity.id)) {
|
|
9364
|
+
candidateIds.add(entity.id);
|
|
9365
|
+
candidates.push(entity);
|
|
9366
|
+
}
|
|
9367
|
+
}
|
|
9296
9368
|
}
|
|
9297
|
-
}
|
|
9369
|
+
}
|
|
9298
9370
|
if (candidates.length < 10 && projectId) {
|
|
9299
9371
|
try {
|
|
9300
9372
|
const listResult = await client2.listMemoryEntities({
|
|
@@ -9303,9 +9375,13 @@ async function assembleContext(options) {
|
|
|
9303
9375
|
limit: 30
|
|
9304
9376
|
});
|
|
9305
9377
|
if (listResult.entities?.length > 0) {
|
|
9306
|
-
const
|
|
9307
|
-
|
|
9308
|
-
|
|
9378
|
+
for (const raw of listResult.entities) {
|
|
9379
|
+
const entity = mapToContextEntity(raw);
|
|
9380
|
+
if (!candidateIds.has(entity.id)) {
|
|
9381
|
+
candidateIds.add(entity.id);
|
|
9382
|
+
candidates.push(entity);
|
|
9383
|
+
}
|
|
9384
|
+
}
|
|
9309
9385
|
}
|
|
9310
9386
|
} catch {}
|
|
9311
9387
|
}
|
|
@@ -9317,9 +9393,33 @@ async function assembleContext(options) {
|
|
|
9317
9393
|
limit: 20
|
|
9318
9394
|
});
|
|
9319
9395
|
if (wsResult.entities?.length > 0) {
|
|
9320
|
-
const
|
|
9321
|
-
|
|
9322
|
-
|
|
9396
|
+
for (const raw of wsResult.entities) {
|
|
9397
|
+
const entity = mapToContextEntity(raw);
|
|
9398
|
+
if (!candidateIds.has(entity.id)) {
|
|
9399
|
+
candidateIds.add(entity.id);
|
|
9400
|
+
candidates.push(entity);
|
|
9401
|
+
}
|
|
9402
|
+
}
|
|
9403
|
+
}
|
|
9404
|
+
} catch {}
|
|
9405
|
+
}
|
|
9406
|
+
let graphRelations = [];
|
|
9407
|
+
if (graphWalkEnabled && candidates.length > 0) {
|
|
9408
|
+
try {
|
|
9409
|
+
const seedCandidates = [...candidates].sort((a, b) => (b.rrf_score ?? 0) - (a.rrf_score ?? 0)).slice(0, GRAPH_WALK_SEED_COUNT);
|
|
9410
|
+
const seedIds = seedCandidates.map((c) => c.id);
|
|
9411
|
+
const walkResult = await discoverRelatedContext(client2, seedIds, GRAPH_WALK_MAX_DEPTH, GRAPH_WALK_MAX_ENTITIES, GRAPH_WALK_MIN_CONFIDENCE);
|
|
9412
|
+
graphRelations = walkResult.relations;
|
|
9413
|
+
const newEntityIds = walkResult.entities.filter((e) => !candidateIds.has(e.id)).map((e) => e.id);
|
|
9414
|
+
if (newEntityIds.length > 0) {
|
|
9415
|
+
const fetchResults = await Promise.allSettled(newEntityIds.map((id) => client2.getMemoryEntity(id)));
|
|
9416
|
+
for (const result of fetchResults) {
|
|
9417
|
+
if (result.status !== "fulfilled" || !result.value.entity)
|
|
9418
|
+
continue;
|
|
9419
|
+
const mapped = mapToContextEntity(result.value.entity);
|
|
9420
|
+
candidateIds.add(mapped.id);
|
|
9421
|
+
candidates.push(mapped);
|
|
9422
|
+
}
|
|
9323
9423
|
}
|
|
9324
9424
|
} catch {}
|
|
9325
9425
|
}
|
|
@@ -9331,10 +9431,31 @@ async function assembleContext(options) {
|
|
|
9331
9431
|
};
|
|
9332
9432
|
}
|
|
9333
9433
|
const scored = candidates.map((entity) => {
|
|
9334
|
-
const { score, reasons } = computeRelevanceScore(entity, taskContext, cardLabels);
|
|
9434
|
+
const { score, reasons } = computeRelevanceScore(entity, taskContext, cardLabels, graphRelations.length > 0 ? graphRelations : undefined);
|
|
9335
9435
|
return { entity, score, reasons };
|
|
9336
9436
|
});
|
|
9337
9437
|
scored.sort((a, b) => b.score - a.score);
|
|
9438
|
+
if (enableLlmReranking && rerankFn && scored.length >= RERANK_MIN_CANDIDATES) {
|
|
9439
|
+
const topN = scored.slice(0, RERANK_TOP_N);
|
|
9440
|
+
const scoreRange = topN[0].score - topN[topN.length - 1].score;
|
|
9441
|
+
if (scoreRange <= RERANK_CLUSTER_THRESHOLD) {
|
|
9442
|
+
try {
|
|
9443
|
+
const rerankCandidates = topN.map((s) => ({
|
|
9444
|
+
id: s.entity.id,
|
|
9445
|
+
title: s.entity.title,
|
|
9446
|
+
snippet: s.entity.content.slice(0, 200)
|
|
9447
|
+
}));
|
|
9448
|
+
const rerankedIds = await rerankFn(taskContext, rerankCandidates);
|
|
9449
|
+
const idOrder = new Map(rerankedIds.map((id, i) => [id, i]));
|
|
9450
|
+
topN.sort((a, b) => {
|
|
9451
|
+
const aIdx = idOrder.get(a.entity.id) ?? 999;
|
|
9452
|
+
const bIdx = idOrder.get(b.entity.id) ?? 999;
|
|
9453
|
+
return aIdx - bIdx;
|
|
9454
|
+
});
|
|
9455
|
+
scored.splice(0, topN.length, ...topN);
|
|
9456
|
+
} catch {}
|
|
9457
|
+
}
|
|
9458
|
+
}
|
|
9338
9459
|
const procedureBudget = Math.floor(tokenBudget * PROCEDURE_BUDGET_FRACTION);
|
|
9339
9460
|
const remainingBudget = tokenBudget - procedureBudget;
|
|
9340
9461
|
const tierBudgets = {
|
|
@@ -9646,7 +9767,7 @@ async function recordContextFeedback(client2, cardId, sessionStatus, progressPer
|
|
|
9646
9767
|
sessionAssemblyMap.delete(cardId);
|
|
9647
9768
|
return { adjusted };
|
|
9648
9769
|
}
|
|
9649
|
-
var DEFAULT_TOKEN_BUDGET = 4000, MAX_TOKENS_PER_ENTITY = 500, MIN_RELEVANCE_THRESHOLD = 0.1, TIER_WEIGHTS, PROCEDURE_BUDGET_FRACTION = 0.15, TIER_BUDGET_ALLOCATION, MIN_REFERENCE_SLOTS = 3, manifestCache, MAX_CACHE_SIZE = 50, sessionAssemblyMap, MAX_SESSION_MAP_SIZE = 100;
|
|
9770
|
+
var DEFAULT_TOKEN_BUDGET = 4000, MAX_TOKENS_PER_ENTITY = 500, MIN_RELEVANCE_THRESHOLD = 0.1, TIER_WEIGHTS, PROCEDURE_BUDGET_FRACTION = 0.15, TIER_BUDGET_ALLOCATION, MIN_REFERENCE_SLOTS = 3, GRAPH_WALK_MAX_DEPTH = 1, GRAPH_WALK_MAX_ENTITIES = 10, GRAPH_WALK_MIN_CONFIDENCE = 0.5, GRAPH_WALK_SEED_COUNT = 5, MAX_QUERY_VARIATIONS = 4, RERANK_CLUSTER_THRESHOLD = 0.05, RERANK_TOP_N = 10, RERANK_MIN_CANDIDATES = 5, RELATION_BONUSES, QUERY_SYNONYMS, manifestCache, MAX_CACHE_SIZE = 50, sessionAssemblyMap, MAX_SESSION_MAP_SIZE = 100;
|
|
9650
9771
|
var init_context_assembly = __esm(() => {
|
|
9651
9772
|
init_dist();
|
|
9652
9773
|
TIER_WEIGHTS = {
|
|
@@ -9659,6 +9780,33 @@ var init_context_assembly = __esm(() => {
|
|
|
9659
9780
|
episode: 0.3,
|
|
9660
9781
|
draft: 0.1
|
|
9661
9782
|
};
|
|
9783
|
+
RELATION_BONUSES = {
|
|
9784
|
+
depends_on: 0.15,
|
|
9785
|
+
resolved_by: 0.2,
|
|
9786
|
+
relates_to: 0.1,
|
|
9787
|
+
implements: 0.15,
|
|
9788
|
+
blocks: 0.15,
|
|
9789
|
+
references: 0.1,
|
|
9790
|
+
extends: 0.1,
|
|
9791
|
+
caused_by: 0.15
|
|
9792
|
+
};
|
|
9793
|
+
QUERY_SYNONYMS = {
|
|
9794
|
+
auth: ["authentication", "authorization", "session"],
|
|
9795
|
+
authentication: ["auth", "session", "sign-in"],
|
|
9796
|
+
login: ["sign-in", "authentication", "session"],
|
|
9797
|
+
bug: ["error", "issue", "defect", "problem"],
|
|
9798
|
+
error: ["exception", "failure", "issue"],
|
|
9799
|
+
fix: ["resolve", "patch", "repair", "correct"],
|
|
9800
|
+
deploy: ["deployment", "release", "ship", "publish"],
|
|
9801
|
+
test: ["testing", "spec", "assertion", "verify"],
|
|
9802
|
+
config: ["configuration", "settings", "setup"],
|
|
9803
|
+
db: ["database", "storage", "persistence"],
|
|
9804
|
+
database: ["storage", "persistence", "data store"],
|
|
9805
|
+
api: ["endpoint", "route", "service"],
|
|
9806
|
+
ui: ["frontend", "component", "view"],
|
|
9807
|
+
perf: ["performance", "speed", "latency"],
|
|
9808
|
+
performance: ["speed", "latency", "optimization"]
|
|
9809
|
+
};
|
|
9662
9810
|
manifestCache = new Map;
|
|
9663
9811
|
sessionAssemblyMap = new Map;
|
|
9664
9812
|
});
|
|
@@ -31103,7 +31251,7 @@ class HarmonyMCPServer {
|
|
|
31103
31251
|
// src/skills.ts
|
|
31104
31252
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
31105
31253
|
import { dirname } from "node:path";
|
|
31106
|
-
var SKILLS_VERSION = "
|
|
31254
|
+
var SKILLS_VERSION = "4";
|
|
31107
31255
|
var VERSION_MARKER_PREFIX = "<!-- skills-version:";
|
|
31108
31256
|
var HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
|
|
31109
31257
|
|
|
@@ -31297,6 +31445,30 @@ Do not start an agent session.
|
|
|
31297
31445
|
|
|
31298
31446
|
---
|
|
31299
31447
|
|
|
31448
|
+
## Step E: Auto-Detect Card for Implementation Tasks
|
|
31449
|
+
|
|
31450
|
+
**IMPORTANT:** This step applies when you are about to implement a plan, feature, or fix
|
|
31451
|
+
that was NOT started via /hmy. Before writing any code, check if the work maps to an
|
|
31452
|
+
existing Harmony card.
|
|
31453
|
+
|
|
31454
|
+
### When to run this check
|
|
31455
|
+
- User says "implement this plan", "build this feature", "fix this bug" (without /hmy)
|
|
31456
|
+
- You are about to execute a plan file
|
|
31457
|
+
- Skip if: the user already started with /hmy, or no Harmony MCP tools are available
|
|
31458
|
+
|
|
31459
|
+
### Detection steps
|
|
31460
|
+
1. Call \`harmony_search_cards\` with keywords from the plan title or task description
|
|
31461
|
+
2. If a card matches (same feature/fix described), tell the user:
|
|
31462
|
+
"This maps to card #N — starting a session to track progress."
|
|
31463
|
+
3. Call \`harmony_start_agent_session\` with \`moveToColumn: "In Progress"\`, \`addLabels: ["agent"]\`
|
|
31464
|
+
4. Track progress with \`harmony_update_agent_progress\` at milestones
|
|
31465
|
+
5. When done, call \`harmony_end_agent_session\` with \`status: "completed"\`, \`moveToColumn: "Review"\`
|
|
31466
|
+
|
|
31467
|
+
### If no match
|
|
31468
|
+
Proceed normally without a session. No action needed.
|
|
31469
|
+
|
|
31470
|
+
---
|
|
31471
|
+
|
|
31300
31472
|
## Key Tools Reference
|
|
31301
31473
|
|
|
31302
31474
|
**Cards:** \`harmony_get_card\`, \`harmony_get_card_by_short_id\`, \`harmony_search_cards\`, \`harmony_create_card\`, \`harmony_update_card\`, \`harmony_move_card\`, \`harmony_delete_card\`, \`harmony_assign_card\`
|
|
@@ -33477,6 +33649,17 @@ When given a card reference (e.g., #42 or a card name), follow this workflow:
|
|
|
33477
33649
|
7. Update progress periodically with \`harmony_update_agent_progress\`
|
|
33478
33650
|
8. When done, call \`harmony_end_agent_session\` and move to "Review"
|
|
33479
33651
|
|
|
33652
|
+
## Auto-Detect Card for Implementation Tasks
|
|
33653
|
+
|
|
33654
|
+
Before implementing a plan or feature, check if it maps to an existing Harmony card:
|
|
33655
|
+
|
|
33656
|
+
1. Use \`harmony_search_cards\` with keywords from the task description
|
|
33657
|
+
2. If a match is found, call \`harmony_start_agent_session\` (agentIdentifier: "claude-code", agentName: "Claude Code", moveToColumn: "In Progress", addLabels: ["agent"])
|
|
33658
|
+
3. Update progress with \`harmony_update_agent_progress\` at milestones
|
|
33659
|
+
4. When done, call \`harmony_end_agent_session\` with status: "completed", moveToColumn: "Review"
|
|
33660
|
+
|
|
33661
|
+
Skip if: work was already started with a card reference, or no matching card exists.
|
|
33662
|
+
|
|
33480
33663
|
## Available Harmony Tools
|
|
33481
33664
|
|
|
33482
33665
|
- \`harmony_get_card\`, \`harmony_get_card_by_short_id\`, \`harmony_search_cards\` - Find cards
|
package/dist/index.js
CHANGED
|
@@ -7070,6 +7070,7 @@ __export(exports_context_assembly, {
|
|
|
7070
7070
|
mapToContextEntity: () => mapToContextEntity,
|
|
7071
7071
|
getSessionAssemblyId: () => getSessionAssemblyId,
|
|
7072
7072
|
getCachedManifest: () => getCachedManifest,
|
|
7073
|
+
expandQuery: () => expandQuery,
|
|
7073
7074
|
computeRelevanceScore: () => computeRelevanceScore,
|
|
7074
7075
|
cacheManifest: () => cacheManifest,
|
|
7075
7076
|
assembleContext: () => assembleContext
|
|
@@ -7108,7 +7109,45 @@ function truncateContent(content, maxTokens) {
|
|
|
7108
7109
|
}
|
|
7109
7110
|
return { text: result, truncated: true };
|
|
7110
7111
|
}
|
|
7111
|
-
function
|
|
7112
|
+
function escapeRegex2(str) {
|
|
7113
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7114
|
+
}
|
|
7115
|
+
function expandQuery(taskContext) {
|
|
7116
|
+
const queries = [taskContext];
|
|
7117
|
+
const lowerQueries = [taskContext.toLowerCase()];
|
|
7118
|
+
const words = taskContext.toLowerCase().split(/\W+/).filter((w) => w.length > 2);
|
|
7119
|
+
const expandableWords = words.filter((w) => QUERY_SYNONYMS[w]);
|
|
7120
|
+
for (const word of expandableWords) {
|
|
7121
|
+
const synonyms = QUERY_SYNONYMS[word];
|
|
7122
|
+
if (!synonyms)
|
|
7123
|
+
continue;
|
|
7124
|
+
const variation = taskContext.replace(new RegExp(`\\b${escapeRegex2(word)}\\b`, "gi"), synonyms[0]);
|
|
7125
|
+
const lowerVariation = variation.toLowerCase();
|
|
7126
|
+
if (lowerVariation !== taskContext.toLowerCase() && !lowerQueries.includes(lowerVariation)) {
|
|
7127
|
+
queries.push(variation);
|
|
7128
|
+
lowerQueries.push(lowerVariation);
|
|
7129
|
+
}
|
|
7130
|
+
if (queries.length >= MAX_QUERY_VARIATIONS)
|
|
7131
|
+
break;
|
|
7132
|
+
}
|
|
7133
|
+
if (words.length >= 3) {
|
|
7134
|
+
const keyPhrases = words.filter((w) => ![
|
|
7135
|
+
"the",
|
|
7136
|
+
"and",
|
|
7137
|
+
"for",
|
|
7138
|
+
"with",
|
|
7139
|
+
"this",
|
|
7140
|
+
"that",
|
|
7141
|
+
"from",
|
|
7142
|
+
"into"
|
|
7143
|
+
].includes(w)).slice(0, 4).join(" ");
|
|
7144
|
+
if (!lowerQueries.includes(keyPhrases)) {
|
|
7145
|
+
queries.push(keyPhrases);
|
|
7146
|
+
}
|
|
7147
|
+
}
|
|
7148
|
+
return queries.slice(0, MAX_QUERY_VARIATIONS);
|
|
7149
|
+
}
|
|
7150
|
+
function computeRelevanceScore(entity, taskContext, cardLabels, graphRelations) {
|
|
7112
7151
|
const reasons = [];
|
|
7113
7152
|
let score = 0;
|
|
7114
7153
|
const hasRrfScore = entity.rrf_score !== undefined && entity.rrf_score > 0;
|
|
@@ -7167,7 +7206,23 @@ function computeRelevanceScore(entity, taskContext, cardLabels) {
|
|
|
7167
7206
|
score += 0.1;
|
|
7168
7207
|
reasons.push("procedure_boost");
|
|
7169
7208
|
}
|
|
7170
|
-
|
|
7209
|
+
if (graphRelations && graphRelations.length > 0) {
|
|
7210
|
+
const entityRelations = graphRelations.filter((r) => r.source_id === entity.id || r.target_id === entity.id);
|
|
7211
|
+
if (entityRelations.length > 0) {
|
|
7212
|
+
let bestBonus = 0;
|
|
7213
|
+
let bestRelType = "";
|
|
7214
|
+
for (const rel of entityRelations) {
|
|
7215
|
+
const bonus = RELATION_BONUSES[rel.relation_type] ?? 0.1;
|
|
7216
|
+
if (bonus > bestBonus) {
|
|
7217
|
+
bestBonus = bonus;
|
|
7218
|
+
bestRelType = rel.relation_type;
|
|
7219
|
+
}
|
|
7220
|
+
}
|
|
7221
|
+
score += bestBonus;
|
|
7222
|
+
reasons.push(`graph_walk(${bestRelType})`);
|
|
7223
|
+
}
|
|
7224
|
+
}
|
|
7225
|
+
score = Math.max(0, Math.min(score, 1));
|
|
7171
7226
|
const tierWeight = TIER_WEIGHTS[entity.memory_tier];
|
|
7172
7227
|
score *= tierWeight;
|
|
7173
7228
|
return { score, reasons };
|
|
@@ -7179,7 +7234,11 @@ async function assembleContext(options) {
|
|
|
7179
7234
|
taskContext,
|
|
7180
7235
|
cardLabels = [],
|
|
7181
7236
|
tokenBudget = DEFAULT_TOKEN_BUDGET,
|
|
7182
|
-
client: client2
|
|
7237
|
+
client: client2,
|
|
7238
|
+
graphWalkEnabled = true,
|
|
7239
|
+
queryExpansionEnabled = true,
|
|
7240
|
+
enableLlmReranking = false,
|
|
7241
|
+
rerankFn
|
|
7183
7242
|
} = options;
|
|
7184
7243
|
const assemblyId = generateAssemblyId();
|
|
7185
7244
|
const manifest = {
|
|
@@ -7195,13 +7254,26 @@ async function assembleContext(options) {
|
|
|
7195
7254
|
reference: { count: 0, tokens: 0 }
|
|
7196
7255
|
}
|
|
7197
7256
|
};
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7257
|
+
const candidates = [];
|
|
7258
|
+
const queries = queryExpansionEnabled ? expandQuery(taskContext) : [taskContext];
|
|
7259
|
+
const searchResults = await Promise.allSettled(queries.map((query) => client2.searchMemoryEntities(workspaceId, query, {
|
|
7260
|
+
project_id: projectId,
|
|
7261
|
+
limit: 30
|
|
7262
|
+
})));
|
|
7263
|
+
const candidateIds = new Set;
|
|
7264
|
+
for (const result of searchResults) {
|
|
7265
|
+
if (result.status !== "fulfilled")
|
|
7266
|
+
continue;
|
|
7267
|
+
if (result.value.entities?.length > 0) {
|
|
7268
|
+
for (const raw of result.value.entities) {
|
|
7269
|
+
const entity = mapToContextEntity(raw);
|
|
7270
|
+
if (!candidateIds.has(entity.id)) {
|
|
7271
|
+
candidateIds.add(entity.id);
|
|
7272
|
+
candidates.push(entity);
|
|
7273
|
+
}
|
|
7274
|
+
}
|
|
7203
7275
|
}
|
|
7204
|
-
}
|
|
7276
|
+
}
|
|
7205
7277
|
if (candidates.length < 10 && projectId) {
|
|
7206
7278
|
try {
|
|
7207
7279
|
const listResult = await client2.listMemoryEntities({
|
|
@@ -7210,9 +7282,13 @@ async function assembleContext(options) {
|
|
|
7210
7282
|
limit: 30
|
|
7211
7283
|
});
|
|
7212
7284
|
if (listResult.entities?.length > 0) {
|
|
7213
|
-
const
|
|
7214
|
-
|
|
7215
|
-
|
|
7285
|
+
for (const raw of listResult.entities) {
|
|
7286
|
+
const entity = mapToContextEntity(raw);
|
|
7287
|
+
if (!candidateIds.has(entity.id)) {
|
|
7288
|
+
candidateIds.add(entity.id);
|
|
7289
|
+
candidates.push(entity);
|
|
7290
|
+
}
|
|
7291
|
+
}
|
|
7216
7292
|
}
|
|
7217
7293
|
} catch {}
|
|
7218
7294
|
}
|
|
@@ -7224,9 +7300,33 @@ async function assembleContext(options) {
|
|
|
7224
7300
|
limit: 20
|
|
7225
7301
|
});
|
|
7226
7302
|
if (wsResult.entities?.length > 0) {
|
|
7227
|
-
const
|
|
7228
|
-
|
|
7229
|
-
|
|
7303
|
+
for (const raw of wsResult.entities) {
|
|
7304
|
+
const entity = mapToContextEntity(raw);
|
|
7305
|
+
if (!candidateIds.has(entity.id)) {
|
|
7306
|
+
candidateIds.add(entity.id);
|
|
7307
|
+
candidates.push(entity);
|
|
7308
|
+
}
|
|
7309
|
+
}
|
|
7310
|
+
}
|
|
7311
|
+
} catch {}
|
|
7312
|
+
}
|
|
7313
|
+
let graphRelations = [];
|
|
7314
|
+
if (graphWalkEnabled && candidates.length > 0) {
|
|
7315
|
+
try {
|
|
7316
|
+
const seedCandidates = [...candidates].sort((a, b) => (b.rrf_score ?? 0) - (a.rrf_score ?? 0)).slice(0, GRAPH_WALK_SEED_COUNT);
|
|
7317
|
+
const seedIds = seedCandidates.map((c) => c.id);
|
|
7318
|
+
const walkResult = await discoverRelatedContext(client2, seedIds, GRAPH_WALK_MAX_DEPTH, GRAPH_WALK_MAX_ENTITIES, GRAPH_WALK_MIN_CONFIDENCE);
|
|
7319
|
+
graphRelations = walkResult.relations;
|
|
7320
|
+
const newEntityIds = walkResult.entities.filter((e) => !candidateIds.has(e.id)).map((e) => e.id);
|
|
7321
|
+
if (newEntityIds.length > 0) {
|
|
7322
|
+
const fetchResults = await Promise.allSettled(newEntityIds.map((id) => client2.getMemoryEntity(id)));
|
|
7323
|
+
for (const result of fetchResults) {
|
|
7324
|
+
if (result.status !== "fulfilled" || !result.value.entity)
|
|
7325
|
+
continue;
|
|
7326
|
+
const mapped = mapToContextEntity(result.value.entity);
|
|
7327
|
+
candidateIds.add(mapped.id);
|
|
7328
|
+
candidates.push(mapped);
|
|
7329
|
+
}
|
|
7230
7330
|
}
|
|
7231
7331
|
} catch {}
|
|
7232
7332
|
}
|
|
@@ -7238,10 +7338,31 @@ async function assembleContext(options) {
|
|
|
7238
7338
|
};
|
|
7239
7339
|
}
|
|
7240
7340
|
const scored = candidates.map((entity) => {
|
|
7241
|
-
const { score, reasons } = computeRelevanceScore(entity, taskContext, cardLabels);
|
|
7341
|
+
const { score, reasons } = computeRelevanceScore(entity, taskContext, cardLabels, graphRelations.length > 0 ? graphRelations : undefined);
|
|
7242
7342
|
return { entity, score, reasons };
|
|
7243
7343
|
});
|
|
7244
7344
|
scored.sort((a, b) => b.score - a.score);
|
|
7345
|
+
if (enableLlmReranking && rerankFn && scored.length >= RERANK_MIN_CANDIDATES) {
|
|
7346
|
+
const topN = scored.slice(0, RERANK_TOP_N);
|
|
7347
|
+
const scoreRange = topN[0].score - topN[topN.length - 1].score;
|
|
7348
|
+
if (scoreRange <= RERANK_CLUSTER_THRESHOLD) {
|
|
7349
|
+
try {
|
|
7350
|
+
const rerankCandidates = topN.map((s) => ({
|
|
7351
|
+
id: s.entity.id,
|
|
7352
|
+
title: s.entity.title,
|
|
7353
|
+
snippet: s.entity.content.slice(0, 200)
|
|
7354
|
+
}));
|
|
7355
|
+
const rerankedIds = await rerankFn(taskContext, rerankCandidates);
|
|
7356
|
+
const idOrder = new Map(rerankedIds.map((id, i) => [id, i]));
|
|
7357
|
+
topN.sort((a, b) => {
|
|
7358
|
+
const aIdx = idOrder.get(a.entity.id) ?? 999;
|
|
7359
|
+
const bIdx = idOrder.get(b.entity.id) ?? 999;
|
|
7360
|
+
return aIdx - bIdx;
|
|
7361
|
+
});
|
|
7362
|
+
scored.splice(0, topN.length, ...topN);
|
|
7363
|
+
} catch {}
|
|
7364
|
+
}
|
|
7365
|
+
}
|
|
7245
7366
|
const procedureBudget = Math.floor(tokenBudget * PROCEDURE_BUDGET_FRACTION);
|
|
7246
7367
|
const remainingBudget = tokenBudget - procedureBudget;
|
|
7247
7368
|
const tierBudgets = {
|
|
@@ -7553,7 +7674,7 @@ async function recordContextFeedback(client2, cardId, sessionStatus, progressPer
|
|
|
7553
7674
|
sessionAssemblyMap.delete(cardId);
|
|
7554
7675
|
return { adjusted };
|
|
7555
7676
|
}
|
|
7556
|
-
var DEFAULT_TOKEN_BUDGET = 4000, MAX_TOKENS_PER_ENTITY = 500, MIN_RELEVANCE_THRESHOLD = 0.1, TIER_WEIGHTS, PROCEDURE_BUDGET_FRACTION = 0.15, TIER_BUDGET_ALLOCATION, MIN_REFERENCE_SLOTS = 3, manifestCache, MAX_CACHE_SIZE = 50, sessionAssemblyMap, MAX_SESSION_MAP_SIZE = 100;
|
|
7677
|
+
var DEFAULT_TOKEN_BUDGET = 4000, MAX_TOKENS_PER_ENTITY = 500, MIN_RELEVANCE_THRESHOLD = 0.1, TIER_WEIGHTS, PROCEDURE_BUDGET_FRACTION = 0.15, TIER_BUDGET_ALLOCATION, MIN_REFERENCE_SLOTS = 3, GRAPH_WALK_MAX_DEPTH = 1, GRAPH_WALK_MAX_ENTITIES = 10, GRAPH_WALK_MIN_CONFIDENCE = 0.5, GRAPH_WALK_SEED_COUNT = 5, MAX_QUERY_VARIATIONS = 4, RERANK_CLUSTER_THRESHOLD = 0.05, RERANK_TOP_N = 10, RERANK_MIN_CANDIDATES = 5, RELATION_BONUSES, QUERY_SYNONYMS, manifestCache, MAX_CACHE_SIZE = 50, sessionAssemblyMap, MAX_SESSION_MAP_SIZE = 100;
|
|
7557
7678
|
var init_context_assembly = __esm(() => {
|
|
7558
7679
|
init_dist();
|
|
7559
7680
|
TIER_WEIGHTS = {
|
|
@@ -7566,6 +7687,33 @@ var init_context_assembly = __esm(() => {
|
|
|
7566
7687
|
episode: 0.3,
|
|
7567
7688
|
draft: 0.1
|
|
7568
7689
|
};
|
|
7690
|
+
RELATION_BONUSES = {
|
|
7691
|
+
depends_on: 0.15,
|
|
7692
|
+
resolved_by: 0.2,
|
|
7693
|
+
relates_to: 0.1,
|
|
7694
|
+
implements: 0.15,
|
|
7695
|
+
blocks: 0.15,
|
|
7696
|
+
references: 0.1,
|
|
7697
|
+
extends: 0.1,
|
|
7698
|
+
caused_by: 0.15
|
|
7699
|
+
};
|
|
7700
|
+
QUERY_SYNONYMS = {
|
|
7701
|
+
auth: ["authentication", "authorization", "session"],
|
|
7702
|
+
authentication: ["auth", "session", "sign-in"],
|
|
7703
|
+
login: ["sign-in", "authentication", "session"],
|
|
7704
|
+
bug: ["error", "issue", "defect", "problem"],
|
|
7705
|
+
error: ["exception", "failure", "issue"],
|
|
7706
|
+
fix: ["resolve", "patch", "repair", "correct"],
|
|
7707
|
+
deploy: ["deployment", "release", "ship", "publish"],
|
|
7708
|
+
test: ["testing", "spec", "assertion", "verify"],
|
|
7709
|
+
config: ["configuration", "settings", "setup"],
|
|
7710
|
+
db: ["database", "storage", "persistence"],
|
|
7711
|
+
database: ["storage", "persistence", "data store"],
|
|
7712
|
+
api: ["endpoint", "route", "service"],
|
|
7713
|
+
ui: ["frontend", "component", "view"],
|
|
7714
|
+
perf: ["performance", "speed", "latency"],
|
|
7715
|
+
performance: ["speed", "latency", "optimization"]
|
|
7716
|
+
};
|
|
7569
7717
|
manifestCache = new Map;
|
|
7570
7718
|
sessionAssemblyMap = new Map;
|
|
7571
7719
|
});
|