@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.
Files changed (31) hide show
  1. package/chunks/{chunk-L7JICQBW.js → chunk-2ARPXEDC.js} +5 -3
  2. package/chunks/{chunk-IIET2K6D.js → chunk-5Z2BKSFF.js} +81 -603
  3. package/chunks/{chunk-MINFB5LT.js → chunk-C76USAC5.js} +30 -6
  4. package/chunks/{chunk-URGEODS5.js → chunk-DQMAQ2VL.js} +4 -4
  5. package/chunks/{chunk-JZU37VQ5.js → chunk-F35MWELH.js} +6 -4
  6. package/chunks/chunk-F4T7POKM.js +545 -0
  7. package/chunks/{chunk-H76V36OF.js → chunk-FPNQLJLD.js} +4 -4
  8. package/chunks/{chunk-KDRROLVN.js → chunk-NBXCXQ3H.js} +2 -2
  9. package/chunks/{chunk-YSU3BWV6.js → chunk-OWBBY5XP.js} +2 -2
  10. package/chunks/{chunk-SMVJRPAH.js → chunk-PRXQW76U.js} +46 -6
  11. package/chunks/{chunk-S4TBVCL2.js → chunk-SLA2OIMG.js} +5 -3
  12. package/chunks/chunk-UQLPHNGH.js +123 -0
  13. package/chunks/{chunk-ITIVYGUG.js → chunk-UW6E7IC4.js} +6 -4
  14. package/chunks/{chunk-HAPVUJ6A.js → chunk-XRHUKKBC.js} +9 -7
  15. package/chunks/{chunk-U6OLJ36B.js → chunk-YJRXLRTE.js} +21 -122
  16. package/chunks/{consolidation-indexer-TOTTDZXW.js → consolidation-indexer-A46RJU4R.js} +6 -5
  17. package/chunks/{day-consolidation-NKO63HZQ.js → day-consolidation-GQ2FDCR2.js} +2 -2
  18. package/chunks/{graphrag-ZI2FSU7S.js → graphrag-6YZ5YPLK.js} +4 -3
  19. package/chunks/{hierarchical-temporal-search-ZD46UMKR.js → hierarchical-temporal-search-VA4D3SON.js} +2 -2
  20. package/chunks/{hybrid-search-ZVLZVGFS.js → hybrid-search-6XMUT66S.js} +6 -5
  21. package/chunks/periodic-consolidation-N5MR77ZN.js +11 -0
  22. package/chunks/{postgres-3ZXBYTPC.js → postgres-WLH3D5HG.js} +2 -2
  23. package/chunks/{recall-GMVHWQWW.js → recall-THTI6ZO2.js} +5 -4
  24. package/chunks/{search-7HZETVMZ.js → search-V7DJ3VNL.js} +5 -4
  25. package/chunks/{session-store-XKPGKXUS.js → session-store-GRKGTMHI.js} +4 -3
  26. package/chunks/{sqlite-JPF5TICX.js → sqlite-DHUQGPR5.js} +2 -2
  27. package/chunks/{src-QAXOD5SB.js → src-54LTTDTH.js} +18 -14
  28. package/chunks/vasana-engine-Z4RXW2SB.js +10 -0
  29. package/gateway.js +1429 -264
  30. package/package.json +1 -1
  31. 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: row.queue_mode ? String(row.queue_mode) : void 0,
1248
- announceMode: row.announce_mode ? String(row.announce_mode) : void 0,
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-MINFB5LT.js.map
1503
+ //# sourceMappingURL=chunk-C76USAC5.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DatabaseManager
3
- } from "./chunk-U6OLJ36B.js";
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-TOTTDZXW.js");
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-TOTTDZXW.js");
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-URGEODS5.js.map
752
+ //# sourceMappingURL=chunk-DQMAQ2VL.js.map
@@ -6,11 +6,13 @@ import {
6
6
  import {
7
7
  listSessions,
8
8
  loadSession
9
- } from "./chunk-L7JICQBW.js";
9
+ } from "./chunk-2ARPXEDC.js";
10
10
  import {
11
- DatabaseManager,
12
11
  initVectorsSchema
13
- } from "./chunk-U6OLJ36B.js";
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-JZU37VQ5.js.map
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