@psiclawops/hypermem 0.5.5 → 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 (56) hide show
  1. package/README.md +108 -62
  2. package/dist/background-indexer.d.ts +18 -0
  3. package/dist/background-indexer.d.ts.map +1 -1
  4. package/dist/background-indexer.js +131 -20
  5. package/dist/cache.d.ts +24 -1
  6. package/dist/cache.d.ts.map +1 -1
  7. package/dist/cache.js +77 -3
  8. package/dist/compositor.d.ts +6 -0
  9. package/dist/compositor.d.ts.map +1 -1
  10. package/dist/compositor.js +471 -129
  11. package/dist/context-backfill.d.ts +46 -0
  12. package/dist/context-backfill.d.ts.map +1 -0
  13. package/dist/context-backfill.js +113 -0
  14. package/dist/context-store.d.ts +77 -0
  15. package/dist/context-store.d.ts.map +1 -0
  16. package/dist/context-store.js +177 -0
  17. package/dist/cross-agent.d.ts +12 -0
  18. package/dist/cross-agent.d.ts.map +1 -1
  19. package/dist/cross-agent.js +31 -19
  20. package/dist/db.d.ts.map +1 -1
  21. package/dist/db.js +8 -0
  22. package/dist/index.d.ts +5 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +26 -7
  25. package/dist/knowledge-lint.js +4 -4
  26. package/dist/message-store.d.ts +31 -2
  27. package/dist/message-store.d.ts.map +1 -1
  28. package/dist/message-store.js +131 -17
  29. package/dist/preference-store.d.ts +1 -1
  30. package/dist/preference-store.js +1 -1
  31. package/dist/profiles.d.ts +4 -2
  32. package/dist/profiles.d.ts.map +1 -1
  33. package/dist/profiles.js +72 -37
  34. package/dist/repair-tool-pairs.d.ts.map +1 -1
  35. package/dist/repair-tool-pairs.js +73 -2
  36. package/dist/schema.d.ts +1 -1
  37. package/dist/schema.d.ts.map +1 -1
  38. package/dist/schema.js +27 -1
  39. package/dist/seed.d.ts +1 -1
  40. package/dist/seed.js +1 -1
  41. package/dist/session-flusher.d.ts +2 -2
  42. package/dist/session-flusher.js +2 -2
  43. package/dist/spawn-context.d.ts +1 -1
  44. package/dist/spawn-context.js +1 -1
  45. package/dist/topic-synthesizer.d.ts.map +1 -1
  46. package/dist/topic-synthesizer.js +4 -3
  47. package/dist/trigger-registry.d.ts +1 -1
  48. package/dist/trigger-registry.js +4 -4
  49. package/dist/types.d.ts +74 -32
  50. package/dist/types.d.ts.map +1 -1
  51. package/dist/vector-store.d.ts +10 -1
  52. package/dist/vector-store.d.ts.map +1 -1
  53. package/dist/vector-store.js +353 -0
  54. package/dist/version.d.ts +5 -5
  55. package/dist/version.js +5 -5
  56. package/package.json +4 -2
package/dist/profiles.js CHANGED
@@ -24,6 +24,14 @@ const BASE_EMBEDDING = {
24
24
  timeout: 10000,
25
25
  batchSize: 32,
26
26
  };
27
+ /** Gemini embedding preset — use with mergeProfile() when switching fleet to Gemini. */
28
+ export const GEMINI_EMBEDDING = {
29
+ provider: 'gemini',
30
+ model: 'gemini-embedding-2-preview',
31
+ dimensions: 3072,
32
+ timeout: 15000,
33
+ batchSize: 100,
34
+ };
27
35
  // ---------------------------------------------------------------------------
28
36
  // light — 64k context window, single agent, constrained resources
29
37
  //
@@ -32,28 +40,37 @@ const BASE_EMBEDDING = {
32
40
  // - Single agent — no cross-session context needed
33
41
  // - Minimal ACA stack — no dreaming, no background indexing overhead
34
42
  // - Low Redis churn — longer flush intervals, shorter history window
35
- // - outputProfile: 'light' — ~100 token standalone directives, no fleet concepts
43
+ // - hyperformProfile: 'light' — ~100 token standalone directives, no fleet concepts
36
44
  // - No parallel operations — sequential fact extraction only
37
45
  // ---------------------------------------------------------------------------
38
46
  const LIGHT_COMPOSITOR = {
39
- defaultTokenBudget: 40000, // leaves ~24k for model output at 64k window
40
- maxHistoryMessages: 200, // keep window tight small models lose coherence past ~150 msgs
41
- maxFacts: 15, // surface top facts only, don't swamp the window
42
- maxCrossSessionContext: 0, // single agent no cross-session
43
- maxRecentToolPairs: 2, // minimal tool history
47
+ // ── Primary budget controls ──
48
+ budgetFraction: 0.625, // 40k effective at 64k window
49
+ reserveFraction: 0.35, // generous small systems do heavy tool work
50
+ historyFraction: 0.35, // ~14k tokens of conversation history
51
+ memoryFraction: 0.30, // ~12k tokens for facts/wiki/semantic
52
+ // ── Absolute fallback ──
53
+ defaultTokenBudget: 40000,
54
+ // ── History internals ──
55
+ maxHistoryMessages: 200,
56
+ warmHistoryBudgetFraction: 0.35,
57
+ keystoneHistoryFraction: 0.1,
58
+ keystoneMaxMessages: 5,
59
+ keystoneMinSignificance: 0.7,
60
+ // ── Memory internals ──
61
+ maxFacts: 15,
62
+ maxCrossSessionContext: 0,
63
+ maxTotalTriggerTokens: 1500,
64
+ wikiTokenCap: 300,
65
+ // ── Tool gradient (internal — safe floor enforced automatically) ──
66
+ maxRecentToolPairs: 2,
44
67
  maxProseToolPairs: 4,
45
- warmHistoryBudgetFraction: 0.35, // slightly less history, more room for context
46
- contextWindowReserve: 0.35, // generous reserve — small models need output headroom
68
+ // ── Dynamic reserve ──
47
69
  dynamicReserveEnabled: true,
48
- dynamicReserveTurnHorizon: 3, // shorter horizon — small sessions
70
+ dynamicReserveTurnHorizon: 3,
49
71
  dynamicReserveMax: 0.50,
50
- keystoneHistoryFraction: 0.1, // light keystone — history window is already small
51
- keystoneMaxMessages: 5,
52
- keystoneMinSignificance: 0.7, // higher bar — only high-signal keystone messages
53
- targetBudgetFraction: 0.50, // Anvil spec: 0.50 for light
54
- maxTotalTriggerTokens: 1500, // tight trigger ceiling
55
- outputProfile: 'light', // standalone density directives only, no fleet concepts
56
- wikiTokenCap: 300, // Anvil spec: 300 for light
72
+ // ── HyperForm ──
73
+ hyperformProfile: 'light',
57
74
  };
58
75
  const LIGHT_INDEXER = {
59
76
  enabled: true,
@@ -87,27 +104,36 @@ export const lightProfile = {
87
104
  // - Mid-range models (Sonnet, GPT-5-mini, Gemini Flash) at 128k
88
105
  // - Small multi-agent setups or single power-user agents
89
106
  // - Full ACA stack — dreaming optional, background indexing on
90
- // - outputProfile: 'standard' — full FOS, no MOD
107
+ // - hyperformProfile: 'standard' — full FOS, no MOD
91
108
  // ---------------------------------------------------------------------------
92
109
  const STANDARD_COMPOSITOR = {
110
+ // ── Primary budget controls ──
111
+ budgetFraction: 0.703, // 90k effective at 128k window
112
+ reserveFraction: 0.25, // balanced — leaves room for large tool results
113
+ historyFraction: 0.40, // ~27k tokens of conversation history
114
+ memoryFraction: 0.40, // ~27k tokens for facts/wiki/semantic
115
+ // ── Absolute fallback ──
93
116
  defaultTokenBudget: 90000,
117
+ // ── History internals ──
94
118
  maxHistoryMessages: 500,
119
+ warmHistoryBudgetFraction: 0.40,
120
+ keystoneHistoryFraction: 0.20,
121
+ keystoneMaxMessages: 15,
122
+ keystoneMinSignificance: 0.5,
123
+ // ── Memory internals ──
95
124
  maxFacts: 30,
96
125
  maxCrossSessionContext: 4000,
126
+ maxTotalTriggerTokens: 4000,
127
+ wikiTokenCap: 600,
128
+ // ── Tool gradient (internal — safe floor enforced automatically) ──
97
129
  maxRecentToolPairs: 3,
98
130
  maxProseToolPairs: 10,
99
- warmHistoryBudgetFraction: 0.40,
100
- contextWindowReserve: 0.25,
131
+ // ── Dynamic reserve ──
101
132
  dynamicReserveEnabled: true,
102
133
  dynamicReserveTurnHorizon: 5,
103
134
  dynamicReserveMax: 0.50,
104
- keystoneHistoryFraction: 0.20,
105
- keystoneMaxMessages: 15,
106
- keystoneMinSignificance: 0.5,
107
- targetBudgetFraction: 0.65, // Anvil spec: 0.65 for standard
108
- maxTotalTriggerTokens: 4000,
109
- outputProfile: 'standard', // full FOS, MOD suppressed
110
- wikiTokenCap: 600, // Anvil spec: 600 for standard
135
+ // ── HyperForm ──
136
+ hyperformProfile: 'standard',
111
137
  };
112
138
  const STANDARD_INDEXER = {
113
139
  enabled: true,
@@ -135,28 +161,37 @@ export const standardProfile = {
135
161
  // - Large context models (Opus, GPT-5.4, Gemini Pro) at 200k+
136
162
  // - Council / multi-agent fleet deployments
137
163
  // - Full ACA stack including dreaming, background indexing, cross-session
138
- // - outputProfile: 'full' — FOS + MOD, full spec
164
+ // - hyperformProfile: 'full' — FOS + MOD, full spec
139
165
  // - Higher keystone threshold — more historical context worth surfacing
140
166
  // ---------------------------------------------------------------------------
141
167
  const EXTENDED_COMPOSITOR = {
168
+ // ── Primary budget controls ──
169
+ budgetFraction: 0.588, // 160k effective at 272k window
170
+ reserveFraction: 0.20, // tighter — large windows have natural headroom
171
+ historyFraction: 0.45, // ~72k tokens of conversation history
172
+ memoryFraction: 0.40, // ~64k tokens for facts/wiki/semantic
173
+ // ── Absolute fallback ──
142
174
  defaultTokenBudget: 160000,
175
+ // ── History internals ──
143
176
  maxHistoryMessages: 1000,
177
+ warmHistoryBudgetFraction: 0.45,
178
+ keystoneHistoryFraction: 0.25,
179
+ keystoneMaxMessages: 30,
180
+ keystoneMinSignificance: 0.4,
181
+ // ── Memory internals ──
144
182
  maxFacts: 60,
145
183
  maxCrossSessionContext: 12000,
184
+ maxTotalTriggerTokens: 10000,
185
+ wikiTokenCap: 800,
186
+ // ── Tool gradient (internal — safe floor enforced automatically) ──
146
187
  maxRecentToolPairs: 5,
147
188
  maxProseToolPairs: 15,
148
- warmHistoryBudgetFraction: 0.45,
149
- contextWindowReserve: 0.20,
189
+ // ── Dynamic reserve ──
150
190
  dynamicReserveEnabled: true,
151
191
  dynamicReserveTurnHorizon: 7,
152
192
  dynamicReserveMax: 0.45,
153
- keystoneHistoryFraction: 0.25,
154
- keystoneMaxMessages: 30,
155
- keystoneMinSignificance: 0.4,
156
- targetBudgetFraction: 0.55, // Anvil spec: 0.55 for extended (history is huge, budget carefully)
157
- maxTotalTriggerTokens: 10000,
158
- outputProfile: 'full', // FOS + MOD — full fleet spec
159
- wikiTokenCap: 800, // Anvil spec: 800 for extended
193
+ // ── HyperForm ──
194
+ hyperformProfile: 'full',
160
195
  };
161
196
  const EXTENDED_INDEXER = {
162
197
  enabled: true,
@@ -210,7 +245,7 @@ export function getProfile(name) {
210
245
  * @example
211
246
  * const config = mergeProfile('light', {
212
247
  * cache: { keyPrefix: 'myapp:' },
213
- * compositor: { outputProfile: 'standard' }, // upgrade tier
248
+ * compositor: { hyperformProfile: 'standard', memoryFraction: 0.50 }, // upgrade tier + more memory
214
249
  * });
215
250
  */
216
251
  export function mergeProfile(name, overrides) {
@@ -1 +1 @@
1
- {"version":3,"file":"repair-tool-pairs.d.ts","sourceRoot":"","sources":["../src/repair-tool-pairs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CAmHpE"}
1
+ {"version":3,"file":"repair-tool-pairs.d.ts","sourceRoot":"","sources":["../src/repair-tool-pairs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CA0LpE"}
@@ -129,9 +129,80 @@ export function repairToolPairs(messages) {
129
129
  }
130
130
  result.push(msg);
131
131
  }
132
+ // ── Pass 4: Intra-message content block integrity ─────────────────────────
133
+ // Anthropic requires that every tool_result content block in a user message
134
+ // references a tool_use_id that exists in the IMMEDIATELY PRECEDING assistant
135
+ // message's content blocks. Pass 3 only checks global existence. This pass
136
+ // enforces adjacency.
137
+ let intraBlockDropped = 0;
138
+ for (let i = 0; i < result.length; i++) {
139
+ const msg = result[i];
140
+ if (msg.role !== 'user' || !Array.isArray(msg.content))
141
+ continue;
142
+ const content = msg.content;
143
+ const hasToolResultBlocks = content.some(b => b.type === 'tool_result');
144
+ if (!hasToolResultBlocks)
145
+ continue;
146
+ // Find the immediately preceding assistant message
147
+ let precedingAssistant = null;
148
+ for (let j = i - 1; j >= 0; j--) {
149
+ if (result[j].role === 'assistant') {
150
+ precedingAssistant = result[j];
151
+ break;
152
+ }
153
+ }
154
+ // Collect tool_use IDs from the preceding assistant message only
155
+ const adjacentCallIds = new Set();
156
+ if (precedingAssistant) {
157
+ // Content array format
158
+ if (Array.isArray(precedingAssistant.content)) {
159
+ for (const block of precedingAssistant.content) {
160
+ if ((block.type === 'toolCall' || block.type === 'tool_use') &&
161
+ typeof block.id === 'string' && block.id) {
162
+ adjacentCallIds.add(block.id);
163
+ }
164
+ }
165
+ }
166
+ // NeutralMessage format
167
+ if (Array.isArray(precedingAssistant.toolCalls)) {
168
+ for (const tc of precedingAssistant.toolCalls) {
169
+ if (typeof tc.id === 'string' && tc.id)
170
+ adjacentCallIds.add(tc.id);
171
+ }
172
+ }
173
+ }
174
+ // Also check pi-agent ToolResultMessages that reference the preceding assistant
175
+ // (these are message-level, not content-block level — skip for adjacency check)
176
+ // Filter: keep tool_result blocks only if their tool_use_id is in the
177
+ // immediately preceding assistant message
178
+ const filteredContent = content.filter(block => {
179
+ if (block.type !== 'tool_result')
180
+ return true;
181
+ const toolUseId = typeof block.tool_use_id === 'string' ? block.tool_use_id : '';
182
+ if (!toolUseId)
183
+ return false; // malformed, drop
184
+ if (adjacentCallIds.has(toolUseId))
185
+ return true;
186
+ intraBlockDropped++;
187
+ return false;
188
+ });
189
+ if (filteredContent.length === 0) {
190
+ // All content blocks were orphaned — remove the message entirely
191
+ result.splice(i, 1);
192
+ i--; // re-check this index
193
+ }
194
+ else if (filteredContent.length !== content.length) {
195
+ result[i] = { ...msg, content: filteredContent };
196
+ }
197
+ }
132
198
  const dropped = messages.length - result.length;
133
- if (dropped > 0) {
134
- console.log(`[hypermem] repairToolPairs: dropped ${dropped} orphaned message(s) (${messages.length} → ${result.length})`);
199
+ if (dropped > 0 || intraBlockDropped > 0) {
200
+ const parts = [];
201
+ if (dropped > 0)
202
+ parts.push(`${dropped} orphaned message(s)`);
203
+ if (intraBlockDropped > 0)
204
+ parts.push(`${intraBlockDropped} intra-message orphaned content block(s)`);
205
+ console.log(`[hypermem] repairToolPairs: dropped ${parts.join(' + ')} (${messages.length} → ${result.length})`);
135
206
  }
136
207
  return result;
137
208
  }
package/dist/schema.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * Contains ONLY conversation data — structured knowledge lives in library.db.
7
7
  */
8
8
  import type { DatabaseSync } from 'node:sqlite';
9
- export declare const LATEST_SCHEMA_VERSION = 6;
9
+ export declare const LATEST_SCHEMA_VERSION = 8;
10
10
  /**
11
11
  * Run migrations on an agent message database.
12
12
  */
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAgJvC;;GAEG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA4F9C;AAED,OAAO,EAAE,qBAAqB,IAAI,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAgJvC;;GAEG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA0H9C;AAED,OAAO,EAAE,qBAAqB,IAAI,cAAc,EAAE,CAAC"}
package/dist/schema.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * Write-heavy, temporal, rotatable.
6
6
  * Contains ONLY conversation data — structured knowledge lives in library.db.
7
7
  */
8
- export const LATEST_SCHEMA_VERSION = 6;
8
+ export const LATEST_SCHEMA_VERSION = 8;
9
9
  function nowIso() {
10
10
  return new Date().toISOString();
11
11
  }
@@ -224,6 +224,32 @@ export function migrate(db) {
224
224
  db.prepare('INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (?, ?)')
225
225
  .run(6, nowIso());
226
226
  }
227
+ // v6 → v7: Turn DAG Phase 2 — add parent_id and depth to messages
228
+ if (currentVersion < 7) {
229
+ const msgCols = db.prepare('PRAGMA table_info(messages)').all()
230
+ .map(r => r.name);
231
+ if (!msgCols.includes('parent_id')) {
232
+ db.exec('ALTER TABLE messages ADD COLUMN parent_id INTEGER REFERENCES messages(id)');
233
+ }
234
+ if (!msgCols.includes('depth')) {
235
+ db.exec('ALTER TABLE messages ADD COLUMN depth INTEGER NOT NULL DEFAULT 0');
236
+ }
237
+ db.exec('CREATE INDEX IF NOT EXISTS idx_messages_parent_id ON messages(parent_id)');
238
+ db.prepare('INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (?, ?)')
239
+ .run(7, nowIso());
240
+ }
241
+ // v7 → v8: Turn DAG Phase 3 — add context_id index for DAG-native reads
242
+ // Also ensures context_id column exists (idempotent with ensureContextSchema)
243
+ if (currentVersion < 8) {
244
+ const msgCols8 = db.prepare('PRAGMA table_info(messages)').all()
245
+ .map(r => r.name);
246
+ if (!msgCols8.includes('context_id')) {
247
+ db.exec('ALTER TABLE messages ADD COLUMN context_id INTEGER');
248
+ }
249
+ db.exec('CREATE INDEX IF NOT EXISTS idx_messages_context_id ON messages(context_id)');
250
+ db.prepare('INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (?, ?)')
251
+ .run(8, nowIso());
252
+ }
227
253
  }
228
254
  export { LATEST_SCHEMA_VERSION as SCHEMA_VERSION };
229
255
  //# sourceMappingURL=schema.js.map
package/dist/seed.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Usage:
8
8
  * const seeder = new WorkspaceSeeder(hypermem);
9
- * const result = await seeder.seedWorkspace('/path/to/workspace', { agentId: 'forge' });
9
+ * const result = await seeder.seedWorkspace('/path/to/workspace', { agentId: 'agent1' });
10
10
  *
11
11
  * Idempotent: skips files whose source hash hasn't changed since last index.
12
12
  * Atomic: each file's chunks are swapped in a single transaction.
package/dist/seed.js CHANGED
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Usage:
8
8
  * const seeder = new WorkspaceSeeder(hypermem);
9
- * const result = await seeder.seedWorkspace('/path/to/workspace', { agentId: 'forge' });
9
+ * const result = await seeder.seedWorkspace('/path/to/workspace', { agentId: 'agent1' });
10
10
  *
11
11
  * Idempotent: skips files whose source hash hasn't changed since last index.
12
12
  * Atomic: each file's chunks are swapped in a single transaction.
@@ -33,8 +33,8 @@ export interface FlushSessionResult {
33
33
  * from those stores naturally.
34
34
  *
35
35
  * @param cache Connected CacheLayer instance
36
- * @param agentId Agent identifier (e.g. "forge")
37
- * @param sessionKey Full session key (e.g. "agent:forge:webchat:scratchpad")
36
+ * @param agentId Agent identifier (e.g. "agent1")
37
+ * @param sessionKey Full session key (e.g. "agent:agent1:webchat:scratchpad")
38
38
  * @param opts Optional flags
39
39
  */
40
40
  export declare function flushSession(cache: CacheLayer, agentId: string, sessionKey: string, opts?: FlushSessionOptions): Promise<FlushSessionResult>;
@@ -18,8 +18,8 @@
18
18
  * from those stores naturally.
19
19
  *
20
20
  * @param cache Connected CacheLayer instance
21
- * @param agentId Agent identifier (e.g. "forge")
22
- * @param sessionKey Full session key (e.g. "agent:forge:webchat:scratchpad")
21
+ * @param agentId Agent identifier (e.g. "agent1")
22
+ * @param sessionKey Full session key (e.g. "agent:agent1:webchat:scratchpad")
23
23
  * @param opts Optional flags
24
24
  */
25
25
  export async function flushSession(cache, agentId, sessionKey, opts = {}) {
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Usage:
8
8
  * const ctx = await buildSpawnContext(messageStore, docChunkStore, agentId, {
9
- * parentSessionKey: 'agent:forge:webchat:main',
9
+ * parentSessionKey: 'agent:agent1:webchat:main',
10
10
  * workingSnapshot: 10,
11
11
  * documents: ['/path/to/spec.md'],
12
12
  * });
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Usage:
8
8
  * const ctx = await buildSpawnContext(messageStore, docChunkStore, agentId, {
9
- * parentSessionKey: 'agent:forge:webchat:main',
9
+ * parentSessionKey: 'agent:agent1:webchat:main',
10
10
  * workingSnapshot: 10,
11
11
  * documents: ['/path/to/spec.md'],
12
12
  * });
@@ -1 +1 @@
1
- {"version":3,"file":"topic-synthesizer.d.ts","sourceRoot":"","sources":["../src/topic-synthesizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,QAAA,MAAM,uBAAuB,KAAK,CAAC;AACnC,QAAA,MAAM,sBAAsB,IAAI,CAAC;AACjC,QAAA,MAAM,4BAA4B,IAAI,CAAC;AACvC,QAAA,MAAM,2BAA2B,MAAM,CAAC;AACxC,QAAA,MAAM,uBAAuB,KAAK,CAAC;AACnC,QAAA,MAAM,uBAAuB,IAAI,CAAC;AAClC,QAAA,MAAM,cAAc,KAAK,CAAC;AAC1B,QAAA,MAAM,eAAe,IAAI,CAAC;AAG1B,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,4BAA4B,EAC5B,2BAA2B,EAC3B,uBAAuB,EACvB,uBAAuB,EACvB,cAAc,EACd,eAAe,GAChB,CAAC;AAIF,MAAM,WAAW,eAAe;IAC9B,uBAAuB,EAAE,MAAM,CAAC;IAChC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,4BAA4B,EAAE,MAAM,CAAC;IACrC,2BAA2B,EAAE,MAAM,CAAC;IACpC,uBAAuB,EAAE,MAAM,CAAC;IAChC,uBAAuB,EAAE,MAAM,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;CACjC;AA6HD,qBAAa,gBAAgB;IAIzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAL1B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;gBAG/B,SAAS,EAAE,YAAY,EACvB,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,YAAY,GAAG,IAAI,EACtD,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,YAAA;IAcpD;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe;IAkFtC;;OAEG;IACH,OAAO,CAAC,eAAe;CA+GxB"}
1
+ {"version":3,"file":"topic-synthesizer.d.ts","sourceRoot":"","sources":["../src/topic-synthesizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,QAAA,MAAM,uBAAuB,KAAK,CAAC;AACnC,QAAA,MAAM,sBAAsB,IAAI,CAAC;AACjC,QAAA,MAAM,4BAA4B,IAAI,CAAC;AACvC,QAAA,MAAM,2BAA2B,MAAM,CAAC;AACxC,QAAA,MAAM,uBAAuB,KAAK,CAAC;AACnC,QAAA,MAAM,uBAAuB,IAAI,CAAC;AAClC,QAAA,MAAM,cAAc,KAAK,CAAC;AAC1B,QAAA,MAAM,eAAe,IAAI,CAAC;AAG1B,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,4BAA4B,EAC5B,2BAA2B,EAC3B,uBAAuB,EACvB,uBAAuB,EACvB,cAAc,EACd,eAAe,GAChB,CAAC;AAIF,MAAM,WAAW,eAAe;IAC9B,uBAAuB,EAAE,MAAM,CAAC;IAChC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,4BAA4B,EAAE,MAAM,CAAC;IACrC,2BAA2B,EAAE,MAAM,CAAC;IACpC,uBAAuB,EAAE,MAAM,CAAC;IAChC,uBAAuB,EAAE,MAAM,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;CACjC;AA6HD,qBAAa,gBAAgB;IAIzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAL1B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;gBAG/B,SAAS,EAAE,YAAY,EACvB,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,YAAY,GAAG,IAAI,EACtD,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,YAAA;IAcpD;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe;IAmFtC;;OAEG;IACH,OAAO,CAAC,eAAe;CA+GxB"}
@@ -35,7 +35,7 @@ function keystoneScore(msg) {
35
35
  const backtickMatches = text.match(/`[^`]+`/g) || [];
36
36
  score += backtickMatches.length * 0.2;
37
37
  // Agent mentions (known patterns)
38
- const agentMentions = text.match(/\b(forge|compass|clarity|sentinel|vanguard|anvil|pylon|vigil|bastion|relay|crucible)\b/gi) || [];
38
+ const agentMentions = text.match(/\b(agent1|agent2|agent4|agent3|agent5|agent6|director1|director2|director7|specialist2|specialist1)\b/gi) || [];
39
39
  score += agentMentions.length * 0.25;
40
40
  // Quoted content
41
41
  const quotedMatches = text.match(/"[^"]{10,}"/g) || [];
@@ -155,9 +155,10 @@ export class TopicSynthesizer {
155
155
  SELECT * FROM topics
156
156
  WHERE agent_id = ?
157
157
  AND message_count >= ?
158
- AND updated_at < datetime('now', '-${staleThresholdMinutes} minutes')
158
+ AND updated_at < datetime('now', '-' || ? || ' minutes')
159
+ -- safe: staleThresholdMinutes is a validated integer
159
160
  ORDER BY updated_at ASC
160
- `).all(agentId, cfg.SYNTHESIS_MIN_MESSAGES);
161
+ `).all(agentId, cfg.SYNTHESIS_MIN_MESSAGES, Math.floor(staleThresholdMinutes));
161
162
  }
162
163
  catch {
163
164
  return result;
@@ -39,7 +39,7 @@ export interface CollectionTrigger {
39
39
  export declare const TRIGGER_REGISTRY_VERSION = "1.0.0";
40
40
  /**
41
41
  * Default trigger registry for standard ACA collections.
42
- * Covers the core ACA offload use case from Anvil's spec.
42
+ * Covers the core ACA offload use case from agent6's spec.
43
43
  */
44
44
  export declare const TRIGGER_REGISTRY: CollectionTrigger[];
45
45
  /** Backward-compat alias — same reference as TRIGGER_REGISTRY */
@@ -13,7 +13,7 @@ import { createHash } from 'node:crypto';
13
13
  export const TRIGGER_REGISTRY_VERSION = '1.0.0';
14
14
  /**
15
15
  * Default trigger registry for standard ACA collections.
16
- * Covers the core ACA offload use case from Anvil's spec.
16
+ * Covers the core ACA offload use case from agent6's spec.
17
17
  */
18
18
  export const TRIGGER_REGISTRY = [
19
19
  {
@@ -61,7 +61,7 @@ export const TRIGGER_REGISTRY = [
61
61
  ],
62
62
  maxTokens: 800,
63
63
  maxChunks: 2,
64
- owner: 'forge',
64
+ owner: 'agent1',
65
65
  category: 'operations',
66
66
  description: 'Agent operational procedures: boot sequence, heartbeat, work queue, session startup',
67
67
  },
@@ -98,7 +98,7 @@ export const TRIGGER_REGISTRY = [
98
98
  ],
99
99
  maxTokens: 1500,
100
100
  maxChunks: 4,
101
- owner: 'forge',
101
+ owner: 'agent1',
102
102
  category: 'memory',
103
103
  description: 'Decision history: past choices, previously agreed approaches, recalled context',
104
104
  },
@@ -124,7 +124,7 @@ export const TRIGGER_REGISTRY = [
124
124
  ],
125
125
  maxTokens: 1200,
126
126
  maxChunks: 3,
127
- owner: 'forge',
127
+ owner: 'agent1',
128
128
  category: 'operations',
129
129
  description: 'Agent tooling reference: CLI commands, config paths, deployment procedures, quick reference',
130
130
  },
package/dist/types.d.ts CHANGED
@@ -70,7 +70,7 @@ export type FactScope = 'agent' | 'session' | 'user';
70
70
  /**
71
71
  * Memory visibility levels:
72
72
  * - private: Only the owning agent can read. Identity, SOUL, personal reflections.
73
- * - org: Agents in the same org (e.g., Forge's directors: Pylon, Vigil, Plane).
73
+ * - org: Agents in the same org (e.g., agent1's directors: Pylon, Vigil, Plane).
74
74
  * - council: All council seats can read.
75
75
  * - fleet: Any agent in the fleet can read.
76
76
  */
@@ -218,6 +218,12 @@ export interface ComposeRequest {
218
218
  * P3.4: topic-aware compositor.
219
219
  */
220
220
  topicId?: string;
221
+ /**
222
+ * When true, skip the C4 window cache fast-exit even if the cursor is fresh.
223
+ * Use this when external L4 state changed between turns, for example facts,
224
+ * wiki pages, or other library-backed context updated out of band.
225
+ */
226
+ skipWindowCache?: boolean;
221
227
  }
222
228
  export interface SlotTokenCounts {
223
229
  system: number;
@@ -262,6 +268,12 @@ export interface ComposeDiagnostics {
262
268
  dynamicReserveActive?: boolean;
263
269
  /** True if dynamic reserve was clamped at dynamicReserveMax and SESSION_PRESSURE_HIGH emitted */
264
270
  sessionPressureHigh?: boolean;
271
+ /** Number of items filtered across all dedup paths (temporal, open-domain, semantic, cross-session) */
272
+ fingerprintDedups?: number;
273
+ /** Number of duplicate-prefix matches where the full normalized content differed */
274
+ fingerprintCollisions?: number;
275
+ /** True when the window cache fast-exit fired and full compose was skipped */
276
+ windowCacheHit?: boolean;
265
277
  }
266
278
  export interface ComposeResult {
267
279
  messages: ProviderMessage[];
@@ -297,7 +309,7 @@ export interface ProviderMessage {
297
309
  * to identify high-signal unprocessed messages.
298
310
  *
299
311
  * Stored in Redis (hm:{a}:s:{s}:cursor) with dual-write to SQLite for
300
- * durability across Redis eviction (Compass Gate 2).
312
+ * durability across Redis eviction (agent2 Gate 2).
301
313
  */
302
314
  export interface SessionCursor {
303
315
  /** StoredMessage.id of the newest message included in the last window */
@@ -356,13 +368,21 @@ export interface EmbeddingProviderConfig {
356
368
  * - 'ollama': local Ollama (nomic-embed-text or any pulled model)
357
369
  * - 'openai': OpenAI Embeddings API (text-embedding-3-small / 3-large)
358
370
  */
359
- provider?: 'ollama' | 'openai';
371
+ provider?: 'ollama' | 'openai' | 'gemini';
360
372
  /** Ollama base URL. Default: http://localhost:11434 */
361
373
  ollamaUrl: string;
362
374
  /** OpenAI API key. Required when provider is 'openai'. */
363
375
  openaiApiKey?: string;
364
376
  /** OpenAI base URL. Default: https://api.openai.com/v1 */
365
377
  openaiBaseUrl?: string;
378
+ /** Gemini API key. Alternative to OAuth — passed as ?key= query param. */
379
+ geminiApiKey?: string;
380
+ /** Gemini API base URL. Default: https://generativelanguage.googleapis.com */
381
+ geminiBaseUrl?: string;
382
+ /** Gemini task type for indexing. Default: RETRIEVAL_DOCUMENT */
383
+ geminiIndexTaskType?: string;
384
+ /** Gemini task type for queries. Default: RETRIEVAL_QUERY */
385
+ geminiQueryTaskType?: string;
366
386
  /**
367
387
  * Embedding model name.
368
388
  * - ollama default: nomic-embed-text (768d)
@@ -390,17 +410,51 @@ export interface CacheConfig {
390
410
  /** @deprecated Use CacheConfig */
391
411
  export type RedisConfig = CacheConfig;
392
412
  export interface CompositorConfig {
413
+ /**
414
+ * Fraction of the detected context window to use as the input token budget.
415
+ * The effective budget is: detectedContextWindow × budgetFraction.
416
+ * reserveFraction is then subtracted for output/tool-call headroom.
417
+ *
418
+ * Range: 0.3–0.85. Default: 0.70
419
+ */
420
+ budgetFraction?: number;
421
+ /**
422
+ * Fraction of the total token budget to reserve for model output and tool
423
+ * call responses. Higher = more headroom for large tool results.
424
+ *
425
+ * Range: 0.10–0.50. Default: 0.25
426
+ */
427
+ reserveFraction?: number;
428
+ /**
429
+ * Fraction of the effective token budget (post-reserve) allocated to
430
+ * conversation history. History fills up to this cap before context slots run.
431
+ *
432
+ * Range: 0.20–0.60. Default: 0.40
433
+ */
434
+ historyFraction?: number;
435
+ /**
436
+ * Fraction of the effective token budget (post-reserve) allocated to the
437
+ * memory pool: facts, wiki, semantic recall, cross-session context, and
438
+ * trigger-fired doc chunks all draw from this shared pool.
439
+ *
440
+ * Range: 0.20–0.70. Default: 0.45
441
+ * Note: historyFraction + memoryFraction should be ≤ 0.90 to leave room
442
+ * for fixed-cost slots (system, identity, HyperForm: typically 3–8k tokens).
443
+ */
444
+ memoryFraction?: number;
445
+ /**
446
+ * @deprecated Use budgetFraction instead. Absolute token fallback used when
447
+ * model detection fails and budgetFraction is not set.
448
+ */
393
449
  defaultTokenBudget: number;
394
450
  maxHistoryMessages: number;
451
+ /** @advanced Replaced by memoryFraction for primary tuning. Hard per-fetch fact count cap. */
395
452
  maxFacts: number;
396
453
  maxCrossSessionContext: number;
397
454
  /**
398
- * Aggregate token ceiling across all trigger-fired doc chunk collections in a
399
- * single compose pass. When unset, hypermem uses a dynamic ceiling of 40% of
400
- * the remaining budget at the start of trigger retrieval.
401
- *
402
- * This prevents pathological prompts from firing many trigger collections at
403
- * once and starving the rest of the prompt budget.
455
+ * @advanced Aggregate token ceiling across all trigger-fired doc chunk
456
+ * collections in a single compose pass. When unset, draws from the memoryFraction
457
+ * pool. Rarely needs manual tuning.
404
458
  */
405
459
  maxTotalTriggerTokens?: number;
406
460
  /**
@@ -423,15 +477,9 @@ export interface CompositorConfig {
423
477
  */
424
478
  warmHistoryBudgetFraction: number;
425
479
  /**
426
- * Fraction of the model context window to reserve for output tokens and
427
- * hypermem operational overhead. The compositor's effective input budget is
428
- * (contextWindow × (1 - contextWindowReserve)).
429
- *
430
- * Higher values = more headroom for large operations, fewer turns before
431
- * session break. Lower values = more context available, higher saturation risk.
432
- *
433
- * Default: 0.25 (25% reserve — leaves 75% for input context)
434
- * Previous default was 0.10 (10% reserve).
480
+ * @advanced Use reserveFraction instead.
481
+ * Fraction of the model context window to reserve for output tokens.
482
+ * Falls back to reserveFraction when set. Default: 0.25
435
483
  */
436
484
  contextWindowReserve?: number;
437
485
  /**
@@ -470,16 +518,10 @@ export interface CompositorConfig {
470
518
  */
471
519
  keystoneMinSignificance?: number;
472
520
  /**
473
- * Fraction of the effective token budget to target for context assembly.
474
- * The compositor fills slots until this fraction is consumed, leaving the
475
- * remainder for conversation history and response headroom.
476
- *
477
- * Lower values = lighter context, faster turns, less memory surfaced.
478
- * Higher values = richer context, more memory, higher saturation risk.
479
- *
521
+ * @advanced Use memoryFraction instead.
522
+ * Fraction of the effective budget to target for context assembly.
523
+ * Honored as a fallback when memoryFraction is not set.
480
524
  * Range: 0.3–0.85. Default: 0.65
481
- * Typical lightweight config: 0.45
482
- * Typical fleet/multi-agent config: 0.65
483
525
  */
484
526
  targetBudgetFraction?: number;
485
527
  /**
@@ -498,19 +540,19 @@ export interface CompositorConfig {
498
540
  */
499
541
  enableMOD?: boolean;
500
542
  /**
501
- * Output profile tier. Controls what FOS content is injected.
543
+ * HyperForm output shaping profile. Controls what FOS/MOD content is injected.
502
544
  *
503
545
  * 'light' — ~100 token standalone directives. No MOD, no fleet concepts.
504
- * Works on any single-agent 64k setup. No DB required.
505
- * 'standard' — Full FOS: density targets, format rules, compression ratios,
506
- * task-context scoping. No MOD.
546
+ * 'standard' Full FOS: density targets, format rules, compression ratios.
507
547
  * 'full' — FOS + MOD. Cross-agent coordination, full spec.
508
548
  *
509
549
  * Backward compat: 'starter' maps to 'light', 'fleet' maps to 'full'.
510
550
  * Default: 'full' (backward-compatible). New install default: 'light'.
511
551
  */
552
+ hyperformProfile?: 'light' | 'standard' | 'full' | 'starter' | 'fleet';
553
+ /** @deprecated Use hyperformProfile */
512
554
  outputProfile?: 'light' | 'standard' | 'full' | 'starter' | 'fleet';
513
- /** @deprecated Use outputProfile */
555
+ /** @deprecated Use hyperformProfile */
514
556
  outputStandard?: 'light' | 'standard' | 'full' | 'starter' | 'fleet';
515
557
  /**
516
558
  * Hard token ceiling for wiki page injection per compose pass.