@psiclawops/hypermem 0.5.6 → 0.6.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 (53) hide show
  1. package/README.md +11 -54
  2. package/dist/background-indexer.d.ts.map +1 -1
  3. package/dist/background-indexer.js +26 -18
  4. package/dist/cache.d.ts.map +1 -1
  5. package/dist/cache.js +16 -2
  6. package/dist/compositor.d.ts.map +1 -1
  7. package/dist/compositor.js +146 -19
  8. package/dist/context-backfill.d.ts +46 -0
  9. package/dist/context-backfill.d.ts.map +1 -0
  10. package/dist/context-backfill.js +113 -0
  11. package/dist/context-store.d.ts +77 -0
  12. package/dist/context-store.d.ts.map +1 -0
  13. package/dist/context-store.js +177 -0
  14. package/dist/cross-agent.d.ts +12 -0
  15. package/dist/cross-agent.d.ts.map +1 -1
  16. package/dist/cross-agent.js +31 -19
  17. package/dist/db.d.ts.map +1 -1
  18. package/dist/db.js +8 -0
  19. package/dist/dreaming-promoter.d.ts +1 -1
  20. package/dist/dreaming-promoter.js +1 -1
  21. package/dist/index.d.ts +4 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +25 -6
  24. package/dist/message-store.d.ts +31 -2
  25. package/dist/message-store.d.ts.map +1 -1
  26. package/dist/message-store.js +131 -17
  27. package/dist/preference-store.d.ts +1 -1
  28. package/dist/preference-store.js +1 -1
  29. package/dist/profiles.d.ts +3 -1
  30. package/dist/profiles.d.ts.map +1 -1
  31. package/dist/profiles.js +8 -0
  32. package/dist/repair-tool-pairs.d.ts.map +1 -1
  33. package/dist/repair-tool-pairs.js +73 -2
  34. package/dist/schema.d.ts +1 -1
  35. package/dist/schema.d.ts.map +1 -1
  36. package/dist/schema.js +27 -1
  37. package/dist/seed.d.ts +1 -1
  38. package/dist/seed.js +1 -1
  39. package/dist/session-flusher.d.ts +2 -2
  40. package/dist/session-flusher.js +2 -2
  41. package/dist/spawn-context.d.ts +1 -1
  42. package/dist/spawn-context.js +1 -1
  43. package/dist/topic-synthesizer.js +1 -1
  44. package/dist/trigger-registry.d.ts +1 -1
  45. package/dist/trigger-registry.js +4 -4
  46. package/dist/types.d.ts +11 -3
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/vector-store.d.ts +10 -1
  49. package/dist/vector-store.d.ts.map +1 -1
  50. package/dist/vector-store.js +353 -0
  51. package/dist/version.d.ts +5 -5
  52. package/dist/version.js +5 -5
  53. package/package.json +3 -2
@@ -0,0 +1,46 @@
1
+ /**
2
+ * hypermem Context Backfill
3
+ *
4
+ * One-time migration that creates context rows for existing conversations
5
+ * that don't yet have an active context. Designed to be idempotent — running
6
+ * it multiple times produces the same result without modifying existing data.
7
+ *
8
+ * Also provides parent chain backfill (Phase 2): reconstructs parent_id/depth
9
+ * for legacy flat conversations that were created before the Turn DAG model.
10
+ */
11
+ import type { DatabaseSync } from 'node:sqlite';
12
+ /**
13
+ * Backfill active contexts for all existing conversations.
14
+ *
15
+ * For each conversation without an active context:
16
+ * 1. Creates an active context via getOrCreateActiveContext
17
+ * 2. If the conversation has messages, advances the head pointer
18
+ * to the highest message ID
19
+ *
20
+ * @returns counts of created and skipped conversations
21
+ */
22
+ export declare function backfillContexts(db: DatabaseSync): {
23
+ created: number;
24
+ skipped: number;
25
+ };
26
+ /**
27
+ * Backfill parent_id and depth for existing messages that lack them.
28
+ *
29
+ * Reconstructs a linear chain per conversation ordered by message_index:
30
+ * - first message: parent_id = NULL, depth = 0
31
+ * - each subsequent: parent_id = previous.id, depth = previous.depth + 1
32
+ *
33
+ * Idempotent: only touches messages where parent_id IS NULL AND depth = 0
34
+ * AND there is more than one message or the message is not the first.
35
+ * In practice, we simply skip messages that already have parent_id set.
36
+ *
37
+ * Also stamps context_id on messages that lack it, using the active context
38
+ * for their conversation.
39
+ *
40
+ * @returns counts of messages updated and conversations processed
41
+ */
42
+ export declare function backfillParentChains(db: DatabaseSync): {
43
+ conversationsProcessed: number;
44
+ messagesUpdated: number;
45
+ };
46
+ //# sourceMappingURL=context-backfill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-backfill.d.ts","sourceRoot":"","sources":["../src/context-backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAwCvF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,YAAY,GACf;IAAE,sBAAsB,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CA+D7D"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * hypermem Context Backfill
3
+ *
4
+ * One-time migration that creates context rows for existing conversations
5
+ * that don't yet have an active context. Designed to be idempotent — running
6
+ * it multiple times produces the same result without modifying existing data.
7
+ *
8
+ * Also provides parent chain backfill (Phase 2): reconstructs parent_id/depth
9
+ * for legacy flat conversations that were created before the Turn DAG model.
10
+ */
11
+ import { getOrCreateActiveContext, updateContextHead } from './context-store.js';
12
+ /**
13
+ * Backfill active contexts for all existing conversations.
14
+ *
15
+ * For each conversation without an active context:
16
+ * 1. Creates an active context via getOrCreateActiveContext
17
+ * 2. If the conversation has messages, advances the head pointer
18
+ * to the highest message ID
19
+ *
20
+ * @returns counts of created and skipped conversations
21
+ */
22
+ export function backfillContexts(db) {
23
+ let created = 0;
24
+ let skipped = 0;
25
+ const conversations = db
26
+ .prepare('SELECT id, agent_id, session_key FROM conversations')
27
+ .all();
28
+ for (const conv of conversations) {
29
+ // Check if an active context already exists for this conversation
30
+ const existing = db
31
+ .prepare("SELECT id FROM contexts WHERE conversation_id = ? AND status = 'active'")
32
+ .get(conv.id);
33
+ if (existing) {
34
+ skipped++;
35
+ continue;
36
+ }
37
+ // Find the max message ID for this conversation (may be null if no messages)
38
+ const maxRow = db
39
+ .prepare('SELECT MAX(id) as max_id FROM messages WHERE conversation_id = ?')
40
+ .get(conv.id);
41
+ const maxId = maxRow?.max_id ?? null;
42
+ // Create the active context
43
+ const context = getOrCreateActiveContext(db, conv.agent_id, conv.session_key, conv.id);
44
+ // If conversation has messages, advance head to the latest
45
+ if (maxId !== null) {
46
+ updateContextHead(db, context.id, maxId);
47
+ }
48
+ created++;
49
+ }
50
+ return { created, skipped };
51
+ }
52
+ /**
53
+ * Backfill parent_id and depth for existing messages that lack them.
54
+ *
55
+ * Reconstructs a linear chain per conversation ordered by message_index:
56
+ * - first message: parent_id = NULL, depth = 0
57
+ * - each subsequent: parent_id = previous.id, depth = previous.depth + 1
58
+ *
59
+ * Idempotent: only touches messages where parent_id IS NULL AND depth = 0
60
+ * AND there is more than one message or the message is not the first.
61
+ * In practice, we simply skip messages that already have parent_id set.
62
+ *
63
+ * Also stamps context_id on messages that lack it, using the active context
64
+ * for their conversation.
65
+ *
66
+ * @returns counts of messages updated and conversations processed
67
+ */
68
+ export function backfillParentChains(db) {
69
+ let conversationsProcessed = 0;
70
+ let messagesUpdated = 0;
71
+ // Get all conversations that have at least one non-first message without parent_id.
72
+ // This is the idempotency guard: after backfill, all messages at index > 0
73
+ // have parent_id set. The first message (index 0) legitimately has parent_id = NULL.
74
+ const conversations = db
75
+ .prepare(`SELECT DISTINCT conversation_id
76
+ FROM messages
77
+ WHERE message_index > 0 AND parent_id IS NULL
78
+ ORDER BY conversation_id`)
79
+ .all();
80
+ const updateStmt = db.prepare('UPDATE messages SET parent_id = ?, depth = ?, context_id = COALESCE(context_id, ?) WHERE id = ?');
81
+ for (const { conversation_id: convId } of conversations) {
82
+ // Get active context for this conversation (if any)
83
+ const ctxRow = db
84
+ .prepare("SELECT id FROM contexts WHERE conversation_id = ? AND status = 'active' LIMIT 1")
85
+ .get(convId);
86
+ const contextId = ctxRow?.id ?? null;
87
+ // Get all messages for this conversation without parent_id, ordered by message_index
88
+ const messages = db
89
+ .prepare('SELECT id, message_index FROM messages WHERE conversation_id = ? AND parent_id IS NULL ORDER BY message_index ASC')
90
+ .all(convId);
91
+ if (messages.length === 0)
92
+ continue;
93
+ // Also get the last message that already HAS a parent_id (if any),
94
+ // so we can chain the backfilled messages after it.
95
+ const lastChainedRow = db
96
+ .prepare('SELECT id, depth FROM messages WHERE conversation_id = ? AND parent_id IS NOT NULL ORDER BY message_index DESC LIMIT 1')
97
+ .get(convId);
98
+ let prevId = lastChainedRow?.id ?? null;
99
+ let prevDepth = lastChainedRow?.depth ?? -1;
100
+ // If there's no prior chain AND the first unlinked message is truly
101
+ // message_index 0, start the chain from scratch
102
+ for (const msg of messages) {
103
+ const depth = prevDepth + 1;
104
+ updateStmt.run(prevId, depth, contextId, msg.id);
105
+ prevId = msg.id;
106
+ prevDepth = depth;
107
+ messagesUpdated++;
108
+ }
109
+ conversationsProcessed++;
110
+ }
111
+ return { conversationsProcessed, messagesUpdated };
112
+ }
113
+ //# sourceMappingURL=context-backfill.js.map
@@ -0,0 +1,77 @@
1
+ /**
2
+ * hypermem Context Store
3
+ *
4
+ * Manages the `contexts` table — a durable record of agent conversation
5
+ * contexts that tracks which session is active, what the current head
6
+ * message is, and supports archival and forking.
7
+ *
8
+ * Each agent + session pair has at most one active context at a time.
9
+ * Contexts are the anchor point for the compositor: they track the
10
+ * head message (most recent message included in composed context) and
11
+ * link back to the underlying conversation.
12
+ *
13
+ * Design principles:
14
+ * - All functions take DatabaseSync as first arg (standalone, no classes)
15
+ * - Fully idempotent — safe to call on every startup
16
+ * - Head pointer is monotone-forward (never moves backward)
17
+ * - Archive is idempotent (no-op if already archived)
18
+ */
19
+ import type { DatabaseSync } from 'node:sqlite';
20
+ export interface Context {
21
+ id: number;
22
+ agentId: string;
23
+ sessionKey: string;
24
+ conversationId: number;
25
+ headMessageId: number | null;
26
+ parentContextId: number | null;
27
+ status: 'active' | 'archived' | 'forked';
28
+ createdAt: string;
29
+ updatedAt: string;
30
+ metadataJson: string | null;
31
+ }
32
+ /**
33
+ * Add the contexts table and related indexes to an existing messages.db.
34
+ * Also ALTERs the messages table to add a context_id foreign key column.
35
+ * Idempotent — safe to call on every startup.
36
+ */
37
+ export declare function ensureContextSchema(db: DatabaseSync): void;
38
+ /**
39
+ * Get the active context for an agent + session pair.
40
+ * Returns null if no active context exists.
41
+ */
42
+ export declare function getActiveContext(db: DatabaseSync, agentId: string, sessionKey: string): Context | null;
43
+ /**
44
+ * Get the active context for an agent + session, creating one if none exists.
45
+ *
46
+ * If an active context already exists, returns it unchanged.
47
+ * Otherwise INSERTs a new context with status='active', head_message_id=NULL,
48
+ * and the given conversationId.
49
+ *
50
+ * Idempotent — safe to call repeatedly.
51
+ */
52
+ export declare function getOrCreateActiveContext(db: DatabaseSync, agentId: string, sessionKey: string, conversationId: number): Context;
53
+ /**
54
+ * Update the head message pointer for a context.
55
+ *
56
+ * Monotone forward: only updates if messageId > current head_message_id
57
+ * (or current is NULL). This prevents accidental regression of the head
58
+ * pointer, matching the compaction-fence monotone progress pattern.
59
+ */
60
+ export declare function updateContextHead(db: DatabaseSync, contextId: number, messageId: number): void;
61
+ /**
62
+ * Archive a context, setting its status to 'archived'.
63
+ * Idempotent — no-op if already archived.
64
+ */
65
+ export declare function archiveContext(db: DatabaseSync, contextId: number): void;
66
+ /**
67
+ * Rotate a session's active context: archive the current active context
68
+ * and create a new one, optionally linking back via parent_context_id.
69
+ *
70
+ * Used on session restarts/rotations so the new context starts with a
71
+ * clean head pointer instead of inheriting the stale tail.
72
+ *
73
+ * Returns the newly created active context.
74
+ * If no active context exists, simply creates one (no archive step).
75
+ */
76
+ export declare function rotateSessionContext(db: DatabaseSync, agentId: string, sessionKey: string, conversationId: number): Context;
77
+ //# sourceMappingURL=context-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-store.d.ts","sourceRoot":"","sources":["../src/context-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAyBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAmC1D;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB,OAAO,GAAG,IAAI,CAShB;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GACrB,OAAO,CAwBT;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,YAAY,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,IAAI,CAeN;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,YAAY,EAChB,SAAS,EAAE,MAAM,GAChB,IAAI,CAMN;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GACrB,OAAO,CA2BT"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * hypermem Context Store
3
+ *
4
+ * Manages the `contexts` table — a durable record of agent conversation
5
+ * contexts that tracks which session is active, what the current head
6
+ * message is, and supports archival and forking.
7
+ *
8
+ * Each agent + session pair has at most one active context at a time.
9
+ * Contexts are the anchor point for the compositor: they track the
10
+ * head message (most recent message included in composed context) and
11
+ * link back to the underlying conversation.
12
+ *
13
+ * Design principles:
14
+ * - All functions take DatabaseSync as first arg (standalone, no classes)
15
+ * - Fully idempotent — safe to call on every startup
16
+ * - Head pointer is monotone-forward (never moves backward)
17
+ * - Archive is idempotent (no-op if already archived)
18
+ */
19
+ // ─── Internal Helpers ───────────────────────────────────────────
20
+ function parseContextRow(row) {
21
+ return {
22
+ id: row.id,
23
+ agentId: row.agent_id,
24
+ sessionKey: row.session_key,
25
+ conversationId: row.conversation_id,
26
+ headMessageId: row.head_message_id ?? null,
27
+ parentContextId: row.parent_context_id ?? null,
28
+ status: row.status,
29
+ createdAt: row.created_at,
30
+ updatedAt: row.updated_at,
31
+ metadataJson: row.metadata_json ?? null,
32
+ };
33
+ }
34
+ function nowIso() {
35
+ return new Date().toISOString();
36
+ }
37
+ // ─── Schema ─────────────────────────────────────────────────────
38
+ /**
39
+ * Add the contexts table and related indexes to an existing messages.db.
40
+ * Also ALTERs the messages table to add a context_id foreign key column.
41
+ * Idempotent — safe to call on every startup.
42
+ */
43
+ export function ensureContextSchema(db) {
44
+ db.exec(`
45
+ CREATE TABLE IF NOT EXISTS contexts (
46
+ id INTEGER PRIMARY KEY,
47
+ agent_id TEXT NOT NULL,
48
+ session_key TEXT NOT NULL,
49
+ conversation_id INTEGER REFERENCES conversations(id),
50
+ head_message_id INTEGER REFERENCES messages(id),
51
+ parent_context_id INTEGER REFERENCES contexts(id),
52
+ status TEXT NOT NULL DEFAULT 'active',
53
+ created_at TEXT NOT NULL,
54
+ updated_at TEXT NOT NULL,
55
+ metadata_json TEXT
56
+ )
57
+ `);
58
+ db.exec(`
59
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_contexts_active_session
60
+ ON contexts(agent_id, session_key, status)
61
+ WHERE status = 'active'
62
+ `);
63
+ db.exec(`
64
+ CREATE INDEX IF NOT EXISTS idx_contexts_head
65
+ ON contexts(head_message_id)
66
+ `);
67
+ // ALTER messages table to add context_id column (PRAGMA guard)
68
+ const msgCols = db.prepare('PRAGMA table_info(messages)').all().map(r => r.name);
69
+ if (!msgCols.includes('context_id')) {
70
+ db.exec('ALTER TABLE messages ADD COLUMN context_id INTEGER REFERENCES contexts(id)');
71
+ }
72
+ }
73
+ // ─── Context Operations ─────────────────────────────────────────
74
+ /**
75
+ * Get the active context for an agent + session pair.
76
+ * Returns null if no active context exists.
77
+ */
78
+ export function getActiveContext(db, agentId, sessionKey) {
79
+ const row = db
80
+ .prepare('SELECT * FROM contexts WHERE agent_id = ? AND session_key = ? AND status = ?')
81
+ .get(agentId, sessionKey, 'active');
82
+ if (!row)
83
+ return null;
84
+ return parseContextRow(row);
85
+ }
86
+ /**
87
+ * Get the active context for an agent + session, creating one if none exists.
88
+ *
89
+ * If an active context already exists, returns it unchanged.
90
+ * Otherwise INSERTs a new context with status='active', head_message_id=NULL,
91
+ * and the given conversationId.
92
+ *
93
+ * Idempotent — safe to call repeatedly.
94
+ */
95
+ export function getOrCreateActiveContext(db, agentId, sessionKey, conversationId) {
96
+ const existing = getActiveContext(db, agentId, sessionKey);
97
+ if (existing)
98
+ return existing;
99
+ const now = nowIso();
100
+ const result = db
101
+ .prepare(`INSERT INTO contexts (agent_id, session_key, conversation_id, head_message_id, parent_context_id, status, created_at, updated_at, metadata_json)
102
+ VALUES (?, ?, ?, NULL, NULL, 'active', ?, ?, NULL)`)
103
+ .run(agentId, sessionKey, conversationId, now, now);
104
+ return {
105
+ id: Number(result.lastInsertRowid),
106
+ agentId,
107
+ sessionKey,
108
+ conversationId,
109
+ headMessageId: null,
110
+ parentContextId: null,
111
+ status: 'active',
112
+ createdAt: now,
113
+ updatedAt: now,
114
+ metadataJson: null,
115
+ };
116
+ }
117
+ /**
118
+ * Update the head message pointer for a context.
119
+ *
120
+ * Monotone forward: only updates if messageId > current head_message_id
121
+ * (or current is NULL). This prevents accidental regression of the head
122
+ * pointer, matching the compaction-fence monotone progress pattern.
123
+ */
124
+ export function updateContextHead(db, contextId, messageId) {
125
+ const now = nowIso();
126
+ const row = db
127
+ .prepare('SELECT head_message_id FROM contexts WHERE id = ?')
128
+ .get(contextId);
129
+ if (!row)
130
+ return;
131
+ // Monotone forward: only advance, never regress
132
+ if (row.head_message_id === null || messageId > row.head_message_id) {
133
+ db.prepare('UPDATE contexts SET head_message_id = ?, updated_at = ? WHERE id = ?').run(messageId, now, contextId);
134
+ }
135
+ }
136
+ /**
137
+ * Archive a context, setting its status to 'archived'.
138
+ * Idempotent — no-op if already archived.
139
+ */
140
+ export function archiveContext(db, contextId) {
141
+ const now = nowIso();
142
+ db.prepare(`UPDATE contexts SET status = 'archived', updated_at = ? WHERE id = ? AND status != 'archived'`).run(now, contextId);
143
+ }
144
+ /**
145
+ * Rotate a session's active context: archive the current active context
146
+ * and create a new one, optionally linking back via parent_context_id.
147
+ *
148
+ * Used on session restarts/rotations so the new context starts with a
149
+ * clean head pointer instead of inheriting the stale tail.
150
+ *
151
+ * Returns the newly created active context.
152
+ * If no active context exists, simply creates one (no archive step).
153
+ */
154
+ export function rotateSessionContext(db, agentId, sessionKey, conversationId) {
155
+ const existing = getActiveContext(db, agentId, sessionKey);
156
+ if (existing) {
157
+ archiveContext(db, existing.id);
158
+ }
159
+ const now = nowIso();
160
+ const result = db
161
+ .prepare(`INSERT INTO contexts (agent_id, session_key, conversation_id, head_message_id, parent_context_id, status, created_at, updated_at, metadata_json)
162
+ VALUES (?, ?, ?, NULL, ?, 'active', ?, ?, NULL)`)
163
+ .run(agentId, sessionKey, conversationId, existing?.id ?? null, now, now);
164
+ return {
165
+ id: Number(result.lastInsertRowid),
166
+ agentId,
167
+ sessionKey,
168
+ conversationId,
169
+ headMessageId: null,
170
+ parentContextId: existing?.id ?? null,
171
+ status: 'active',
172
+ createdAt: now,
173
+ updatedAt: now,
174
+ metadataJson: null,
175
+ };
176
+ }
177
+ //# sourceMappingURL=context-store.js.map
@@ -25,6 +25,18 @@ export interface OrgRegistry {
25
25
  }
26
26
  /**
27
27
  * Default fleet org structure.
28
+ *
29
+ * ── EXAMPLE DATA ──────────────────────────────────────────────────────────
30
+ * The agent names below (agent1, agent2, director1, etc.) are PLACEHOLDERS.
31
+ * Replace them with your own agent IDs to match your fleet configuration.
32
+ *
33
+ * Single-agent installs: you don't need to edit this. Your agent ID is
34
+ * resolved automatically at runtime from your OpenClaw config.
35
+ *
36
+ * Multi-agent installs: edit the agents map and orgs map below, then
37
+ * rebuild (`npm run build`). See INSTALL.md § "Configure your fleet" for
38
+ * a worked example.
39
+ * ─────────────────────────────────────────────────────────────────────────
28
40
  */
29
41
  export declare function defaultOrgRegistry(): OrgRegistry;
30
42
  export declare function canAccess(requester: AgentIdentity, target: AgentIdentity, visibility: MemoryVisibility, registry: OrgRegistry): boolean;
@@ -1 +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;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,+BAAyB,CAAC;AAI5D;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,eAAe,EAC1B,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,WAAW,GACpB,OAAO,EAAE,CAgCX"}
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;;;;;;;;;;;;;;GAcG;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;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,+BAAyB,CAAC;AAI5D;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,eAAe,EAC1B,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,WAAW,GACpB,OAAO,EAAE,CAgCX"}
@@ -19,30 +19,42 @@
19
19
  import { FleetStore } from './fleet-store.js';
20
20
  /**
21
21
  * Default fleet org structure.
22
+ *
23
+ * ── EXAMPLE DATA ──────────────────────────────────────────────────────────
24
+ * The agent names below (agent1, agent2, director1, etc.) are PLACEHOLDERS.
25
+ * Replace them with your own agent IDs to match your fleet configuration.
26
+ *
27
+ * Single-agent installs: you don't need to edit this. Your agent ID is
28
+ * resolved automatically at runtime from your OpenClaw config.
29
+ *
30
+ * Multi-agent installs: edit the agents map and orgs map below, then
31
+ * rebuild (`npm run build`). See INSTALL.md § "Configure your fleet" for
32
+ * a worked example.
33
+ * ─────────────────────────────────────────────────────────────────────────
22
34
  */
23
35
  export function defaultOrgRegistry() {
24
36
  const agents = {
25
- alice: { agentId: 'alice', tier: 'council' },
26
- bob: { agentId: 'bob', tier: 'council' },
27
- clarity: { agentId: 'clarity', tier: 'council' },
28
- dave: { agentId: 'dave', tier: 'council' },
29
- carol: { agentId: 'carol', tier: 'council' },
30
- oscar: { agentId: 'oscar', tier: 'council' },
31
- hank: { agentId: 'hank', tier: 'director', org: 'alice-org', councilLead: 'alice' },
32
- jack: { agentId: 'jack', tier: 'director', org: 'alice-org', councilLead: 'alice' },
33
- irene: { agentId: 'irene', tier: 'director', org: 'alice-org', councilLead: 'alice' },
34
- eve: { agentId: 'eve', tier: 'director', org: 'bob-org', councilLead: 'bob' },
35
- frank: { agentId: 'frank', tier: 'director', org: 'bob-org', councilLead: 'bob' },
36
- grace: { agentId: 'grace', tier: 'director', org: 'bob-org', councilLead: 'bob' },
37
- leo: { agentId: 'leo', tier: 'director', org: 'dave-org', councilLead: 'dave' },
38
- kate: { agentId: 'kate', tier: 'director', org: 'dave-org', councilLead: 'dave' },
39
- mike: { agentId: 'mike', tier: 'specialist' },
40
- nancy: { agentId: 'nancy', tier: 'specialist' },
37
+ agent1: { agentId: 'agent1', tier: 'council' },
38
+ agent2: { agentId: 'agent2', tier: 'council' },
39
+ agent4: { agentId: 'agent4', tier: 'council' },
40
+ agent3: { agentId: 'agent3', tier: 'council' },
41
+ agent6: { agentId: 'agent6', tier: 'council' },
42
+ agent5: { agentId: 'agent5', tier: 'council' },
43
+ director1: { agentId: 'director1', tier: 'director', org: 'agent1-org', councilLead: 'agent1' },
44
+ director2: { agentId: 'director2', tier: 'director', org: 'agent1-org', councilLead: 'agent1' },
45
+ director3: { agentId: 'director3', tier: 'director', org: 'agent1-org', councilLead: 'agent1' },
46
+ director4: { agentId: 'director4', tier: 'director', org: 'agent2-org', councilLead: 'agent2' },
47
+ director5: { agentId: 'director5', tier: 'director', org: 'agent2-org', councilLead: 'agent2' },
48
+ director6: { agentId: 'director6', tier: 'director', org: 'agent2-org', councilLead: 'agent2' },
49
+ director7: { agentId: 'director7', tier: 'director', org: 'agent3-org', councilLead: 'agent3' },
50
+ director8: { agentId: 'director8', tier: 'director', org: 'agent3-org', councilLead: 'agent3' },
51
+ specialist1: { agentId: 'specialist1', tier: 'specialist' },
52
+ specialist2: { agentId: 'specialist2', tier: 'specialist' },
41
53
  };
42
54
  const orgs = {
43
- 'alice-org': ['alice', 'hank', 'jack', 'irene'],
44
- 'bob-org': ['bob', 'eve', 'frank', 'grace'],
45
- 'dave-org': ['dave', 'leo', 'kate'],
55
+ 'agent1-org': ['agent1', 'director1', 'director2', 'director3'],
56
+ 'agent2-org': ['agent2', 'director4', 'director5', 'director6'],
57
+ 'agent3-org': ['agent3', 'director7', 'director8'],
46
58
  };
47
59
  return { orgs, agents };
48
60
  }
package/dist/db.d.ts.map CHANGED
@@ -1 +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;AAqC3C,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;IAWtB;;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"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAuC3C,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;IAwB3C;;;;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;IAWtB;;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"}
package/dist/db.js CHANGED
@@ -15,6 +15,8 @@ import path from 'node:path';
15
15
  import { migrate } from './schema.js';
16
16
  import { migrateLibrary } from './library-schema.js';
17
17
  import { ENGINE_VERSION } from './version.js';
18
+ import { ensureContextSchema } from './context-store.js';
19
+ import { backfillContexts } from './context-backfill.js';
18
20
  // sqlite-vec extension loading — optional dependency
19
21
  import { createRequire } from 'node:module';
20
22
  let sqliteVecAvailable = null;
@@ -106,6 +108,12 @@ export class DatabaseManager {
106
108
  db = new DatabaseSync(dbPath);
107
109
  applyPragmas(db);
108
110
  migrate(db);
111
+ ensureContextSchema(db);
112
+ // Backfill contexts for existing conversations on first run after migration
113
+ const contextCount = db.prepare('SELECT COUNT(*) as cnt FROM contexts').get();
114
+ if (contextCount.cnt === 0) {
115
+ backfillContexts(db);
116
+ }
109
117
  this.messageDbs.set(agentId, db);
110
118
  return db;
111
119
  }
@@ -67,7 +67,7 @@ export interface DreamerResult {
67
67
  }
68
68
  /**
69
69
  * Resolve the workspace directory for an agent.
70
- * Council agents live at ~/.openclaw/workspace/{agentId}/
70
+ * Council agents live at ~/.openclaw/workspace-council/{agentId}/
71
71
  * Other agents at ~/.openclaw/workspace/{agentId}/
72
72
  */
73
73
  export declare function resolveAgentWorkspacePath(agentId: string): Promise<string | null>;
@@ -31,7 +31,7 @@ export const DEFAULT_DREAMER_CONFIG = {
31
31
  // ─── Workspace path resolution ───────────────────────────────────────────────
32
32
  /**
33
33
  * Resolve the workspace directory for an agent.
34
- * Council agents live at ~/.openclaw/workspace/{agentId}/
34
+ * Council agents live at ~/.openclaw/workspace-council/{agentId}/
35
35
  * Other agents at ~/.openclaw/workspace/{agentId}/
36
36
  */
37
37
  export async function resolveAgentWorkspacePath(agentId) {
package/dist/index.d.ts CHANGED
@@ -25,6 +25,8 @@ export type { FleetAgent, FleetOrg, AgentCapability } from './fleet-store.js';
25
25
  export { SystemStore } from './system-store.js';
26
26
  export type { SystemState, SystemEvent } from './system-store.js';
27
27
  export { WorkStore } from './work-store.js';
28
+ export { ensureContextSchema, getActiveContext, getOrCreateActiveContext, updateContextHead, archiveContext, rotateSessionContext } from './context-store.js';
29
+ export type { Context } from './context-store.js';
28
30
  export type { WorkItem, WorkEvent, WorkStatus } from './work-store.js';
29
31
  export { DesiredStateStore } from './desired-state-store.js';
30
32
  export { evictStaleContent, DEFAULT_EVICTION_CONFIG } from './image-eviction.js';
@@ -98,8 +100,8 @@ import { type OrgRegistry } from './cross-agent.js';
98
100
  *
99
101
  * Usage:
100
102
  * const hm = await hypermem.create({ dataDir: '~/.openclaw/hypermem' });
101
- * await hm.record('alice', 'agent:alice:webchat:main', userMsg);
102
- * const result = await hm.compose({ agentId: 'alice', sessionKey: '...', ... });
103
+ * await hm.record('agent1', 'agent:agent1:webchat:main', userMsg);
104
+ * const result = await hm.compose({ agentId: 'agent1', sessionKey: '...', ... });
103
105
  */
104
106
  export declare class HyperMem {
105
107
  readonly dbManager: DatabaseManager;