@symerian/symi 3.0.17 → 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.
- package/dist/{audio-preflight-CBDFctZN.js → audio-preflight-BfmZbg4Y.js} +4 -4
- package/dist/{audio-preflight-gsZSpG-6.js → audio-preflight-DcuC-liM.js} +4 -4
- package/dist/build-info.json +3 -3
- package/dist/bundled/boot-md/handler.js +8 -8
- package/dist/bundled/session-memory/handler.js +7 -7
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/{chrome-nPMY1XTJ.js → chrome-Bo7cbvFK.js} +5 -5
- package/dist/{chrome-BjVab8gM.js → chrome-DYp18Q0t.js} +5 -5
- package/dist/{deliver-D-QFqm31.js → deliver-ChSIbiMM.js} +1 -1
- package/dist/{deliver-B4-bcot9.js → deliver-DEgRQM4J.js} +1 -1
- package/dist/extensionAPI.js +7 -7
- package/dist/{image-CDwtQjmt.js → image-Bx-hvoNJ.js} +1 -1
- package/dist/{image-CcS-vzTA.js → image-CQl_mjWk.js} +1 -1
- package/dist/llm-slug-generator.js +7 -7
- package/dist/{manager-BnEdHzmO.js → manager-D_pn0urG.js} +1 -1
- package/dist/{manager-09r0qPze.js → manager-YQxK2t0C.js} +1 -1
- package/dist/{pi-embedded-CWsY69-4.js → pi-embedded-CLw_ZzEZ.js} +16 -16
- package/dist/{pi-embedded-helpers-BBMy-lqr.js → pi-embedded-helpers-B5I53aw6.js} +4 -4
- package/dist/{pi-embedded-helpers-ChEYbgVj.js → pi-embedded-helpers-sUAEIC9X.js} +4 -4
- package/dist/plugin-sdk/{accounts-BfyWsC_i.js → accounts-CWFytwbR.js} +3 -3
- package/dist/plugin-sdk/{active-listener-DcJW7xAT.js → active-listener-BkZ4jHrL.js} +2 -2
- package/dist/plugin-sdk/{agent-scope-ChbGV6of.js → agent-scope-C9gfY_Gk.js} +2 -2
- package/dist/plugin-sdk/{audio-preflight-D3GtNLqW.js → audio-preflight-HKbdzXLZ.js} +21 -21
- package/dist/plugin-sdk/{bindings-CN2Qmefj.js → bindings-BaKIqPPy.js} +2 -2
- package/dist/plugin-sdk/{channel-web-DTyqujjA.js → channel-web-D5nWiTH1.js} +18 -18
- package/dist/plugin-sdk/{chrome-BKzAKr3K.js → chrome-klTSnz-9.js} +3 -3
- package/dist/plugin-sdk/{chunk-DhDkBujV.js → chunk-BbrYSny_.js} +1 -1
- package/dist/plugin-sdk/{command-format-CVrYFyZS.js → command-format-BN6tyZt6.js} +1 -1
- package/dist/plugin-sdk/{commands-registry-17yfZkHZ.js → commands-registry-CTzKKtY6.js} +4 -4
- package/dist/plugin-sdk/{config-7wk65zKC.js → config-Crv2qEdJ.js} +9 -9
- package/dist/plugin-sdk/{consolidate-exbAW0ml.js → consolidate-DT1QH65Q.js} +2 -2
- package/dist/plugin-sdk/{deliver-TxAcw7J5.js → deliver-7rOvAlrc.js} +12 -12
- package/dist/plugin-sdk/{diagnostic-Debx4frd.js → diagnostic-0nsxhWp7.js} +1 -1
- package/dist/plugin-sdk/{fs-safe-wBYbAkJF.js → fs-safe-DfWYBeWF.js} +1 -1
- package/dist/plugin-sdk/{gemini-auth-7U2pm2Ky.js → gemini-auth-C0N0_u49.js} +1 -1
- package/dist/plugin-sdk/{image-BtDVmYA5.js → image-WOSl2apK.js} +4 -4
- package/dist/plugin-sdk/index.js +43 -43
- package/dist/plugin-sdk/{ir-CKMvRrGW.js → ir-9J84MTls.js} +4 -4
- package/dist/plugin-sdk/{local-roots-c_gaPs01.js → local-roots-OLRDbvyY.js} +3 -3
- package/dist/plugin-sdk/{login-DUym1Jy0.js → login-C7x4q0i2.js} +7 -7
- package/dist/plugin-sdk/{login-qr-B-WBdvrX.js → login-qr-Dv5_MoAW.js} +9 -9
- package/dist/plugin-sdk/{manager-B71SCzos.js → manager-C83tK17x.js} +8 -8
- package/dist/plugin-sdk/{manifest-registry-Dnic6Chh.js → manifest-registry-CJMV-PI7.js} +1 -1
- package/dist/plugin-sdk/{markdown-tables-Dur7OTlM.js → markdown-tables-DXNKz5y_.js} +1 -1
- package/dist/plugin-sdk/{message-channel-BrAhJJV_.js → message-channel-aGy1HbQQ.js} +1 -1
- package/dist/plugin-sdk/{model-selection-B9qaVQSJ.js → model-selection-C-3-tpe7.js} +4 -4
- package/dist/plugin-sdk/{outbound-DB1wDM8b.js → outbound-DquCeSy5.js} +6 -6
- package/dist/plugin-sdk/{pi-auth-json-ZO118hoy.js → pi-auth-json-D9PDCXGn.js} +1 -1
- package/dist/plugin-sdk/{pi-embedded-helpers-s_U0Un7j.js → pi-embedded-helpers-D3ygfH7l.js} +16 -16
- package/dist/plugin-sdk/{plugins-DF81oSaI.js → plugins-DOwnSg9D.js} +4 -4
- package/dist/plugin-sdk/{pw-ai-CTwP02uv.js → pw-ai-rlengLjb.js} +8 -8
- package/dist/plugin-sdk/{qmd-manager-CBaSGant.js → qmd-manager-BzxFjRFa.js} +4 -4
- package/dist/plugin-sdk/{registry-CZVURNhF.js → registry-5iFfixlB.js} +2 -2
- package/dist/plugin-sdk/{replies-hwRbkU3z.js → replies-BXOzO_H5.js} +7 -7
- package/dist/plugin-sdk/{reply-prefix-CaXmzZlx.js → reply-prefix-INAKTqCU.js} +1 -1
- package/dist/plugin-sdk/{resolve-outbound-target-fxVSOBmk.js → resolve-outbound-target-DvbxHtqp.js} +2 -2
- package/dist/plugin-sdk/{resolve-route-ClCyiOeu.js → resolve-route-URXlY3AK.js} +3 -3
- package/dist/plugin-sdk/{runner-Cq5jvwQ7.js → runner-Bv0_DWoH.js} +9 -9
- package/dist/plugin-sdk/{session-B_TkB65Y.js → session-C3r8l7ou.js} +4 -4
- package/dist/plugin-sdk/{skill-commands-0LF9HTGr.js → skill-commands-KjLUGIdZ.js} +5 -5
- package/dist/plugin-sdk/{skills-BIT_O7J0.js → skills-BrsD4L5c.js} +7 -7
- package/dist/plugin-sdk/{sqlite-Bx5Y5U5X.js → sqlite-CjW7ME1H.js} +1 -1
- package/dist/plugin-sdk/{subsystem-CXqYeDy-.js → subsystem-DcOg1xJr.js} +1 -1
- package/dist/plugin-sdk/{synthesis-DtsYAj1E.js → synthesis-CY7YAasV.js} +38 -38
- package/dist/plugin-sdk/{target-errors-B8mokOeH.js → target-errors-BVWJGWFq.js} +2 -2
- package/dist/plugin-sdk/{thinking-Ca0DhqzO.js → thinking-CtsTDPOi.js} +3 -3
- package/dist/plugin-sdk/{tokens-CvlONEqh.js → tokens-8lqOTZCB.js} +1 -1
- package/dist/plugin-sdk/{tool-images-DpBaWEHT.js → tool-images-Cl_rGIUZ.js} +2 -2
- package/dist/plugin-sdk/{tool-loop-detection-BOvUFa0f.js → tool-loop-detection-Da4WUT_P.js} +2 -2
- package/dist/plugin-sdk/{unified-runner-CnM7lyNd.js → unified-runner-nwMnsZyj.js} +60 -60
- package/dist/plugin-sdk/web-BlweOZDp.js +54 -0
- package/dist/plugin-sdk/{whatsapp-actions-CvnfsFJm.js → whatsapp-actions-DpfaGYs7.js} +21 -21
- package/dist/{pw-ai-BW8_KeDf.js → pw-ai-BqxJG-Wh.js} +1 -1
- package/dist/{pw-ai-j9IE1K0-.js → pw-ai-C-NSGye0.js} +1 -1
- package/dist/{runner-8ALr2UII.js → runner-COGFTeDw.js} +1 -1
- package/dist/{runner-C4-9kFdR.js → runner-DhCi2lT1.js} +1 -1
- package/dist/{synthesis-Cph3LhA1.js → synthesis-CXZu24Vx.js} +7 -7
- package/dist/{synthesis-Cus0A2dL.js → synthesis-DrPxcMlQ.js} +7 -7
- package/dist/{unified-runner-CX80YMTk.js → unified-runner-iByUazvW.js} +16 -16
- package/dist/{web-ChozvJ7I.js → web-EsMQBIYf.js} +7 -7
- package/dist/{web-DFlsbXmQ.js → web-PPg5y6xI.js} +7 -7
- package/package.json +1 -1
- package/dist/plugin-sdk/web-CIPJBHAU.js +0 -54
- package/extensions/copilot-proxy/README.md +0 -24
- package/extensions/copilot-proxy/index.ts +0 -154
- package/extensions/copilot-proxy/node_modules/.bin/symi +0 -21
- package/extensions/copilot-proxy/package.json +0 -15
- package/extensions/copilot-proxy/symi.plugin.json +0 -9
- package/extensions/device-pair/index.ts +0 -642
- package/extensions/device-pair/symi.plugin.json +0 -20
- package/extensions/diagnostics-otel/index.ts +0 -15
- package/extensions/diagnostics-otel/node_modules/.bin/acorn +0 -21
- package/extensions/diagnostics-otel/node_modules/.bin/symi +0 -21
- package/extensions/diagnostics-otel/package.json +0 -27
- package/extensions/diagnostics-otel/src/service.test.ts +0 -290
- package/extensions/diagnostics-otel/src/service.ts +0 -666
- package/extensions/diagnostics-otel/symi.plugin.json +0 -8
- package/extensions/google-antigravity-auth/README.md +0 -24
- package/extensions/google-antigravity-auth/index.ts +0 -424
- package/extensions/google-antigravity-auth/node_modules/.bin/symi +0 -21
- package/extensions/google-antigravity-auth/package.json +0 -15
- package/extensions/google-antigravity-auth/symi.plugin.json +0 -9
- package/extensions/google-gemini-cli-auth/README.md +0 -35
- package/extensions/google-gemini-cli-auth/index.ts +0 -75
- package/extensions/google-gemini-cli-auth/node_modules/.bin/symi +0 -21
- package/extensions/google-gemini-cli-auth/oauth.test.ts +0 -162
- package/extensions/google-gemini-cli-auth/oauth.ts +0 -636
- package/extensions/google-gemini-cli-auth/package.json +0 -15
- package/extensions/google-gemini-cli-auth/symi.plugin.json +0 -9
- package/extensions/learning-loop/index.ts +0 -159
- package/extensions/learning-loop/node_modules/.bin/symi +0 -21
- package/extensions/learning-loop/package.json +0 -18
- package/extensions/learning-loop/src/analytics/gateway-methods.ts +0 -230
- package/extensions/learning-loop/src/analytics/metrics-aggregator.ts +0 -153
- package/extensions/learning-loop/src/capture/run-tracker.ts +0 -181
- package/extensions/learning-loop/src/capture/serializer.ts +0 -74
- package/extensions/learning-loop/src/db.ts +0 -583
- package/extensions/learning-loop/src/feedback/explicit-feedback.ts +0 -58
- package/extensions/learning-loop/src/feedback/implicit-signals.ts +0 -89
- package/extensions/learning-loop/src/graph/edge-inference.ts +0 -189
- package/extensions/learning-loop/src/graph/graph-retrieval.ts +0 -144
- package/extensions/learning-loop/src/graph/graph-store.ts +0 -183
- package/extensions/learning-loop/src/hooks.ts +0 -244
- package/extensions/learning-loop/src/injection/cache.ts +0 -73
- package/extensions/learning-loop/src/injection/context-injector.ts +0 -104
- package/extensions/learning-loop/src/injection/prompt-builder.ts +0 -43
- package/extensions/learning-loop/src/learning/embedding-bridge.ts +0 -54
- package/extensions/learning-loop/src/learning/learning-extractor.ts +0 -217
- package/extensions/learning-loop/src/learning/learning-store.ts +0 -158
- package/extensions/learning-loop/src/learning/retrieval.ts +0 -87
- package/extensions/learning-loop/src/math/confidence-intervals.ts +0 -62
- package/extensions/learning-loop/src/math/ewma.ts +0 -51
- package/extensions/learning-loop/src/math/weighted-scorer.ts +0 -42
- package/extensions/learning-loop/src/schema.ts +0 -176
- package/extensions/learning-loop/src/scoring/normalization.ts +0 -32
- package/extensions/learning-loop/src/scoring/quality-engine.ts +0 -78
- package/extensions/learning-loop/src/scoring/signal-extractors.ts +0 -155
- package/extensions/learning-loop/src/test/context-injector.test.ts +0 -142
- package/extensions/learning-loop/src/test/fixes.test.ts +0 -1286
- package/extensions/learning-loop/src/test/graph.test.ts +0 -711
- package/extensions/learning-loop/src/test/integration.test.ts +0 -312
- package/extensions/learning-loop/src/test/learning-store.test.ts +0 -191
- package/extensions/learning-loop/src/test/math.test.ts +0 -148
- package/extensions/learning-loop/src/test/quality-engine.test.ts +0 -231
- package/extensions/learning-loop/src/test/run-tracker.test.ts +0 -143
- package/extensions/learning-loop/src/types.ts +0 -281
- package/extensions/learning-loop/symi.plugin.json +0 -46
- package/extensions/llm-task/README.md +0 -97
- package/extensions/llm-task/index.ts +0 -6
- package/extensions/llm-task/package.json +0 -12
- package/extensions/llm-task/src/llm-task-tool.test.ts +0 -138
- package/extensions/llm-task/src/llm-task-tool.ts +0 -249
- package/extensions/llm-task/symi.plugin.json +0 -21
- package/extensions/memory-lancedb/config.ts +0 -161
- package/extensions/memory-lancedb/index.test.ts +0 -330
- package/extensions/memory-lancedb/index.ts +0 -670
- package/extensions/memory-lancedb/node_modules/.bin/arrow2csv +0 -21
- package/extensions/memory-lancedb/node_modules/.bin/openai +0 -21
- package/extensions/memory-lancedb/node_modules/.bin/symi +0 -21
- package/extensions/memory-lancedb/package.json +0 -20
- package/extensions/memory-lancedb/symi.plugin.json +0 -71
- package/extensions/minimax-portal-auth/README.md +0 -33
- package/extensions/minimax-portal-auth/index.ts +0 -161
- package/extensions/minimax-portal-auth/node_modules/.bin/symi +0 -21
- package/extensions/minimax-portal-auth/oauth.ts +0 -247
- package/extensions/minimax-portal-auth/package.json +0 -15
- package/extensions/minimax-portal-auth/symi.plugin.json +0 -9
- package/extensions/model-equalizer/index.ts +0 -80
- package/extensions/model-equalizer/skills/model-equalizer/SKILL.md +0 -58
- package/extensions/model-equalizer/src/detection.ts +0 -62
- package/extensions/model-equalizer/src/enhancer.ts +0 -63
- package/extensions/model-equalizer/src/test/detection.test.ts +0 -218
- package/extensions/model-equalizer/src/test/enhancer.test.ts +0 -137
- package/extensions/model-equalizer/src/test/integration.test.ts +0 -185
- package/extensions/model-equalizer/src/types.ts +0 -24
- package/extensions/model-equalizer/symi.plugin.json +0 -12
- package/extensions/phone-control/index.ts +0 -421
- package/extensions/phone-control/symi.plugin.json +0 -10
- package/extensions/pipeline/README.md +0 -75
- package/extensions/pipeline/SKILL.md +0 -97
- package/extensions/pipeline/index.ts +0 -18
- package/extensions/pipeline/package.json +0 -11
- package/extensions/pipeline/src/pipeline-tool.test.ts +0 -345
- package/extensions/pipeline/src/pipeline-tool.ts +0 -266
- package/extensions/pipeline/src/windows-spawn.test.ts +0 -148
- package/extensions/pipeline/src/windows-spawn.ts +0 -193
- package/extensions/pipeline/symi.plugin.json +0 -10
- package/extensions/qwen-portal-auth/README.md +0 -24
- package/extensions/qwen-portal-auth/index.ts +0 -134
- package/extensions/qwen-portal-auth/oauth.ts +0 -190
- package/extensions/qwen-portal-auth/symi.plugin.json +0 -9
- package/extensions/talk-voice/index.ts +0 -150
- package/extensions/talk-voice/symi.plugin.json +0 -10
- package/extensions/thread-ownership/index.test.ts +0 -180
- package/extensions/thread-ownership/index.ts +0 -133
- package/extensions/thread-ownership/symi.plugin.json +0 -28
- package/skills/1password/SKILL.md +0 -71
- package/skills/1password/references/cli-examples.md +0 -29
- package/skills/1password/references/get-started.md +0 -17
- package/skills/apple-notes/SKILL.md +0 -78
- package/skills/apple-reminders/SKILL.md +0 -119
- package/skills/bear-notes/SKILL.md +0 -108
- package/skills/blogwatcher/SKILL.md +0 -70
- package/skills/blucli/SKILL.md +0 -48
- package/skills/bluebubbles/SKILL.md +0 -132
- package/skills/camsnap/SKILL.md +0 -46
- package/skills/canvas/SKILL.md +0 -204
- package/skills/connect-email/SKILL.md +0 -142
- package/skills/document-generation/SKILL.md +0 -83
- package/skills/eightctl/SKILL.md +0 -51
- package/skills/food-order/SKILL.md +0 -49
- package/skills/gemini/SKILL.md +0 -44
- package/skills/gh-issues/SKILL.md +0 -865
- package/skills/gifgrep/SKILL.md +0 -80
- package/skills/github/SKILL.md +0 -164
- package/skills/gog/SKILL.md +0 -117
- package/skills/goplaces/SKILL.md +0 -53
- package/skills/healthcheck/SKILL.md +0 -246
- package/skills/himalaya/SKILL.md +0 -258
- package/skills/himalaya/references/configuration.md +0 -184
- package/skills/himalaya/references/message-composition.md +0 -199
- package/skills/imsg/SKILL.md +0 -122
- package/skills/long-task/SKILL.md +0 -58
- package/skills/long-task/scripts/detach-task.sh +0 -187
- package/skills/nano-banana-pro/SKILL.md +0 -59
- package/skills/nano-banana-pro/scripts/generate_image.py +0 -184
- package/skills/nano-pdf/SKILL.md +0 -39
- package/skills/notion/SKILL.md +0 -173
- package/skills/obsidian/SKILL.md +0 -82
- package/skills/openai-image-gen/SKILL.md +0 -90
- package/skills/openai-image-gen/scripts/gen.py +0 -240
- package/skills/openai-whisper/SKILL.md +0 -39
- package/skills/openai-whisper-api/SKILL.md +0 -53
- package/skills/openai-whisper-api/scripts/transcribe.sh +0 -85
- package/skills/openhue/SKILL.md +0 -113
- package/skills/oracle/SKILL.md +0 -126
- package/skills/ordercli/SKILL.md +0 -79
- package/skills/peekaboo/SKILL.md +0 -191
- package/skills/reactions-extensive/SKILL.md +0 -30
- package/skills/reactions-minimal/SKILL.md +0 -31
- package/skills/safe-edit/SKILL.md +0 -51
- package/skills/sag/SKILL.md +0 -88
- package/skills/sherpa-onnx-tts/SKILL.md +0 -104
- package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +0 -178
- package/skills/songsee/SKILL.md +0 -50
- package/skills/sonoscli/SKILL.md +0 -66
- package/skills/spotify-player/SKILL.md +0 -65
- package/skills/symihub/SKILL.md +0 -78
- package/skills/things-mac/SKILL.md +0 -87
- package/skills/tmux/SKILL.md +0 -153
- package/skills/tmux/scripts/find-sessions.sh +0 -112
- package/skills/tmux/scripts/wait-for-text.sh +0 -83
- package/skills/trello/SKILL.md +0 -96
- package/skills/video-frames/SKILL.md +0 -47
- package/skills/video-frames/scripts/frame.sh +0 -81
- package/skills/voice-call/SKILL.md +0 -46
- package/skills/wacli/SKILL.md +0 -73
- package/skills/weather/SKILL.md +0 -113
- package/skills/xurl/SKILL.md +0 -462
|
@@ -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
|
-
}
|