@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.
- package/README.md +11 -54
- package/dist/background-indexer.d.ts.map +1 -1
- package/dist/background-indexer.js +26 -18
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +16 -2
- package/dist/compositor.d.ts.map +1 -1
- package/dist/compositor.js +146 -19
- package/dist/context-backfill.d.ts +46 -0
- package/dist/context-backfill.d.ts.map +1 -0
- package/dist/context-backfill.js +113 -0
- package/dist/context-store.d.ts +77 -0
- package/dist/context-store.d.ts.map +1 -0
- package/dist/context-store.js +177 -0
- package/dist/cross-agent.d.ts +12 -0
- package/dist/cross-agent.d.ts.map +1 -1
- package/dist/cross-agent.js +31 -19
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +8 -0
- package/dist/dreaming-promoter.d.ts +1 -1
- package/dist/dreaming-promoter.js +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -6
- package/dist/message-store.d.ts +31 -2
- package/dist/message-store.d.ts.map +1 -1
- package/dist/message-store.js +131 -17
- package/dist/preference-store.d.ts +1 -1
- package/dist/preference-store.js +1 -1
- package/dist/profiles.d.ts +3 -1
- package/dist/profiles.d.ts.map +1 -1
- package/dist/profiles.js +8 -0
- package/dist/repair-tool-pairs.d.ts.map +1 -1
- package/dist/repair-tool-pairs.js +73 -2
- package/dist/schema.d.ts +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +27 -1
- package/dist/seed.d.ts +1 -1
- package/dist/seed.js +1 -1
- package/dist/session-flusher.d.ts +2 -2
- package/dist/session-flusher.js +2 -2
- package/dist/spawn-context.d.ts +1 -1
- package/dist/spawn-context.js +1 -1
- package/dist/topic-synthesizer.js +1 -1
- package/dist/trigger-registry.d.ts +1 -1
- package/dist/trigger-registry.js +4 -4
- package/dist/types.d.ts +11 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-store.d.ts +10 -1
- package/dist/vector-store.d.ts.map +1 -1
- package/dist/vector-store.js +353 -0
- package/dist/version.d.ts +5 -5
- package/dist/version.js +5 -5
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -383,52 +383,7 @@ Slot-level budget allocation is shown in the [hypercompositor diagram](#what-the
|
|
|
383
383
|
|
|
384
384
|
## Requirements
|
|
385
385
|
|
|
386
|
-
**Current release: hypermem 0.5.6.**
|
|
387
|
-
|
|
388
|
-
### Release highlights
|
|
389
|
-
|
|
390
|
-
**0.5.6** — Content fingerprint dedup and hardening
|
|
391
|
-
- O(1) fingerprint dedup across all retrieval paths (temporal, open-domain, semantic, cross-session). Catches rephrased near-duplicates that substring matching missed.
|
|
392
|
-
- Identity bootstrap pre-fingerprinting: SOUL.md, USER.md, IDENTITY.md content already in the prompt is never double-injected by retrieval.
|
|
393
|
-
- Indexer circuit breaker with startup integrity check for library.db corruption. Graceful degradation, not cascading failure.
|
|
394
|
-
- SQL parameterization hardening on datetime and FTS5 paths.
|
|
395
|
-
|
|
396
|
-
**0.5.5** — Tuning collapse and config schema
|
|
397
|
-
- Plugin config schema: all tuning knobs declarable in `openclaw.json`. No more manual config.json edits.
|
|
398
|
-
- Tuning simplified to 4 primary knobs: `budgetFraction`, `reserveFraction`, `historyFraction`, `memoryFraction`.
|
|
399
|
-
- Identity and doc chunk dedup against OpenClaw bootstrap injection.
|
|
400
|
-
- Window cache with freshness diagnostics.
|
|
401
|
-
|
|
402
|
-
**0.5.0** — Redis removal and context engine
|
|
403
|
-
- Redis replaced with SQLite in-memory cache. Zero external services.
|
|
404
|
-
- Context engine plugin: runs as an OpenClaw `contextEngine` slot, composing prompts per-turn.
|
|
405
|
-
- Transform-first assembly: tool results compressed before budget allocation, not after.
|
|
406
|
-
- Cluster-aware budget shaping: related tool turns grouped and trimmed together.
|
|
407
|
-
- Hybrid FTS5 + KNN retrieval with Reciprocal Rank Fusion.
|
|
408
|
-
- Workspace seeding: agents auto-ingest their workspace docs on bootstrap.
|
|
409
|
-
- Runtime profiles: `light`, `standard`, `full`.
|
|
410
|
-
- Obsidian import and export.
|
|
411
|
-
- Metrics dashboard primitives.
|
|
412
|
-
|
|
413
|
-
**0.4.0** — Eviction and migration
|
|
414
|
-
- Image and heavy-content eviction pre-pass in assembly. Old screenshots and large tool outputs aged out before they compete for budget.
|
|
415
|
-
- Engine version stamps in library.db. Schema migration runs automatically on version bump.
|
|
416
|
-
- Migration guides and scripts for Cognee, QMD, Mem0, Zep, Honcho, and raw MEMORY.md files.
|
|
417
|
-
|
|
418
|
-
**0.3.0** — Subagent context and retrieval
|
|
419
|
-
- Subagent context inheritance: spawned subagents get bounded parent context, session-scoped docs, and relevant facts.
|
|
420
|
-
- Tool Gradient v2: turn-age tiers with head+tail truncation on tool results.
|
|
421
|
-
- Cursor-aware indexer with ghost message suppression.
|
|
422
|
-
|
|
423
|
-
**0.2.0** — Retrieval access control
|
|
424
|
-
- Trigger registry ownership and auditability.
|
|
425
|
-
- Retrieval access control, trigger fallback paths, and history rebalance.
|
|
426
|
-
|
|
427
|
-
**0.1.0** — Core architecture
|
|
428
|
-
- Four-layer memory: in-memory cache, message history, vector search, structured library.
|
|
429
|
-
- 8-level priority compositor with slot-based prompt assembly.
|
|
430
|
-
- Cross-agent memory access with visibility-scoped permissions.
|
|
431
|
-
- Knowledge graph with DAG traversal.
|
|
386
|
+
**Current release: hypermem 0.5.6.** Changelog: [CHANGELOG.md](./CHANGELOG.md)
|
|
432
387
|
|
|
433
388
|
| Requirement | Version | Notes |
|
|
434
389
|
|---|---|---|
|
|
@@ -538,6 +493,8 @@ Full reference: **[docs/TUNING.md](./docs/TUNING.md)**
|
|
|
538
493
|
|
|
539
494
|
## API
|
|
540
495
|
|
|
496
|
+
> **Note:** The examples below use placeholder agent names (`my-agent`, `agent1`, etc.). Replace these with your actual agent IDs from your OpenClaw config. Single-agent installs typically use `main`. Multi-agent fleets use whatever IDs you've configured. See [INSTALL.md § "Configure your fleet"](./INSTALL.md#step-5--configure-your-fleet-multi-agent-only) for details.
|
|
497
|
+
|
|
541
498
|
```typescript
|
|
542
499
|
import { HyperMem } from '@psiclawops/hypermem';
|
|
543
500
|
|
|
@@ -551,18 +508,18 @@ const hm = await HyperMem.create({
|
|
|
551
508
|
});
|
|
552
509
|
|
|
553
510
|
// Record and compose
|
|
554
|
-
await hm.recordUserMessage('
|
|
511
|
+
await hm.recordUserMessage('my-agent', 'agent:my-agent:webchat:main', 'How does drift detection work?');
|
|
555
512
|
|
|
556
513
|
const composed = await hm.compose({
|
|
557
|
-
agentId: '
|
|
558
|
-
sessionKey: 'agent:
|
|
514
|
+
agentId: 'my-agent',
|
|
515
|
+
sessionKey: 'agent:my-agent:webchat:main',
|
|
559
516
|
prompt: 'How does drift detection work?',
|
|
560
517
|
tokenBudget: 4000,
|
|
561
518
|
provider: 'anthropic',
|
|
562
519
|
});
|
|
563
520
|
|
|
564
521
|
// Refresh tool compression after each turn
|
|
565
|
-
await hm.refreshCacheGradient('
|
|
522
|
+
await hm.refreshCacheGradient('my-agent', 'agent:my-agent:webchat:main');
|
|
566
523
|
```
|
|
567
524
|
|
|
568
525
|
Spawning a subagent with parent context:
|
|
@@ -571,10 +528,10 @@ Spawning a subagent with parent context:
|
|
|
571
528
|
import { buildSpawnContext, MessageStore, DocChunkStore } from '@psiclawops/hypermem';
|
|
572
529
|
|
|
573
530
|
const spawn = await buildSpawnContext(
|
|
574
|
-
new MessageStore(hm.dbManager.getMessageDb('
|
|
531
|
+
new MessageStore(hm.dbManager.getMessageDb('my-agent')),
|
|
575
532
|
new DocChunkStore(hm.dbManager.getLibraryDb()),
|
|
576
|
-
'
|
|
577
|
-
{ parentSessionKey: 'agent:
|
|
533
|
+
'my-agent',
|
|
534
|
+
{ parentSessionKey: 'agent:my-agent:webchat:main', workingSnapshot: 12 }
|
|
578
535
|
);
|
|
579
536
|
```
|
|
580
537
|
|
|
@@ -586,7 +543,7 @@ const spawn = await buildSpawnContext(
|
|
|
586
543
|
|
|
587
544
|
```bash
|
|
588
545
|
node bin/hypermem-status.mjs # full dashboard
|
|
589
|
-
node bin/hypermem-status.mjs --agent
|
|
546
|
+
node bin/hypermem-status.mjs --agent my-agent # scoped to one agent
|
|
590
547
|
node bin/hypermem-status.mjs --json # machine-readable output
|
|
591
548
|
node bin/hypermem-status.mjs --health # health checks only (exit 1 on failure)
|
|
592
549
|
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"background-indexer.d.ts","sourceRoot":"","sources":["../src/background-indexer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAiB,aAAa,EAAe,aAAa,EAAE,MAAM,YAAY,CAAC;AAK3F,OAAO,EAA2B,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAOrF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"background-indexer.d.ts","sourceRoot":"","sources":["../src/background-indexer.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,23 +32,31 @@ import { isSafeForSharedVisibility } from './secret-scanner.js';
|
|
|
32
32
|
// Used to populate the `domain` column on extracted facts so that
|
|
33
33
|
// domain-scoped retrieval (e.g. getActiveFacts({ domain: 'infrastructure' }))
|
|
34
34
|
// returns results. New agents default to 'general'.
|
|
35
|
+
//
|
|
36
|
+
// ── EXAMPLE DATA ──────────────────────────────────────────────────
|
|
37
|
+
// The agent names below (agent1, director1, etc.) are PLACEHOLDERS.
|
|
38
|
+
// Replace them with your own agent IDs and domain labels to match
|
|
39
|
+
// your fleet. Single-agent installs don't need to edit this:
|
|
40
|
+
// unknown agents fall through to 'general' automatically.
|
|
41
|
+
// See INSTALL.md § "Configure your fleet" for details.
|
|
42
|
+
// ─────────────────────────────────────────────────────────────────
|
|
35
43
|
const AGENT_DOMAIN_MAP = {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
agent1: 'infrastructure',
|
|
45
|
+
director2: 'infrastructure',
|
|
46
|
+
director1: 'infrastructure',
|
|
47
|
+
director3: 'infrastructure',
|
|
48
|
+
agent2: 'product',
|
|
49
|
+
director4: 'product',
|
|
50
|
+
director5: 'product',
|
|
51
|
+
director6: 'product',
|
|
52
|
+
agent3: 'security',
|
|
53
|
+
director7: 'security',
|
|
54
|
+
director8: 'security',
|
|
55
|
+
agent4: 'ux',
|
|
56
|
+
agent6: 'governance',
|
|
57
|
+
agent5: 'strategy',
|
|
58
|
+
specialist1: 'development',
|
|
59
|
+
specialist2: 'communications',
|
|
52
60
|
main: 'general',
|
|
53
61
|
'channel-mini': 'general',
|
|
54
62
|
};
|
|
@@ -83,7 +91,7 @@ function extractFactCandidates(content) {
|
|
|
83
91
|
// Preference patterns — medium confidence (0.60)
|
|
84
92
|
const preferencePatterns = [
|
|
85
93
|
/(?:prefer|always use|never use|don't use|avoid) (.{10,150})/gi,
|
|
86
|
-
/(?:operator) (?:wants|prefers|likes|hates|dislikes) (.{10,150})/gi,
|
|
94
|
+
/(?:operator|operator) (?:wants|prefers|likes|hates|dislikes) (.{10,150})/gi,
|
|
87
95
|
];
|
|
88
96
|
// Operational patterns: deployments, incidents, fixes — high confidence (0.70)
|
|
89
97
|
const operationalPatterns = [
|
|
@@ -137,7 +145,7 @@ const OPERATIONAL_BOILERPLATE = [
|
|
|
137
145
|
/still\s*waiting/i,
|
|
138
146
|
/will\s*pick\s*(it\s*)?up\s*(on\s*(next|the))?/i,
|
|
139
147
|
/message\s*is\s*in\s*(his|her|their|the)\s*queue/i,
|
|
140
|
-
/sent\s+to\s+(
|
|
148
|
+
/sent\s+to\s+(agent6|agent2|agent4|agent3|agent5|agent1)/i,
|
|
141
149
|
/dispatched\s+(it\s+)?to/i,
|
|
142
150
|
/timed\s*out\s*after/i,
|
|
143
151
|
/\bNO_REPLY\b/,
|
package/dist/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/cache.js
CHANGED
|
@@ -244,15 +244,22 @@ export class CacheLayer {
|
|
|
244
244
|
return { seq: row.seq, msg: null, fallback: true };
|
|
245
245
|
}
|
|
246
246
|
});
|
|
247
|
+
// Helper: check if a message contains any error tool results
|
|
248
|
+
const hasErrorToolResult = (msg) => {
|
|
249
|
+
if (!msg.toolResults)
|
|
250
|
+
return false;
|
|
251
|
+
return msg.toolResults.some((tr) => tr.isError === true);
|
|
252
|
+
};
|
|
247
253
|
const clusters = [];
|
|
248
254
|
for (let i = 0; i < chronological.length; i++) {
|
|
249
255
|
const current = chronological[i];
|
|
250
256
|
if (current.fallback || !current.msg) {
|
|
251
|
-
clusters.push({ startSeq: current.seq, endSeq: current.seq, tokenCost: 500 });
|
|
257
|
+
clusters.push({ startSeq: current.seq, endSeq: current.seq, tokenCost: 500, hasError: false });
|
|
252
258
|
continue;
|
|
253
259
|
}
|
|
254
260
|
let endSeq = current.seq;
|
|
255
261
|
let tokenCost = estimateMessageTokens(current.msg);
|
|
262
|
+
let clusterHasError = hasErrorToolResult(current.msg);
|
|
256
263
|
if (current.msg.toolCalls && current.msg.toolCalls.length > 0) {
|
|
257
264
|
const callIds = new Set(current.msg.toolCalls.map(tc => tc.id).filter(Boolean));
|
|
258
265
|
let j = i + 1;
|
|
@@ -264,6 +271,8 @@ export class CacheLayer {
|
|
|
264
271
|
if (callIds.size > 0 && resultIds.length > 0 && !resultIds.some(id => callIds.has(id)))
|
|
265
272
|
break;
|
|
266
273
|
tokenCost += estimateMessageTokens(candidate.msg);
|
|
274
|
+
if (hasErrorToolResult(candidate.msg))
|
|
275
|
+
clusterHasError = true;
|
|
267
276
|
endSeq = candidate.seq;
|
|
268
277
|
j++;
|
|
269
278
|
}
|
|
@@ -276,12 +285,17 @@ export class CacheLayer {
|
|
|
276
285
|
if (candidate.fallback || !candidate.msg || !candidate.msg.toolResults || candidate.msg.toolResults.length === 0 || (candidate.msg.toolCalls && candidate.msg.toolCalls.length > 0))
|
|
277
286
|
break;
|
|
278
287
|
tokenCost += estimateMessageTokens(candidate.msg);
|
|
288
|
+
if (hasErrorToolResult(candidate.msg))
|
|
289
|
+
clusterHasError = true;
|
|
279
290
|
endSeq = candidate.seq;
|
|
280
291
|
j++;
|
|
281
292
|
}
|
|
282
293
|
i = j - 1;
|
|
283
294
|
}
|
|
284
|
-
clusters
|
|
295
|
+
// Error clusters get a 90% cost discount so they survive trimming longer.
|
|
296
|
+
// Failed tool calls are high-signal context the model needs to avoid repeating mistakes.
|
|
297
|
+
const effectiveCost = clusterHasError ? Math.max(50, Math.ceil(tokenCost * 0.1)) : tokenCost;
|
|
298
|
+
clusters.push({ startSeq: current.seq, endSeq, tokenCost: effectiveCost, hasError: clusterHasError });
|
|
285
299
|
}
|
|
286
300
|
let tokenSum = 0;
|
|
287
301
|
let keepFromSeq = null;
|
package/dist/compositor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compositor.d.ts","sourceRoot":"","sources":["../src/compositor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAGb,cAAc,EAGd,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EAMlB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,KAAK,QAAQ,GAAG,UAAU,CAAC;AAI3B,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"compositor.d.ts","sourceRoot":"","sources":["../src/compositor.ts"],"names":[],"mappings":"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"}
|
package/dist/compositor.js
CHANGED
|
@@ -17,7 +17,8 @@ import { SessionTopicMap } from './session-topic-map.js';
|
|
|
17
17
|
import { toProviderFormat } from './provider-translator.js';
|
|
18
18
|
import { DocChunkStore } from './doc-chunk-store.js';
|
|
19
19
|
import { hybridSearch } from './hybrid-retrieval.js';
|
|
20
|
-
import { ensureCompactionFenceSchema, updateCompactionFence } from './compaction-fence.js';
|
|
20
|
+
import { ensureCompactionFenceSchema, updateCompactionFence, getCompactionFence } from './compaction-fence.js';
|
|
21
|
+
import { getActiveContext } from './context-store.js';
|
|
21
22
|
import { rankKeystones, scoreKeystone } from './keystone-scorer.js';
|
|
22
23
|
import { buildOrgRegistryFromDb, defaultOrgRegistry } from './cross-agent.js';
|
|
23
24
|
import { getActiveFOS, matchMOD, renderFOS, renderMOD, renderLightFOS, resolveOutputTier, buildActionVerificationSummary } from './fos-mod.js';
|
|
@@ -372,7 +373,7 @@ function stripSecurityPreamble(content) {
|
|
|
372
373
|
return stripped.trim().length > 20 ? stripped.trim() : content;
|
|
373
374
|
}
|
|
374
375
|
// Minimum floor: if trimming would leave less than 30% of original content, return a
|
|
375
|
-
// stripped
|
|
376
|
+
// stripped sentinel instead of a misleading fragment. A partial result that looks
|
|
376
377
|
// complete is worse than a clear signal that the result was dropped.
|
|
377
378
|
// Applied only in applyTierPayloadCap (pressure-driven trimming), not in structural
|
|
378
379
|
// truncation paths where head+tail is always semantically useful.
|
|
@@ -642,9 +643,9 @@ function applyTierPayloadCap(msg, perResultCap, perTurnCap, usedSoFar = 0, maxTa
|
|
|
642
643
|
// render the truncated result as: [security notice] + [middle marker] + [last line].
|
|
643
644
|
const stripped = stripSecurityPreamble(content);
|
|
644
645
|
// Floor check (TUNE-015): if the cap would leave less than 30% of the stripped content
|
|
645
|
-
// AND less than 2000 chars absolute, return a
|
|
646
|
+
// AND less than 2000 chars absolute, return a sentinel instead of a misleading fragment.
|
|
646
647
|
// Partial results that look complete are worse than a clear dropped-result signal.
|
|
647
|
-
// The absolute floor prevents the
|
|
648
|
+
// The absolute floor prevents the sentinel from firing on large natural truncations
|
|
648
649
|
// (e.g., 110k → 16k is a meaningful slice, not a misleading fragment).
|
|
649
650
|
if (perResultCap < stripped.length * TOOL_GRADIENT_MIN_USEFUL_FRACTION && perResultCap < 2_000) {
|
|
650
651
|
content = `[result too large for current context budget \u2014 ${stripped.length} chars stripped]`;
|
|
@@ -907,6 +908,21 @@ export class Compositor {
|
|
|
907
908
|
const { reserve: dynamicReserve, avgTurnCost, dynamic: isDynamic, pressureHigh } = computeDynamicReserve(sampleMessages, totalWindow, this.config);
|
|
908
909
|
const budget = request.tokenBudget || resolveModelBudget(request.model, this.config.defaultTokenBudget, dynamicReserve, this.config.budgetFraction);
|
|
909
910
|
let remaining = budget;
|
|
911
|
+
// Phase 0 fence enforcement: resolve the compaction fence for this conversation.
|
|
912
|
+
// All downstream message queries use this as a lower bound to exclude zombie
|
|
913
|
+
// messages below the fence that should have been compacted.
|
|
914
|
+
let fenceMessageId;
|
|
915
|
+
if (sampleConv) {
|
|
916
|
+
try {
|
|
917
|
+
ensureCompactionFenceSchema(db);
|
|
918
|
+
const fence = getCompactionFence(db, sampleConv.id);
|
|
919
|
+
if (fence)
|
|
920
|
+
fenceMessageId = fence.fenceMessageId;
|
|
921
|
+
}
|
|
922
|
+
catch {
|
|
923
|
+
// Fence lookup is best-effort — never fail composition
|
|
924
|
+
}
|
|
925
|
+
}
|
|
910
926
|
const warnings = [];
|
|
911
927
|
const slots = {
|
|
912
928
|
system: 0,
|
|
@@ -993,6 +1009,15 @@ export class Compositor {
|
|
|
993
1009
|
let composedActiveTopicId;
|
|
994
1010
|
let composedActiveTopicName;
|
|
995
1011
|
if (request.includeHistory !== false) {
|
|
1012
|
+
// Phase 3 (Turn DAG): resolve active context for DAG-native reads.
|
|
1013
|
+
// This is the primary branch-scoping mechanism; fence remains as transitional safety.
|
|
1014
|
+
let activeContext = null;
|
|
1015
|
+
try {
|
|
1016
|
+
activeContext = getActiveContext(db, request.agentId, request.sessionKey);
|
|
1017
|
+
}
|
|
1018
|
+
catch {
|
|
1019
|
+
// Context resolution is best-effort — fall back to fence-based reads
|
|
1020
|
+
}
|
|
996
1021
|
// P3.4: Look up the active topic for this session (non-fatal)
|
|
997
1022
|
let activeTopicId;
|
|
998
1023
|
let activeTopic;
|
|
@@ -1024,7 +1049,7 @@ export class Compositor {
|
|
|
1024
1049
|
// Hoist resolved topic id+name so the window dual-write and wiki injection sections can access them
|
|
1025
1050
|
composedActiveTopicId = activeTopicId;
|
|
1026
1051
|
composedActiveTopicName = activeTopic?.name;
|
|
1027
|
-
const rawHistoryMessages = await this.getHistory(request.agentId, request.sessionKey, request.historyDepth || this.config.maxHistoryMessages, store, activeTopicId);
|
|
1052
|
+
const rawHistoryMessages = await this.getHistory(request.agentId, request.sessionKey, request.historyDepth || this.config.maxHistoryMessages, store, activeTopicId, fenceMessageId, activeContext);
|
|
1028
1053
|
// Deduplicate history by StoredMessage.id (second line of defense after
|
|
1029
1054
|
// pushHistory() tail-check dedup). Guards against any duplicates that
|
|
1030
1055
|
// slipped through the warm path — e.g. bootstrap re-runs on existing sessions.
|
|
@@ -1084,7 +1109,7 @@ export class Compositor {
|
|
|
1084
1109
|
let keystoneMessages = [];
|
|
1085
1110
|
let keystoneTokens = 0;
|
|
1086
1111
|
if (request.includeKeystones !== false && includedHistory.length >= 30 && keystoneFraction > 0) {
|
|
1087
|
-
const keystoneResult = await this.buildKeystones(db, request.agentId, includedHistory, historyTokens, keystoneFraction, keystoneMaxMsgs, request.prompt, libDb || undefined);
|
|
1112
|
+
const keystoneResult = await this.buildKeystones(db, request.agentId, includedHistory, historyTokens, keystoneFraction, keystoneMaxMsgs, request.prompt, libDb || undefined, fenceMessageId, activeContext);
|
|
1088
1113
|
if (keystoneResult) {
|
|
1089
1114
|
keystoneMessages = keystoneResult.keystoneMessages;
|
|
1090
1115
|
keystoneTokens = keystoneResult.keystoneTokens;
|
|
@@ -1102,7 +1127,7 @@ export class Compositor {
|
|
|
1102
1127
|
let crossTopicTokens = 0;
|
|
1103
1128
|
if (request.includeKeystones !== false && activeTopic && this.vectorStore) {
|
|
1104
1129
|
try {
|
|
1105
|
-
const rawCrossTopicKeystones = await this.getKeystonesByTopic(request.agentId, request.sessionKey, activeTopic, includedHistory, db, 3);
|
|
1130
|
+
const rawCrossTopicKeystones = await this.getKeystonesByTopic(request.agentId, request.sessionKey, activeTopic, includedHistory, db, 3, fenceMessageId, activeContext);
|
|
1106
1131
|
if (rawCrossTopicKeystones.length > 0) {
|
|
1107
1132
|
// Token budget: cap the full cross-topic block at 15% of remaining,
|
|
1108
1133
|
// including the header line.
|
|
@@ -1968,6 +1993,27 @@ export class Compositor {
|
|
|
1968
1993
|
const conversation = store.getConversation(sessionKey);
|
|
1969
1994
|
if (!conversation)
|
|
1970
1995
|
return;
|
|
1996
|
+
// Phase 3 (Turn DAG): resolve active context for DAG-native warm preload.
|
|
1997
|
+
// Uses context.head_message_id to walk only the active branch.
|
|
1998
|
+
let activeContext = null;
|
|
1999
|
+
try {
|
|
2000
|
+
activeContext = getActiveContext(db, agentId, sessionKey);
|
|
2001
|
+
}
|
|
2002
|
+
catch {
|
|
2003
|
+
// Context resolution is best-effort
|
|
2004
|
+
}
|
|
2005
|
+
// Phase 0 fence enforcement: resolve compaction fence for warm bootstrap.
|
|
2006
|
+
// Fence remains as transitional safety — primary scoping is via DAG walk.
|
|
2007
|
+
let warmFenceMessageId;
|
|
2008
|
+
try {
|
|
2009
|
+
ensureCompactionFenceSchema(db);
|
|
2010
|
+
const fence = getCompactionFence(db, conversation.id);
|
|
2011
|
+
if (fence)
|
|
2012
|
+
warmFenceMessageId = fence.fenceMessageId;
|
|
2013
|
+
}
|
|
2014
|
+
catch {
|
|
2015
|
+
// Fence lookup is best-effort
|
|
2016
|
+
}
|
|
1971
2017
|
// Fetch a generous pool from SQLite, apply gradient transform, then
|
|
1972
2018
|
// token-budget-cap the warm set. This replaces the old WARM_BOOTSTRAP_CAP
|
|
1973
2019
|
// message-count constant which was a blunt instrument — 100 messages of
|
|
@@ -1977,7 +2023,19 @@ export class Compositor {
|
|
|
1977
2023
|
const reserve = this.config.contextWindowReserve ?? 0.15;
|
|
1978
2024
|
const effectiveBudget = resolveModelBudget(opts?.model, this.config.defaultTokenBudget, reserve, this.config.budgetFraction);
|
|
1979
2025
|
const warmBudget = Math.floor(effectiveBudget * (this.config.warmHistoryBudgetFraction ?? 0.4));
|
|
1980
|
-
|
|
2026
|
+
// Phase 3 (Turn DAG): prefer DAG walk from context head for warm preload.
|
|
2027
|
+
// This ensures only active-branch messages enter the warm cache.
|
|
2028
|
+
let rawHistory;
|
|
2029
|
+
if (activeContext?.headMessageId) {
|
|
2030
|
+
rawHistory = store.getHistoryByDAGWalk(activeContext.headMessageId, this.config.maxHistoryMessages);
|
|
2031
|
+
// DAG walk may return empty for legacy data — fall back to fence-scoped query
|
|
2032
|
+
if (rawHistory.length === 0) {
|
|
2033
|
+
rawHistory = store.getRecentMessages(conversation.id, this.config.maxHistoryMessages, warmFenceMessageId);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
else {
|
|
2037
|
+
rawHistory = store.getRecentMessages(conversation.id, this.config.maxHistoryMessages, warmFenceMessageId);
|
|
2038
|
+
}
|
|
1981
2039
|
const transformedForWarm = applyToolGradient(rawHistory, {
|
|
1982
2040
|
totalWindowTokens: resolveModelWindow(opts?.model, this.config.defaultTokenBudget),
|
|
1983
2041
|
});
|
|
@@ -2027,7 +2085,36 @@ export class Compositor {
|
|
|
2027
2085
|
const conversation = store.getConversation(sessionKey);
|
|
2028
2086
|
if (!conversation)
|
|
2029
2087
|
return;
|
|
2030
|
-
|
|
2088
|
+
// Phase 3 (Turn DAG): resolve active context for DAG-native gradient refresh
|
|
2089
|
+
let activeContext = null;
|
|
2090
|
+
try {
|
|
2091
|
+
activeContext = getActiveContext(db, agentId, sessionKey);
|
|
2092
|
+
}
|
|
2093
|
+
catch {
|
|
2094
|
+
// Context resolution is best-effort
|
|
2095
|
+
}
|
|
2096
|
+
// Phase 0 fence enforcement for gradient refresh (transitional safety)
|
|
2097
|
+
let gradientFenceMessageId;
|
|
2098
|
+
try {
|
|
2099
|
+
ensureCompactionFenceSchema(db);
|
|
2100
|
+
const fence = getCompactionFence(db, conversation.id);
|
|
2101
|
+
if (fence)
|
|
2102
|
+
gradientFenceMessageId = fence.fenceMessageId;
|
|
2103
|
+
}
|
|
2104
|
+
catch {
|
|
2105
|
+
// Fence lookup is best-effort
|
|
2106
|
+
}
|
|
2107
|
+
// Phase 3: prefer DAG walk from context head
|
|
2108
|
+
let rawHistory;
|
|
2109
|
+
if (activeContext?.headMessageId) {
|
|
2110
|
+
rawHistory = store.getHistoryByDAGWalk(activeContext.headMessageId, this.config.maxHistoryMessages);
|
|
2111
|
+
if (rawHistory.length === 0) {
|
|
2112
|
+
rawHistory = store.getRecentMessages(conversation.id, this.config.maxHistoryMessages, gradientFenceMessageId);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
else {
|
|
2116
|
+
rawHistory = store.getRecentMessages(conversation.id, this.config.maxHistoryMessages, gradientFenceMessageId);
|
|
2117
|
+
}
|
|
2031
2118
|
const transformedHistory = applyToolGradient(rawHistory, {
|
|
2032
2119
|
totalWindowTokens: tokenBudget && tokenBudget > 0
|
|
2033
2120
|
? Math.max(tokenBudget, Math.floor(tokenBudget / 0.80))
|
|
@@ -2088,21 +2175,30 @@ export class Compositor {
|
|
|
2088
2175
|
* The Redis path is unaffected — Redis doesn't index by topic, so topic
|
|
2089
2176
|
* filtering only applies to the SQLite fallback.
|
|
2090
2177
|
*/
|
|
2091
|
-
async getHistory(agentId, sessionKey, limit, store, topicId) {
|
|
2178
|
+
async getHistory(agentId, sessionKey, limit, store, topicId, fenceMessageId, activeContext) {
|
|
2092
2179
|
// Pass limit through to Redis — this is the correct enforcement point.
|
|
2093
2180
|
// Previously getHistory() ignored the limit on the Redis path (LRANGE 0 -1),
|
|
2094
2181
|
// meaning historyDepth in the compose request had no effect on hot sessions.
|
|
2095
2182
|
const cached = await this.cache.getHistory(agentId, sessionKey, limit);
|
|
2096
2183
|
if (cached.length > 0)
|
|
2097
2184
|
return cached;
|
|
2185
|
+
// Phase 3 (Turn DAG): walk from context.head_message_id backward through
|
|
2186
|
+
// parent_id links. This is the primary correctness mechanism — the fence
|
|
2187
|
+
// remains as transitional safety only.
|
|
2188
|
+
if (activeContext?.headMessageId) {
|
|
2189
|
+
const dagMessages = store.getHistoryByDAGWalk(activeContext.headMessageId, limit);
|
|
2190
|
+
if (dagMessages.length > 0)
|
|
2191
|
+
return dagMessages;
|
|
2192
|
+
// DAG walk returned empty (e.g., legacy data without parent chains) — fall through
|
|
2193
|
+
}
|
|
2098
2194
|
const conversation = store.getConversation(sessionKey);
|
|
2099
2195
|
if (!conversation)
|
|
2100
2196
|
return [];
|
|
2101
2197
|
if (topicId) {
|
|
2102
2198
|
// P3.4: Option B — active topic messages + legacy NULL messages
|
|
2103
|
-
return store.getRecentMessagesByTopic(conversation.id, topicId, limit);
|
|
2199
|
+
return store.getRecentMessagesByTopic(conversation.id, topicId, limit, fenceMessageId);
|
|
2104
2200
|
}
|
|
2105
|
-
return store.getRecentMessages(conversation.id, limit);
|
|
2201
|
+
return store.getRecentMessages(conversation.id, limit, fenceMessageId);
|
|
2106
2202
|
}
|
|
2107
2203
|
// ─── L4 Library Builders ─────────────────────────────────────
|
|
2108
2204
|
/**
|
|
@@ -2388,6 +2484,9 @@ export class Compositor {
|
|
|
2388
2484
|
* Build cross-session context by finding recent activity
|
|
2389
2485
|
* in other sessions for this agent.
|
|
2390
2486
|
*/
|
|
2487
|
+
// TODO Phase 1: buildCrossSessionContext queries OTHER conversations. Each has its
|
|
2488
|
+
// own compaction fence. Per-conversation fence filtering should be added here so
|
|
2489
|
+
// zombie messages from other sessions don't leak into cross-session context.
|
|
2391
2490
|
buildCrossSessionContext(agentId, currentSessionKey, db, _libraryDb, existingFingerprints // C3: skip entries already in facts/semantic recall
|
|
2392
2491
|
) {
|
|
2393
2492
|
const conversation = db.prepare('SELECT id FROM conversations WHERE session_key = ?').get(currentSessionKey);
|
|
@@ -2459,7 +2558,7 @@ export class Compositor {
|
|
|
2459
2558
|
* Returns null if keystones cannot be injected (no cutoff ID found,
|
|
2460
2559
|
* no candidates, or all errors).
|
|
2461
2560
|
*/
|
|
2462
|
-
async buildKeystones(db, agentId, includedHistory, historyTokens, keystoneFraction, keystoneMaxMsgs, prompt, libraryDb) {
|
|
2561
|
+
async buildKeystones(db, agentId, includedHistory, historyTokens, keystoneFraction, keystoneMaxMsgs, prompt, libraryDb, fenceMessageId, activeContext) {
|
|
2463
2562
|
const keystoneBudget = Math.floor(historyTokens * keystoneFraction);
|
|
2464
2563
|
if (keystoneBudget <= 0)
|
|
2465
2564
|
return null;
|
|
@@ -2515,6 +2614,14 @@ export class Compositor {
|
|
|
2515
2614
|
// Episodes query is best-effort
|
|
2516
2615
|
}
|
|
2517
2616
|
}
|
|
2617
|
+
const fenceClause = fenceMessageId != null ? 'AND m.id >= ?' : '';
|
|
2618
|
+
// Phase 3 (Turn DAG): prefer context_id scoping over conversation_id+fence
|
|
2619
|
+
const contextClause = activeContext ? 'AND m.context_id = ?' : '';
|
|
2620
|
+
const baseParams = [conversationId, cutoffId];
|
|
2621
|
+
if (fenceMessageId != null)
|
|
2622
|
+
baseParams.push(fenceMessageId);
|
|
2623
|
+
if (activeContext)
|
|
2624
|
+
baseParams.push(activeContext.id);
|
|
2518
2625
|
const baseQuery = `
|
|
2519
2626
|
SELECT
|
|
2520
2627
|
m.id,
|
|
@@ -2525,6 +2632,8 @@ export class Compositor {
|
|
|
2525
2632
|
FROM messages m
|
|
2526
2633
|
WHERE m.conversation_id = ?
|
|
2527
2634
|
AND m.id < ?
|
|
2635
|
+
${fenceClause}
|
|
2636
|
+
${contextClause}
|
|
2528
2637
|
AND m.text_content IS NOT NULL
|
|
2529
2638
|
AND m.is_heartbeat = 0
|
|
2530
2639
|
AND m.text_content != ''
|
|
@@ -2539,6 +2648,12 @@ export class Compositor {
|
|
|
2539
2648
|
.join(' OR ');
|
|
2540
2649
|
if (ftsTerms) {
|
|
2541
2650
|
try {
|
|
2651
|
+
const ftsParams = [conversationId, cutoffId];
|
|
2652
|
+
if (fenceMessageId != null)
|
|
2653
|
+
ftsParams.push(fenceMessageId);
|
|
2654
|
+
if (activeContext)
|
|
2655
|
+
ftsParams.push(activeContext.id);
|
|
2656
|
+
ftsParams.push(ftsTerms);
|
|
2542
2657
|
candidateRows = db.prepare(`
|
|
2543
2658
|
SELECT
|
|
2544
2659
|
m.id,
|
|
@@ -2549,6 +2664,8 @@ export class Compositor {
|
|
|
2549
2664
|
FROM messages m
|
|
2550
2665
|
WHERE m.conversation_id = ?
|
|
2551
2666
|
AND m.id < ?
|
|
2667
|
+
${fenceClause}
|
|
2668
|
+
${contextClause}
|
|
2552
2669
|
AND m.text_content IS NOT NULL
|
|
2553
2670
|
AND m.is_heartbeat = 0
|
|
2554
2671
|
AND m.text_content != ''
|
|
@@ -2558,19 +2675,19 @@ export class Compositor {
|
|
|
2558
2675
|
LIMIT 100
|
|
2559
2676
|
)
|
|
2560
2677
|
LIMIT 200
|
|
2561
|
-
`).all(
|
|
2678
|
+
`).all(...ftsParams);
|
|
2562
2679
|
}
|
|
2563
2680
|
catch {
|
|
2564
2681
|
// FTS query may fail on special characters — fall back to base query
|
|
2565
|
-
candidateRows = db.prepare(baseQuery).all(
|
|
2682
|
+
candidateRows = db.prepare(baseQuery).all(...baseParams);
|
|
2566
2683
|
}
|
|
2567
2684
|
}
|
|
2568
2685
|
else {
|
|
2569
|
-
candidateRows = db.prepare(baseQuery).all(
|
|
2686
|
+
candidateRows = db.prepare(baseQuery).all(...baseParams);
|
|
2570
2687
|
}
|
|
2571
2688
|
}
|
|
2572
2689
|
else {
|
|
2573
|
-
candidateRows = db.prepare(baseQuery).all(
|
|
2690
|
+
candidateRows = db.prepare(baseQuery).all(...baseParams);
|
|
2574
2691
|
}
|
|
2575
2692
|
if (candidateRows.length === 0)
|
|
2576
2693
|
return null;
|
|
@@ -2652,7 +2769,7 @@ export class Compositor {
|
|
|
2652
2769
|
* @param maxKeystones - Max cross-topic keystones to return (default 3)
|
|
2653
2770
|
* @returns Scored keystones sorted by score DESC, deduplicated by message id
|
|
2654
2771
|
*/
|
|
2655
|
-
async getKeystonesByTopic(agentId, sessionKey, activeTopic, currentMessages, db, maxKeystones = 3) {
|
|
2772
|
+
async getKeystonesByTopic(agentId, sessionKey, activeTopic, currentMessages, db, maxKeystones = 3, fenceMessageId, activeContext) {
|
|
2656
2773
|
const otherTopics = db.prepare(`
|
|
2657
2774
|
SELECT id, name
|
|
2658
2775
|
FROM topics
|
|
@@ -2673,6 +2790,14 @@ export class Compositor {
|
|
|
2673
2790
|
for (const topic of otherTopics) {
|
|
2674
2791
|
let topicMessages;
|
|
2675
2792
|
try {
|
|
2793
|
+
const topicFenceClause = fenceMessageId != null ? 'AND m.id >= ?' : '';
|
|
2794
|
+
// Phase 3 (Turn DAG): constrain cross-topic queries to active context_id
|
|
2795
|
+
const topicContextClause = activeContext ? 'AND m.context_id = ?' : '';
|
|
2796
|
+
const topicParams = [sessionKey, agentId, topic.id];
|
|
2797
|
+
if (fenceMessageId != null)
|
|
2798
|
+
topicParams.push(fenceMessageId);
|
|
2799
|
+
if (activeContext)
|
|
2800
|
+
topicParams.push(activeContext.id);
|
|
2676
2801
|
topicMessages = db.prepare(`
|
|
2677
2802
|
SELECT m.id, m.message_index, m.role, m.text_content, m.created_at
|
|
2678
2803
|
FROM messages m
|
|
@@ -2680,12 +2805,14 @@ export class Compositor {
|
|
|
2680
2805
|
WHERE c.session_key = ?
|
|
2681
2806
|
AND c.agent_id = ?
|
|
2682
2807
|
AND m.topic_id = ?
|
|
2808
|
+
${topicFenceClause}
|
|
2809
|
+
${topicContextClause}
|
|
2683
2810
|
AND m.text_content IS NOT NULL
|
|
2684
2811
|
AND m.text_content != ''
|
|
2685
2812
|
AND m.is_heartbeat = 0
|
|
2686
2813
|
ORDER BY m.message_index DESC
|
|
2687
2814
|
LIMIT 50
|
|
2688
|
-
`).all(
|
|
2815
|
+
`).all(...topicParams);
|
|
2689
2816
|
}
|
|
2690
2817
|
catch {
|
|
2691
2818
|
// Corrupt topic data — skip this topic, never throw
|