@psiclawops/hypermem 0.1.0
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 +296 -0
- package/LICENSE +190 -0
- package/README.md +243 -0
- package/dist/background-indexer.d.ts +117 -0
- package/dist/background-indexer.d.ts.map +1 -0
- package/dist/background-indexer.js +732 -0
- package/dist/compaction-fence.d.ts +89 -0
- package/dist/compaction-fence.d.ts.map +1 -0
- package/dist/compaction-fence.js +153 -0
- package/dist/compositor.d.ts +139 -0
- package/dist/compositor.d.ts.map +1 -0
- package/dist/compositor.js +1109 -0
- package/dist/cross-agent.d.ts +57 -0
- package/dist/cross-agent.d.ts.map +1 -0
- package/dist/cross-agent.js +254 -0
- package/dist/db.d.ts +131 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +398 -0
- package/dist/desired-state-store.d.ts +100 -0
- package/dist/desired-state-store.d.ts.map +1 -0
- package/dist/desired-state-store.js +212 -0
- package/dist/doc-chunk-store.d.ts +115 -0
- package/dist/doc-chunk-store.d.ts.map +1 -0
- package/dist/doc-chunk-store.js +278 -0
- package/dist/doc-chunker.d.ts +99 -0
- package/dist/doc-chunker.d.ts.map +1 -0
- package/dist/doc-chunker.js +324 -0
- package/dist/episode-store.d.ts +48 -0
- package/dist/episode-store.d.ts.map +1 -0
- package/dist/episode-store.js +135 -0
- package/dist/fact-store.d.ts +57 -0
- package/dist/fact-store.d.ts.map +1 -0
- package/dist/fact-store.js +175 -0
- package/dist/fleet-store.d.ts +144 -0
- package/dist/fleet-store.d.ts.map +1 -0
- package/dist/fleet-store.js +276 -0
- package/dist/hybrid-retrieval.d.ts +60 -0
- package/dist/hybrid-retrieval.d.ts.map +1 -0
- package/dist/hybrid-retrieval.js +340 -0
- package/dist/index.d.ts +611 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1042 -0
- package/dist/knowledge-graph.d.ts +110 -0
- package/dist/knowledge-graph.d.ts.map +1 -0
- package/dist/knowledge-graph.js +305 -0
- package/dist/knowledge-store.d.ts +72 -0
- package/dist/knowledge-store.d.ts.map +1 -0
- package/dist/knowledge-store.js +241 -0
- package/dist/library-schema.d.ts +22 -0
- package/dist/library-schema.d.ts.map +1 -0
- package/dist/library-schema.js +717 -0
- package/dist/message-store.d.ts +76 -0
- package/dist/message-store.d.ts.map +1 -0
- package/dist/message-store.js +273 -0
- package/dist/preference-store.d.ts +54 -0
- package/dist/preference-store.d.ts.map +1 -0
- package/dist/preference-store.js +109 -0
- package/dist/preservation-gate.d.ts +82 -0
- package/dist/preservation-gate.d.ts.map +1 -0
- package/dist/preservation-gate.js +150 -0
- package/dist/provider-translator.d.ts +40 -0
- package/dist/provider-translator.d.ts.map +1 -0
- package/dist/provider-translator.js +349 -0
- package/dist/rate-limiter.d.ts +76 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +179 -0
- package/dist/redis.d.ts +188 -0
- package/dist/redis.d.ts.map +1 -0
- package/dist/redis.js +534 -0
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +203 -0
- package/dist/secret-scanner.d.ts +51 -0
- package/dist/secret-scanner.d.ts.map +1 -0
- package/dist/secret-scanner.js +248 -0
- package/dist/seed.d.ts +108 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +177 -0
- package/dist/system-store.d.ts +73 -0
- package/dist/system-store.d.ts.map +1 -0
- package/dist/system-store.js +182 -0
- package/dist/topic-store.d.ts +45 -0
- package/dist/topic-store.d.ts.map +1 -0
- package/dist/topic-store.js +136 -0
- package/dist/types.d.ts +329 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/vector-store.d.ts +132 -0
- package/dist/vector-store.d.ts.map +1 -0
- package/dist/vector-store.js +498 -0
- package/dist/work-store.d.ts +112 -0
- package/dist/work-store.d.ts.map +1 -0
- package/dist/work-store.js +273 -0
- package/package.json +57 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-Agent Memory Access
|
|
3
|
+
*
|
|
4
|
+
* Enables agents to read each other's memory with visibility-scoped access.
|
|
5
|
+
* All structured knowledge lives in the central library DB, so cross-agent
|
|
6
|
+
* queries are now single-DB operations — no per-agent DB hopping.
|
|
7
|
+
*
|
|
8
|
+
* Visibility levels:
|
|
9
|
+
* - private: Owner only. Identity, SOUL, personal reflections, raw conversations.
|
|
10
|
+
* - org: Same org (council lead + their directors).
|
|
11
|
+
* - council: All council seats.
|
|
12
|
+
* - fleet: Any agent.
|
|
13
|
+
*
|
|
14
|
+
* What's ALWAYS private (hardcoded, not configurable):
|
|
15
|
+
* - Raw message history — an agent's conversations are theirs
|
|
16
|
+
* - Identity/SOUL-derived knowledge — domain='identity'
|
|
17
|
+
* - Facts with scope='session' — ephemeral session context
|
|
18
|
+
*/
|
|
19
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
20
|
+
import type { MemoryVisibility, CrossAgentQuery, AgentIdentity } from './types.js';
|
|
21
|
+
import { DatabaseManager } from './db.js';
|
|
22
|
+
export interface OrgRegistry {
|
|
23
|
+
orgs: Record<string, string[]>;
|
|
24
|
+
agents: Record<string, AgentIdentity>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Default fleet org structure.
|
|
28
|
+
*/
|
|
29
|
+
export declare function defaultOrgRegistry(): OrgRegistry;
|
|
30
|
+
export declare function canAccess(requester: AgentIdentity, target: AgentIdentity, visibility: MemoryVisibility, registry: OrgRegistry): boolean;
|
|
31
|
+
export declare function visibilityFilter(requester: AgentIdentity, targetAgentId: string, registry: OrgRegistry): {
|
|
32
|
+
clause: string;
|
|
33
|
+
canReadPrivate: boolean;
|
|
34
|
+
canReadOrg: boolean;
|
|
35
|
+
canReadCouncil: boolean;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Build an OrgRegistry by reading from the fleet_agents and fleet_orgs tables.
|
|
39
|
+
*
|
|
40
|
+
* Falls back to defaultOrgRegistry() when:
|
|
41
|
+
* - The library DB is unavailable
|
|
42
|
+
* - The fleet tables are empty (not yet seeded)
|
|
43
|
+
* - An unexpected schema error occurs
|
|
44
|
+
*
|
|
45
|
+
* This replaces the hardcoded registry with a live source, so new agents and
|
|
46
|
+
* org restructures propagate automatically without a code change.
|
|
47
|
+
*
|
|
48
|
+
* Merge strategy: DB entries OVERRIDE hardcoded defaults. Agents present only
|
|
49
|
+
* in the hardcoded registry (not yet seeded to the DB) are preserved as fallback.
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildOrgRegistryFromDb(libraryDb: DatabaseSync): OrgRegistry;
|
|
52
|
+
/**
|
|
53
|
+
* Query another agent's memory with visibility-scoped access.
|
|
54
|
+
* All queries go to the central library DB — no per-agent DB needed.
|
|
55
|
+
*/
|
|
56
|
+
export declare function crossAgentQuery(dbManager: DatabaseManager, query: CrossAgentQuery, registry: OrgRegistry): unknown[];
|
|
57
|
+
//# sourceMappingURL=cross-agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-agent.d.ts","sourceRoot":"","sources":["../src/cross-agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,aAAa,EACd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAK1C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACvC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,WAAW,CA2BhD;AAID,wBAAgB,SAAS,CACvB,SAAS,EAAE,aAAa,EACxB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,gBAAgB,EAC5B,QAAQ,EAAE,WAAW,GACpB,OAAO,CAaT;AASD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,aAAa,EACxB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,WAAW,GACpB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,CA6B3F;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,YAAY,GAAG,WAAW,CAgD3E;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,eAAe,EAC1B,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,WAAW,GACpB,OAAO,EAAE,CAgCX"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-Agent Memory Access
|
|
3
|
+
*
|
|
4
|
+
* Enables agents to read each other's memory with visibility-scoped access.
|
|
5
|
+
* All structured knowledge lives in the central library DB, so cross-agent
|
|
6
|
+
* queries are now single-DB operations — no per-agent DB hopping.
|
|
7
|
+
*
|
|
8
|
+
* Visibility levels:
|
|
9
|
+
* - private: Owner only. Identity, SOUL, personal reflections, raw conversations.
|
|
10
|
+
* - org: Same org (council lead + their directors).
|
|
11
|
+
* - council: All council seats.
|
|
12
|
+
* - fleet: Any agent.
|
|
13
|
+
*
|
|
14
|
+
* What's ALWAYS private (hardcoded, not configurable):
|
|
15
|
+
* - Raw message history — an agent's conversations are theirs
|
|
16
|
+
* - Identity/SOUL-derived knowledge — domain='identity'
|
|
17
|
+
* - Facts with scope='session' — ephemeral session context
|
|
18
|
+
*/
|
|
19
|
+
import { FleetStore } from './fleet-store.js';
|
|
20
|
+
/**
|
|
21
|
+
* Default fleet org structure.
|
|
22
|
+
*/
|
|
23
|
+
export function defaultOrgRegistry() {
|
|
24
|
+
const agents = {
|
|
25
|
+
forge: { agentId: 'forge', tier: 'council' },
|
|
26
|
+
compass: { agentId: 'compass', tier: 'council' },
|
|
27
|
+
clarity: { agentId: 'clarity', tier: 'council' },
|
|
28
|
+
sentinel: { agentId: 'sentinel', tier: 'council' },
|
|
29
|
+
anvil: { agentId: 'anvil', tier: 'council' },
|
|
30
|
+
vanguard: { agentId: 'vanguard', tier: 'council' },
|
|
31
|
+
pylon: { agentId: 'pylon', tier: 'director', org: 'forge-org', councilLead: 'forge' },
|
|
32
|
+
vigil: { agentId: 'vigil', tier: 'director', org: 'forge-org', councilLead: 'forge' },
|
|
33
|
+
plane: { agentId: 'plane', tier: 'director', org: 'forge-org', councilLead: 'forge' },
|
|
34
|
+
helm: { agentId: 'helm', tier: 'director', org: 'compass-org', councilLead: 'compass' },
|
|
35
|
+
chisel: { agentId: 'chisel', tier: 'director', org: 'compass-org', councilLead: 'compass' },
|
|
36
|
+
facet: { agentId: 'facet', tier: 'director', org: 'compass-org', councilLead: 'compass' },
|
|
37
|
+
bastion: { agentId: 'bastion', tier: 'director', org: 'sentinel-org', councilLead: 'sentinel' },
|
|
38
|
+
gauge: { agentId: 'gauge', tier: 'director', org: 'sentinel-org', councilLead: 'sentinel' },
|
|
39
|
+
crucible: { agentId: 'crucible', tier: 'specialist' },
|
|
40
|
+
relay: { agentId: 'relay', tier: 'specialist' },
|
|
41
|
+
};
|
|
42
|
+
const orgs = {
|
|
43
|
+
'forge-org': ['forge', 'pylon', 'vigil', 'plane'],
|
|
44
|
+
'compass-org': ['compass', 'helm', 'chisel', 'facet'],
|
|
45
|
+
'sentinel-org': ['sentinel', 'bastion', 'gauge'],
|
|
46
|
+
};
|
|
47
|
+
return { orgs, agents };
|
|
48
|
+
}
|
|
49
|
+
// ─── Access Control ──────────────────────────────────────────────
|
|
50
|
+
export function canAccess(requester, target, visibility, registry) {
|
|
51
|
+
if (requester.agentId === target.agentId)
|
|
52
|
+
return true;
|
|
53
|
+
switch (visibility) {
|
|
54
|
+
case 'private': return false;
|
|
55
|
+
case 'org': return sameOrg(requester, target, registry);
|
|
56
|
+
case 'council':
|
|
57
|
+
if (requester.tier === 'council')
|
|
58
|
+
return true;
|
|
59
|
+
if (requester.councilLead === target.agentId)
|
|
60
|
+
return true;
|
|
61
|
+
return false;
|
|
62
|
+
case 'fleet': return true;
|
|
63
|
+
default: return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function sameOrg(a, b, registry) {
|
|
67
|
+
for (const members of Object.values(registry.orgs)) {
|
|
68
|
+
if (members.includes(a.agentId) && members.includes(b.agentId))
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
export function visibilityFilter(requester, targetAgentId, registry) {
|
|
74
|
+
const target = registry.agents[targetAgentId];
|
|
75
|
+
if (!target) {
|
|
76
|
+
// Restrictive Default: unknown agents get fleet-only visibility.
|
|
77
|
+
// This is a deliberate safety-side fallback — queries succeed with narrowed
|
|
78
|
+
// results rather than failing. The warning surfaces registry gaps so operators
|
|
79
|
+
// can add the missing agent to the org registry.
|
|
80
|
+
// See ARCHITECTURE.md § Cross-Agent Access Control → Unknown Agent Fallback.
|
|
81
|
+
console.warn(`[cross-agent] visibilityFilter: agent "${targetAgentId}" not found in registry — restricting to fleet-only visibility. Add this agent to the org registry if this is unexpected.`);
|
|
82
|
+
return { clause: "visibility = 'fleet'", canReadPrivate: false, canReadOrg: false, canReadCouncil: false };
|
|
83
|
+
}
|
|
84
|
+
if (requester.agentId === targetAgentId) {
|
|
85
|
+
return { clause: '1=1', canReadPrivate: true, canReadOrg: true, canReadCouncil: true };
|
|
86
|
+
}
|
|
87
|
+
const canReadOrg = sameOrg(requester, target, registry);
|
|
88
|
+
const canReadCouncil = requester.tier === 'council' || requester.councilLead === targetAgentId;
|
|
89
|
+
const levels = ["'fleet'"];
|
|
90
|
+
if (canReadCouncil)
|
|
91
|
+
levels.push("'council'");
|
|
92
|
+
if (canReadOrg)
|
|
93
|
+
levels.push("'org'");
|
|
94
|
+
return {
|
|
95
|
+
clause: `visibility IN (${levels.join(', ')})`,
|
|
96
|
+
canReadPrivate: false,
|
|
97
|
+
canReadOrg,
|
|
98
|
+
canReadCouncil,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// ─── Live Registry Loader ────────────────────────────────────────
|
|
102
|
+
/**
|
|
103
|
+
* Build an OrgRegistry by reading from the fleet_agents and fleet_orgs tables.
|
|
104
|
+
*
|
|
105
|
+
* Falls back to defaultOrgRegistry() when:
|
|
106
|
+
* - The library DB is unavailable
|
|
107
|
+
* - The fleet tables are empty (not yet seeded)
|
|
108
|
+
* - An unexpected schema error occurs
|
|
109
|
+
*
|
|
110
|
+
* This replaces the hardcoded registry with a live source, so new agents and
|
|
111
|
+
* org restructures propagate automatically without a code change.
|
|
112
|
+
*
|
|
113
|
+
* Merge strategy: DB entries OVERRIDE hardcoded defaults. Agents present only
|
|
114
|
+
* in the hardcoded registry (not yet seeded to the DB) are preserved as fallback.
|
|
115
|
+
*/
|
|
116
|
+
export function buildOrgRegistryFromDb(libraryDb) {
|
|
117
|
+
const fallback = defaultOrgRegistry();
|
|
118
|
+
try {
|
|
119
|
+
const fleetStore = new FleetStore(libraryDb);
|
|
120
|
+
const dbAgents = fleetStore.listAgents();
|
|
121
|
+
if (dbAgents.length === 0) {
|
|
122
|
+
// Fleet not yet seeded — use hardcoded registry
|
|
123
|
+
return fallback;
|
|
124
|
+
}
|
|
125
|
+
const agents = { ...fallback.agents };
|
|
126
|
+
for (const agent of dbAgents) {
|
|
127
|
+
const tier = agent.tier;
|
|
128
|
+
agents[agent.id] = {
|
|
129
|
+
agentId: agent.id,
|
|
130
|
+
tier,
|
|
131
|
+
org: agent.orgId ?? undefined,
|
|
132
|
+
councilLead: agent.reportsTo ?? undefined,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Build orgs from DB: group agents by orgId
|
|
136
|
+
const orgs = {};
|
|
137
|
+
// Seed with hardcoded orgs for agents not in DB
|
|
138
|
+
for (const [orgId, members] of Object.entries(fallback.orgs)) {
|
|
139
|
+
orgs[orgId] = [...members];
|
|
140
|
+
}
|
|
141
|
+
// Apply DB agents — update org membership
|
|
142
|
+
for (const agent of dbAgents) {
|
|
143
|
+
if (!agent.orgId)
|
|
144
|
+
continue;
|
|
145
|
+
if (!orgs[agent.orgId]) {
|
|
146
|
+
orgs[agent.orgId] = [];
|
|
147
|
+
}
|
|
148
|
+
if (!orgs[agent.orgId].includes(agent.id)) {
|
|
149
|
+
orgs[agent.orgId].push(agent.id);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { orgs, agents };
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// DB error is non-fatal — fall back to hardcoded registry
|
|
156
|
+
return fallback;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// ─── Cross-Agent Query Engine ────────────────────────────────────
|
|
160
|
+
/**
|
|
161
|
+
* Query another agent's memory with visibility-scoped access.
|
|
162
|
+
* All queries go to the central library DB — no per-agent DB needed.
|
|
163
|
+
*/
|
|
164
|
+
export function crossAgentQuery(dbManager, query, registry) {
|
|
165
|
+
const requester = registry.agents[query.requesterId];
|
|
166
|
+
if (!requester)
|
|
167
|
+
throw new Error(`Unknown requester agent: ${query.requesterId}`);
|
|
168
|
+
const target = registry.agents[query.targetAgentId];
|
|
169
|
+
if (!target)
|
|
170
|
+
throw new Error(`Unknown target agent: ${query.targetAgentId}`);
|
|
171
|
+
// All structured knowledge is now in the library DB
|
|
172
|
+
const db = dbManager.getLibraryDb();
|
|
173
|
+
const filter = visibilityFilter(requester, query.targetAgentId, registry);
|
|
174
|
+
const limit = query.limit || 20;
|
|
175
|
+
switch (query.memoryType) {
|
|
176
|
+
case 'facts':
|
|
177
|
+
return queryFacts(db, query, filter.clause, limit);
|
|
178
|
+
case 'knowledge':
|
|
179
|
+
return queryKnowledge(db, query, filter.clause, limit);
|
|
180
|
+
case 'topics':
|
|
181
|
+
return queryTopics(db, query, filter.clause, limit);
|
|
182
|
+
case 'episodes':
|
|
183
|
+
return queryEpisodes(db, query, filter.clause, limit);
|
|
184
|
+
case 'messages':
|
|
185
|
+
// Messages are always private
|
|
186
|
+
return [];
|
|
187
|
+
default:
|
|
188
|
+
return [
|
|
189
|
+
...queryFacts(db, query, filter.clause, Math.ceil(limit / 4)),
|
|
190
|
+
...queryKnowledge(db, query, filter.clause, Math.ceil(limit / 4)),
|
|
191
|
+
...queryTopics(db, query, filter.clause, Math.ceil(limit / 4)),
|
|
192
|
+
...queryEpisodes(db, query, filter.clause, Math.ceil(limit / 4)),
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function queryFacts(db, query, visFilter, limit) {
|
|
197
|
+
let sql = `SELECT id, domain, content, confidence, visibility, created_at
|
|
198
|
+
FROM facts WHERE agent_id = ? AND ${visFilter}
|
|
199
|
+
AND scope != 'session'
|
|
200
|
+
AND (domain IS NULL OR domain != 'identity')
|
|
201
|
+
AND superseded_by IS NULL
|
|
202
|
+
AND decay_score < 0.8`;
|
|
203
|
+
const params = [query.targetAgentId];
|
|
204
|
+
if (query.domain) {
|
|
205
|
+
sql += ' AND domain = ?';
|
|
206
|
+
params.push(query.domain);
|
|
207
|
+
}
|
|
208
|
+
sql += ' ORDER BY confidence DESC, created_at DESC LIMIT ?';
|
|
209
|
+
params.push(limit);
|
|
210
|
+
return db.prepare(sql).all(...params).map((r) => ({
|
|
211
|
+
type: 'fact',
|
|
212
|
+
sourceAgent: query.targetAgentId,
|
|
213
|
+
...r,
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
function queryKnowledge(db, query, visFilter, limit) {
|
|
217
|
+
let sql = `SELECT id, domain, key, content, confidence, visibility, updated_at
|
|
218
|
+
FROM knowledge WHERE agent_id = ? AND ${visFilter}
|
|
219
|
+
AND superseded_by IS NULL
|
|
220
|
+
AND domain != 'identity'`;
|
|
221
|
+
const params = [query.targetAgentId];
|
|
222
|
+
if (query.domain) {
|
|
223
|
+
sql += ' AND domain = ?';
|
|
224
|
+
params.push(query.domain);
|
|
225
|
+
}
|
|
226
|
+
sql += ' ORDER BY confidence DESC, updated_at DESC LIMIT ?';
|
|
227
|
+
params.push(limit);
|
|
228
|
+
return db.prepare(sql).all(...params).map((r) => ({
|
|
229
|
+
type: 'knowledge',
|
|
230
|
+
sourceAgent: query.targetAgentId,
|
|
231
|
+
...r,
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
function queryTopics(db, query, visFilter, limit) {
|
|
235
|
+
const sql = `SELECT id, name, description, status, visibility, message_count, updated_at
|
|
236
|
+
FROM topics WHERE agent_id = ? AND ${visFilter} AND status = 'active'
|
|
237
|
+
ORDER BY updated_at DESC LIMIT ?`;
|
|
238
|
+
return db.prepare(sql).all(query.targetAgentId, limit).map((r) => ({
|
|
239
|
+
type: 'topic',
|
|
240
|
+
sourceAgent: query.targetAgentId,
|
|
241
|
+
...r,
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
function queryEpisodes(db, query, visFilter, limit) {
|
|
245
|
+
const sql = `SELECT id, event_type, summary, significance, visibility, participants, created_at
|
|
246
|
+
FROM episodes WHERE agent_id = ? AND ${visFilter}
|
|
247
|
+
ORDER BY significance DESC, created_at DESC LIMIT ?`;
|
|
248
|
+
return db.prepare(sql).all(query.targetAgentId, limit).map((r) => ({
|
|
249
|
+
type: 'episode',
|
|
250
|
+
sourceAgent: query.targetAgentId,
|
|
251
|
+
...r,
|
|
252
|
+
}));
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=cross-agent.js.map
|
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HyperMem Database Manager
|
|
3
|
+
*
|
|
4
|
+
* Three-file architecture per agent:
|
|
5
|
+
* agents/{agentId}/messages.db — write-heavy conversation log (rotatable)
|
|
6
|
+
* agents/{agentId}/vectors.db — search index (reconstructable)
|
|
7
|
+
* library.db — fleet-wide structured knowledge (crown jewel)
|
|
8
|
+
*
|
|
9
|
+
* Uses node:sqlite (built into Node 22+) for synchronous, zero-dependency access.
|
|
10
|
+
*/
|
|
11
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
12
|
+
export interface DatabaseManagerConfig {
|
|
13
|
+
dataDir: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validate agentId to prevent path traversal.
|
|
17
|
+
* Must match [a-z0-9][a-z0-9-]* (lowercase alphanumeric + hyphens, no dots or slashes).
|
|
18
|
+
*/
|
|
19
|
+
declare function validateAgentId(agentId: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Validate rotated DB filename to prevent path traversal.
|
|
22
|
+
* Must match the expected rotation pattern: messages_YYYYQN(_N)?.db
|
|
23
|
+
*/
|
|
24
|
+
declare function validateRotatedFilename(filename: string): void;
|
|
25
|
+
export { validateAgentId, validateRotatedFilename };
|
|
26
|
+
export declare class DatabaseManager {
|
|
27
|
+
private readonly dataDir;
|
|
28
|
+
private readonly messageDbs;
|
|
29
|
+
private readonly vectorDbs;
|
|
30
|
+
private libraryDb;
|
|
31
|
+
private _vecAvailable;
|
|
32
|
+
/** Whether sqlite-vec was successfully loaded on the most recent DB open. */
|
|
33
|
+
get vecAvailable(): boolean;
|
|
34
|
+
constructor(config?: Partial<DatabaseManagerConfig>);
|
|
35
|
+
/**
|
|
36
|
+
* Get or create the message database for an agent.
|
|
37
|
+
* This is the write-heavy, rotatable conversation log.
|
|
38
|
+
*/
|
|
39
|
+
getMessageDb(agentId: string): DatabaseSync;
|
|
40
|
+
/**
|
|
41
|
+
* Get or create the vector database for an agent.
|
|
42
|
+
* This is the search index — fully reconstructable.
|
|
43
|
+
* Returns null if sqlite-vec is not available.
|
|
44
|
+
*/
|
|
45
|
+
getVectorDb(agentId: string): DatabaseSync | null;
|
|
46
|
+
/**
|
|
47
|
+
* Get or create the shared (fleet-wide) vector database.
|
|
48
|
+
* Unlike per-agent vector DBs, this is a single vectors.db at the root of dataDir,
|
|
49
|
+
* shared across all agents. Facts and episodes from all agents are indexed together,
|
|
50
|
+
* keyed by (source_table, source_id) in vec_index_map.
|
|
51
|
+
* Returns null if sqlite-vec is not available.
|
|
52
|
+
*/
|
|
53
|
+
getSharedVectorDb(): DatabaseSync | null;
|
|
54
|
+
/**
|
|
55
|
+
* Get or create the shared library database.
|
|
56
|
+
* This is the fleet-wide knowledge store — the crown jewel.
|
|
57
|
+
*/
|
|
58
|
+
getLibraryDb(): DatabaseSync;
|
|
59
|
+
/**
|
|
60
|
+
* @deprecated Use getMessageDb() instead. Kept for migration period.
|
|
61
|
+
* Maps to getMessageDb() for backward compatibility.
|
|
62
|
+
*/
|
|
63
|
+
getAgentDb(agentId: string): DatabaseSync;
|
|
64
|
+
/**
|
|
65
|
+
* Ensure agent metadata exists in the message DB.
|
|
66
|
+
*/
|
|
67
|
+
ensureAgent(agentId: string, meta?: {
|
|
68
|
+
displayName?: string;
|
|
69
|
+
tier?: string;
|
|
70
|
+
org?: string;
|
|
71
|
+
}): void;
|
|
72
|
+
/**
|
|
73
|
+
* Ensure agent exists in the fleet registry (library DB).
|
|
74
|
+
*/
|
|
75
|
+
private ensureFleetAgent;
|
|
76
|
+
/**
|
|
77
|
+
* List all agents with message databases.
|
|
78
|
+
*/
|
|
79
|
+
listAgents(): string[];
|
|
80
|
+
/**
|
|
81
|
+
* Get the path to an agent's directory.
|
|
82
|
+
*/
|
|
83
|
+
getAgentDir(agentId: string): string;
|
|
84
|
+
/**
|
|
85
|
+
* List rotated message DB files for an agent.
|
|
86
|
+
*/
|
|
87
|
+
listRotatedDbs(agentId: string): string[];
|
|
88
|
+
/**
|
|
89
|
+
* Get the size of the active messages.db for an agent (in bytes).
|
|
90
|
+
*/
|
|
91
|
+
getMessageDbSize(agentId: string): number;
|
|
92
|
+
/**
|
|
93
|
+
* Rotate the message database for an agent.
|
|
94
|
+
*
|
|
95
|
+
* 1. Closes the active messages.db connection
|
|
96
|
+
* 2. Renames messages.db → messages_{YYYYQN}.db (e.g., messages_2026Q1.db)
|
|
97
|
+
* 3. Removes associated WAL/SHM files
|
|
98
|
+
* 4. Next call to getMessageDb() creates a fresh database
|
|
99
|
+
*
|
|
100
|
+
* The rotated file is read-only archive material. The vector index
|
|
101
|
+
* retains references to it via source_db in vec_index_map.
|
|
102
|
+
*
|
|
103
|
+
* Returns the path to the rotated file, or null if rotation wasn't needed.
|
|
104
|
+
*/
|
|
105
|
+
rotateMessageDb(agentId: string): string | null;
|
|
106
|
+
/**
|
|
107
|
+
* Check if an agent's message database needs rotation.
|
|
108
|
+
* Triggers on:
|
|
109
|
+
* - Size exceeds threshold (default 100MB)
|
|
110
|
+
* - Time since creation exceeds threshold (default 90 days)
|
|
111
|
+
*
|
|
112
|
+
* Returns the reason for rotation, or null if no rotation needed.
|
|
113
|
+
*/
|
|
114
|
+
shouldRotate(agentId: string, opts?: {
|
|
115
|
+
maxSizeBytes?: number;
|
|
116
|
+
maxAgeDays?: number;
|
|
117
|
+
}): {
|
|
118
|
+
reason: 'size' | 'age';
|
|
119
|
+
current: number;
|
|
120
|
+
threshold: number;
|
|
121
|
+
} | null;
|
|
122
|
+
/**
|
|
123
|
+
* Open a rotated message database as read-only for querying.
|
|
124
|
+
*/
|
|
125
|
+
openRotatedDb(agentId: string, filename: string): DatabaseSync;
|
|
126
|
+
/**
|
|
127
|
+
* Close all open database connections.
|
|
128
|
+
*/
|
|
129
|
+
close(): void;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=db.d.ts.map
|
package/dist/db.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoC3C,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;CACjB;AAQD;;;GAGG;AACH,iBAAS,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAI9C;AAED;;;GAGG;AACH,iBAAS,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAIvD;AAmBD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,CAAC;AAEpD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmC;IAC7D,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,aAAa,CAAwB;IAE7C,6EAA6E;IAC7E,IAAI,YAAY,IAAI,OAAO,CAE1B;gBAEW,MAAM,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC;IAKnD;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY;IAiB3C;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IA0BjD;;;;;;OAMG;IACH,iBAAiB,IAAI,YAAY,GAAG,IAAI;IAsBxC;;;OAGG;IACH,YAAY,IAAI,YAAY;IAa5B;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY;IAIzC;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAClC,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,GAAG,IAAI;IA2BR;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA+BxB;;OAEG;IACH,UAAU,IAAI,MAAM,EAAE;IAStB;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAKpC;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAQzC;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAWzC;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IA8C/C;;;;;;;OAOG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QACnC,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG;QAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IA4BzE;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY;IAc9D;;OAEG;IACH,KAAK,IAAI,IAAI;CAgBd"}
|