@symerian/symi 3.0.18 → 3.0.19

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.
Files changed (116) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  3. package/package.json +1 -1
  4. package/extensions/copilot-proxy/README.md +0 -24
  5. package/extensions/copilot-proxy/index.ts +0 -154
  6. package/extensions/copilot-proxy/node_modules/.bin/symi +0 -21
  7. package/extensions/copilot-proxy/package.json +0 -15
  8. package/extensions/copilot-proxy/symi.plugin.json +0 -9
  9. package/extensions/device-pair/index.ts +0 -642
  10. package/extensions/device-pair/symi.plugin.json +0 -20
  11. package/extensions/diagnostics-otel/index.ts +0 -15
  12. package/extensions/diagnostics-otel/node_modules/.bin/acorn +0 -21
  13. package/extensions/diagnostics-otel/node_modules/.bin/symi +0 -21
  14. package/extensions/diagnostics-otel/package.json +0 -27
  15. package/extensions/diagnostics-otel/src/service.test.ts +0 -290
  16. package/extensions/diagnostics-otel/src/service.ts +0 -666
  17. package/extensions/diagnostics-otel/symi.plugin.json +0 -8
  18. package/extensions/google-antigravity-auth/README.md +0 -24
  19. package/extensions/google-antigravity-auth/index.ts +0 -424
  20. package/extensions/google-antigravity-auth/node_modules/.bin/symi +0 -21
  21. package/extensions/google-antigravity-auth/package.json +0 -15
  22. package/extensions/google-antigravity-auth/symi.plugin.json +0 -9
  23. package/extensions/google-gemini-cli-auth/README.md +0 -35
  24. package/extensions/google-gemini-cli-auth/index.ts +0 -75
  25. package/extensions/google-gemini-cli-auth/node_modules/.bin/symi +0 -21
  26. package/extensions/google-gemini-cli-auth/oauth.test.ts +0 -162
  27. package/extensions/google-gemini-cli-auth/oauth.ts +0 -636
  28. package/extensions/google-gemini-cli-auth/package.json +0 -15
  29. package/extensions/google-gemini-cli-auth/symi.plugin.json +0 -9
  30. package/extensions/learning-loop/index.ts +0 -159
  31. package/extensions/learning-loop/node_modules/.bin/symi +0 -21
  32. package/extensions/learning-loop/package.json +0 -18
  33. package/extensions/learning-loop/src/analytics/gateway-methods.ts +0 -230
  34. package/extensions/learning-loop/src/analytics/metrics-aggregator.ts +0 -153
  35. package/extensions/learning-loop/src/capture/run-tracker.ts +0 -181
  36. package/extensions/learning-loop/src/capture/serializer.ts +0 -74
  37. package/extensions/learning-loop/src/db.ts +0 -583
  38. package/extensions/learning-loop/src/feedback/explicit-feedback.ts +0 -58
  39. package/extensions/learning-loop/src/feedback/implicit-signals.ts +0 -89
  40. package/extensions/learning-loop/src/graph/edge-inference.ts +0 -189
  41. package/extensions/learning-loop/src/graph/graph-retrieval.ts +0 -144
  42. package/extensions/learning-loop/src/graph/graph-store.ts +0 -183
  43. package/extensions/learning-loop/src/hooks.ts +0 -244
  44. package/extensions/learning-loop/src/injection/cache.ts +0 -73
  45. package/extensions/learning-loop/src/injection/context-injector.ts +0 -104
  46. package/extensions/learning-loop/src/injection/prompt-builder.ts +0 -43
  47. package/extensions/learning-loop/src/learning/embedding-bridge.ts +0 -54
  48. package/extensions/learning-loop/src/learning/learning-extractor.ts +0 -217
  49. package/extensions/learning-loop/src/learning/learning-store.ts +0 -158
  50. package/extensions/learning-loop/src/learning/retrieval.ts +0 -87
  51. package/extensions/learning-loop/src/math/confidence-intervals.ts +0 -62
  52. package/extensions/learning-loop/src/math/ewma.ts +0 -51
  53. package/extensions/learning-loop/src/math/weighted-scorer.ts +0 -42
  54. package/extensions/learning-loop/src/schema.ts +0 -176
  55. package/extensions/learning-loop/src/scoring/normalization.ts +0 -32
  56. package/extensions/learning-loop/src/scoring/quality-engine.ts +0 -78
  57. package/extensions/learning-loop/src/scoring/signal-extractors.ts +0 -155
  58. package/extensions/learning-loop/src/test/context-injector.test.ts +0 -142
  59. package/extensions/learning-loop/src/test/fixes.test.ts +0 -1286
  60. package/extensions/learning-loop/src/test/graph.test.ts +0 -711
  61. package/extensions/learning-loop/src/test/integration.test.ts +0 -312
  62. package/extensions/learning-loop/src/test/learning-store.test.ts +0 -191
  63. package/extensions/learning-loop/src/test/math.test.ts +0 -148
  64. package/extensions/learning-loop/src/test/quality-engine.test.ts +0 -231
  65. package/extensions/learning-loop/src/test/run-tracker.test.ts +0 -143
  66. package/extensions/learning-loop/src/types.ts +0 -281
  67. package/extensions/learning-loop/symi.plugin.json +0 -46
  68. package/extensions/llm-task/README.md +0 -97
  69. package/extensions/llm-task/index.ts +0 -6
  70. package/extensions/llm-task/package.json +0 -12
  71. package/extensions/llm-task/src/llm-task-tool.test.ts +0 -138
  72. package/extensions/llm-task/src/llm-task-tool.ts +0 -249
  73. package/extensions/llm-task/symi.plugin.json +0 -21
  74. package/extensions/memory-lancedb/config.ts +0 -161
  75. package/extensions/memory-lancedb/index.test.ts +0 -330
  76. package/extensions/memory-lancedb/index.ts +0 -670
  77. package/extensions/memory-lancedb/node_modules/.bin/arrow2csv +0 -21
  78. package/extensions/memory-lancedb/node_modules/.bin/openai +0 -21
  79. package/extensions/memory-lancedb/node_modules/.bin/symi +0 -21
  80. package/extensions/memory-lancedb/package.json +0 -20
  81. package/extensions/memory-lancedb/symi.plugin.json +0 -71
  82. package/extensions/minimax-portal-auth/README.md +0 -33
  83. package/extensions/minimax-portal-auth/index.ts +0 -161
  84. package/extensions/minimax-portal-auth/node_modules/.bin/symi +0 -21
  85. package/extensions/minimax-portal-auth/oauth.ts +0 -247
  86. package/extensions/minimax-portal-auth/package.json +0 -15
  87. package/extensions/minimax-portal-auth/symi.plugin.json +0 -9
  88. package/extensions/model-equalizer/index.ts +0 -80
  89. package/extensions/model-equalizer/skills/model-equalizer/SKILL.md +0 -58
  90. package/extensions/model-equalizer/src/detection.ts +0 -62
  91. package/extensions/model-equalizer/src/enhancer.ts +0 -63
  92. package/extensions/model-equalizer/src/test/detection.test.ts +0 -218
  93. package/extensions/model-equalizer/src/test/enhancer.test.ts +0 -137
  94. package/extensions/model-equalizer/src/test/integration.test.ts +0 -185
  95. package/extensions/model-equalizer/src/types.ts +0 -24
  96. package/extensions/model-equalizer/symi.plugin.json +0 -12
  97. package/extensions/phone-control/index.ts +0 -421
  98. package/extensions/phone-control/symi.plugin.json +0 -10
  99. package/extensions/pipeline/README.md +0 -75
  100. package/extensions/pipeline/SKILL.md +0 -97
  101. package/extensions/pipeline/index.ts +0 -18
  102. package/extensions/pipeline/package.json +0 -11
  103. package/extensions/pipeline/src/pipeline-tool.test.ts +0 -345
  104. package/extensions/pipeline/src/pipeline-tool.ts +0 -266
  105. package/extensions/pipeline/src/windows-spawn.test.ts +0 -148
  106. package/extensions/pipeline/src/windows-spawn.ts +0 -193
  107. package/extensions/pipeline/symi.plugin.json +0 -10
  108. package/extensions/qwen-portal-auth/README.md +0 -24
  109. package/extensions/qwen-portal-auth/index.ts +0 -134
  110. package/extensions/qwen-portal-auth/oauth.ts +0 -190
  111. package/extensions/qwen-portal-auth/symi.plugin.json +0 -9
  112. package/extensions/talk-voice/index.ts +0 -150
  113. package/extensions/talk-voice/symi.plugin.json +0 -10
  114. package/extensions/thread-ownership/index.test.ts +0 -180
  115. package/extensions/thread-ownership/index.ts +0 -133
  116. package/extensions/thread-ownership/symi.plugin.json +0 -28
@@ -1,217 +0,0 @@
1
- /**
2
- * Deterministic pattern extraction from scored runs.
3
- * No LLM calls -- all patterns are derived from mathematical analysis
4
- * of run history and tool call traces.
5
- */
6
-
7
- import type { DatabaseManager } from "../db.js";
8
- import type { EdgeInference } from "../graph/edge-inference.js";
9
- import { getEWMAValue } from "../math/ewma.js";
10
- import type {
11
- CompletedRun,
12
- QualityScore,
13
- LearningLoopConfig,
14
- LearningCategory,
15
- RunRow,
16
- } from "../types.js";
17
- import type { EmbeddingBridge } from "./embedding-bridge.js";
18
- import type { LearningStore } from "./learning-store.js";
19
-
20
- export type LearningExtractor = ReturnType<typeof createLearningExtractor>;
21
-
22
- type ExtractedLearning = {
23
- category: LearningCategory;
24
- content: string;
25
- confidence: number;
26
- };
27
-
28
- export function createLearningExtractor(params: {
29
- db: DatabaseManager;
30
- learningStore: LearningStore;
31
- config: LearningLoopConfig;
32
- edgeInference?: EdgeInference;
33
- embeddingBridge?: EmbeddingBridge;
34
- }) {
35
- const { db, learningStore, config, edgeInference, embeddingBridge } = params;
36
-
37
- /**
38
- * Extract learnings from a completed, scored run.
39
- * Returns the IDs of newly created learnings.
40
- */
41
- async function extract(run: CompletedRun, score: QualityScore): Promise<string[]> {
42
- const learnings: ExtractedLearning[] = [];
43
-
44
- learnings.push(...extractToolPatterns(run, score));
45
- learnings.push(...extractErrorRecovery(run));
46
- learnings.push(...extractModelAffinity(run, score));
47
- learnings.push(...extractAntiPatterns(run));
48
-
49
- // Batch-embed all learning contents if bridge is available
50
- const contents = learnings.map((l) => l.content);
51
- const embeddings =
52
- embeddingBridge && contents.length > 0
53
- ? await embeddingBridge.embedBatch(contents)
54
- : contents.map(() => null);
55
-
56
- const ids: string[] = [];
57
- for (let i = 0; i < learnings.length; i++) {
58
- const learning = learnings[i]!;
59
- const id = learningStore.addLearning({
60
- runId: run.runId,
61
- category: learning.category,
62
- content: learning.content,
63
- embedding: embeddings[i] ?? null,
64
- confidence: learning.confidence,
65
- });
66
- if (id) {
67
- ids.push(id);
68
- // Infer graph edges for the newly created learning
69
- if (edgeInference) {
70
- const record = learningStore.getLearning(id);
71
- if (record) {
72
- edgeInference.onLearningCreated(record, run.runId);
73
- }
74
- }
75
- }
76
- }
77
-
78
- return ids;
79
- }
80
-
81
- /**
82
- * Tool Pattern: Same tool set (order-independent) succeeds 3+ times with score >= 0.7.
83
- * Uses Jaccard similarity >= 0.8 on tool sets instead of exact sequence matching.
84
- */
85
- function extractToolPatterns(run: CompletedRun, score: QualityScore): ExtractedLearning[] {
86
- if (score.score < 0.7 || run.toolCalls.length < 2) return [];
87
-
88
- const toolSet = new Set(run.toolCalls.map((tc) => tc.toolName));
89
-
90
- const recentRuns = db.getRunsByModel(run.provider, run.model, 50);
91
- let matchCount = 0;
92
-
93
- for (const prevRow of recentRuns) {
94
- if (prevRow.run_id === run.runId) continue;
95
- if ((prevRow.quality_score ?? 0) < 0.7) continue;
96
-
97
- const prevToolCalls = db.getToolCalls(prevRow.run_id);
98
- const prevToolSet = new Set(prevToolCalls.map((tc) => tc.tool_name));
99
-
100
- // Jaccard similarity on tool sets
101
- const intersection = [...toolSet].filter((t) => prevToolSet.has(t)).length;
102
- const union = new Set([...toolSet, ...prevToolSet]).size;
103
- const jaccard = union > 0 ? intersection / union : 0;
104
-
105
- if (jaccard >= 0.8) matchCount++;
106
- }
107
-
108
- if (matchCount < 2) return []; // Need 3+ total (including current)
109
-
110
- const uniqueTools = [...toolSet].sort();
111
- return [
112
- {
113
- category: "tool_pattern",
114
- content: `Tool combination [${uniqueTools.join(", ")}] is effective for tasks handled by ${run.provider}/${run.model}. Used successfully ${matchCount + 1} times.`,
115
- confidence: Math.min(1.0, 0.5 + matchCount * 0.1),
116
- },
117
- ];
118
- }
119
-
120
- /**
121
- * Error Recovery: Tool error followed by successful retry with different params
122
- */
123
- function extractErrorRecovery(run: CompletedRun): ExtractedLearning[] {
124
- const results: ExtractedLearning[] = [];
125
-
126
- for (let i = 1; i < run.toolCalls.length; i++) {
127
- const prev = run.toolCalls[i - 1]!;
128
- const curr = run.toolCalls[i]!;
129
-
130
- if (
131
- !prev.success &&
132
- curr.success &&
133
- prev.toolName === curr.toolName &&
134
- prev.paramHash !== curr.paramHash
135
- ) {
136
- results.push({
137
- category: "error_recovery",
138
- content: `Tool "${prev.toolName}" failed with "${prev.error ?? "unknown error"}", retry with different parameters succeeded.`,
139
- confidence: 0.7,
140
- });
141
- }
142
- }
143
-
144
- return results;
145
- }
146
-
147
- /**
148
- * Model Affinity: Model's EWMA quality > global mean + 1 std dev
149
- */
150
- function extractModelAffinity(run: CompletedRun, score: QualityScore): ExtractedLearning[] {
151
- const leaderboard = db.getModelLeaderboard();
152
- if (leaderboard.length < 2) return [];
153
-
154
- const globalSum = leaderboard.reduce((s, m) => s + m.avgQuality, 0);
155
- const globalMean = globalSum / leaderboard.length;
156
- const variance =
157
- leaderboard.reduce((s, m) => s + (m.avgQuality - globalMean) ** 2, 0) / leaderboard.length;
158
- const stdDev = Math.sqrt(variance);
159
-
160
- if (stdDev === 0) return [];
161
-
162
- const modelEntry = leaderboard.find(
163
- (m) => m.provider === run.provider && m.model === run.model,
164
- );
165
- if (!modelEntry || modelEntry.runCount < 5) return [];
166
-
167
- if (modelEntry.avgQuality > globalMean + stdDev) {
168
- return [
169
- {
170
- category: "model_affinity",
171
- content: `Model ${run.provider}/${run.model} performs above average (quality: ${modelEntry.avgQuality.toFixed(3)} vs global mean: ${globalMean.toFixed(3)}).`,
172
- confidence: Math.min(1.0, 0.5 + modelEntry.runCount * 0.02),
173
- },
174
- ];
175
- }
176
-
177
- return [];
178
- }
179
-
180
- /**
181
- * Anti-Pattern: Tool error rate > 50% over 5+ runs
182
- */
183
- function extractAntiPatterns(run: CompletedRun): ExtractedLearning[] {
184
- const results: ExtractedLearning[] = [];
185
-
186
- // Aggregate tool error rates from recent runs
187
- const recentRuns = db.getRecentRuns(50);
188
- const toolStats = new Map<string, { total: number; errors: number; lastError: string }>();
189
-
190
- for (const row of recentRuns) {
191
- const toolCalls = db.getToolCalls(row.run_id);
192
- for (const tc of toolCalls) {
193
- const stat = toolStats.get(tc.tool_name) ?? { total: 0, errors: 0, lastError: "" };
194
- stat.total++;
195
- if (!tc.success) {
196
- stat.errors++;
197
- stat.lastError = tc.error ?? "unknown error";
198
- }
199
- toolStats.set(tc.tool_name, stat);
200
- }
201
- }
202
-
203
- for (const [toolName, stat] of toolStats) {
204
- if (stat.total >= 5 && stat.errors / stat.total > 0.5) {
205
- results.push({
206
- category: "anti_pattern",
207
- content: `Tool "${toolName}" has a high error rate (${stat.errors}/${stat.total} = ${((stat.errors / stat.total) * 100).toFixed(0)}%). Common error: "${stat.lastError}".`,
208
- confidence: Math.min(1.0, 0.5 + stat.total * 0.02),
209
- });
210
- }
211
- }
212
-
213
- return results;
214
- }
215
-
216
- return { extract };
217
- }
@@ -1,158 +0,0 @@
1
- /**
2
- * CRUD operations for learning records.
3
- * Handles storage, retrieval, deduplication, and embedding management.
4
- */
5
-
6
- import crypto from "node:crypto";
7
- import { buildFtsQuery } from "../../../../src/memory/hybrid.js";
8
- import { cosineSimilarity } from "../../../../src/memory/internal.js";
9
- import type { DatabaseManager } from "../db.js";
10
- import type { GraphStore } from "../graph/graph-store.js";
11
- import type { LearningRecord, LearningRow, LearningCategory } from "../types.js";
12
-
13
- export type LearningStore = ReturnType<typeof createLearningStore>;
14
-
15
- const DEDUP_THRESHOLD = 0.92;
16
-
17
- function rowToRecord(row: LearningRow): LearningRecord {
18
- return {
19
- id: row.id,
20
- runId: row.run_id,
21
- category: row.category as LearningCategory,
22
- content: row.content,
23
- embedding: row.embedding ? (JSON.parse(row.embedding) as number[]) : null,
24
- confidence: row.confidence,
25
- appliedCount: row.applied_count,
26
- createdAt: row.created_at,
27
- updatedAt: row.updated_at,
28
- };
29
- }
30
-
31
- export function createLearningStore(params: { db: DatabaseManager; graphStore?: GraphStore }) {
32
- const { db, graphStore } = params;
33
-
34
- function generateId(): string {
35
- return `lrn_${Date.now()}_${crypto.randomBytes(4).toString("hex")}`;
36
- }
37
-
38
- /**
39
- * Check if a learning with similar content already exists.
40
- * Uses cosine similarity on embeddings if available, otherwise exact match.
41
- */
42
- function isDuplicate(content: string, embedding: number[] | null): boolean {
43
- if (!embedding) {
44
- // Fallback: check exact content match
45
- const all = db.getAllLearnings(500);
46
- return all.some((row) => row.content === content);
47
- }
48
-
49
- // Check cosine similarity against existing embeddings
50
- const all = db.getAllLearnings(500);
51
- for (const row of all) {
52
- if (!row.embedding) continue;
53
- const existing = JSON.parse(row.embedding) as number[];
54
- if (cosineSimilarity(embedding, existing) >= DEDUP_THRESHOLD) {
55
- return true;
56
- }
57
- }
58
-
59
- return false;
60
- }
61
-
62
- /**
63
- * Add a new learning record, with deduplication.
64
- * Returns the learning ID if created, or null if deduplicated.
65
- */
66
- function addLearning(params: {
67
- runId: string;
68
- category: LearningCategory;
69
- content: string;
70
- embedding: number[] | null;
71
- confidence: number;
72
- }): string | null {
73
- if (isDuplicate(params.content, params.embedding)) {
74
- return null;
75
- }
76
-
77
- const now = Date.now();
78
- const record: LearningRecord = {
79
- id: generateId(),
80
- runId: params.runId,
81
- category: params.category,
82
- content: params.content,
83
- embedding: params.embedding,
84
- confidence: params.confidence,
85
- appliedCount: 0,
86
- createdAt: now,
87
- updatedAt: now,
88
- };
89
-
90
- db.insertLearning(record);
91
- return record.id;
92
- }
93
-
94
- function getLearning(id: string): LearningRecord | null {
95
- const row = db.getLearning(id);
96
- return row ? rowToRecord(row) : null;
97
- }
98
-
99
- function listLearnings(opts?: { category?: LearningCategory; limit?: number }): LearningRecord[] {
100
- const rows = opts?.category
101
- ? db.getLearningsByCategory(opts.category, opts.limit ?? 100)
102
- : db.getAllLearnings(opts?.limit ?? 100);
103
- return rows.map(rowToRecord);
104
- }
105
-
106
- function searchByText(query: string, limit: number = 20): LearningRecord[] {
107
- const ftsQuery = buildFtsQuery(query);
108
- if (!ftsQuery) return [];
109
-
110
- const rows = db.searchLearningsFts(ftsQuery, limit);
111
- return rows.map(rowToRecord);
112
- }
113
-
114
- /**
115
- * Retrieve learnings by vector similarity.
116
- * Falls back to FTS if no embeddings are available.
117
- */
118
- function searchByVector(
119
- queryEmbedding: number[],
120
- limit: number = 10,
121
- minScore: number = 0.3,
122
- ): Array<LearningRecord & { score: number }> {
123
- const all = db.getAllLearnings(1000);
124
- const results: Array<LearningRecord & { score: number }> = [];
125
-
126
- for (const row of all) {
127
- if (!row.embedding) continue;
128
- const embedding = JSON.parse(row.embedding) as number[];
129
- const score = cosineSimilarity(queryEmbedding, embedding);
130
- if (score >= minScore) {
131
- results.push({ ...rowToRecord(row), score });
132
- }
133
- }
134
-
135
- results.sort((a, b) => b.score - a.score);
136
- return results.slice(0, limit);
137
- }
138
-
139
- function recordApplication(id: string): void {
140
- db.incrementAppliedCount(id);
141
- }
142
-
143
- function removeLearning(id: string): void {
144
- graphStore?.removeEdgesFor(id);
145
- db.deleteLearning(id);
146
- }
147
-
148
- return {
149
- addLearning,
150
- getLearning,
151
- listLearnings,
152
- searchByText,
153
- searchByVector,
154
- recordApplication,
155
- removeLearning,
156
- isDuplicate,
157
- };
158
- }
@@ -1,87 +0,0 @@
1
- /**
2
- * Learning retrieval for context injection.
3
- * Supports vector similarity search with FTS5 fallback.
4
- */
5
-
6
- import { mmrRerank, type MMRItem } from "../../../../src/memory/mmr.js";
7
- import { calculateTemporalDecayMultiplier } from "../../../../src/memory/temporal-decay.js";
8
- import type { GraphRetrieval } from "../graph/graph-retrieval.js";
9
- import type { LearningRecord, LearningLoopConfig } from "../types.js";
10
- import type { LearningStore } from "./learning-store.js";
11
-
12
- export type RetrievalResult = {
13
- learning: LearningRecord;
14
- score: number;
15
- decayedScore: number;
16
- contested?: boolean;
17
- };
18
-
19
- /**
20
- * Retrieve relevant learnings for a given query.
21
- *
22
- * 1. Vector search (if embedding available) or FTS fallback
23
- * 2. Apply temporal decay
24
- * 3. MMR rerank for diversity
25
- * 4. Return top N
26
- */
27
- export function retrieveLearnings(params: {
28
- learningStore: LearningStore;
29
- config: LearningLoopConfig;
30
- queryEmbedding: number[] | null;
31
- queryText: string;
32
- limit?: number;
33
- graphRetrieval?: GraphRetrieval;
34
- }): RetrievalResult[] {
35
- const { learningStore, config, queryEmbedding, queryText, graphRetrieval } = params;
36
- const limit = params.limit ?? config.injection.maxLearnings;
37
- const minRelevance = config.injection.minRelevance;
38
- const halfLifeDays = config.decay.halfLifeDays;
39
- const nowMs = Date.now();
40
-
41
- // Step 1: Retrieve candidates
42
- let candidates: Array<LearningRecord & { score: number }>;
43
-
44
- if (queryEmbedding) {
45
- candidates = learningStore.searchByVector(queryEmbedding, limit * 3, minRelevance * 0.5);
46
- } else {
47
- // FTS fallback
48
- const textResults = learningStore.searchByText(queryText, limit * 3);
49
- candidates = textResults.map((r, i) => ({
50
- ...r,
51
- score: 1.0 - i * (0.8 / Math.max(1, textResults.length)),
52
- }));
53
- }
54
-
55
- if (candidates.length === 0) return [];
56
-
57
- // Step 2: Apply temporal decay
58
- const DAY_MS = 24 * 60 * 60 * 1000;
59
- const withDecay = candidates.map((c) => {
60
- const ageInDays = (nowMs - c.updatedAt) / DAY_MS;
61
- const decayMultiplier = calculateTemporalDecayMultiplier({ ageInDays, halfLifeDays });
62
- const decayedScore = c.score * decayMultiplier;
63
- return { learning: c, score: c.score, decayedScore };
64
- });
65
-
66
- // Filter by minimum relevance after decay
67
- const filtered = withDecay.filter((r) => r.decayedScore >= minRelevance);
68
- if (filtered.length === 0) return [];
69
-
70
- // Step 3: Graph expansion (if available)
71
- if (graphRetrieval) {
72
- return graphRetrieval.expandWithGraph(filtered, limit);
73
- }
74
-
75
- // Step 4: MMR rerank for diversity
76
- const mmrItems: (MMRItem & { original: RetrievalResult })[] = filtered.map((r) => ({
77
- id: r.learning.id,
78
- score: r.decayedScore,
79
- content: r.learning.content,
80
- original: r,
81
- }));
82
-
83
- const reranked = mmrRerank(mmrItems, { enabled: true, lambda: 0.7 });
84
-
85
- // Step 5: Take top N
86
- return reranked.slice(0, limit).map((item) => item.original);
87
- }
@@ -1,62 +0,0 @@
1
- /**
2
- * Wilson Score Interval for binomial proportions.
3
- *
4
- * Provides a confidence interval for a proportion derived from a binomial
5
- * distribution. Used to rank learnings by confidence: items with more
6
- * observations and higher success rates rank higher.
7
- *
8
- * Formula (lower bound, 95% CI):
9
- * (p + z^2/(2n) - z * sqrt(p*(1-p)/n + z^2/(4n^2))) / (1 + z^2/n)
10
- *
11
- * Complexity: O(1)
12
- */
13
-
14
- const Z_95 = 1.96;
15
-
16
- /**
17
- * Compute the lower bound of the Wilson score interval.
18
- *
19
- * @param successes - Number of positive outcomes
20
- * @param total - Total number of trials
21
- * @param z - Z-score for confidence level (default: 1.96 for 95%)
22
- * @returns Lower bound of the confidence interval in [0, 1]
23
- */
24
- export function wilsonScoreLowerBound(successes: number, total: number, z: number = Z_95): number {
25
- if (total <= 0) return 0;
26
-
27
- const p = successes / total;
28
- const z2 = z * z;
29
- const denominator = 1 + z2 / total;
30
-
31
- const numerator =
32
- p + z2 / (2 * total) - z * Math.sqrt((p * (1 - p)) / total + z2 / (4 * total * total));
33
-
34
- return Math.max(0, Math.min(1, numerator / denominator));
35
- }
36
-
37
- /**
38
- * Compute both bounds of the Wilson score interval.
39
- *
40
- * @param successes - Number of positive outcomes
41
- * @param total - Total number of trials
42
- * @param z - Z-score for confidence level (default: 1.96 for 95%)
43
- * @returns [lowerBound, upperBound] each in [0, 1]
44
- */
45
- export function wilsonScoreInterval(
46
- successes: number,
47
- total: number,
48
- z: number = Z_95,
49
- ): [number, number] {
50
- if (total <= 0) return [0, 0];
51
-
52
- const p = successes / total;
53
- const z2 = z * z;
54
- const denominator = 1 + z2 / total;
55
- const center = p + z2 / (2 * total);
56
- const spread = z * Math.sqrt((p * (1 - p)) / total + z2 / (4 * total * total));
57
-
58
- const lower = Math.max(0, (center - spread) / denominator);
59
- const upper = Math.min(1, (center + spread) / denominator);
60
-
61
- return [lower, upper];
62
- }
@@ -1,51 +0,0 @@
1
- /**
2
- * Exponentially Weighted Moving Average (EWMA).
3
- *
4
- * Maintains a running weighted average where recent observations
5
- * contribute more than older ones. O(1) per update.
6
- *
7
- * Formula: S_t = alpha * x_t + (1 - alpha) * S_{t-1}
8
- */
9
-
10
- export type EWMAState = {
11
- value: number;
12
- count: number;
13
- };
14
-
15
- const DEFAULT_ALPHA = 0.1;
16
-
17
- export function createEWMA(alpha: number = DEFAULT_ALPHA): EWMAState {
18
- return { value: 0, count: 0 };
19
- }
20
-
21
- /**
22
- * Update EWMA with a new observation. Returns a new state (immutable).
23
- * First observation initializes directly; subsequent observations use exponential weighting.
24
- *
25
- * Complexity: O(1)
26
- */
27
- export function updateEWMA(
28
- state: EWMAState,
29
- observation: number,
30
- alpha: number = DEFAULT_ALPHA,
31
- ): EWMAState {
32
- if (!Number.isFinite(observation)) return state;
33
-
34
- const clampedAlpha = Math.max(0, Math.min(1, alpha));
35
-
36
- if (state.count === 0) {
37
- return { value: observation, count: 1 };
38
- }
39
-
40
- return {
41
- value: clampedAlpha * observation + (1 - clampedAlpha) * state.value,
42
- count: state.count + 1,
43
- };
44
- }
45
-
46
- /**
47
- * Get the current EWMA value, or a fallback if no observations exist.
48
- */
49
- export function getEWMAValue(state: EWMAState, fallback: number = 0): number {
50
- return state.count === 0 ? fallback : state.value;
51
- }
@@ -1,42 +0,0 @@
1
- /**
2
- * Confidence-weighted arithmetic mean combiner.
3
- *
4
- * Combines multiple independent signals into a single score, where each
5
- * signal contributes proportionally to its weight AND confidence.
6
- *
7
- * Formula: score = sum(w_i * c_i * s_i) / sum(w_i * c_i)
8
- *
9
- * Complexity: O(k) where k = number of signals
10
- */
11
-
12
- export type WeightedSignal = {
13
- value: number;
14
- weight: number;
15
- confidence: number;
16
- };
17
-
18
- /**
19
- * Compute a confidence-weighted score from multiple signals.
20
- *
21
- * @param signals - Array of signals with value in [0,1], weight > 0, confidence in [0,1]
22
- * @returns Combined score in [0, 1], or 0 if no valid signals
23
- */
24
- export function computeWeightedScore(signals: WeightedSignal[]): number {
25
- let numerator = 0;
26
- let denominator = 0;
27
-
28
- for (const signal of signals) {
29
- if (signal.confidence <= 0 || signal.weight <= 0) continue;
30
- if (!Number.isFinite(signal.value)) continue;
31
-
32
- const clampedValue = Math.max(0, Math.min(1, signal.value));
33
- const clampedConfidence = Math.max(0, Math.min(1, signal.confidence));
34
- const effectiveWeight = signal.weight * clampedConfidence;
35
-
36
- numerator += effectiveWeight * clampedValue;
37
- denominator += effectiveWeight;
38
- }
39
-
40
- if (denominator === 0) return 0;
41
- return numerator / denominator;
42
- }