@psiclawops/hypermem 0.5.4 → 0.5.6
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 +143 -54
- package/dist/background-indexer.d.ts +18 -0
- package/dist/background-indexer.d.ts.map +1 -1
- package/dist/background-indexer.js +122 -19
- package/dist/cache.d.ts +24 -1
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +61 -1
- package/dist/compositor.d.ts +6 -0
- package/dist/compositor.d.ts.map +1 -1
- package/dist/compositor.js +331 -116
- package/dist/cross-agent.js +18 -18
- package/dist/dreaming-promoter.d.ts +1 -1
- package/dist/dreaming-promoter.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/knowledge-lint.js +4 -4
- package/dist/profiles.d.ts +1 -1
- package/dist/profiles.d.ts.map +1 -1
- package/dist/profiles.js +64 -37
- 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.d.ts.map +1 -1
- package/dist/topic-synthesizer.js +4 -3
- package/dist/trigger-registry.d.ts +1 -1
- package/dist/trigger-registry.js +4 -4
- package/dist/types.d.ts +65 -31
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-store.d.ts.map +1 -1
- package/dist/vector-store.js +8 -0
- package/dist/version.d.ts +3 -3
- package/dist/version.js +3 -3
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -116,7 +116,7 @@ send transcript to model tool compression by turn age
|
|
|
116
116
|
model responds → append again keystone guard + hyperform profile
|
|
117
117
|
composed prompt → model
|
|
118
118
|
┌──────────────────┐ model responds → afterTurn ingest
|
|
119
|
-
│
|
|
119
|
+
│ loop until full │ → write back to all 4 layers
|
|
120
120
|
└──────────────────┘
|
|
121
121
|
|
|
122
122
|
When it fills: When budget is exceeded:
|
|
@@ -127,7 +127,7 @@ When it fills: When budget is exceeded:
|
|
|
127
127
|
|
|
128
128
|
| | Standard | hypercompositor |
|
|
129
129
|
|---|---|---|
|
|
130
|
-
| Context source | Growing transcript |
|
|
130
|
+
| Context source | Growing transcript only | Transcript + 3 additional storage layers |
|
|
131
131
|
| When context fills | Trim + summarize (lossy) | Budget allocation (lossless storage) |
|
|
132
132
|
| Old decisions | Lost after compaction | Retrievable via keystones + semantic recall |
|
|
133
133
|
| Topic changes | All history competes equally | Scoped retrieval by active topic |
|
|
@@ -136,21 +136,45 @@ When it fills: When budget is exceeded:
|
|
|
136
136
|
|
|
137
137
|
High-signal turns are marked as keystones and survive pressure trimming ahead of ordinary history.
|
|
138
138
|
|
|
139
|
+
The compositor fills 9 slots in priority order (system prompt → identity → hyperform → history → facts → wiki → semantic recall → cross-session → action summary). Each slot consumes tokens from the remaining budget before the next slot runs. Slots that don't fit this turn stay in storage, not destroyed.
|
|
140
|
+
|
|
141
|
+
For the full fill order, budget formula, and all configuration knobs, see **[Tuning](#tuning)** below and **[docs/TUNING.md](./docs/TUNING.md)**.
|
|
142
|
+
|
|
139
143
|
---
|
|
140
144
|
|
|
141
145
|
## hyperform
|
|
142
146
|
|
|
143
147
|
Raw model output has two problems. It drifts from your standards (sycophancy, hedging, pagination, formatting) and it drifts from your facts (confabulation, contradiction, stale claims). hyperform handles both: normalization enforces consistency, confabulation resistance checks output against what's actually stored.
|
|
144
148
|
|
|
145
|
-
|
|
149
|
+
Consistent output isn't just aesthetic. A model that paginates short answers, preambles with filler, or inflates lists uses more output tokens per turn. Over hundreds of turns, that compounds into real cost. hyperform directives compress output at the source: fewer tokens generated means lower API spend per session, and less context pressure for subsequent turns.
|
|
150
|
+
|
|
151
|
+
### Behavior standards
|
|
146
152
|
|
|
147
|
-
|
|
153
|
+
Behavior standards define how your agents write. Anti-sycophancy rules prevent filler openings. Density targets compress answers. Anti-pattern bans remove common AI markers (em dashes, AI vocabulary, inflated significance). These rules apply to all models equally.
|
|
154
|
+
|
|
155
|
+
| Tier | Tokens | What it injects |
|
|
148
156
|
|---|---|---|
|
|
149
|
-
| `light` | ~100 |
|
|
150
|
-
| `standard` | ~250 | Full directive set
|
|
151
|
-
| `full` | ~
|
|
157
|
+
| `light` | ~100 | 9 standalone directives: lead with answer, no sycophancy, no em dashes, AI vocab ban, length targets (simple/analysis/code), filler ban, no pagination of short answers, evidence calibration, numbers over adjectives. No database required. |
|
|
158
|
+
| `standard` | ~250 | Full directive set from the `fleet_output_standard` table: structural rules, density targets per task type, anti-patterns, format rules, compression ratios, voice directives, and task-context overrides. Falls back to `light` directives if no record exists. |
|
|
159
|
+
| `full` | ~250 + adaptation | Same directives as `standard`, plus model adaptation (see below). |
|
|
160
|
+
|
|
161
|
+
### Model adaptation
|
|
162
|
+
|
|
163
|
+
Different models have different default behaviors. GPT-5.4 tends toward 2x verbosity and long lists. Claude Opus defaults to hedging and preambles. Gemini produces bulleted summaries where prose would be more direct. Model adaptation corrects for these tendencies per model.
|
|
164
|
+
|
|
165
|
+
Adaptation entries are stored in the `model_output_directives` table and matched by model ID using exact match, then glob pattern (longest wins), then wildcard fallback. Each entry contains:
|
|
166
|
+
|
|
167
|
+
- **Calibration** — known model tendencies and specific adjustments (e.g., "2x verbosity: cut first drafts in half")
|
|
168
|
+
- **Corrections** — hard/medium/soft severity rules applied in order (e.g., "No preamble before the answer")
|
|
169
|
+
- **Task overrides** — per-task-type adjustments
|
|
170
|
+
|
|
171
|
+
Model adaptation is only active at the `full` tier. At `light` and `standard`, model-specific corrections are suppressed.
|
|
172
|
+
|
|
173
|
+
The `model_output_directives` table starts empty. You populate it with corrections for the models you run. See [docs/TUNING.md](./docs/TUNING.md#creating-custom-entries) for the schema and SQL examples.
|
|
152
174
|
|
|
153
|
-
|
|
175
|
+
### Before and after
|
|
176
|
+
|
|
177
|
+
The same prompt, GPT-5.4, with and without `hyperformProfile: "light"`:
|
|
154
178
|
|
|
155
179
|
```
|
|
156
180
|
Prompt: "How should I size my context window budget for a long-running agent session?"
|
|
@@ -172,11 +196,13 @@ Would you like me to go deeper on any of these?
|
|
|
172
196
|
WITH outputProfile: "light":
|
|
173
197
|
For a 128k window: reserve 14k for identity/system, target 46k for history, 10k for recent
|
|
174
198
|
tool context, and leave ~30k as allocator reserve. hypermem handles slot competition
|
|
175
|
-
automatically
|
|
199
|
+
automatically — set `reserveFraction` to your preferred floor and let the compositor fill.
|
|
176
200
|
```
|
|
177
201
|
|
|
178
202
|
**Confabulation resistance** checks output against stored facts before claims are recorded. No LLM call. Pattern matching against the fact corpus, with confidence scoring and contradiction detection. Unsupported claims are flagged, contradictions surface in diagnostics, and a confabulation risk score is attached to the stored episode.
|
|
179
203
|
|
|
204
|
+
Set `compositor.hyperformProfile` to `light`, `standard`, or `full`. For tier selection guidance, configuration details, and custom entry creation, see **[Tuning](#tuning)** below and **[docs/TUNING.md](./docs/TUNING.md)**.
|
|
205
|
+
|
|
180
206
|
---
|
|
181
207
|
|
|
182
208
|
## What it solves
|
|
@@ -197,17 +223,32 @@ OpenClaw 2026.4.7 ships memory wiki for structured storage. hypermem goes furthe
|
|
|
197
223
|
|
|
198
224
|
Spawned subagents inherit a bounded context block: recent parent turns, session-scoped documents, and relevant facts. Scope is isolated from the shared library. Documents are cleaned up on completion.
|
|
199
225
|
|
|
226
|
+
### Context that doesn't repeat itself
|
|
227
|
+
|
|
228
|
+
Retrieval paths pull from four layers, trigger shortcuts, temporal indexes, open-domain FTS5, semantic recall, and cross-session summaries. Without dedup, the same fact surfaces through multiple paths and wastes budget on repetition.
|
|
229
|
+
|
|
230
|
+
hypermem runs content fingerprint dedup across all compose-time retrieval. Every fact, temporal result, open-domain hit, and semantic recall entry is normalized and fingerprinted on a 120-char prefix. O(1) lookup in a shared set catches duplicates regardless of which retrieval path produced them, including rephrased near-duplicates that substring matching missed. Diagnostics track dedup counts and fingerprint collisions per compose call.
|
|
231
|
+
|
|
232
|
+
Identity content (SOUL.md, USER.md, IDENTITY.md) and doc chunks already injected by OpenClaw's bootstrap are fingerprinted before retrieval runs, so the compositor never double-injects content the runtime already placed in the prompt.
|
|
233
|
+
|
|
234
|
+
### Integrity under failure
|
|
235
|
+
|
|
236
|
+
The background indexer runs a startup integrity check against `library.db` on every boot. If the schema is corrupt, tables are missing, or critical indexes are damaged, the indexer enters circuit-breaker mode: it logs the failure, skips indexing for the session, and avoids cascading writes into a broken database. The agent still runs with cached and in-memory data while the operator is notified.
|
|
237
|
+
|
|
238
|
+
SQL queries that interpolate datetime values are fully parameterized. FTS5 trigger terms are quoted to prevent injection through crafted content. These aren't theoretical: agentic sessions ingest arbitrary user and tool output into the fact store, and unparameterized queries on that path were a real attack surface.
|
|
239
|
+
|
|
200
240
|
---
|
|
201
241
|
|
|
202
242
|
## Pressure management
|
|
203
243
|
|
|
204
|
-
hypermem composes context fresh on every turn, but a long-running session still accumulates history in its JSONL transcript. When that grows large enough, incoming tool results have nowhere to land and get silently stripped.
|
|
244
|
+
hypermem composes context fresh on every turn, but a long-running session still accumulates history in its JSONL transcript. When that grows large enough, incoming tool results have nowhere to land and get silently stripped. Four automatic paths handle this:
|
|
205
245
|
|
|
206
246
|
| Path | Trigger | Action |
|
|
207
247
|
|---|---|---|
|
|
208
248
|
| **Pressure-tiered tool-loop trim** | Any tool-loop turn | Measures projected occupancy before results land; trims large results at 80%+ and truncates the messages[] array for the current turn |
|
|
209
249
|
| **AfterTurn trim** | Every turn at >80% | Pre-emptive headroom cut after the assistant replies, before the next turn arrives |
|
|
210
250
|
| **Deep compaction** | compact() at >85% | Cuts in-memory cache to 25% budget and truncates JSONL to ~20% depth. Bypasses the normal reshape guard |
|
|
251
|
+
| **Reshape guard** | Structured tool history on downshift | `canPersistReshapedHistory()` blocks a lower-context snapshot from overwriting the full JSONL history |
|
|
211
252
|
|
|
212
253
|
**The one thing these paths cannot fix:** a session whose JSONL transcript on disk is already at 98% when the gateway restarts. The JSONL loads into runtime context before any compaction runs. Check `session_status` on startup. If you're above 85%, start a fresh session.
|
|
213
254
|
|
|
@@ -281,6 +322,8 @@ Retrieval follows a fixed pipeline on every compose call:
|
|
|
281
322
|
|
|
282
323
|
FTS5 queries use compound indexes on `agentId + sort key` and prefix optimization (3+ chars, capped at 8 terms, OR queries). These indexes yielded a 25% read improvement over baseline despite a 47% increase in stored data.
|
|
283
324
|
|
|
325
|
+
### Retrieval pipeline
|
|
326
|
+
|
|
284
327
|
**L4: Library DB.** Per-agent storage can't hold shared knowledge. Facts established by one agent, wiki pages synthesized from cross-agent topics, shared registry state: these belong to the system, not one agent. One shared SQLite database:
|
|
285
328
|
|
|
286
329
|
| Collection | What it holds |
|
|
@@ -311,17 +354,17 @@ Facts are ranked by `confidence × recencyDecay`, where decay is exponential wit
|
|
|
311
354
|
│
|
|
312
355
|
topic detection ──► scope retrieval to active thread
|
|
313
356
|
│
|
|
314
|
-
|
|
315
|
-
│ query 4 layers (parallel)
|
|
316
|
-
│
|
|
317
|
-
│ L1 in-memory L2 History
|
|
357
|
+
┌────┴───────────────────────────────────────────────┐
|
|
358
|
+
│ query 4 layers (parallel) │
|
|
359
|
+
│ │
|
|
360
|
+
│ L1 in-memory L2 History L3 Vectors L4 Library │
|
|
318
361
|
│ hot state durable semantic facts/wiki │
|
|
319
362
|
│ 0.1ms 0.16ms 0.29ms 0.08ms │
|
|
320
|
-
|
|
363
|
+
└────┬───────────────────────────────────────────────┘
|
|
321
364
|
│
|
|
322
365
|
budget allocator ──► 10 slots, fixed token cap
|
|
323
366
|
│
|
|
324
|
-
tool compression ──►
|
|
367
|
+
tool compression ──► clusterNeutralMessages() → T0 full → T1 6k → T2 800 → T3 150-char stub
|
|
325
368
|
│
|
|
326
369
|
keystone guard ──► high-signal turns survive pressure
|
|
327
370
|
│
|
|
@@ -340,14 +383,52 @@ Slot-level budget allocation is shown in the [hypercompositor diagram](#what-the
|
|
|
340
383
|
|
|
341
384
|
## Requirements
|
|
342
385
|
|
|
343
|
-
**Current release: hypermem 0.5.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
-
|
|
349
|
-
-
|
|
350
|
-
-
|
|
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.
|
|
351
432
|
|
|
352
433
|
| Requirement | Version | Notes |
|
|
353
434
|
|---|---|---|
|
|
@@ -360,9 +441,8 @@ SQLite is a library, not a service. All four layers run in-process with no exter
|
|
|
360
441
|
**Runtime version constants** (importable from the package):
|
|
361
442
|
```typescript
|
|
362
443
|
import {
|
|
363
|
-
ENGINE_VERSION, // '0.5.
|
|
444
|
+
ENGINE_VERSION, // '0.5.6'
|
|
364
445
|
MIN_NODE_VERSION, // '22.0.0'
|
|
365
|
-
MIN_SQLITE_VERSION, // '3.35.0'
|
|
366
446
|
SQLITE_VEC_VERSION, // '0.1.9'
|
|
367
447
|
MAIN_SCHEMA_VERSION, // 6 (hypermem.db)
|
|
368
448
|
LIBRARY_SCHEMA_VERSION_EXPORT, // 12 (library.db)
|
|
@@ -407,49 +487,52 @@ If you prefer, hand the install to your OpenClaw agent:
|
|
|
407
487
|
|
|
408
488
|
### Tuning
|
|
409
489
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
| Profile | Context window | Budget fraction | Best for |
|
|
413
|
-
|---|---|---|---|
|
|
414
|
-
| `light` | 64k | 0.50 | Single-agent installs, minimal parallel work |
|
|
415
|
-
| `standard` | 128k | 0.65 | Normal OpenClaw deployments |
|
|
416
|
-
| `full` | 200k+ | 0.55 | Large-context or multi-agent installs, maximum richness |
|
|
490
|
+
Two independent surfaces: **context assembly** (what fills the context window) and **output shaping** (how the model writes). Pick a profile first — most deployments adjust one or two settings on top.
|
|
417
491
|
|
|
418
|
-
|
|
492
|
+
| Profile | Target window | Best for |
|
|
493
|
+
|---|---|---|
|
|
494
|
+
| `light` | 64k | Single agent, small models, constrained resources |
|
|
495
|
+
| `standard` | 128k | Normal deployments, small fleets |
|
|
496
|
+
| `full` | 200k+ | Multi-agent fleets, large-context models |
|
|
419
497
|
|
|
420
|
-
|
|
498
|
+
Start with `light`. Use `mergeProfile()` to adjust individual settings:
|
|
421
499
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
500
|
+
```typescript
|
|
501
|
+
import { mergeProfile } from '@psiclawops/hypermem';
|
|
502
|
+
const config = mergeProfile('standard', { compositor: { maxFacts: 40 } });
|
|
503
|
+
```
|
|
425
504
|
|
|
426
|
-
Drop a `~/.openclaw/hypermem/config.json` to override
|
|
505
|
+
Drop a `~/.openclaw/hypermem/config.json` to override defaults (takes effect on gateway restart):
|
|
427
506
|
|
|
428
507
|
```json
|
|
429
508
|
{
|
|
430
|
-
"deferToolPruning": true,
|
|
431
509
|
"compositor": {
|
|
432
|
-
"
|
|
433
|
-
"
|
|
434
|
-
"contextWindowReserve": 0.25,
|
|
435
|
-
"outputProfile": "standard"
|
|
510
|
+
"budgetFraction": 0.70,
|
|
511
|
+
"hyperformProfile": "standard"
|
|
436
512
|
}
|
|
437
513
|
}
|
|
438
514
|
```
|
|
439
515
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
`deferToolPruning: true` tells hypermem to skip its own T0/T1/T2/T3 gradient when OpenClaw's native `contextPruning` extension is active (Anthropic and Google providers). On those providers, OpenClaw's pruner handles tool result trimming: ratio-driven at >30% context fill, soft-trim head+tail for results over 4,000 chars, hard-clear above 50k total, with the last 3 assistant turns always protected. hypermem's gradient remains active as fallback for other providers (GPT-5.4, etc.). Default: `true` for Anthropic installs.
|
|
443
|
-
|
|
444
|
-
`outputProfile` valid values: `"light"` (~100 tokens: anti-sycophancy, em dash ban, AI vocab ban, length targets, evidence calibration), `"standard"` (~250 tokens: full directive set plus pagination and hedging rules), `"full"` (~400 tokens: complete normalization with full directive set and model-specific calibration). Default: `"standard"`.
|
|
516
|
+
Or configure through `openclaw.json` (preferred for managed deployments):
|
|
445
517
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
518
|
+
```json
|
|
519
|
+
{
|
|
520
|
+
"plugins": {
|
|
521
|
+
"entries": {
|
|
522
|
+
"hypercompositor": {
|
|
523
|
+
"config": {
|
|
524
|
+
"compositor": { "budgetFraction": 0.70 },
|
|
525
|
+
"hyperformProfile": "standard"
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
450
531
|
```
|
|
451
532
|
|
|
452
|
-
|
|
533
|
+
Plugin config in `openclaw.json` takes precedence over `config.json`. Both sources are merged, with plugin config winning on overlap. The config schema is validated on gateway start and visible via `openclaw config get plugins.entries.hypercompositor.config`.
|
|
534
|
+
|
|
535
|
+
Full reference: **[docs/TUNING.md](./docs/TUNING.md)**
|
|
453
536
|
|
|
454
537
|
---
|
|
455
538
|
|
|
@@ -545,6 +628,12 @@ Design guide: [PsiClawOps/AgenticCognitiveArchitecture](https://github.com/PsiCl
|
|
|
545
628
|
|
|
546
629
|
---
|
|
547
630
|
|
|
631
|
+
## Acknowledgments
|
|
632
|
+
|
|
633
|
+
The embedding-space fidelity threshold used in compaction validation was informed by the geometric preservation mathematics published by the [libravdb](https://github.com/xDarkicex/openclaw-memory-libravdb) project.
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
548
637
|
## License
|
|
549
638
|
|
|
550
639
|
Apache-2.0, [PsiClawOps](https://github.com/PsiClawOps)
|
|
@@ -56,6 +56,10 @@ export declare class BackgroundIndexer {
|
|
|
56
56
|
private vectorStore;
|
|
57
57
|
private synthesizer;
|
|
58
58
|
private tickCount;
|
|
59
|
+
/** Circuit breaker: consecutive tick failure count. Resets on success. */
|
|
60
|
+
private consecutiveFailures;
|
|
61
|
+
/** True when the indexer is running in backoff mode due to repeated failures. */
|
|
62
|
+
private inBackoff;
|
|
59
63
|
constructor(config?: Partial<IndexerConfig>, getMessageDb?: ((agentId: string) => DatabaseSync) | undefined, getLibraryDb?: (() => DatabaseSync) | undefined, listAgents?: (() => string[]) | undefined, getCursor?: CursorFetcher | undefined, dreamerConfig?: Partial<DreamerConfig>);
|
|
60
64
|
/**
|
|
61
65
|
* Set the vector store for embedding new facts/episodes at index time.
|
|
@@ -66,6 +70,20 @@ export declare class BackgroundIndexer {
|
|
|
66
70
|
* Start periodic indexing.
|
|
67
71
|
*/
|
|
68
72
|
start(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Circuit breaker for tick failures.
|
|
75
|
+
*
|
|
76
|
+
* - Tracks consecutive failures.
|
|
77
|
+
* - After 3 failures, logs actionable recovery guidance once, then switches
|
|
78
|
+
* the indexer to 10× backoff interval so it stops spamming the log.
|
|
79
|
+
* - On the next successful tick, resets state and restores normal interval.
|
|
80
|
+
*/
|
|
81
|
+
private _handleTickError;
|
|
82
|
+
/**
|
|
83
|
+
* Reset the circuit breaker and restore normal interval after a successful tick.
|
|
84
|
+
* Called at the end of a successful tick().
|
|
85
|
+
*/
|
|
86
|
+
private _resetCircuitBreaker;
|
|
69
87
|
/**
|
|
70
88
|
* Stop periodic indexing.
|
|
71
89
|
*/
|
|
@@ -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;AAuCrD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,wFAAwF;IACxF,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;AAEnG,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AA+XD,qBAAa,iBAAiB;
|
|
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;AAuCrD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,wFAAwF;IACxF,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;AAEnG,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AA+XD,qBAAa,iBAAiB;IAe1B,OAAO,CAAC,YAAY,CAAC;IACrB,OAAO,CAAC,YAAY,CAAC;IACrB,OAAO,CAAC,UAAU,CAAC;IACnB,OAAO,CAAC,SAAS,CAAC;IAjBpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;IACvD,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,SAAS,CAAa;IAC9B,0EAA0E;IAC1E,OAAO,CAAC,mBAAmB,CAAa;IACxC,iFAAiF;IACjF,OAAO,CAAC,SAAS,CAAkB;gBAGjC,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,EACvB,YAAY,CAAC,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,YAAY,aAAA,EAChD,YAAY,CAAC,GAAE,MAAM,YAAY,aAAA,EACjC,UAAU,CAAC,GAAE,MAAM,MAAM,EAAE,aAAA,EAC3B,SAAS,CAAC,EAAE,aAAa,YAAA,EACjC,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC;IA8BxC;;;OAGG;IACH,cAAc,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI;IAIrC;;OAEG;IACH,KAAK,IAAI,IAAI;IAkDb;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAkDxB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAiB5B;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IA2IrC;;;;;;;;;OASG;YACW,YAAY;IA4M1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA+B5B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAK/B;;OAEG;IACH,OAAO,CAAC,YAAY;IAsBpB;;OAEG;IACH,OAAO,CAAC,YAAY;IAWpB;;;OAGG;IACH,OAAO,CAAC,UAAU;IA8ClB;;OAEG;IACH,OAAO,CAAC,aAAa;IAarB;;;;;;;OAOG;IACG,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgF7C;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,YAAY,GAAG,cAAc,EAAE;CAezD;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,YAAY,EAC/C,YAAY,EAAE,MAAM,YAAY,EAChC,UAAU,EAAE,MAAM,MAAM,EAAE,EAC1B,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,EAC/B,SAAS,CAAC,EAAE,aAAa,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GACrC,iBAAiB,CAInB"}
|
|
@@ -33,22 +33,22 @@ import { isSafeForSharedVisibility } from './secret-scanner.js';
|
|
|
33
33
|
// domain-scoped retrieval (e.g. getActiveFacts({ domain: 'infrastructure' }))
|
|
34
34
|
// returns results. New agents default to 'general'.
|
|
35
35
|
const AGENT_DOMAIN_MAP = {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
alice: 'infrastructure',
|
|
37
|
+
jack: 'infrastructure',
|
|
38
|
+
hank: 'infrastructure',
|
|
39
|
+
irene: 'infrastructure',
|
|
40
|
+
bob: 'product',
|
|
41
|
+
eve: 'product',
|
|
42
|
+
frank: 'product',
|
|
43
|
+
grace: 'product',
|
|
44
|
+
dave: 'security',
|
|
45
|
+
leo: 'security',
|
|
46
|
+
kate: 'security',
|
|
47
47
|
clarity: 'ux',
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
carol: 'governance',
|
|
49
|
+
oscar: 'strategy',
|
|
50
|
+
mike: 'development',
|
|
51
|
+
nancy: 'communications',
|
|
52
52
|
main: 'general',
|
|
53
53
|
'channel-mini': 'general',
|
|
54
54
|
};
|
|
@@ -83,7 +83,7 @@ function extractFactCandidates(content) {
|
|
|
83
83
|
// Preference patterns — medium confidence (0.60)
|
|
84
84
|
const preferencePatterns = [
|
|
85
85
|
/(?:prefer|always use|never use|don't use|avoid) (.{10,150})/gi,
|
|
86
|
-
/(?:
|
|
86
|
+
/(?:operator) (?:wants|prefers|likes|hates|dislikes) (.{10,150})/gi,
|
|
87
87
|
];
|
|
88
88
|
// Operational patterns: deployments, incidents, fixes — high confidence (0.70)
|
|
89
89
|
const operationalPatterns = [
|
|
@@ -137,7 +137,7 @@ const OPERATIONAL_BOILERPLATE = [
|
|
|
137
137
|
/still\s*waiting/i,
|
|
138
138
|
/will\s*pick\s*(it\s*)?up\s*(on\s*(next|the))?/i,
|
|
139
139
|
/message\s*is\s*in\s*(his|her|their|the)\s*queue/i,
|
|
140
|
-
/sent\s+to\s+(
|
|
140
|
+
/sent\s+to\s+(carol|bob|clarity|dave|oscar|alice)/i,
|
|
141
141
|
/dispatched\s+(it\s+)?to/i,
|
|
142
142
|
/timed\s*out\s*after/i,
|
|
143
143
|
/\bNO_REPLY\b/,
|
|
@@ -408,6 +408,10 @@ export class BackgroundIndexer {
|
|
|
408
408
|
vectorStore = null;
|
|
409
409
|
synthesizer = null;
|
|
410
410
|
tickCount = 0;
|
|
411
|
+
/** Circuit breaker: consecutive tick failure count. Resets on success. */
|
|
412
|
+
consecutiveFailures = 0;
|
|
413
|
+
/** True when the indexer is running in backoff mode due to repeated failures. */
|
|
414
|
+
inBackoff = false;
|
|
411
415
|
constructor(config, getMessageDb, getLibraryDb, listAgents, getCursor, dreamerConfig) {
|
|
412
416
|
this.getMessageDb = getMessageDb;
|
|
413
417
|
this.getLibraryDb = getLibraryDb;
|
|
@@ -457,9 +461,31 @@ export class BackgroundIndexer {
|
|
|
457
461
|
return;
|
|
458
462
|
if (this.intervalHandle)
|
|
459
463
|
return;
|
|
464
|
+
// Startup integrity check — catch corruption before the first tick writes anything.
|
|
465
|
+
if (this.getLibraryDb) {
|
|
466
|
+
try {
|
|
467
|
+
const libDb = this.getLibraryDb();
|
|
468
|
+
if (libDb) {
|
|
469
|
+
const row = libDb.prepare('PRAGMA quick_check').get();
|
|
470
|
+
if (row?.integrity_check && row.integrity_check !== 'ok') {
|
|
471
|
+
console.error('[indexer] ⚠️ library.db integrity check failed: ' + row.integrity_check + '\n' +
|
|
472
|
+
'[indexer] Recovery: stop OpenClaw, run ' +
|
|
473
|
+
'`sqlite3 ~/.openclaw/hypermem/library.db ".recover" | sqlite3 ~/.openclaw/hypermem/library_recovered.db`' +
|
|
474
|
+
', swap the files, and restart. If recovery fails, delete library.db — the indexer rebuilds from message history.');
|
|
475
|
+
// Don't start the interval — nothing will succeed with a corrupt DB.
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
// If we can't even open the DB, log and bail — don't start the interval.
|
|
482
|
+
console.error('[indexer] Could not open library.db for integrity check:', err.message);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
460
486
|
// Run once immediately
|
|
461
487
|
this.tick().catch(err => {
|
|
462
|
-
|
|
488
|
+
this._handleTickError(err, 'initial');
|
|
463
489
|
});
|
|
464
490
|
// Run episode vector backfill once at startup (no-op if already done)
|
|
465
491
|
if (this.vectorStore && this.getLibraryDb) {
|
|
@@ -470,11 +496,83 @@ export class BackgroundIndexer {
|
|
|
470
496
|
// Then periodically
|
|
471
497
|
this.intervalHandle = setInterval(() => {
|
|
472
498
|
this.tick().catch(err => {
|
|
473
|
-
|
|
499
|
+
this._handleTickError(err, 'periodic');
|
|
474
500
|
});
|
|
475
501
|
}, this.config.periodicInterval);
|
|
476
502
|
console.log(`[indexer] Started with interval ${this.config.periodicInterval}ms, batchSize ${this.config.batchSize}, maxPerTick ${this.config.maxMessagesPerTick}`);
|
|
477
503
|
}
|
|
504
|
+
/**
|
|
505
|
+
* Circuit breaker for tick failures.
|
|
506
|
+
*
|
|
507
|
+
* - Tracks consecutive failures.
|
|
508
|
+
* - After 3 failures, logs actionable recovery guidance once, then switches
|
|
509
|
+
* the indexer to 10× backoff interval so it stops spamming the log.
|
|
510
|
+
* - On the next successful tick, resets state and restores normal interval.
|
|
511
|
+
*/
|
|
512
|
+
_handleTickError(err, phase) {
|
|
513
|
+
this.consecutiveFailures++;
|
|
514
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
515
|
+
const isSqliteCorrupt = msg.includes('database disk image is malformed') ||
|
|
516
|
+
msg.includes('SQLITE_CORRUPT') ||
|
|
517
|
+
(err instanceof Error && 'code' in err && err.code === 'ERR_SQLITE_ERROR');
|
|
518
|
+
if (this.consecutiveFailures < 3) {
|
|
519
|
+
// First 1–2 failures: log normally.
|
|
520
|
+
console.error(`[indexer] ${phase === 'initial' ? 'Initial' : 'Periodic'} tick failed (attempt ${this.consecutiveFailures}/3):`, err);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (this.consecutiveFailures === 3) {
|
|
524
|
+
// Third failure: log once with recovery instructions, then enter backoff.
|
|
525
|
+
if (isSqliteCorrupt) {
|
|
526
|
+
console.error(`[indexer] ⛔ Tick failed 3 times consecutively — library.db appears corrupted. Entering backoff mode.\n` +
|
|
527
|
+
`[indexer] Recovery steps:\n` +
|
|
528
|
+
`[indexer] 1. Stop OpenClaw: openclaw gateway stop\n` +
|
|
529
|
+
`[indexer] 2. Check damage: sqlite3 ~/.openclaw/hypermem/library.db "PRAGMA integrity_check"\n` +
|
|
530
|
+
`[indexer] 3. Attempt recovery: sqlite3 ~/.openclaw/hypermem/library.db ".recover" | sqlite3 ~/.openclaw/hypermem/library_recovered.db\n` +
|
|
531
|
+
`[indexer] 4. Swap: mv library.db library_corrupt.bak && mv library_recovered.db library.db\n` +
|
|
532
|
+
`[indexer] 5. If recovery fails, delete library.db — the indexer rebuilds from message history on next start.\n` +
|
|
533
|
+
`[indexer] 6. Restart: openclaw gateway start\n` +
|
|
534
|
+
`[indexer] Indexer will retry every ${(this.config.periodicInterval * 10) / 60000} minutes until then.`);
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
console.error(`[indexer] ⛔ Tick failed 3 times consecutively (${msg}). Entering backoff mode. ` +
|
|
538
|
+
`Will retry every ${(this.config.periodicInterval * 10) / 60000} minutes.`);
|
|
539
|
+
}
|
|
540
|
+
// Switch to backoff interval.
|
|
541
|
+
this.inBackoff = true;
|
|
542
|
+
if (this.intervalHandle) {
|
|
543
|
+
clearInterval(this.intervalHandle);
|
|
544
|
+
}
|
|
545
|
+
this.intervalHandle = setInterval(() => {
|
|
546
|
+
this.tick().catch(backoffErr => {
|
|
547
|
+
this._handleTickError(backoffErr, 'periodic');
|
|
548
|
+
});
|
|
549
|
+
}, this.config.periodicInterval * 10);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
// Beyond 3: silent (already logged, in backoff — don't spam).
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Reset the circuit breaker and restore normal interval after a successful tick.
|
|
556
|
+
* Called at the end of a successful tick().
|
|
557
|
+
*/
|
|
558
|
+
_resetCircuitBreaker() {
|
|
559
|
+
if (this.consecutiveFailures === 0)
|
|
560
|
+
return;
|
|
561
|
+
const wasInBackoff = this.inBackoff;
|
|
562
|
+
this.consecutiveFailures = 0;
|
|
563
|
+
this.inBackoff = false;
|
|
564
|
+
if (wasInBackoff) {
|
|
565
|
+
// Restore normal interval.
|
|
566
|
+
if (this.intervalHandle)
|
|
567
|
+
clearInterval(this.intervalHandle);
|
|
568
|
+
this.intervalHandle = setInterval(() => {
|
|
569
|
+
this.tick().catch(err => {
|
|
570
|
+
this._handleTickError(err, 'periodic');
|
|
571
|
+
});
|
|
572
|
+
}, this.config.periodicInterval);
|
|
573
|
+
console.log('[indexer] Circuit breaker reset — tick succeeded, restored normal interval.');
|
|
574
|
+
}
|
|
575
|
+
}
|
|
478
576
|
/**
|
|
479
577
|
* Stop periodic indexing.
|
|
480
578
|
*/
|
|
@@ -494,6 +592,7 @@ export class BackgroundIndexer {
|
|
|
494
592
|
}
|
|
495
593
|
this.running = true;
|
|
496
594
|
const results = [];
|
|
595
|
+
let tickSucceeded = false;
|
|
497
596
|
try {
|
|
498
597
|
if (!this.listAgents || !this.getMessageDb || !this.getLibraryDb) {
|
|
499
598
|
console.warn('[indexer] Missing database accessors — skipping');
|
|
@@ -601,8 +700,12 @@ export class BackgroundIndexer {
|
|
|
601
700
|
}
|
|
602
701
|
}
|
|
603
702
|
}
|
|
703
|
+
// If we reach here, the tick completed without throwing.
|
|
704
|
+
tickSucceeded = true;
|
|
604
705
|
}
|
|
605
706
|
finally {
|
|
707
|
+
if (tickSucceeded)
|
|
708
|
+
this._resetCircuitBreaker();
|
|
606
709
|
this.running = false;
|
|
607
710
|
}
|
|
608
711
|
return results;
|
package/dist/cache.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Same public interface, zero external dependencies, zero TCP overhead.
|
|
6
6
|
*/
|
|
7
7
|
import { DatabaseSync } from 'node:sqlite';
|
|
8
|
-
import type { CacheConfig, SessionMeta, SessionCursor, StoredMessage, NeutralMessage } from './types.js';
|
|
8
|
+
import type { CacheConfig, ComposeDiagnostics, SessionMeta, SessionCursor, StoredMessage, NeutralMessage } from './types.js';
|
|
9
9
|
export interface ModelState {
|
|
10
10
|
model: string;
|
|
11
11
|
tokenBudget: number;
|
|
@@ -13,6 +13,13 @@ export interface ModelState {
|
|
|
13
13
|
historyDepth: number;
|
|
14
14
|
reshapedAt?: string;
|
|
15
15
|
}
|
|
16
|
+
export interface WindowCacheMeta {
|
|
17
|
+
slots: Record<string, number>;
|
|
18
|
+
totalTokens: number;
|
|
19
|
+
warnings: string[];
|
|
20
|
+
diagnostics: ComposeDiagnostics;
|
|
21
|
+
composedAt: string;
|
|
22
|
+
}
|
|
16
23
|
export declare class CacheLayer {
|
|
17
24
|
private db;
|
|
18
25
|
private readonly config;
|
|
@@ -41,6 +48,7 @@ export declare class CacheLayer {
|
|
|
41
48
|
private stmtEvictHistory;
|
|
42
49
|
private stmtSetWindow;
|
|
43
50
|
private stmtGetWindow;
|
|
51
|
+
private stmtGetFreshWindowBundle;
|
|
44
52
|
private stmtDeleteWindow;
|
|
45
53
|
private stmtEvictWindows;
|
|
46
54
|
private stmtSetKv;
|
|
@@ -67,6 +75,21 @@ export declare class CacheLayer {
|
|
|
67
75
|
setWindow(agentId: string, sessionKey: string, messages: NeutralMessage[], ttlSeconds?: number): Promise<void>;
|
|
68
76
|
getWindow(agentId: string, sessionKey: string): Promise<NeutralMessage[] | null>;
|
|
69
77
|
invalidateWindow(agentId: string, sessionKey: string): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Returns the cached window + metadata only if a single read shows the cache
|
|
80
|
+
* and cursor still refer to the same composed window.
|
|
81
|
+
* Used for C4 window cache fast-exit in compositor.ts.
|
|
82
|
+
*/
|
|
83
|
+
getFreshWindowBundle(agentId: string, sessionKey: string, lastMessageId: number): Promise<{
|
|
84
|
+
messages: NeutralMessage[];
|
|
85
|
+
meta: WindowCacheMeta;
|
|
86
|
+
} | null>;
|
|
87
|
+
/**
|
|
88
|
+
* Store compose result metadata alongside the window cache.
|
|
89
|
+
* Enables the C4 fast-exit to return a complete ComposeResult without re-running.
|
|
90
|
+
*/
|
|
91
|
+
setWindowMeta(agentId: string, sessionKey: string, meta: WindowCacheMeta, ttl: number): Promise<void>;
|
|
92
|
+
getWindowMeta(agentId: string, sessionKey: string): Promise<WindowCacheMeta | null>;
|
|
70
93
|
setCursor(agentId: string, sessionKey: string, cursor: SessionCursor): Promise<void>;
|
|
71
94
|
getCursor(agentId: string, sessionKey: string): Promise<SessionCursor | null>;
|
|
72
95
|
warmSession(agentId: string, sessionKey: string, slots: {
|