@hmharness/evolution 0.14.25 → 0.14.27

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.
@@ -1,4 +1,4 @@
1
- import type { BenchCase } from './bench.ts';
1
+ import type { BenchCase } from './bench.js';
2
2
  export type CandidateTarget = 'prompt' | 'skill' | 'context' | 'tool_policy' | 'model_router' | 'workflow' | 'harness';
3
3
  export declare const CANDIDATE_TARGETS: CandidateTarget[];
4
4
  export interface EvolutionCandidate {
@@ -1,4 +1,4 @@
1
- import type { DpoPair } from './reward-model.ts';
1
+ import type { DpoPair } from './reward-model.js';
2
2
  import type { ProviderConfig } from '@hmharness/kernel';
3
3
  /** One provider's fine-tuning dialect. Everything provider-specific lives
4
4
  * here; the transport and job flow above it are generic. */
package/dist/evolve.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type ProviderConfig } from '@hmharness/kernel';
2
- import { type BenchCase } from './bench.ts';
2
+ import { type BenchCase } from './bench.js';
3
3
  export interface SkillProposal {
4
4
  name: string;
5
5
  description: string;
package/dist/index.d.ts CHANGED
@@ -1,26 +1,26 @@
1
- export * from './memory.ts';
2
- export * from './ranker.ts';
3
- export * from './candidates.ts';
4
- export * from './skillpayload.ts';
5
- export * from './dataset.ts';
6
- export * from './readiness.ts';
7
- export * from './labels.ts';
8
- export * from './reward-model.ts';
9
- export * from './judge.ts';
10
- export * from './sequential.ts';
11
- export * from './insights.ts';
12
- export * from './skills.ts';
13
- export * from './bench.ts';
14
- export * from './evolve.ts';
15
- export * from './patches.ts';
16
- export * from './impact.ts';
17
- export * from './workflows.ts';
18
- export * from './knowledge.ts';
19
- export * from './radar.ts';
20
- export * from './external.ts';
21
- export * from './knowledge-graph.ts';
22
- export * from './router-evolution.ts';
23
- export * from './context-evolution.ts';
24
- export * from './rl-governance.ts';
25
- export * from './policy-optimizer.ts';
26
- export * from './cloud-finetune.ts';
1
+ export * from './memory.js';
2
+ export * from './ranker.js';
3
+ export * from './candidates.js';
4
+ export * from './skillpayload.js';
5
+ export * from './dataset.js';
6
+ export * from './readiness.js';
7
+ export * from './labels.js';
8
+ export * from './reward-model.js';
9
+ export * from './judge.js';
10
+ export * from './sequential.js';
11
+ export * from './insights.js';
12
+ export * from './skills.js';
13
+ export * from './bench.js';
14
+ export * from './evolve.js';
15
+ export * from './patches.js';
16
+ export * from './impact.js';
17
+ export * from './workflows.js';
18
+ export * from './knowledge.js';
19
+ export * from './radar.js';
20
+ export * from './external.js';
21
+ export * from './knowledge-graph.js';
22
+ export * from './router-evolution.js';
23
+ export * from './context-evolution.js';
24
+ export * from './rl-governance.js';
25
+ export * from './policy-optimizer.js';
26
+ export * from './cloud-finetune.js';
@@ -16,3 +16,26 @@ export declare function recordInsight(home: string, insight: Insight): Promise<v
16
16
  export declare function readInsights(home: string, limit?: number): Promise<Insight[]>;
17
17
  /** Summarize recent insights for the system prompt (bounded). */
18
18
  export declare function recentInsights(home: string, limit?: number): Promise<string>;
19
+ /** Task-similarity feature set: per-WORD bigrams plus single chars as
20
+ * tokens (2026-09-27). Cross-word bigrams that include the space turned
21
+ * out to be a noise factory - 'fix' and 'xxx' shared 'x ' and unrelated
22
+ * natural sentences scored 0.06. Words are split on non-alphanumerics;
23
+ * CJK text has no spaces, so a whole Chinese clause becomes one word and
24
+ * keeps every character bigram. Pure. */
25
+ export declare function bigrams(s: string): Set<string>;
26
+ /** Jaccard similarity of two bigram sets. Pure. */
27
+ export declare function jaccard(a: Set<string>, b: Set<string>): number;
28
+ /** Noise floor: below this similarity an insight is unrelated to the task.
29
+ * With per-word bigrams the highest unrelated natural-language pairs score
30
+ * ~0.04 (stopword overlap: 'the' contributes th/he); related tasks land at
31
+ * 0.5+. The floor sits between the two, with margin on both sides. */
32
+ export declare const INSIGHT_SIM_FLOOR = 0.05;
33
+ /**
34
+ * The K most RELEVANT insights for this task (recency as tiebreak), falling
35
+ * back to the K most recent when nothing clears the noise floor - so the
36
+ * prompt always carries some experience, exactly like before.
37
+ */
38
+ export declare function retrieveInsights(home: string, task: string, opts?: {
39
+ topK?: number;
40
+ pool?: number;
41
+ }): Promise<string>;
package/dist/insights.js CHANGED
@@ -78,3 +78,88 @@ export async function recentInsights(home, limit = 5) {
78
78
  return '';
79
79
  }
80
80
  }
81
+ /* ---------------- experience retrieval (2026-09-27, plan A) ----------------
82
+ * The injection used to be "last 5 insights" - pure recency. After 300
83
+ * tasks, a question similar to task #50 got five UNRELATED recent lessons
84
+ * instead of that one. Industry direction (Agent KB / Agent Workflow
85
+ * Memory, verified 2026-09-27): retrieve relevant experience per task.
86
+ * Zero-dependency red line -> character-bigram Jaccard (works well for CJK
87
+ * too), a small recency tiebreak, graceful fallback to recency order. */
88
+ /** Task-similarity feature set: per-WORD bigrams plus single chars as
89
+ * tokens (2026-09-27). Cross-word bigrams that include the space turned
90
+ * out to be a noise factory - 'fix' and 'xxx' shared 'x ' and unrelated
91
+ * natural sentences scored 0.06. Words are split on non-alphanumerics;
92
+ * CJK text has no spaces, so a whole Chinese clause becomes one word and
93
+ * keeps every character bigram. Pure. */
94
+ export function bigrams(s) {
95
+ const words = s.toLowerCase().split(/[^a-z0-9\u4e00-\u9fff]+/).filter(Boolean);
96
+ const out = new Set();
97
+ for (const w of words) {
98
+ if (w.length === 1) {
99
+ out.add(w);
100
+ continue;
101
+ }
102
+ for (let i = 0; i < w.length - 1; i++)
103
+ out.add(w.slice(i, i + 2));
104
+ }
105
+ return out;
106
+ }
107
+ /** Jaccard similarity of two bigram sets. Pure. */
108
+ export function jaccard(a, b) {
109
+ if (!a.size || !b.size)
110
+ return 0;
111
+ let inter = 0;
112
+ for (const g of a)
113
+ if (b.has(g))
114
+ inter++;
115
+ return inter / (a.size + b.size - inter);
116
+ }
117
+ function formatInsight(i) {
118
+ return `- [${i.outcome}] ${i.task.slice(0, 60)} (turns ${i.turns}, tools ${i.toolsUsed.join(',') || 'none'})`;
119
+ }
120
+ /** Noise floor: below this similarity an insight is unrelated to the task.
121
+ * With per-word bigrams the highest unrelated natural-language pairs score
122
+ * ~0.04 (stopword overlap: 'the' contributes th/he); related tasks land at
123
+ * 0.5+. The floor sits between the two, with margin on both sides. */
124
+ export const INSIGHT_SIM_FLOOR = 0.05;
125
+ /**
126
+ * The K most RELEVANT insights for this task (recency as tiebreak), falling
127
+ * back to the K most recent when nothing clears the noise floor - so the
128
+ * prompt always carries some experience, exactly like before.
129
+ */
130
+ export async function retrieveInsights(home, task, opts = {}) {
131
+ const topK = opts.topK ?? 5;
132
+ const pool = opts.pool ?? 500;
133
+ let rows = [];
134
+ try {
135
+ const text = await readFile(join(home, 'insights', 'insights.jsonl'), 'utf8');
136
+ rows = text.trim().split('\n').filter(Boolean)
137
+ .map((l) => { try {
138
+ return JSON.parse(l);
139
+ }
140
+ catch {
141
+ return null;
142
+ } })
143
+ .filter((x) => x !== null)
144
+ .slice(-pool);
145
+ }
146
+ catch {
147
+ return ''; // no archive at all - same behaviour as recentInsights
148
+ }
149
+ if (rows.length === 0)
150
+ return '';
151
+ const q = bigrams(task);
152
+ if (q.size === 0)
153
+ return rows.slice(-topK).map(formatInsight).join('\n');
154
+ // newest first within the pool, so idx IS the recency rank
155
+ const newestFirst = [...rows].reverse();
156
+ const scored = newestFirst
157
+ .map((r, idx) => ({ r, s: jaccard(q, bigrams(r.task)) + (1 - idx / (newestFirst.length + 1)) * 1e-4 }))
158
+ .filter((x) => x.s > INSIGHT_SIM_FLOOR)
159
+ .sort((a, b) => b.s - a.s)
160
+ .slice(0, topK)
161
+ .map((x) => x.r);
162
+ if (scored.length === 0)
163
+ return rows.slice(-topK).map(formatInsight).join('\n');
164
+ return scored.map(formatInsight).join('\n');
165
+ }
@@ -1,5 +1,5 @@
1
1
  import { type ProviderConfig } from '@hmharness/kernel';
2
- import { type SkillProposal } from './evolve.ts';
2
+ import { type SkillProposal } from './evolve.js';
3
3
  declare function fetchPage(url: string): Promise<string | null>;
4
4
  /** One refresh cycle: fetch -> compare -> maybe one knowledge draft.
5
5
  * Returns a short human-readable summary (or 'offline' etc.). */
@@ -1,4 +1,4 @@
1
- import type { HumanLabel } from './labels.ts';
1
+ import type { HumanLabel } from './labels.js';
2
2
  export type { HumanLabel };
3
3
  export interface RewardFeatures {
4
4
  /** outcome bucket: 2=ok, 1=turn-budget, 0=error */
@@ -10,7 +10,7 @@
10
10
  * a workflow is just a skill with a shape.
11
11
  */
12
12
  import { type ProviderConfig } from '@hmharness/kernel';
13
- import { type SkillProposal } from './evolve.ts';
13
+ import { type SkillProposal } from './evolve.js';
14
14
  export interface WorkflowCluster {
15
15
  key: string;
16
16
  tasks: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hmharness/evolution",
3
- "version": "0.14.25",
3
+ "version": "0.14.27",
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",
@@ -15,7 +15,7 @@
15
15
  "build": "tsc -p tsconfig.build.json"
16
16
  },
17
17
  "dependencies": {
18
- "@hmharness/kernel": "0.9.4"
18
+ "@hmharness/kernel": "*"
19
19
  },
20
20
  "files": [
21
21
  "dist"