@yugenlab/vaayu 0.1.0 → 0.1.2
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/chunks/{chunk-L7JICQBW.js → chunk-2ARPXEDC.js} +5 -3
- package/chunks/{chunk-IIET2K6D.js → chunk-5Z2BKSFF.js} +81 -603
- package/chunks/{chunk-MINFB5LT.js → chunk-C76USAC5.js} +30 -6
- package/chunks/{chunk-URGEODS5.js → chunk-DQMAQ2VL.js} +4 -4
- package/chunks/{chunk-JZU37VQ5.js → chunk-F35MWELH.js} +6 -4
- package/chunks/chunk-F4T7POKM.js +545 -0
- package/chunks/{chunk-H76V36OF.js → chunk-FPNQLJLD.js} +4 -4
- package/chunks/{chunk-KDRROLVN.js → chunk-NBXCXQ3H.js} +2 -2
- package/chunks/{chunk-YSU3BWV6.js → chunk-OWBBY5XP.js} +2 -2
- package/chunks/{chunk-SMVJRPAH.js → chunk-PRXQW76U.js} +46 -6
- package/chunks/{chunk-S4TBVCL2.js → chunk-SLA2OIMG.js} +5 -3
- package/chunks/chunk-UQLPHNGH.js +123 -0
- package/chunks/{chunk-ITIVYGUG.js → chunk-UW6E7IC4.js} +6 -4
- package/chunks/{chunk-HAPVUJ6A.js → chunk-XRHUKKBC.js} +9 -7
- package/chunks/{chunk-U6OLJ36B.js → chunk-YJRXLRTE.js} +21 -122
- package/chunks/{consolidation-indexer-TOTTDZXW.js → consolidation-indexer-A46RJU4R.js} +6 -5
- package/chunks/{day-consolidation-NKO63HZQ.js → day-consolidation-GQ2FDCR2.js} +2 -2
- package/chunks/{graphrag-ZI2FSU7S.js → graphrag-6YZ5YPLK.js} +4 -3
- package/chunks/{hierarchical-temporal-search-ZD46UMKR.js → hierarchical-temporal-search-VA4D3SON.js} +2 -2
- package/chunks/{hybrid-search-ZVLZVGFS.js → hybrid-search-6XMUT66S.js} +6 -5
- package/chunks/periodic-consolidation-N5MR77ZN.js +11 -0
- package/chunks/{postgres-3ZXBYTPC.js → postgres-WLH3D5HG.js} +2 -2
- package/chunks/{recall-GMVHWQWW.js → recall-THTI6ZO2.js} +5 -4
- package/chunks/{search-7HZETVMZ.js → search-V7DJ3VNL.js} +5 -4
- package/chunks/{session-store-XKPGKXUS.js → session-store-GRKGTMHI.js} +4 -3
- package/chunks/{sqlite-JPF5TICX.js → sqlite-DHUQGPR5.js} +2 -2
- package/chunks/{src-QAXOD5SB.js → src-54LTTDTH.js} +18 -14
- package/chunks/vasana-engine-Z4RXW2SB.js +10 -0
- package/gateway.js +1429 -264
- package/package.json +1 -1
- package/chunks/periodic-consolidation-BPKOZDGB.js +0 -10
|
@@ -133,8 +133,12 @@ async function runMigrations(pool) {
|
|
|
133
133
|
await pool.query("ALTER TABLE agent_runs ADD COLUMN IF NOT EXISTS agent_id TEXT");
|
|
134
134
|
await pool.query("ALTER TABLE agent_runs ADD COLUMN IF NOT EXISTS parent_agent_id TEXT");
|
|
135
135
|
await pool.query(
|
|
136
|
-
"CREATE TABLE IF NOT EXISTS session_routes (session_id TEXT PRIMARY KEY, agent_id TEXT, lane_id TEXT, queue_mode TEXT, announce_mode TEXT, updated_at TEXT)"
|
|
136
|
+
"CREATE TABLE IF NOT EXISTS session_routes (session_id TEXT PRIMARY KEY, agent_id TEXT, lane_id TEXT, queue_mode TEXT, announce_mode TEXT, pin_provider_id TEXT, pin_model TEXT, pin_reason TEXT, pin_until TEXT, updated_at TEXT)"
|
|
137
137
|
);
|
|
138
|
+
await pool.query("ALTER TABLE session_routes ADD COLUMN IF NOT EXISTS pin_provider_id TEXT");
|
|
139
|
+
await pool.query("ALTER TABLE session_routes ADD COLUMN IF NOT EXISTS pin_model TEXT");
|
|
140
|
+
await pool.query("ALTER TABLE session_routes ADD COLUMN IF NOT EXISTS pin_reason TEXT");
|
|
141
|
+
await pool.query("ALTER TABLE session_routes ADD COLUMN IF NOT EXISTS pin_until TEXT");
|
|
138
142
|
await pool.query(
|
|
139
143
|
"CREATE INDEX IF NOT EXISTS session_routes_updated_idx ON session_routes(updated_at)"
|
|
140
144
|
);
|
|
@@ -1238,14 +1242,30 @@ var createSessionInboxStore = (pool) => {
|
|
|
1238
1242
|
};
|
|
1239
1243
|
|
|
1240
1244
|
// packages/storage/src/postgres/session-routes.ts
|
|
1241
|
-
var selectRoute = "SELECT session_id, agent_id, lane_id, queue_mode, announce_mode, updated_at ";
|
|
1245
|
+
var selectRoute = "SELECT session_id, agent_id, lane_id, queue_mode, announce_mode, pin_provider_id, pin_model, pin_reason, pin_until, updated_at ";
|
|
1246
|
+
var mapQueueMode = (value) => {
|
|
1247
|
+
if (value === "followup" || value === "collect" || value === "steer" || value === "drop" || value === "reject") {
|
|
1248
|
+
return value;
|
|
1249
|
+
}
|
|
1250
|
+
return void 0;
|
|
1251
|
+
};
|
|
1252
|
+
var mapAnnounceMode = (value) => {
|
|
1253
|
+
if (value === "hub" || value === "channel" || value === "none") {
|
|
1254
|
+
return value;
|
|
1255
|
+
}
|
|
1256
|
+
return void 0;
|
|
1257
|
+
};
|
|
1242
1258
|
var mapRoute = (row) => {
|
|
1243
1259
|
return {
|
|
1244
1260
|
sessionId: String(row.session_id),
|
|
1245
1261
|
agentId: row.agent_id ? String(row.agent_id) : void 0,
|
|
1246
1262
|
laneId: row.lane_id ? String(row.lane_id) : void 0,
|
|
1247
|
-
queueMode:
|
|
1248
|
-
announceMode:
|
|
1263
|
+
queueMode: mapQueueMode(row.queue_mode),
|
|
1264
|
+
announceMode: mapAnnounceMode(row.announce_mode),
|
|
1265
|
+
pinProviderId: row.pin_provider_id ? String(row.pin_provider_id) : void 0,
|
|
1266
|
+
pinModel: row.pin_model ? String(row.pin_model) : void 0,
|
|
1267
|
+
pinReason: row.pin_reason ? String(row.pin_reason) : void 0,
|
|
1268
|
+
pinUntil: row.pin_until ? String(row.pin_until) : void 0,
|
|
1249
1269
|
updatedAt: row.updated_at ? String(row.updated_at) : (/* @__PURE__ */ new Date()).toISOString()
|
|
1250
1270
|
};
|
|
1251
1271
|
};
|
|
@@ -1260,13 +1280,17 @@ var createSessionRouteStore = (pool) => {
|
|
|
1260
1280
|
async setSessionRoute(sessionId, route) {
|
|
1261
1281
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1262
1282
|
const result = await pool.query(
|
|
1263
|
-
`INSERT INTO session_routes (session_id, agent_id, lane_id, queue_mode, announce_mode, updated_at) VALUES ($1,$2,$3,$4,$5,$6) ON CONFLICT (session_id) DO UPDATE SET agent_id = EXCLUDED.agent_id, lane_id = EXCLUDED.lane_id, queue_mode = EXCLUDED.queue_mode, announce_mode = EXCLUDED.announce_mode, updated_at = EXCLUDED.updated_at RETURNING ${selectRoute.replace("SELECT ", "")}`,
|
|
1283
|
+
`INSERT INTO session_routes (session_id, agent_id, lane_id, queue_mode, announce_mode, pin_provider_id, pin_model, pin_reason, pin_until, updated_at) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) ON CONFLICT (session_id) DO UPDATE SET agent_id = EXCLUDED.agent_id, lane_id = EXCLUDED.lane_id, queue_mode = EXCLUDED.queue_mode, announce_mode = EXCLUDED.announce_mode, pin_provider_id = EXCLUDED.pin_provider_id, pin_model = EXCLUDED.pin_model, pin_reason = EXCLUDED.pin_reason, pin_until = EXCLUDED.pin_until, updated_at = EXCLUDED.updated_at RETURNING ${selectRoute.replace("SELECT ", "")}`,
|
|
1264
1284
|
[
|
|
1265
1285
|
sessionId,
|
|
1266
1286
|
route.agentId ?? null,
|
|
1267
1287
|
route.laneId ?? null,
|
|
1268
1288
|
route.queueMode ?? null,
|
|
1269
1289
|
route.announceMode ?? null,
|
|
1290
|
+
route.pinProviderId ?? null,
|
|
1291
|
+
route.pinModel ?? null,
|
|
1292
|
+
route.pinReason ?? null,
|
|
1293
|
+
route.pinUntil ?? null,
|
|
1270
1294
|
updatedAt
|
|
1271
1295
|
]
|
|
1272
1296
|
);
|
|
@@ -1476,4 +1500,4 @@ var PostgresStorage = class _PostgresStorage {
|
|
|
1476
1500
|
export {
|
|
1477
1501
|
PostgresStorage
|
|
1478
1502
|
};
|
|
1479
|
-
//# sourceMappingURL=chunk-
|
|
1503
|
+
//# sourceMappingURL=chunk-C76USAC5.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DatabaseManager
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UQLPHNGH.js";
|
|
4
4
|
import {
|
|
5
5
|
getChitraguptaHome
|
|
6
6
|
} from "./chunk-KC6NRZ7U.js";
|
|
@@ -135,7 +135,7 @@ var PeriodicConsolidation = class {
|
|
|
135
135
|
const filePath = this.getReportPath("monthly", period);
|
|
136
136
|
this._writeReport(filePath, markdown);
|
|
137
137
|
try {
|
|
138
|
-
const { indexConsolidationSummary } = await import("./consolidation-indexer-
|
|
138
|
+
const { indexConsolidationSummary } = await import("./consolidation-indexer-A46RJU4R.js");
|
|
139
139
|
await indexConsolidationSummary("monthly", period, markdown, this._project);
|
|
140
140
|
} catch {
|
|
141
141
|
}
|
|
@@ -278,7 +278,7 @@ var PeriodicConsolidation = class {
|
|
|
278
278
|
const filePath = this.getReportPath("yearly", period);
|
|
279
279
|
this._writeReport(filePath, markdown);
|
|
280
280
|
try {
|
|
281
|
-
const { indexConsolidationSummary } = await import("./consolidation-indexer-
|
|
281
|
+
const { indexConsolidationSummary } = await import("./consolidation-indexer-A46RJU4R.js");
|
|
282
282
|
await indexConsolidationSummary("yearly", period, markdown, this._project);
|
|
283
283
|
} catch {
|
|
284
284
|
}
|
|
@@ -749,4 +749,4 @@ var PeriodicConsolidation = class {
|
|
|
749
749
|
export {
|
|
750
750
|
PeriodicConsolidation
|
|
751
751
|
};
|
|
752
|
-
//# sourceMappingURL=chunk-
|
|
752
|
+
//# sourceMappingURL=chunk-DQMAQ2VL.js.map
|
|
@@ -6,11 +6,13 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
listSessions,
|
|
8
8
|
loadSession
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-2ARPXEDC.js";
|
|
10
10
|
import {
|
|
11
|
-
DatabaseManager,
|
|
12
11
|
initVectorsSchema
|
|
13
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-YJRXLRTE.js";
|
|
13
|
+
import {
|
|
14
|
+
DatabaseManager
|
|
15
|
+
} from "./chunk-UQLPHNGH.js";
|
|
14
16
|
import {
|
|
15
17
|
getChitraguptaHome
|
|
16
18
|
} from "./chunk-KC6NRZ7U.js";
|
|
@@ -711,4 +713,4 @@ export {
|
|
|
711
713
|
RecallEngine,
|
|
712
714
|
migrateEmbeddingsJson
|
|
713
715
|
};
|
|
714
|
-
//# sourceMappingURL=chunk-
|
|
716
|
+
//# sourceMappingURL=chunk-F35MWELH.js.map
|
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DatabaseManager
|
|
3
|
+
} from "./chunk-UQLPHNGH.js";
|
|
4
|
+
|
|
5
|
+
// ../chitragupta/packages/smriti/src/vasana-engine.ts
|
|
6
|
+
var FNV_OFFSET = 2166136261;
|
|
7
|
+
var FNV_PRIME = 16777619;
|
|
8
|
+
function fnv1a(input) {
|
|
9
|
+
let hash = FNV_OFFSET;
|
|
10
|
+
for (let i = 0; i < input.length; i++) {
|
|
11
|
+
hash ^= input.charCodeAt(i);
|
|
12
|
+
hash = Math.imul(hash, FNV_PRIME) >>> 0;
|
|
13
|
+
}
|
|
14
|
+
return hash.toString(16).padStart(8, "0");
|
|
15
|
+
}
|
|
16
|
+
var DEFAULT_CONFIG = {
|
|
17
|
+
lambda: 50,
|
|
18
|
+
changePointThreshold: 0.3,
|
|
19
|
+
stabilityWindow: 5,
|
|
20
|
+
windowSize: 20,
|
|
21
|
+
holdoutTrainRatio: 0.7,
|
|
22
|
+
accuracyThreshold: 0.6,
|
|
23
|
+
decayHalfLifeMs: 30 * 864e5,
|
|
24
|
+
promotionMinProjects: 3,
|
|
25
|
+
maxRunLength: 200,
|
|
26
|
+
priorMu: 0,
|
|
27
|
+
priorKappa: 1,
|
|
28
|
+
priorAlpha: 1,
|
|
29
|
+
anomalyRevertWindow: 3,
|
|
30
|
+
anomalyConfirmRatio: 0.5
|
|
31
|
+
};
|
|
32
|
+
var HARD_CEILINGS = {
|
|
33
|
+
windowSize: 500,
|
|
34
|
+
maxRunLength: 2e3,
|
|
35
|
+
stabilityWindow: 100
|
|
36
|
+
};
|
|
37
|
+
function logsumexp(xs) {
|
|
38
|
+
if (xs.length === 0) return -Infinity;
|
|
39
|
+
const m = Math.max(...xs);
|
|
40
|
+
if (m === -Infinity) return -Infinity;
|
|
41
|
+
let s = 0;
|
|
42
|
+
for (let i = 0; i < xs.length; i++) s += Math.exp(xs[i] - m);
|
|
43
|
+
return m + Math.log(s);
|
|
44
|
+
}
|
|
45
|
+
function lgamma(z) {
|
|
46
|
+
const c = [
|
|
47
|
+
0.9999999999998099,
|
|
48
|
+
676.5203681218851,
|
|
49
|
+
-1259.1392167224028,
|
|
50
|
+
771.3234287776531,
|
|
51
|
+
-176.6150291621406,
|
|
52
|
+
12.507343278686905,
|
|
53
|
+
-0.13857109526572012,
|
|
54
|
+
9984369578019572e-21,
|
|
55
|
+
15056327351493116e-23
|
|
56
|
+
];
|
|
57
|
+
if (z < 0.5) return Math.log(Math.PI / Math.sin(Math.PI * z)) - lgamma(1 - z);
|
|
58
|
+
z -= 1;
|
|
59
|
+
let x = c[0];
|
|
60
|
+
for (let i = 1; i < 9; i++) x += c[i] / (z + i);
|
|
61
|
+
const t = z + 7.5;
|
|
62
|
+
return 0.5 * Math.log(2 * Math.PI) + (z + 0.5) * Math.log(t) - t + Math.log(x);
|
|
63
|
+
}
|
|
64
|
+
function logStudentT(x, nu, mu, sigma) {
|
|
65
|
+
const safeSigma = sigma > 1e-15 ? sigma : 1e-15;
|
|
66
|
+
const z = (x - mu) / safeSigma;
|
|
67
|
+
return lgamma((nu + 1) / 2) - lgamma(nu / 2) - 0.5 * Math.log(nu * Math.PI * safeSigma * safeSigma) - (nu + 1) / 2 * Math.log(1 + z * z / nu);
|
|
68
|
+
}
|
|
69
|
+
var VasanaEngine = class {
|
|
70
|
+
cfg;
|
|
71
|
+
states = /* @__PURE__ */ new Map();
|
|
72
|
+
obs = /* @__PURE__ */ new Map();
|
|
73
|
+
cache = /* @__PURE__ */ new Map();
|
|
74
|
+
constructor(config) {
|
|
75
|
+
const merged = { ...DEFAULT_CONFIG, ...config };
|
|
76
|
+
for (const [k, ceil] of Object.entries(HARD_CEILINGS)) {
|
|
77
|
+
const key = k;
|
|
78
|
+
if (typeof merged[key] === "number" && typeof ceil === "number")
|
|
79
|
+
merged[key] = Math.min(merged[key], ceil);
|
|
80
|
+
}
|
|
81
|
+
this.cfg = merged;
|
|
82
|
+
}
|
|
83
|
+
// ── Core API ─────────────────────────────────────────────────────────
|
|
84
|
+
/** Observe a samskara: extract features and update BOCPD state per dimension. */
|
|
85
|
+
observe(samskara) {
|
|
86
|
+
for (const [feat, val] of this.extractFeatures(samskara)) {
|
|
87
|
+
if (!this.states.has(feat)) this.states.set(feat, this.initState());
|
|
88
|
+
const buf = this.obs.get(feat) ?? [];
|
|
89
|
+
buf.push(val);
|
|
90
|
+
if (buf.length > this.cfg.windowSize) buf.splice(0, buf.length - this.cfg.windowSize);
|
|
91
|
+
this.obs.set(feat, buf);
|
|
92
|
+
this.updateBOCPD(feat, val);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Run crystallization: stability check, holdout validation, vasana upsert. */
|
|
96
|
+
crystallize(project) {
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
const res = { created: [], reinforced: [], pending: [], changePoints: [], anomalies: [], timestamp: now };
|
|
99
|
+
const db = DatabaseManager.instance().get("agent");
|
|
100
|
+
const rows = db.prepare(
|
|
101
|
+
`SELECT id, session_id, pattern_type, pattern_content, observation_count,
|
|
102
|
+
confidence, pramana_type, project, created_at, updated_at
|
|
103
|
+
FROM samskaras WHERE project = ? OR project IS NULL
|
|
104
|
+
ORDER BY updated_at DESC LIMIT ?`
|
|
105
|
+
).all(project, this.cfg.windowSize * 10);
|
|
106
|
+
for (const [key, ids] of this.clusterSamskaras(rows)) {
|
|
107
|
+
const feat = `cluster:${key}`;
|
|
108
|
+
const st = this.states.get(feat);
|
|
109
|
+
if (!st) {
|
|
110
|
+
res.pending.push(key);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const deviation = this.classifyDeviation(st);
|
|
114
|
+
if (deviation === "change-point") {
|
|
115
|
+
st.stableCount = 0;
|
|
116
|
+
res.changePoints.push(key);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (deviation === "anomaly") {
|
|
120
|
+
res.anomalies.push(key);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
st.stableCount++;
|
|
124
|
+
if (st.stableCount < this.cfg.stabilityWindow) {
|
|
125
|
+
res.pending.push(key);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const o = this.obs.get(feat) ?? [];
|
|
129
|
+
if (o.length < 4) {
|
|
130
|
+
res.pending.push(key);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const acc = this.holdoutValidation(o);
|
|
134
|
+
if (acc < this.cfg.accuracyThreshold) {
|
|
135
|
+
res.pending.push(key);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const stability = this.stabilityScore(st);
|
|
139
|
+
const valence = this.assignValence(rows.filter((r) => ids.has(r.id)));
|
|
140
|
+
const vid = fnv1a(key + ":" + project);
|
|
141
|
+
const existing = this.loadVasana(vid);
|
|
142
|
+
if (existing) {
|
|
143
|
+
existing.strength = Math.min(1, existing.strength + 0.1);
|
|
144
|
+
existing.stability = stability;
|
|
145
|
+
existing.reinforcementCount++;
|
|
146
|
+
existing.lastActivated = now;
|
|
147
|
+
existing.predictiveAccuracy = acc;
|
|
148
|
+
existing.updatedAt = now;
|
|
149
|
+
this.saveVasana(existing);
|
|
150
|
+
res.reinforced.push(existing);
|
|
151
|
+
} else {
|
|
152
|
+
const rep = rows.find((r) => ids.has(r.id));
|
|
153
|
+
const v = {
|
|
154
|
+
id: vid,
|
|
155
|
+
tendency: key.replace(/:/g, "-"),
|
|
156
|
+
description: rep?.pattern_content ?? key,
|
|
157
|
+
strength: 0.5 + acc * 0.3,
|
|
158
|
+
stability,
|
|
159
|
+
valence,
|
|
160
|
+
sourceSamskaras: [...ids],
|
|
161
|
+
reinforcementCount: 1,
|
|
162
|
+
lastActivated: now,
|
|
163
|
+
predictiveAccuracy: acc,
|
|
164
|
+
project,
|
|
165
|
+
createdAt: now,
|
|
166
|
+
updatedAt: now
|
|
167
|
+
};
|
|
168
|
+
this.saveVasana(v);
|
|
169
|
+
res.created.push(v);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return res;
|
|
173
|
+
}
|
|
174
|
+
/** Reinforce with diminishing returns: delta = 0.1 / (1 + ln(1 + count)). */
|
|
175
|
+
reinforce(vasanaId) {
|
|
176
|
+
const v = this.loadVasana(vasanaId);
|
|
177
|
+
if (!v) return;
|
|
178
|
+
v.strength = Math.min(1, v.strength + 0.1 / (1 + Math.log(1 + v.reinforcementCount)));
|
|
179
|
+
v.reinforcementCount++;
|
|
180
|
+
v.lastActivated = v.updatedAt = Date.now();
|
|
181
|
+
this.saveVasana(v);
|
|
182
|
+
}
|
|
183
|
+
/** Weaken by fixed decrement. */
|
|
184
|
+
weaken(vasanaId) {
|
|
185
|
+
const v = this.loadVasana(vasanaId);
|
|
186
|
+
if (!v) return;
|
|
187
|
+
v.strength = Math.max(0, v.strength - 0.15);
|
|
188
|
+
v.updatedAt = Date.now();
|
|
189
|
+
this.saveVasana(v);
|
|
190
|
+
}
|
|
191
|
+
/** Get vasanas for a project (includes global), sorted by strength desc. */
|
|
192
|
+
getVasanas(project, topK = 20) {
|
|
193
|
+
const rows = DatabaseManager.instance().get("agent").prepare(
|
|
194
|
+
`SELECT id,name,description,valence,strength,stability,source_samskaras,
|
|
195
|
+
project,created_at,updated_at,last_activated,activation_count
|
|
196
|
+
FROM vasanas WHERE project=? OR project IS NULL OR project='__global__'
|
|
197
|
+
ORDER BY strength DESC LIMIT ?`
|
|
198
|
+
).all(project, topK);
|
|
199
|
+
return rows.map((r) => this.toVasana(r));
|
|
200
|
+
}
|
|
201
|
+
/** Promote project vasanas to global when found in >= promotionMinProjects. */
|
|
202
|
+
promoteToGlobal() {
|
|
203
|
+
const now = Date.now();
|
|
204
|
+
const res = { promoted: [], projectSources: {}, timestamp: now };
|
|
205
|
+
const db = DatabaseManager.instance().get("agent");
|
|
206
|
+
const rows = db.prepare(
|
|
207
|
+
`SELECT id,name,description,valence,strength,stability,source_samskaras,
|
|
208
|
+
project,created_at,updated_at,last_activated,activation_count
|
|
209
|
+
FROM vasanas WHERE project IS NOT NULL AND project!='__global__' AND strength>=0.4
|
|
210
|
+
ORDER BY name`
|
|
211
|
+
).all();
|
|
212
|
+
const byName = /* @__PURE__ */ new Map();
|
|
213
|
+
for (const r of rows) {
|
|
214
|
+
const k = r.name.toLowerCase();
|
|
215
|
+
byName.set(k, [...byName.get(k) ?? [], r]);
|
|
216
|
+
}
|
|
217
|
+
for (const [tendency, group] of byName) {
|
|
218
|
+
const projects = new Set(group.map((r) => r.project));
|
|
219
|
+
if (projects.size < this.cfg.promotionMinProjects) continue;
|
|
220
|
+
if (db.prepare(`SELECT 1 FROM vasanas WHERE name=? AND (project IS NULL OR project='__global__')`).get(tendency)) continue;
|
|
221
|
+
const allSrc = [];
|
|
222
|
+
for (const r of group) allSrc.push(...jsonArr(r.source_samskaras));
|
|
223
|
+
const votes = { positive: 0, negative: 0, neutral: 0 };
|
|
224
|
+
for (const r of group) votes[r.valence]++;
|
|
225
|
+
const valence = ["positive", "negative", "neutral"].reduce((a, b) => votes[a] >= votes[b] ? a : b);
|
|
226
|
+
const gv = {
|
|
227
|
+
id: fnv1a(tendency + ":__global__"),
|
|
228
|
+
tendency,
|
|
229
|
+
description: group[0].description,
|
|
230
|
+
strength: group.reduce((s, r) => s + r.strength, 0) / group.length,
|
|
231
|
+
stability: Math.max(...group.map((r) => r.stability)),
|
|
232
|
+
valence,
|
|
233
|
+
sourceSamskaras: [...new Set(allSrc)],
|
|
234
|
+
reinforcementCount: group.reduce((s, r) => s + r.activation_count, 0),
|
|
235
|
+
lastActivated: now,
|
|
236
|
+
predictiveAccuracy: Math.max(...group.map((r) => r.stability)),
|
|
237
|
+
project: "__global__",
|
|
238
|
+
createdAt: now,
|
|
239
|
+
updatedAt: now
|
|
240
|
+
};
|
|
241
|
+
this.saveVasana(gv);
|
|
242
|
+
res.promoted.push(gv);
|
|
243
|
+
res.projectSources[tendency] = [...projects];
|
|
244
|
+
}
|
|
245
|
+
return res;
|
|
246
|
+
}
|
|
247
|
+
/** Exponential decay: strength *= exp(-ln2 * elapsed / halfLife). Deletes below 0.01. */
|
|
248
|
+
decay(halfLifeMs) {
|
|
249
|
+
const hl = halfLifeMs ?? this.cfg.decayHalfLifeMs;
|
|
250
|
+
const now = Date.now();
|
|
251
|
+
const db = DatabaseManager.instance().get("agent");
|
|
252
|
+
const rows = db.prepare(`SELECT id, strength, last_activated FROM vasanas`).all();
|
|
253
|
+
let deleted = 0;
|
|
254
|
+
for (const r of rows) {
|
|
255
|
+
const elapsed = now - (r.last_activated ?? now);
|
|
256
|
+
if (elapsed <= 0) continue;
|
|
257
|
+
const s = r.strength * Math.exp(-Math.LN2 * elapsed / hl);
|
|
258
|
+
if (s < 0.01) {
|
|
259
|
+
db.prepare(`DELETE FROM vasanas WHERE id=?`).run(r.id);
|
|
260
|
+
deleted++;
|
|
261
|
+
} else db.prepare(`UPDATE vasanas SET strength=?,updated_at=? WHERE id=?`).run(s, now, r.id);
|
|
262
|
+
}
|
|
263
|
+
return deleted;
|
|
264
|
+
}
|
|
265
|
+
/** Persist BOCPD state (run-length distributions + observations) to SQLite. */
|
|
266
|
+
persist() {
|
|
267
|
+
const json = JSON.stringify({
|
|
268
|
+
features: Object.fromEntries(this.states),
|
|
269
|
+
observations: Object.fromEntries(this.obs)
|
|
270
|
+
});
|
|
271
|
+
DatabaseManager.instance().get("agent").prepare(
|
|
272
|
+
`INSERT OR REPLACE INTO consolidation_rules
|
|
273
|
+
(id, category, rule_text, confidence, source_sessions, created_at, updated_at, hit_count, project)
|
|
274
|
+
VALUES ((SELECT id FROM consolidation_rules WHERE category='bocpd_state' AND project='__vasana_engine__'),
|
|
275
|
+
'bocpd_state',?,1.0,NULL,?,?,1,'__vasana_engine__')`
|
|
276
|
+
).run(json, Date.now(), Date.now());
|
|
277
|
+
}
|
|
278
|
+
/** Restore BOCPD state from SQLite. No-op if nothing persisted. */
|
|
279
|
+
restore() {
|
|
280
|
+
const row = DatabaseManager.instance().get("agent").prepare(
|
|
281
|
+
`SELECT rule_text FROM consolidation_rules
|
|
282
|
+
WHERE category='bocpd_state' AND project='__vasana_engine__' LIMIT 1`
|
|
283
|
+
).get();
|
|
284
|
+
if (!row) return;
|
|
285
|
+
try {
|
|
286
|
+
const s = JSON.parse(row.rule_text);
|
|
287
|
+
this.states = new Map(Object.entries(s.features));
|
|
288
|
+
this.obs = new Map(Object.entries(s.observations).map(([k, v]) => [k, Array.isArray(v) ? v : []]));
|
|
289
|
+
} catch (err) {
|
|
290
|
+
process.stderr.write(`[vasana-engine] restore() failed to parse BOCPD state: ${err instanceof Error ? err.message : err}
|
|
291
|
+
`);
|
|
292
|
+
this.states.clear();
|
|
293
|
+
this.obs.clear();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// ── BOCPD Core (Adams & MacKay 2007) ─────────────────────────────────
|
|
297
|
+
initState() {
|
|
298
|
+
return {
|
|
299
|
+
logR: [0],
|
|
300
|
+
// P(r=0) = 1
|
|
301
|
+
stats: [{
|
|
302
|
+
mu: this.cfg.priorMu,
|
|
303
|
+
kappa: this.cfg.priorKappa,
|
|
304
|
+
alpha: this.cfg.priorAlpha,
|
|
305
|
+
beta: this.cfg.priorAlpha
|
|
306
|
+
}],
|
|
307
|
+
stableCount: 0,
|
|
308
|
+
totalObs: 0,
|
|
309
|
+
recentCpProbs: []
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Online BOCPD update for one observation on one feature dimension.
|
|
314
|
+
*
|
|
315
|
+
* 1. Predictive P(x_t | r) via Student-t with Normal-Gamma sufficient stats
|
|
316
|
+
* 2. Growth: P(r_t=r+1) = P(x|r) * P(r) * (1-H)
|
|
317
|
+
* 3. Change-point: P(r_t=0) = sum_r P(x|r) * P(r) * H
|
|
318
|
+
* 4. Normalize, update sufficient stats, prune low-probability tails
|
|
319
|
+
*/
|
|
320
|
+
updateBOCPD(feat, x) {
|
|
321
|
+
const st = this.states.get(feat);
|
|
322
|
+
const logH = -Math.log(this.cfg.lambda);
|
|
323
|
+
const log1H = Math.log(1 - 1 / this.cfg.lambda);
|
|
324
|
+
const n = st.logR.length;
|
|
325
|
+
const lpp = new Array(n);
|
|
326
|
+
for (let r = 0; r < n; r++) {
|
|
327
|
+
const s = st.stats[r];
|
|
328
|
+
lpp[r] = logStudentT(x, 2 * s.alpha, s.mu, Math.sqrt(s.beta * (s.kappa + 1) / (s.alpha * s.kappa)));
|
|
329
|
+
}
|
|
330
|
+
const cpTerms = new Array(n);
|
|
331
|
+
const newLR = new Array(n + 1);
|
|
332
|
+
for (let r = 0; r < n; r++) {
|
|
333
|
+
newLR[r + 1] = lpp[r] + st.logR[r] + log1H;
|
|
334
|
+
cpTerms[r] = lpp[r] + st.logR[r] + logH;
|
|
335
|
+
}
|
|
336
|
+
newLR[0] = logsumexp(cpTerms);
|
|
337
|
+
const logZ = logsumexp(newLR);
|
|
338
|
+
for (let i = 0; i <= n; i++) newLR[i] -= logZ;
|
|
339
|
+
const newS = new Array(n + 1);
|
|
340
|
+
newS[0] = {
|
|
341
|
+
mu: this.cfg.priorMu,
|
|
342
|
+
kappa: this.cfg.priorKappa,
|
|
343
|
+
alpha: this.cfg.priorAlpha,
|
|
344
|
+
beta: this.cfg.priorAlpha
|
|
345
|
+
};
|
|
346
|
+
for (let r = 0; r < n; r++) {
|
|
347
|
+
const p = st.stats[r];
|
|
348
|
+
const k = p.kappa + 1;
|
|
349
|
+
const dx = x - p.mu;
|
|
350
|
+
newS[r + 1] = {
|
|
351
|
+
mu: (p.kappa * p.mu + x) / k,
|
|
352
|
+
kappa: k,
|
|
353
|
+
alpha: p.alpha + 0.5,
|
|
354
|
+
beta: p.beta + 0.5 * p.kappa * dx * dx / k
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
if (newLR.length > this.cfg.maxRunLength) {
|
|
358
|
+
const idx = newLR.map((lp, i) => ({ lp, i })).sort((a, b) => b.lp - a.lp);
|
|
359
|
+
const keep = new Set(idx.slice(0, this.cfg.maxRunLength).map((e) => e.i));
|
|
360
|
+
const pR = [], pS = [];
|
|
361
|
+
for (let i = 0; i <= n; i++) if (keep.has(i)) {
|
|
362
|
+
pR.push(newLR[i]);
|
|
363
|
+
pS.push(newS[i]);
|
|
364
|
+
}
|
|
365
|
+
const norm = logsumexp(pR);
|
|
366
|
+
for (let i = 0; i < pR.length; i++) pR[i] -= norm;
|
|
367
|
+
st.logR = pR;
|
|
368
|
+
st.stats = pS;
|
|
369
|
+
} else {
|
|
370
|
+
st.logR = newLR;
|
|
371
|
+
st.stats = newS;
|
|
372
|
+
}
|
|
373
|
+
st.totalObs++;
|
|
374
|
+
const cpProb = st.logR.length > 0 ? Math.exp(st.logR[0]) : 0;
|
|
375
|
+
if (!st.recentCpProbs) st.recentCpProbs = [];
|
|
376
|
+
st.recentCpProbs.push(cpProb);
|
|
377
|
+
if (st.recentCpProbs.length > this.cfg.anomalyRevertWindow) {
|
|
378
|
+
st.recentCpProbs.shift();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Classify a deviation as change-point, anomaly, or stable.
|
|
383
|
+
*
|
|
384
|
+
* Joint anomaly/change-point discrimination (arxiv 2508.06385):
|
|
385
|
+
* - If P(r=0) > threshold and persists across the revert window → change-point
|
|
386
|
+
* - If P(r=0) > threshold but reverts within the window → anomaly (one-off)
|
|
387
|
+
* - Otherwise → stable
|
|
388
|
+
*/
|
|
389
|
+
classifyDeviation(st) {
|
|
390
|
+
if (st.logR.length === 0) return "stable";
|
|
391
|
+
const cpProb = Math.exp(st.logR[0]);
|
|
392
|
+
if (cpProb <= this.cfg.changePointThreshold) return "stable";
|
|
393
|
+
const recent = st.recentCpProbs ?? [cpProb];
|
|
394
|
+
const exceedCount = recent.filter((p) => p > this.cfg.changePointThreshold).length;
|
|
395
|
+
const ratio = exceedCount / Math.max(1, recent.length);
|
|
396
|
+
if (recent.length >= this.cfg.anomalyRevertWindow && ratio >= this.cfg.anomalyConfirmRatio) {
|
|
397
|
+
return "change-point";
|
|
398
|
+
}
|
|
399
|
+
return "anomaly";
|
|
400
|
+
}
|
|
401
|
+
isChangePoint(st) {
|
|
402
|
+
return this.classifyDeviation(st) === "change-point";
|
|
403
|
+
}
|
|
404
|
+
stabilityScore(st) {
|
|
405
|
+
if (st.logR.length === 0) return 0;
|
|
406
|
+
return Math.max(0, Math.min(1, 1 - Math.exp(st.logR[0])));
|
|
407
|
+
}
|
|
408
|
+
// ── Feature Extraction & Validation ──────────────────────────────────
|
|
409
|
+
extractFeatures(s) {
|
|
410
|
+
const f = /* @__PURE__ */ new Map();
|
|
411
|
+
const tMap = {
|
|
412
|
+
"tool-sequence": 0.2,
|
|
413
|
+
preference: 0.4,
|
|
414
|
+
decision: 0.6,
|
|
415
|
+
correction: 0.8,
|
|
416
|
+
convention: 1
|
|
417
|
+
};
|
|
418
|
+
f.set(`type:${s.patternType}`, tMap[s.patternType] ?? 0.5);
|
|
419
|
+
f.set("confidence", s.confidence);
|
|
420
|
+
f.set("log_obs", Math.min(1, Math.log(1 + s.observationCount) / Math.log(101)));
|
|
421
|
+
const ch = parseInt(fnv1a(s.patternContent), 16);
|
|
422
|
+
f.set(`cluster:${s.patternType}:${fnv1a(s.patternContent)}`, ch / 4294967295);
|
|
423
|
+
return f;
|
|
424
|
+
}
|
|
425
|
+
/** 70/30 holdout: fraction of test points within 1.5 sigma of train mean. */
|
|
426
|
+
holdoutValidation(obs) {
|
|
427
|
+
if (obs.length < 4) return 0;
|
|
428
|
+
const si = Math.floor(obs.length * this.cfg.holdoutTrainRatio);
|
|
429
|
+
const train = obs.slice(0, si), test = obs.slice(si);
|
|
430
|
+
if (!train.length || !test.length) return 0;
|
|
431
|
+
const mu = train.reduce((a, b) => a + b, 0) / train.length;
|
|
432
|
+
const std = Math.sqrt(train.reduce((a, v) => a + (v - mu) ** 2, 0) / train.length + 1e-10);
|
|
433
|
+
let ok = 0;
|
|
434
|
+
for (const v of test) if (Math.abs(v - mu) <= 1.5 * std) ok++;
|
|
435
|
+
return ok / test.length;
|
|
436
|
+
}
|
|
437
|
+
assignValence(rows) {
|
|
438
|
+
let pos = 0, neg = 0;
|
|
439
|
+
for (const r of rows) {
|
|
440
|
+
if (r.pattern_type === "correction") neg += r.confidence;
|
|
441
|
+
else if (r.pattern_type !== "tool-sequence" || r.confidence > 0.6)
|
|
442
|
+
pos += r.confidence * (r.pattern_type === "tool-sequence" ? 0.5 : 1);
|
|
443
|
+
}
|
|
444
|
+
const tot = pos + neg;
|
|
445
|
+
if (tot < 0.1) return "neutral";
|
|
446
|
+
return pos / tot > 0.6 ? "positive" : neg / tot > 0.6 ? "negative" : "neutral";
|
|
447
|
+
}
|
|
448
|
+
clusterSamskaras(rows) {
|
|
449
|
+
const m = /* @__PURE__ */ new Map();
|
|
450
|
+
for (const r of rows) {
|
|
451
|
+
const k = `${r.pattern_type}:${fnv1a(r.pattern_content.toLowerCase().trim().replace(/\s+/g, " "))}`;
|
|
452
|
+
const s = m.get(k) ?? /* @__PURE__ */ new Set();
|
|
453
|
+
s.add(r.id);
|
|
454
|
+
m.set(k, s);
|
|
455
|
+
}
|
|
456
|
+
return m;
|
|
457
|
+
}
|
|
458
|
+
// ── SQLite Helpers ───────────────────────────────────────────────────
|
|
459
|
+
// Schema mapping: Vasana.tendency→name, sourceSamskaras→source_samskaras(JSON),
|
|
460
|
+
// reinforcementCount→activation_count, lastActivated→last_activated
|
|
461
|
+
saveVasana(v) {
|
|
462
|
+
const db = DatabaseManager.instance().get("agent");
|
|
463
|
+
const existing = db.prepare(
|
|
464
|
+
`SELECT id FROM vasanas WHERE name=? AND (project=? OR (project IS NULL AND ?='__global__'))`
|
|
465
|
+
).get(v.tendency, v.project, v.project);
|
|
466
|
+
if (existing) {
|
|
467
|
+
db.prepare(
|
|
468
|
+
`UPDATE vasanas SET description=?,valence=?,strength=?,stability=?,
|
|
469
|
+
source_samskaras=?,updated_at=?,last_activated=?,activation_count=? WHERE id=?`
|
|
470
|
+
).run(
|
|
471
|
+
v.description,
|
|
472
|
+
v.valence,
|
|
473
|
+
v.strength,
|
|
474
|
+
v.stability,
|
|
475
|
+
JSON.stringify(v.sourceSamskaras),
|
|
476
|
+
v.updatedAt,
|
|
477
|
+
v.lastActivated,
|
|
478
|
+
v.reinforcementCount,
|
|
479
|
+
existing.id
|
|
480
|
+
);
|
|
481
|
+
} else {
|
|
482
|
+
db.prepare(
|
|
483
|
+
`INSERT INTO vasanas (name,description,valence,strength,stability,source_samskaras,
|
|
484
|
+
project,created_at,updated_at,last_activated,activation_count) VALUES (?,?,?,?,?,?,?,?,?,?,?)`
|
|
485
|
+
).run(
|
|
486
|
+
v.tendency,
|
|
487
|
+
v.description,
|
|
488
|
+
v.valence,
|
|
489
|
+
v.strength,
|
|
490
|
+
v.stability,
|
|
491
|
+
JSON.stringify(v.sourceSamskaras),
|
|
492
|
+
v.project === "__global__" ? null : v.project,
|
|
493
|
+
v.createdAt,
|
|
494
|
+
v.updatedAt,
|
|
495
|
+
v.lastActivated,
|
|
496
|
+
v.reinforcementCount
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
this.cache.set(v.id, v);
|
|
500
|
+
}
|
|
501
|
+
loadVasana(vid) {
|
|
502
|
+
if (this.cache.has(vid)) return this.cache.get(vid);
|
|
503
|
+
const rows = DatabaseManager.instance().get("agent").prepare(
|
|
504
|
+
`SELECT id,name,description,valence,strength,stability,source_samskaras,
|
|
505
|
+
project,created_at,updated_at,last_activated,activation_count FROM vasanas`
|
|
506
|
+
).all();
|
|
507
|
+
for (const r of rows) {
|
|
508
|
+
const v = this.toVasana(r);
|
|
509
|
+
this.cache.set(v.id, v);
|
|
510
|
+
}
|
|
511
|
+
return this.cache.get(vid) ?? null;
|
|
512
|
+
}
|
|
513
|
+
toVasana(r) {
|
|
514
|
+
const proj = r.project ?? "__global__";
|
|
515
|
+
return {
|
|
516
|
+
id: fnv1a(r.name + ":" + proj),
|
|
517
|
+
tendency: r.name,
|
|
518
|
+
description: r.description,
|
|
519
|
+
strength: r.strength,
|
|
520
|
+
stability: r.stability,
|
|
521
|
+
valence: r.valence,
|
|
522
|
+
sourceSamskaras: jsonArr(r.source_samskaras),
|
|
523
|
+
reinforcementCount: r.activation_count,
|
|
524
|
+
lastActivated: r.last_activated ?? r.updated_at,
|
|
525
|
+
predictiveAccuracy: r.stability,
|
|
526
|
+
project: proj,
|
|
527
|
+
createdAt: r.created_at,
|
|
528
|
+
updatedAt: r.updated_at
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
function jsonArr(s) {
|
|
533
|
+
if (!s) return [];
|
|
534
|
+
try {
|
|
535
|
+
const p = JSON.parse(s);
|
|
536
|
+
return Array.isArray(p) ? p : [];
|
|
537
|
+
} catch {
|
|
538
|
+
return [];
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export {
|
|
543
|
+
VasanaEngine
|
|
544
|
+
};
|
|
545
|
+
//# sourceMappingURL=chunk-F4T7POKM.js.map
|