@hmharness/evolution 0.8.0 → 0.8.1

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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './memory.ts';
2
+ export * from './ranker.ts';
2
3
  export * from './insights.ts';
3
4
  export * from './skills.ts';
4
5
  export * from './bench.ts';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./memory.js";
2
+ export * from "./ranker.js";
2
3
  export * from "./insights.js";
3
4
  export * from "./skills.js";
4
5
  export * from "./bench.js";
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @hmharness/evolution - context ranker (V2 blueprint M5).
3
+ *
4
+ * Scores retrieval candidates for the context pack. First version uses the
5
+ * blueprint's fixed weights; Evolution is meant to optimize the weights
6
+ * later (that is why they live in one exported constant, not inline math).
7
+ * All inputs are normalized 0..1 by the caller; tokenCost is normalized
8
+ * against the candidate budget by normalize().
9
+ */
10
+ export interface ContextCandidate {
11
+ /** where it came from: memory | skill | insight | agents-md | history */
12
+ source: string;
13
+ /** reference (text or id) - ranked, not necessarily injected verbatim */
14
+ contentRef: string;
15
+ relevance: number;
16
+ recency: number;
17
+ importance: number;
18
+ dependency: number;
19
+ similarity: number;
20
+ /** raw token estimate; normalized by the ranker */
21
+ tokenCost: number;
22
+ }
23
+ export declare const RANK_WEIGHTS: {
24
+ readonly relevance: 0.3;
25
+ readonly dependency: 0.2;
26
+ readonly recency: 0.15;
27
+ readonly importance: 0.15;
28
+ readonly similarity: 0.1;
29
+ readonly tokenCost: -0.1;
30
+ };
31
+ /** Rank candidates: fixed weights, token cost normalized to the batch max,
32
+ * ties broken by cheaper-first. Returns a NEW sorted array. */
33
+ export declare function rankContext(candidates: ContextCandidate[]): Array<ContextCandidate & {
34
+ score: number;
35
+ }>;
36
+ /** Pick the top-K candidates under a token budget (greedy, ranked order). */
37
+ export declare function packContext(candidates: ContextCandidate[], budgetTokens: number): Array<ContextCandidate & {
38
+ score: number;
39
+ }>;
40
+ /** The four memory classes (V2 M5). Entries classify by prefix today:
41
+ * [self-note]/tool lessons -> procedural; (distilled) -> semantic;
42
+ * session recaps -> episodic; design/decision notes -> project. */
43
+ export type MemoryClass = 'episodic' | 'semantic' | 'procedural' | 'project';
44
+ export declare function classifyMemory(entry: string): MemoryClass;
package/dist/ranker.js ADDED
@@ -0,0 +1,55 @@
1
+ export const RANK_WEIGHTS = {
2
+ relevance: 0.30,
3
+ dependency: 0.20,
4
+ recency: 0.15,
5
+ importance: 0.15,
6
+ similarity: 0.10,
7
+ tokenCost: -0.10,
8
+ };
9
+ const clamp01 = (n) => Number.isFinite(n) ? Math.max(0, Math.min(1, n)) : 0;
10
+ /** Rank candidates: fixed weights, token cost normalized to the batch max,
11
+ * ties broken by cheaper-first. Returns a NEW sorted array. */
12
+ export function rankContext(candidates) {
13
+ const maxCost = Math.max(1, ...candidates.map((c) => c.tokenCost));
14
+ return candidates
15
+ .map((c) => {
16
+ const n = {
17
+ relevance: clamp01(c.relevance),
18
+ dependency: clamp01(c.dependency),
19
+ recency: clamp01(c.recency),
20
+ importance: clamp01(c.importance),
21
+ similarity: clamp01(c.similarity),
22
+ tokenCost: clamp01(c.tokenCost / maxCost),
23
+ };
24
+ const score = RANK_WEIGHTS.relevance * n.relevance +
25
+ RANK_WEIGHTS.dependency * n.dependency +
26
+ RANK_WEIGHTS.recency * n.recency +
27
+ RANK_WEIGHTS.importance * n.importance +
28
+ RANK_WEIGHTS.similarity * n.similarity +
29
+ RANK_WEIGHTS.tokenCost * n.tokenCost;
30
+ return { ...c, score: Math.round(score * 1000) / 1000 };
31
+ })
32
+ .sort((a, b) => b.score - a.score || a.tokenCost - b.tokenCost);
33
+ }
34
+ /** Pick the top-K candidates under a token budget (greedy, ranked order). */
35
+ export function packContext(candidates, budgetTokens) {
36
+ const ranked = rankContext(candidates);
37
+ const out = [];
38
+ let used = 0;
39
+ for (const c of ranked) {
40
+ if (used + c.tokenCost > budgetTokens)
41
+ continue;
42
+ out.push(c);
43
+ used += c.tokenCost;
44
+ }
45
+ return out;
46
+ }
47
+ export function classifyMemory(entry) {
48
+ if (/^\(distilled\)/.test(entry) || /api|sdk|版本|参数/.test(entry.slice(0, 80)))
49
+ return 'semantic';
50
+ if (/design|架构|决策|why|adr/i.test(entry.slice(0, 80)))
51
+ return 'project';
52
+ if (/self-note|工具|失败|重试|how to|怎么/.test(entry.slice(0, 80)))
53
+ return 'procedural';
54
+ return 'episodic';
55
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hmharness/evolution",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "hmharness evolution subsystem: persistent memory, insight capture, skill library, and the bench that gives evolution its fitness signal. First-class kernel citizen, not a plugin.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",