@psiclawops/hypermem 0.1.0 → 0.5.1
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/ARCHITECTURE.md +4 -3
- package/README.md +457 -174
- package/dist/background-indexer.d.ts +19 -4
- package/dist/background-indexer.d.ts.map +1 -1
- package/dist/background-indexer.js +329 -17
- package/dist/cache.d.ts +110 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +495 -0
- package/dist/compaction-fence.d.ts +1 -1
- package/dist/compaction-fence.js +1 -1
- package/dist/compositor.d.ts +114 -27
- package/dist/compositor.d.ts.map +1 -1
- package/dist/compositor.js +1678 -229
- package/dist/content-type-classifier.d.ts +41 -0
- package/dist/content-type-classifier.d.ts.map +1 -0
- package/dist/content-type-classifier.js +181 -0
- package/dist/cross-agent.d.ts +5 -0
- package/dist/cross-agent.d.ts.map +1 -1
- package/dist/cross-agent.js +5 -0
- package/dist/db.d.ts +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +6 -2
- package/dist/desired-state-store.d.ts +1 -1
- package/dist/desired-state-store.d.ts.map +1 -1
- package/dist/desired-state-store.js +15 -5
- package/dist/doc-chunk-store.d.ts +26 -1
- package/dist/doc-chunk-store.d.ts.map +1 -1
- package/dist/doc-chunk-store.js +114 -1
- package/dist/doc-chunker.d.ts +1 -1
- package/dist/doc-chunker.js +1 -1
- package/dist/dreaming-promoter.d.ts +86 -0
- package/dist/dreaming-promoter.d.ts.map +1 -0
- package/dist/dreaming-promoter.js +381 -0
- package/dist/episode-store.d.ts +2 -1
- package/dist/episode-store.d.ts.map +1 -1
- package/dist/episode-store.js +4 -4
- package/dist/fact-store.d.ts +19 -1
- package/dist/fact-store.d.ts.map +1 -1
- package/dist/fact-store.js +64 -3
- package/dist/fleet-store.d.ts +1 -1
- package/dist/fleet-store.js +1 -1
- package/dist/fos-mod.d.ts +178 -0
- package/dist/fos-mod.d.ts.map +1 -0
- package/dist/fos-mod.js +416 -0
- package/dist/hybrid-retrieval.d.ts +5 -1
- package/dist/hybrid-retrieval.d.ts.map +1 -1
- package/dist/hybrid-retrieval.js +7 -3
- package/dist/image-eviction.d.ts +49 -0
- package/dist/image-eviction.d.ts.map +1 -0
- package/dist/image-eviction.js +251 -0
- package/dist/index.d.ts +50 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +73 -43
- package/dist/keystone-scorer.d.ts +51 -0
- package/dist/keystone-scorer.d.ts.map +1 -0
- package/dist/keystone-scorer.js +52 -0
- package/dist/knowledge-graph.d.ts +1 -1
- package/dist/knowledge-graph.js +1 -1
- package/dist/knowledge-lint.d.ts +29 -0
- package/dist/knowledge-lint.d.ts.map +1 -0
- package/dist/knowledge-lint.js +116 -0
- package/dist/knowledge-store.d.ts +1 -1
- package/dist/knowledge-store.d.ts.map +1 -1
- package/dist/knowledge-store.js +8 -2
- package/dist/library-schema.d.ts +3 -3
- package/dist/library-schema.d.ts.map +1 -1
- package/dist/library-schema.js +324 -3
- package/dist/message-store.d.ts +15 -2
- package/dist/message-store.d.ts.map +1 -1
- package/dist/message-store.js +51 -1
- package/dist/metrics-dashboard.d.ts +114 -0
- package/dist/metrics-dashboard.d.ts.map +1 -0
- package/dist/metrics-dashboard.js +260 -0
- package/dist/obsidian-exporter.d.ts +57 -0
- package/dist/obsidian-exporter.d.ts.map +1 -0
- package/dist/obsidian-exporter.js +274 -0
- package/dist/obsidian-watcher.d.ts +147 -0
- package/dist/obsidian-watcher.d.ts.map +1 -0
- package/dist/obsidian-watcher.js +403 -0
- package/dist/open-domain.d.ts +46 -0
- package/dist/open-domain.d.ts.map +1 -0
- package/dist/open-domain.js +125 -0
- package/dist/preference-store.d.ts +1 -1
- package/dist/preference-store.js +1 -1
- package/dist/preservation-gate.d.ts +1 -1
- package/dist/preservation-gate.js +1 -1
- package/dist/proactive-pass.d.ts +63 -0
- package/dist/proactive-pass.d.ts.map +1 -0
- package/dist/proactive-pass.js +239 -0
- package/dist/profiles.d.ts +44 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +227 -0
- package/dist/provider-translator.d.ts +13 -3
- package/dist/provider-translator.d.ts.map +1 -1
- package/dist/provider-translator.js +63 -9
- package/dist/rate-limiter.d.ts +1 -1
- package/dist/rate-limiter.js +1 -1
- package/dist/repair-tool-pairs.d.ts +38 -0
- package/dist/repair-tool-pairs.d.ts.map +1 -0
- package/dist/repair-tool-pairs.js +138 -0
- package/dist/retrieval-policy.d.ts +51 -0
- package/dist/retrieval-policy.d.ts.map +1 -0
- package/dist/retrieval-policy.js +77 -0
- package/dist/schema.d.ts +2 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +28 -2
- package/dist/secret-scanner.d.ts +1 -1
- package/dist/secret-scanner.js +1 -1
- package/dist/seed.d.ts +2 -2
- package/dist/seed.js +2 -2
- package/dist/session-flusher.d.ts +53 -0
- package/dist/session-flusher.d.ts.map +1 -0
- package/dist/session-flusher.js +69 -0
- package/dist/session-topic-map.d.ts +41 -0
- package/dist/session-topic-map.d.ts.map +1 -0
- package/dist/session-topic-map.js +77 -0
- package/dist/spawn-context.d.ts +54 -0
- package/dist/spawn-context.d.ts.map +1 -0
- package/dist/spawn-context.js +159 -0
- package/dist/system-store.d.ts +1 -1
- package/dist/system-store.js +1 -1
- package/dist/temporal-store.d.ts +80 -0
- package/dist/temporal-store.d.ts.map +1 -0
- package/dist/temporal-store.js +149 -0
- package/dist/topic-detector.d.ts +35 -0
- package/dist/topic-detector.d.ts.map +1 -0
- package/dist/topic-detector.js +249 -0
- package/dist/topic-store.d.ts +1 -1
- package/dist/topic-store.js +1 -1
- package/dist/topic-synthesizer.d.ts +51 -0
- package/dist/topic-synthesizer.d.ts.map +1 -0
- package/dist/topic-synthesizer.js +315 -0
- package/dist/trigger-registry.d.ts +63 -0
- package/dist/trigger-registry.d.ts.map +1 -0
- package/dist/trigger-registry.js +163 -0
- package/dist/types.d.ts +214 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/vector-store.d.ts +43 -5
- package/dist/vector-store.d.ts.map +1 -1
- package/dist/vector-store.js +189 -10
- package/dist/version.d.ts +34 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +34 -0
- package/dist/wiki-page-emitter.d.ts +65 -0
- package/dist/wiki-page-emitter.d.ts.map +1 -0
- package/dist/wiki-page-emitter.js +258 -0
- package/dist/work-store.d.ts +1 -1
- package/dist/work-store.js +1 -1
- package/package.json +15 -5
- package/dist/redis.d.ts +0 -188
- package/dist/redis.d.ts.map +0 -1
- package/dist/redis.js +0 -534
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Cache Layer
|
|
3
|
+
*
|
|
4
|
+
* Drop-in replacement for RedisLayer using SQLite :memory: ATTACH.
|
|
5
|
+
* Same public interface, zero external dependencies, zero TCP overhead.
|
|
6
|
+
*/
|
|
7
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
8
|
+
import type { CacheConfig, SessionMeta, SessionCursor, StoredMessage, NeutralMessage } from './types.js';
|
|
9
|
+
export interface ModelState {
|
|
10
|
+
model: string;
|
|
11
|
+
tokenBudget: number;
|
|
12
|
+
composedAt: string;
|
|
13
|
+
historyDepth: number;
|
|
14
|
+
reshapedAt?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class CacheLayer {
|
|
17
|
+
private db;
|
|
18
|
+
private readonly config;
|
|
19
|
+
private _connected;
|
|
20
|
+
private stmtSetSlot;
|
|
21
|
+
private stmtGetSlot;
|
|
22
|
+
private stmtSetTopicSlot;
|
|
23
|
+
private stmtGetTopicSlot;
|
|
24
|
+
private stmtTouchSlots;
|
|
25
|
+
private stmtEvictSlots;
|
|
26
|
+
private stmtActivateSession;
|
|
27
|
+
private stmtDeactivateSession;
|
|
28
|
+
private stmtGetActiveSessions;
|
|
29
|
+
private stmtSetMeta;
|
|
30
|
+
private stmtGetMeta;
|
|
31
|
+
private stmtTouchSession;
|
|
32
|
+
private stmtGetMaxSeq;
|
|
33
|
+
private stmtInsertHistory;
|
|
34
|
+
private stmtGetHistory;
|
|
35
|
+
private stmtGetHistoryLimit;
|
|
36
|
+
private stmtHistoryExists;
|
|
37
|
+
private stmtDeleteHistory;
|
|
38
|
+
private stmtDeleteOldHistory;
|
|
39
|
+
private stmtGetAllHistoryDesc;
|
|
40
|
+
private stmtDeleteHistoryBeforeSeq;
|
|
41
|
+
private stmtEvictHistory;
|
|
42
|
+
private stmtSetWindow;
|
|
43
|
+
private stmtGetWindow;
|
|
44
|
+
private stmtDeleteWindow;
|
|
45
|
+
private stmtEvictWindows;
|
|
46
|
+
private stmtSetKv;
|
|
47
|
+
private stmtGetKv;
|
|
48
|
+
private stmtDeleteKv;
|
|
49
|
+
constructor(config?: Partial<CacheConfig>);
|
|
50
|
+
connect(db?: DatabaseSync): Promise<boolean>;
|
|
51
|
+
private _prepareStatements;
|
|
52
|
+
get isConnected(): boolean;
|
|
53
|
+
setProfile(agentId: string, profile: Record<string, unknown>): Promise<void>;
|
|
54
|
+
getProfile(agentId: string): Promise<Record<string, unknown> | null>;
|
|
55
|
+
addActiveSession(agentId: string, sessionKey: string): Promise<void>;
|
|
56
|
+
removeActiveSession(agentId: string, sessionKey: string): Promise<void>;
|
|
57
|
+
getActiveSessions(agentId: string): Promise<string[]>;
|
|
58
|
+
setSlot(agentId: string, sessionKey: string, slot: string, value: string): Promise<void>;
|
|
59
|
+
getSlot(agentId: string, sessionKey: string, slot: string): Promise<string | null>;
|
|
60
|
+
setSessionMeta(agentId: string, sessionKey: string, meta: SessionMeta): Promise<void>;
|
|
61
|
+
getSessionMeta(agentId: string, sessionKey: string): Promise<SessionMeta | null>;
|
|
62
|
+
pushHistory(agentId: string, sessionKey: string, messages: StoredMessage[], maxMessages?: number): Promise<void>;
|
|
63
|
+
replaceHistory(agentId: string, sessionKey: string, messages: NeutralMessage[], maxMessages?: number): Promise<void>;
|
|
64
|
+
getHistory(agentId: string, sessionKey: string, limit?: number): Promise<StoredMessage[]>;
|
|
65
|
+
sessionExists(agentId: string, sessionKey: string): Promise<boolean>;
|
|
66
|
+
trimHistoryToTokenBudget(agentId: string, sessionKey: string, tokenBudget: number): Promise<number>;
|
|
67
|
+
setWindow(agentId: string, sessionKey: string, messages: NeutralMessage[], ttlSeconds?: number): Promise<void>;
|
|
68
|
+
getWindow(agentId: string, sessionKey: string): Promise<NeutralMessage[] | null>;
|
|
69
|
+
invalidateWindow(agentId: string, sessionKey: string): Promise<void>;
|
|
70
|
+
setCursor(agentId: string, sessionKey: string, cursor: SessionCursor): Promise<void>;
|
|
71
|
+
getCursor(agentId: string, sessionKey: string): Promise<SessionCursor | null>;
|
|
72
|
+
warmSession(agentId: string, sessionKey: string, slots: {
|
|
73
|
+
system?: string;
|
|
74
|
+
identity?: string;
|
|
75
|
+
context?: string;
|
|
76
|
+
facts?: string;
|
|
77
|
+
tools?: string;
|
|
78
|
+
meta?: SessionMeta;
|
|
79
|
+
history?: StoredMessage[];
|
|
80
|
+
}): Promise<void>;
|
|
81
|
+
evictSession(agentId: string, sessionKey: string): Promise<void>;
|
|
82
|
+
touchSession(agentId: string, sessionKey: string): Promise<void>;
|
|
83
|
+
flushPrefix(): Promise<number>;
|
|
84
|
+
setFleetCache(key: string, value: string, ttl?: number): Promise<void>;
|
|
85
|
+
getFleetCache(key: string): Promise<string | null>;
|
|
86
|
+
delFleetCache(key: string): Promise<void>;
|
|
87
|
+
cacheFleetAgent(agentId: string, data: Record<string, unknown>): Promise<void>;
|
|
88
|
+
getCachedFleetAgent(agentId: string): Promise<Record<string, unknown> | null>;
|
|
89
|
+
cacheFleetSummary(summary: Record<string, unknown>): Promise<void>;
|
|
90
|
+
getCachedFleetSummary(): Promise<Record<string, unknown> | null>;
|
|
91
|
+
invalidateFleetAgent(agentId: string): Promise<void>;
|
|
92
|
+
setQueryEmbedding(agentId: string, sessionKey: string, embedding: Float32Array): Promise<void>;
|
|
93
|
+
getQueryEmbedding(agentId: string, sessionKey: string): Promise<Float32Array | null>;
|
|
94
|
+
setTopicSlot(agentId: string, sessionKey: string, topicId: string, slot: string, value: string): Promise<void>;
|
|
95
|
+
getTopicSlot(agentId: string, sessionKey: string, topicId: string, slot: string): Promise<string | null>;
|
|
96
|
+
setTopicWindow(agentId: string, sessionKey: string, topicId: string, messages: NeutralMessage[], ttl?: number): Promise<void>;
|
|
97
|
+
getTopicWindow(agentId: string, sessionKey: string, topicId: string): Promise<NeutralMessage[] | null>;
|
|
98
|
+
invalidateTopicWindow(agentId: string, sessionKey: string, topicId: string): Promise<void>;
|
|
99
|
+
warmTopicSession(agentId: string, sessionKey: string, topicId: string, slots: {
|
|
100
|
+
history?: StoredMessage[];
|
|
101
|
+
window?: NeutralMessage[];
|
|
102
|
+
context?: string;
|
|
103
|
+
facts?: string;
|
|
104
|
+
cursor?: string;
|
|
105
|
+
}): Promise<void>;
|
|
106
|
+
setModelState(agentId: string, sessionKey: string, state: ModelState): Promise<void>;
|
|
107
|
+
getModelState(agentId: string, sessionKey: string): Promise<ModelState | null>;
|
|
108
|
+
disconnect(): Promise<void>;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Cache Layer
|
|
3
|
+
*
|
|
4
|
+
* Drop-in replacement for RedisLayer using SQLite :memory: ATTACH.
|
|
5
|
+
* Same public interface, zero external dependencies, zero TCP overhead.
|
|
6
|
+
*/
|
|
7
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
8
|
+
const DEFAULT_CONFIG = {
|
|
9
|
+
keyPrefix: 'hm:',
|
|
10
|
+
sessionTTL: 14400,
|
|
11
|
+
historyTTL: 86400,
|
|
12
|
+
};
|
|
13
|
+
function now() {
|
|
14
|
+
return Math.floor(Date.now() / 1000);
|
|
15
|
+
}
|
|
16
|
+
export class CacheLayer {
|
|
17
|
+
db = null;
|
|
18
|
+
config;
|
|
19
|
+
_connected = false;
|
|
20
|
+
stmtSetSlot;
|
|
21
|
+
stmtGetSlot;
|
|
22
|
+
stmtSetTopicSlot;
|
|
23
|
+
stmtGetTopicSlot;
|
|
24
|
+
stmtTouchSlots;
|
|
25
|
+
stmtEvictSlots;
|
|
26
|
+
stmtActivateSession;
|
|
27
|
+
stmtDeactivateSession;
|
|
28
|
+
stmtGetActiveSessions;
|
|
29
|
+
stmtSetMeta;
|
|
30
|
+
stmtGetMeta;
|
|
31
|
+
stmtTouchSession;
|
|
32
|
+
stmtGetMaxSeq;
|
|
33
|
+
stmtInsertHistory;
|
|
34
|
+
stmtGetHistory;
|
|
35
|
+
stmtGetHistoryLimit;
|
|
36
|
+
stmtHistoryExists;
|
|
37
|
+
stmtDeleteHistory;
|
|
38
|
+
stmtDeleteOldHistory;
|
|
39
|
+
stmtGetAllHistoryDesc;
|
|
40
|
+
stmtDeleteHistoryBeforeSeq;
|
|
41
|
+
stmtEvictHistory;
|
|
42
|
+
stmtSetWindow;
|
|
43
|
+
stmtGetWindow;
|
|
44
|
+
stmtDeleteWindow;
|
|
45
|
+
stmtEvictWindows;
|
|
46
|
+
stmtSetKv;
|
|
47
|
+
stmtGetKv;
|
|
48
|
+
stmtDeleteKv;
|
|
49
|
+
constructor(config) {
|
|
50
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
51
|
+
}
|
|
52
|
+
async connect(db) {
|
|
53
|
+
if (this._connected)
|
|
54
|
+
return true;
|
|
55
|
+
this.db = db ?? new DatabaseSync(':memory:');
|
|
56
|
+
this.db.exec("ATTACH DATABASE ':memory:' AS cache");
|
|
57
|
+
this.db.exec([
|
|
58
|
+
"CREATE TABLE IF NOT EXISTS cache.slots (agent_id TEXT NOT NULL, session_key TEXT NOT NULL, topic_id TEXT NOT NULL DEFAULT '', slot_name TEXT NOT NULL, value TEXT NOT NULL, expires_at INTEGER NOT NULL, PRIMARY KEY (agent_id, session_key, topic_id, slot_name));",
|
|
59
|
+
"CREATE TABLE IF NOT EXISTS cache.history (agent_id TEXT NOT NULL, session_key TEXT NOT NULL, topic_id TEXT NOT NULL DEFAULT '', seq INTEGER NOT NULL, message TEXT NOT NULL, PRIMARY KEY (agent_id, session_key, topic_id, seq));",
|
|
60
|
+
"CREATE TABLE IF NOT EXISTS cache.sessions (agent_id TEXT NOT NULL, session_key TEXT NOT NULL, meta TEXT, active INTEGER NOT NULL DEFAULT 1, touched_at INTEGER NOT NULL, PRIMARY KEY (agent_id, session_key));",
|
|
61
|
+
"CREATE TABLE IF NOT EXISTS cache.windows (agent_id TEXT NOT NULL, session_key TEXT NOT NULL, topic_id TEXT NOT NULL DEFAULT '', messages TEXT NOT NULL, expires_at INTEGER NOT NULL, PRIMARY KEY (agent_id, session_key, topic_id));",
|
|
62
|
+
"CREATE TABLE IF NOT EXISTS cache.kv (key TEXT PRIMARY KEY, value TEXT NOT NULL, expires_at INTEGER NOT NULL DEFAULT 0);"
|
|
63
|
+
].join(' '));
|
|
64
|
+
this._prepareStatements();
|
|
65
|
+
this._connected = true;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
_prepareStatements() {
|
|
69
|
+
const db = this.db;
|
|
70
|
+
this.stmtSetSlot = db.prepare("INSERT OR REPLACE INTO cache.slots (agent_id,session_key,topic_id,slot_name,value,expires_at) VALUES (?,?,'',?,?,?)");
|
|
71
|
+
this.stmtGetSlot = db.prepare("SELECT value FROM cache.slots WHERE agent_id=? AND session_key=? AND topic_id='' AND slot_name=? AND expires_at>?");
|
|
72
|
+
this.stmtSetTopicSlot = db.prepare("INSERT OR REPLACE INTO cache.slots (agent_id,session_key,topic_id,slot_name,value,expires_at) VALUES (?,?,?,?,?,?)");
|
|
73
|
+
this.stmtGetTopicSlot = db.prepare("SELECT value FROM cache.slots WHERE agent_id=? AND session_key=? AND topic_id=? AND slot_name=? AND expires_at>?");
|
|
74
|
+
this.stmtTouchSlots = db.prepare("UPDATE cache.slots SET expires_at=? WHERE agent_id=? AND session_key=? AND topic_id=''");
|
|
75
|
+
this.stmtEvictSlots = db.prepare("DELETE FROM cache.slots WHERE agent_id=? AND session_key=?");
|
|
76
|
+
this.stmtActivateSession = db.prepare("INSERT INTO cache.sessions (agent_id,session_key,meta,active,touched_at) VALUES (?,?,NULL,1,?) ON CONFLICT (agent_id,session_key) DO UPDATE SET active=1,touched_at=excluded.touched_at");
|
|
77
|
+
this.stmtDeactivateSession = db.prepare("UPDATE cache.sessions SET active=0 WHERE agent_id=? AND session_key=?");
|
|
78
|
+
this.stmtGetActiveSessions = db.prepare("SELECT session_key FROM cache.sessions WHERE agent_id=? AND active=1");
|
|
79
|
+
this.stmtSetMeta = db.prepare("INSERT INTO cache.sessions (agent_id,session_key,meta,active,touched_at) VALUES (?,?,?,COALESCE((SELECT active FROM cache.sessions WHERE agent_id=? AND session_key=?),1),?) ON CONFLICT (agent_id,session_key) DO UPDATE SET meta=excluded.meta,touched_at=excluded.touched_at");
|
|
80
|
+
this.stmtGetMeta = db.prepare("SELECT meta FROM cache.sessions WHERE agent_id=? AND session_key=?");
|
|
81
|
+
this.stmtTouchSession = db.prepare("UPDATE cache.sessions SET touched_at=? WHERE agent_id=? AND session_key=?");
|
|
82
|
+
this.stmtGetMaxSeq = db.prepare("SELECT MAX(seq) AS max_seq FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id=?");
|
|
83
|
+
this.stmtInsertHistory = db.prepare("INSERT OR IGNORE INTO cache.history (agent_id,session_key,topic_id,seq,message) VALUES (?,?,?,?,?)");
|
|
84
|
+
this.stmtGetHistory = db.prepare("SELECT message FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id='' ORDER BY seq ASC");
|
|
85
|
+
this.stmtGetHistoryLimit = db.prepare("SELECT message FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id='' AND seq IN (SELECT seq FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id='' ORDER BY seq DESC LIMIT ?) ORDER BY seq ASC");
|
|
86
|
+
this.stmtHistoryExists = db.prepare("SELECT 1 FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id='' LIMIT 1");
|
|
87
|
+
this.stmtDeleteHistory = db.prepare("DELETE FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id=''");
|
|
88
|
+
this.stmtDeleteOldHistory = db.prepare("DELETE FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id='' AND seq NOT IN (SELECT seq FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id='' ORDER BY seq DESC LIMIT ?)");
|
|
89
|
+
this.stmtGetAllHistoryDesc = db.prepare("SELECT seq,message FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id='' ORDER BY seq DESC");
|
|
90
|
+
this.stmtDeleteHistoryBeforeSeq = db.prepare("DELETE FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id='' AND seq<?");
|
|
91
|
+
this.stmtEvictHistory = db.prepare("DELETE FROM cache.history WHERE agent_id=? AND session_key=?");
|
|
92
|
+
this.stmtSetWindow = db.prepare("INSERT OR REPLACE INTO cache.windows (agent_id,session_key,topic_id,messages,expires_at) VALUES (?,?,?,?,?)");
|
|
93
|
+
this.stmtGetWindow = db.prepare("SELECT messages FROM cache.windows WHERE agent_id=? AND session_key=? AND topic_id=? AND expires_at>?");
|
|
94
|
+
this.stmtDeleteWindow = db.prepare("DELETE FROM cache.windows WHERE agent_id=? AND session_key=? AND topic_id=?");
|
|
95
|
+
this.stmtEvictWindows = db.prepare("DELETE FROM cache.windows WHERE agent_id=? AND session_key=?");
|
|
96
|
+
this.stmtSetKv = db.prepare("INSERT OR REPLACE INTO cache.kv (key,value,expires_at) VALUES (?,?,?)");
|
|
97
|
+
this.stmtGetKv = db.prepare("SELECT value FROM cache.kv WHERE key=? AND (expires_at=0 OR expires_at>?)");
|
|
98
|
+
this.stmtDeleteKv = db.prepare("DELETE FROM cache.kv WHERE key=?");
|
|
99
|
+
}
|
|
100
|
+
get isConnected() {
|
|
101
|
+
return this._connected && this.db !== null;
|
|
102
|
+
}
|
|
103
|
+
// ─── Agent-Level Operations ──────────────────────────────────
|
|
104
|
+
async setProfile(agentId, profile) {
|
|
105
|
+
if (!this.isConnected)
|
|
106
|
+
return;
|
|
107
|
+
this.stmtSetKv.run(`${this.config.keyPrefix}${agentId}:profile`, JSON.stringify(profile), 0);
|
|
108
|
+
}
|
|
109
|
+
async getProfile(agentId) {
|
|
110
|
+
if (!this.isConnected)
|
|
111
|
+
return null;
|
|
112
|
+
const row = this.stmtGetKv.get(`${this.config.keyPrefix}${agentId}:profile`, now());
|
|
113
|
+
return row ? JSON.parse(row.value) : null;
|
|
114
|
+
}
|
|
115
|
+
async addActiveSession(agentId, sessionKey) {
|
|
116
|
+
if (!this.isConnected)
|
|
117
|
+
return;
|
|
118
|
+
this.stmtActivateSession.run(agentId, sessionKey, now());
|
|
119
|
+
}
|
|
120
|
+
async removeActiveSession(agentId, sessionKey) {
|
|
121
|
+
if (!this.isConnected)
|
|
122
|
+
return;
|
|
123
|
+
this.stmtDeactivateSession.run(agentId, sessionKey);
|
|
124
|
+
}
|
|
125
|
+
async getActiveSessions(agentId) {
|
|
126
|
+
if (!this.isConnected)
|
|
127
|
+
return [];
|
|
128
|
+
const rows = this.stmtGetActiveSessions.all(agentId);
|
|
129
|
+
return rows.map(r => r.session_key);
|
|
130
|
+
}
|
|
131
|
+
// ─── Session Slot Operations ─────────────────────────────────
|
|
132
|
+
async setSlot(agentId, sessionKey, slot, value) {
|
|
133
|
+
if (!this.isConnected)
|
|
134
|
+
return;
|
|
135
|
+
this.stmtSetSlot.run(agentId, sessionKey, slot, value, now() + this.config.sessionTTL);
|
|
136
|
+
}
|
|
137
|
+
async getSlot(agentId, sessionKey, slot) {
|
|
138
|
+
if (!this.isConnected)
|
|
139
|
+
return null;
|
|
140
|
+
const row = this.stmtGetSlot.get(agentId, sessionKey, slot, now());
|
|
141
|
+
return row?.value ?? null;
|
|
142
|
+
}
|
|
143
|
+
async setSessionMeta(agentId, sessionKey, meta) {
|
|
144
|
+
if (!this.isConnected)
|
|
145
|
+
return;
|
|
146
|
+
this.stmtSetMeta.run(agentId, sessionKey, JSON.stringify(meta), agentId, sessionKey, now());
|
|
147
|
+
}
|
|
148
|
+
async getSessionMeta(agentId, sessionKey) {
|
|
149
|
+
if (!this.isConnected)
|
|
150
|
+
return null;
|
|
151
|
+
const row = this.stmtGetMeta.get(agentId, sessionKey);
|
|
152
|
+
if (!row?.meta)
|
|
153
|
+
return null;
|
|
154
|
+
return JSON.parse(row.meta);
|
|
155
|
+
}
|
|
156
|
+
// ─── History Operations ──────────────────────────────────────
|
|
157
|
+
async pushHistory(agentId, sessionKey, messages, maxMessages = 250) {
|
|
158
|
+
if (!this.isConnected || messages.length === 0)
|
|
159
|
+
return;
|
|
160
|
+
// Get current max seq for dedup
|
|
161
|
+
const maxRow = this.stmtGetMaxSeq.get(agentId, sessionKey, '');
|
|
162
|
+
const maxSeq = maxRow?.max_seq ?? -1;
|
|
163
|
+
// Filter already-stored messages (same dedup logic as Redis path)
|
|
164
|
+
let filteredMessages = messages;
|
|
165
|
+
if (maxSeq >= 0) {
|
|
166
|
+
filteredMessages = messages.filter(m => m.id > maxSeq);
|
|
167
|
+
}
|
|
168
|
+
if (filteredMessages.length === 0)
|
|
169
|
+
return;
|
|
170
|
+
let seq = maxSeq + 1;
|
|
171
|
+
for (const msg of filteredMessages) {
|
|
172
|
+
this.stmtInsertHistory.run(agentId, sessionKey, '', seq++, JSON.stringify(msg));
|
|
173
|
+
}
|
|
174
|
+
// Soft cap: keep only last maxMessages
|
|
175
|
+
this.stmtDeleteOldHistory.run(agentId, sessionKey, agentId, sessionKey, maxMessages);
|
|
176
|
+
}
|
|
177
|
+
async replaceHistory(agentId, sessionKey, messages, maxMessages = 250) {
|
|
178
|
+
if (!this.isConnected)
|
|
179
|
+
return;
|
|
180
|
+
this.stmtDeleteHistory.run(agentId, sessionKey);
|
|
181
|
+
const slice = messages.slice(-maxMessages);
|
|
182
|
+
for (let i = 0; i < slice.length; i++) {
|
|
183
|
+
this.stmtInsertHistory.run(agentId, sessionKey, '', i, JSON.stringify(slice[i]));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async getHistory(agentId, sessionKey, limit) {
|
|
187
|
+
if (!this.isConnected)
|
|
188
|
+
return [];
|
|
189
|
+
let rows;
|
|
190
|
+
if (limit) {
|
|
191
|
+
rows = this.stmtGetHistoryLimit.all(agentId, sessionKey, agentId, sessionKey, limit);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
rows = this.stmtGetHistory.all(agentId, sessionKey);
|
|
195
|
+
}
|
|
196
|
+
return rows.map(r => JSON.parse(r.message));
|
|
197
|
+
}
|
|
198
|
+
async sessionExists(agentId, sessionKey) {
|
|
199
|
+
if (!this.isConnected)
|
|
200
|
+
return false;
|
|
201
|
+
const row = this.stmtHistoryExists.get(agentId, sessionKey);
|
|
202
|
+
return row !== undefined;
|
|
203
|
+
}
|
|
204
|
+
async trimHistoryToTokenBudget(agentId, sessionKey, tokenBudget) {
|
|
205
|
+
if (!this.isConnected || tokenBudget <= 0)
|
|
206
|
+
return 0;
|
|
207
|
+
const rows = this.stmtGetAllHistoryDesc.all(agentId, sessionKey);
|
|
208
|
+
if (rows.length <= 10)
|
|
209
|
+
return 0;
|
|
210
|
+
const estimateMessageTokens = (msg) => {
|
|
211
|
+
let msgTokens = Math.ceil((msg.textContent?.length ?? 0) / 4);
|
|
212
|
+
if (msg.toolCalls)
|
|
213
|
+
msgTokens += Math.ceil(JSON.stringify(msg.toolCalls).length / 2);
|
|
214
|
+
if (msg.toolResults)
|
|
215
|
+
msgTokens += Math.ceil(JSON.stringify(msg.toolResults).length / 2);
|
|
216
|
+
return msgTokens;
|
|
217
|
+
};
|
|
218
|
+
const chronological = rows
|
|
219
|
+
.slice()
|
|
220
|
+
.reverse()
|
|
221
|
+
.map(row => {
|
|
222
|
+
try {
|
|
223
|
+
return { seq: row.seq, msg: JSON.parse(row.message), fallback: false };
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return { seq: row.seq, msg: null, fallback: true };
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
const clusters = [];
|
|
230
|
+
for (let i = 0; i < chronological.length; i++) {
|
|
231
|
+
const current = chronological[i];
|
|
232
|
+
if (current.fallback || !current.msg) {
|
|
233
|
+
clusters.push({ startSeq: current.seq, endSeq: current.seq, tokenCost: 500 });
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
let endSeq = current.seq;
|
|
237
|
+
let tokenCost = estimateMessageTokens(current.msg);
|
|
238
|
+
if (current.msg.toolCalls && current.msg.toolCalls.length > 0) {
|
|
239
|
+
const callIds = new Set(current.msg.toolCalls.map(tc => tc.id).filter(Boolean));
|
|
240
|
+
let j = i + 1;
|
|
241
|
+
while (j < chronological.length) {
|
|
242
|
+
const candidate = chronological[j];
|
|
243
|
+
if (candidate.fallback || !candidate.msg || !candidate.msg.toolResults || candidate.msg.toolResults.length === 0)
|
|
244
|
+
break;
|
|
245
|
+
const resultIds = candidate.msg.toolResults.map(tr => tr.callId).filter(Boolean);
|
|
246
|
+
if (callIds.size > 0 && resultIds.length > 0 && !resultIds.some(id => callIds.has(id)))
|
|
247
|
+
break;
|
|
248
|
+
tokenCost += estimateMessageTokens(candidate.msg);
|
|
249
|
+
endSeq = candidate.seq;
|
|
250
|
+
j++;
|
|
251
|
+
}
|
|
252
|
+
i = j - 1;
|
|
253
|
+
}
|
|
254
|
+
else if (current.msg.toolResults && current.msg.toolResults.length > 0) {
|
|
255
|
+
let j = i + 1;
|
|
256
|
+
while (j < chronological.length) {
|
|
257
|
+
const candidate = chronological[j];
|
|
258
|
+
if (candidate.fallback || !candidate.msg || !candidate.msg.toolResults || candidate.msg.toolResults.length === 0 || (candidate.msg.toolCalls && candidate.msg.toolCalls.length > 0))
|
|
259
|
+
break;
|
|
260
|
+
tokenCost += estimateMessageTokens(candidate.msg);
|
|
261
|
+
endSeq = candidate.seq;
|
|
262
|
+
j++;
|
|
263
|
+
}
|
|
264
|
+
i = j - 1;
|
|
265
|
+
}
|
|
266
|
+
clusters.push({ startSeq: current.seq, endSeq, tokenCost });
|
|
267
|
+
}
|
|
268
|
+
let tokenSum = 0;
|
|
269
|
+
let keepFromSeq = null;
|
|
270
|
+
for (let i = clusters.length - 1; i >= 0; i--) {
|
|
271
|
+
const cluster = clusters[i];
|
|
272
|
+
if (tokenSum + cluster.tokenCost > tokenBudget)
|
|
273
|
+
break;
|
|
274
|
+
tokenSum += cluster.tokenCost;
|
|
275
|
+
keepFromSeq = cluster.startSeq;
|
|
276
|
+
}
|
|
277
|
+
if (keepFromSeq === null) {
|
|
278
|
+
keepFromSeq = clusters[clusters.length - 1]?.startSeq ?? null;
|
|
279
|
+
}
|
|
280
|
+
if (keepFromSeq === null)
|
|
281
|
+
return 0;
|
|
282
|
+
const oldestSeq = clusters[0]?.startSeq ?? keepFromSeq;
|
|
283
|
+
if (keepFromSeq <= oldestSeq)
|
|
284
|
+
return 0;
|
|
285
|
+
const cutSeq = keepFromSeq - 1;
|
|
286
|
+
const stmt = this.db.prepare("DELETE FROM cache.history WHERE agent_id=? AND session_key=? AND topic_id='' AND seq<=?");
|
|
287
|
+
stmt.run(agentId, sessionKey, cutSeq);
|
|
288
|
+
const trimmed = rows.filter(r => r.seq <= cutSeq).length;
|
|
289
|
+
console.log(`[hypermem-cache] trimHistoryToTokenBudget: trimmed ${trimmed} messages from ${agentId}/${sessionKey}`);
|
|
290
|
+
return trimmed;
|
|
291
|
+
}
|
|
292
|
+
// ─── Window Cache Operations ─────────────────────────────────
|
|
293
|
+
async setWindow(agentId, sessionKey, messages, ttlSeconds = 120) {
|
|
294
|
+
if (!this.isConnected)
|
|
295
|
+
return;
|
|
296
|
+
this.stmtSetWindow.run(agentId, sessionKey, '', JSON.stringify(messages), now() + ttlSeconds);
|
|
297
|
+
}
|
|
298
|
+
async getWindow(agentId, sessionKey) {
|
|
299
|
+
if (!this.isConnected)
|
|
300
|
+
return null;
|
|
301
|
+
const row = this.stmtGetWindow.get(agentId, sessionKey, '', now());
|
|
302
|
+
return row ? JSON.parse(row.messages) : null;
|
|
303
|
+
}
|
|
304
|
+
async invalidateWindow(agentId, sessionKey) {
|
|
305
|
+
if (!this.isConnected)
|
|
306
|
+
return;
|
|
307
|
+
this.stmtDeleteWindow.run(agentId, sessionKey, '');
|
|
308
|
+
}
|
|
309
|
+
// ─── Session Cursor Operations ────────────────────────────────
|
|
310
|
+
async setCursor(agentId, sessionKey, cursor) {
|
|
311
|
+
if (!this.isConnected)
|
|
312
|
+
return;
|
|
313
|
+
this.stmtSetKv.run(`${this.config.keyPrefix}${agentId}:s:${sessionKey}:cursor`, JSON.stringify(cursor), now() + this.config.historyTTL);
|
|
314
|
+
}
|
|
315
|
+
async getCursor(agentId, sessionKey) {
|
|
316
|
+
if (!this.isConnected)
|
|
317
|
+
return null;
|
|
318
|
+
const row = this.stmtGetKv.get(`${this.config.keyPrefix}${agentId}:s:${sessionKey}:cursor`, now());
|
|
319
|
+
return row ? JSON.parse(row.value) : null;
|
|
320
|
+
}
|
|
321
|
+
// ─── Bulk Session Operations ──────────────────────────────────
|
|
322
|
+
async warmSession(agentId, sessionKey, slots) {
|
|
323
|
+
if (!this.isConnected)
|
|
324
|
+
return;
|
|
325
|
+
const exp = now() + this.config.sessionTTL;
|
|
326
|
+
if (slots.system)
|
|
327
|
+
this.stmtSetSlot.run(agentId, sessionKey, 'system', slots.system, exp);
|
|
328
|
+
if (slots.identity)
|
|
329
|
+
this.stmtSetSlot.run(agentId, sessionKey, 'identity', slots.identity, exp);
|
|
330
|
+
if (slots.context)
|
|
331
|
+
this.stmtSetSlot.run(agentId, sessionKey, 'context', slots.context, exp);
|
|
332
|
+
if (slots.facts)
|
|
333
|
+
this.stmtSetSlot.run(agentId, sessionKey, 'facts', slots.facts, exp);
|
|
334
|
+
if (slots.tools)
|
|
335
|
+
this.stmtSetSlot.run(agentId, sessionKey, 'tools', slots.tools, exp);
|
|
336
|
+
if (slots.meta)
|
|
337
|
+
await this.setSessionMeta(agentId, sessionKey, slots.meta);
|
|
338
|
+
if (slots.history && slots.history.length > 0)
|
|
339
|
+
await this.pushHistory(agentId, sessionKey, slots.history);
|
|
340
|
+
await this.addActiveSession(agentId, sessionKey);
|
|
341
|
+
}
|
|
342
|
+
async evictSession(agentId, sessionKey) {
|
|
343
|
+
if (!this.isConnected)
|
|
344
|
+
return;
|
|
345
|
+
this.stmtEvictSlots.run(agentId, sessionKey);
|
|
346
|
+
this.stmtEvictHistory.run(agentId, sessionKey);
|
|
347
|
+
this.stmtEvictWindows.run(agentId, sessionKey);
|
|
348
|
+
this.stmtDeactivateSession.run(agentId, sessionKey);
|
|
349
|
+
// cursor
|
|
350
|
+
this.stmtDeleteKv.run(`${this.config.keyPrefix}${agentId}:s:${sessionKey}:cursor`);
|
|
351
|
+
}
|
|
352
|
+
// ─── Touch / TTL ─────────────────────────────────────────────
|
|
353
|
+
async touchSession(agentId, sessionKey) {
|
|
354
|
+
if (!this.isConnected)
|
|
355
|
+
return;
|
|
356
|
+
const exp = now() + this.config.sessionTTL;
|
|
357
|
+
this.stmtTouchSlots.run(exp, agentId, sessionKey);
|
|
358
|
+
this.stmtTouchSession.run(now(), agentId, sessionKey);
|
|
359
|
+
}
|
|
360
|
+
async flushPrefix() {
|
|
361
|
+
if (!this.isConnected)
|
|
362
|
+
return 0;
|
|
363
|
+
const db = this.db;
|
|
364
|
+
const counts = [
|
|
365
|
+
db.prepare("DELETE FROM cache.slots").run().changes,
|
|
366
|
+
db.prepare("DELETE FROM cache.history").run().changes,
|
|
367
|
+
db.prepare("DELETE FROM cache.sessions").run().changes,
|
|
368
|
+
db.prepare("DELETE FROM cache.windows").run().changes,
|
|
369
|
+
db.prepare("DELETE FROM cache.kv").run().changes,
|
|
370
|
+
];
|
|
371
|
+
return counts.reduce((a, b) => a + b, 0);
|
|
372
|
+
}
|
|
373
|
+
// ─── Fleet Cache (Library L4 Hot Layer) ──────────────────────
|
|
374
|
+
async setFleetCache(key, value, ttl = 600) {
|
|
375
|
+
if (!this.isConnected)
|
|
376
|
+
return;
|
|
377
|
+
this.stmtSetKv.run(`${this.config.keyPrefix}fleet:${key}`, value, now() + ttl);
|
|
378
|
+
}
|
|
379
|
+
async getFleetCache(key) {
|
|
380
|
+
if (!this.isConnected)
|
|
381
|
+
return null;
|
|
382
|
+
const row = this.stmtGetKv.get(`${this.config.keyPrefix}fleet:${key}`, now());
|
|
383
|
+
return row?.value ?? null;
|
|
384
|
+
}
|
|
385
|
+
async delFleetCache(key) {
|
|
386
|
+
if (!this.isConnected)
|
|
387
|
+
return;
|
|
388
|
+
this.stmtDeleteKv.run(`${this.config.keyPrefix}fleet:${key}`);
|
|
389
|
+
}
|
|
390
|
+
async cacheFleetAgent(agentId, data) {
|
|
391
|
+
await this.setFleetCache(`agent:${agentId}`, JSON.stringify(data));
|
|
392
|
+
}
|
|
393
|
+
async getCachedFleetAgent(agentId) {
|
|
394
|
+
const val = await this.getFleetCache(`agent:${agentId}`);
|
|
395
|
+
return val ? JSON.parse(val) : null;
|
|
396
|
+
}
|
|
397
|
+
async cacheFleetSummary(summary) {
|
|
398
|
+
await this.setFleetCache('summary', JSON.stringify(summary), 120);
|
|
399
|
+
}
|
|
400
|
+
async getCachedFleetSummary() {
|
|
401
|
+
const val = await this.getFleetCache('summary');
|
|
402
|
+
return val ? JSON.parse(val) : null;
|
|
403
|
+
}
|
|
404
|
+
async invalidateFleetAgent(agentId) {
|
|
405
|
+
await this.delFleetCache(`agent:${agentId}`);
|
|
406
|
+
await this.delFleetCache('summary');
|
|
407
|
+
}
|
|
408
|
+
// ─── Query Embedding Cache ────────────────────────────────────
|
|
409
|
+
async setQueryEmbedding(agentId, sessionKey, embedding) {
|
|
410
|
+
if (!this.isConnected)
|
|
411
|
+
return;
|
|
412
|
+
const encoded = Buffer.from(embedding.buffer).toString('base64');
|
|
413
|
+
this.stmtSetKv.run(`${this.config.keyPrefix}${agentId}:s:${sessionKey}:qembed`, encoded, now() + this.config.sessionTTL);
|
|
414
|
+
}
|
|
415
|
+
async getQueryEmbedding(agentId, sessionKey) {
|
|
416
|
+
if (!this.isConnected)
|
|
417
|
+
return null;
|
|
418
|
+
try {
|
|
419
|
+
const row = this.stmtGetKv.get(`${this.config.keyPrefix}${agentId}:s:${sessionKey}:qembed`, now());
|
|
420
|
+
if (!row)
|
|
421
|
+
return null;
|
|
422
|
+
const buf = Buffer.from(row.value, 'base64');
|
|
423
|
+
return new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4);
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
// ─── Topic-Scoped Session Operations ─────────────────────────
|
|
430
|
+
async setTopicSlot(agentId, sessionKey, topicId, slot, value) {
|
|
431
|
+
if (!this.isConnected)
|
|
432
|
+
return;
|
|
433
|
+
this.stmtSetTopicSlot.run(agentId, sessionKey, topicId, slot, value, now() + this.config.sessionTTL);
|
|
434
|
+
}
|
|
435
|
+
async getTopicSlot(agentId, sessionKey, topicId, slot) {
|
|
436
|
+
if (!this.isConnected)
|
|
437
|
+
return null;
|
|
438
|
+
const row = this.stmtGetTopicSlot.get(agentId, sessionKey, topicId, slot, now());
|
|
439
|
+
return row?.value ?? null;
|
|
440
|
+
}
|
|
441
|
+
async setTopicWindow(agentId, sessionKey, topicId, messages, ttl = 120) {
|
|
442
|
+
if (!this.isConnected)
|
|
443
|
+
return;
|
|
444
|
+
this.stmtSetWindow.run(agentId, sessionKey, topicId, JSON.stringify(messages), now() + ttl);
|
|
445
|
+
}
|
|
446
|
+
async getTopicWindow(agentId, sessionKey, topicId) {
|
|
447
|
+
if (!this.isConnected)
|
|
448
|
+
return null;
|
|
449
|
+
const row = this.stmtGetWindow.get(agentId, sessionKey, topicId, now());
|
|
450
|
+
return row ? JSON.parse(row.messages) : null;
|
|
451
|
+
}
|
|
452
|
+
async invalidateTopicWindow(agentId, sessionKey, topicId) {
|
|
453
|
+
if (!this.isConnected)
|
|
454
|
+
return;
|
|
455
|
+
this.stmtDeleteWindow.run(agentId, sessionKey, topicId);
|
|
456
|
+
}
|
|
457
|
+
async warmTopicSession(agentId, sessionKey, topicId, slots) {
|
|
458
|
+
if (!this.isConnected)
|
|
459
|
+
return;
|
|
460
|
+
const exp = now() + this.config.sessionTTL;
|
|
461
|
+
if (slots.context)
|
|
462
|
+
this.stmtSetTopicSlot.run(agentId, sessionKey, topicId, 'context', slots.context, exp);
|
|
463
|
+
if (slots.facts)
|
|
464
|
+
this.stmtSetTopicSlot.run(agentId, sessionKey, topicId, 'facts', slots.facts, exp);
|
|
465
|
+
if (slots.cursor)
|
|
466
|
+
this.stmtSetTopicSlot.run(agentId, sessionKey, topicId, 'cursor', slots.cursor, exp);
|
|
467
|
+
if (slots.window)
|
|
468
|
+
this.stmtSetWindow.run(agentId, sessionKey, topicId, JSON.stringify(slots.window), now() + 120);
|
|
469
|
+
if (slots.history && slots.history.length > 0) {
|
|
470
|
+
// Topic history goes into the topic_id-scoped rows
|
|
471
|
+
const maxRow = this.stmtGetMaxSeq.get(agentId, sessionKey, topicId);
|
|
472
|
+
let seq = (maxRow?.max_seq ?? -1) + 1;
|
|
473
|
+
for (const msg of slots.history) {
|
|
474
|
+
this.stmtInsertHistory.run(agentId, sessionKey, topicId, seq++, JSON.stringify(msg));
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
// ─── Model State ─────────────────────────────────────────────
|
|
479
|
+
async setModelState(agentId, sessionKey, state) {
|
|
480
|
+
if (!this.isConnected)
|
|
481
|
+
return;
|
|
482
|
+
this.stmtSetKv.run(`${this.config.keyPrefix}${agentId}:s:${sessionKey}:modelstate`, JSON.stringify(state), now() + this.config.sessionTTL);
|
|
483
|
+
}
|
|
484
|
+
async getModelState(agentId, sessionKey) {
|
|
485
|
+
if (!this.isConnected)
|
|
486
|
+
return null;
|
|
487
|
+
const row = this.stmtGetKv.get(`${this.config.keyPrefix}${agentId}:s:${sessionKey}:modelstate`, now());
|
|
488
|
+
return row ? JSON.parse(row.value) : null;
|
|
489
|
+
}
|
|
490
|
+
async disconnect() {
|
|
491
|
+
this._connected = false;
|
|
492
|
+
this.db = null;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
//# sourceMappingURL=cache.js.map
|
package/dist/compaction-fence.js
CHANGED