hippo-memory 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/api.d.ts +20 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +23 -3
- package/dist/api.js.map +1 -1
- package/dist/benchmarks/e1.3/incident-recall-eval.js +74 -0
- package/dist/benchmarks/e1.3/incident-recall-eval.js.map +1 -0
- package/dist/benchmarks/e1.3/scenarios.json +2587 -0
- package/dist/benchmarks/e1.3/slack-1000-event-smoke.js +102 -0
- package/dist/benchmarks/e1.3/slack-1000-event-smoke.js.map +1 -0
- package/dist/cli.js +82 -0
- package/dist/cli.js.map +1 -1
- package/dist/connectors/slack/backfill.d.ts +42 -0
- package/dist/connectors/slack/backfill.d.ts.map +1 -0
- package/dist/connectors/slack/backfill.js +76 -0
- package/dist/connectors/slack/backfill.js.map +1 -0
- package/dist/connectors/slack/deletion.d.ts +14 -0
- package/dist/connectors/slack/deletion.d.ts.map +1 -0
- package/dist/connectors/slack/deletion.js +46 -0
- package/dist/connectors/slack/deletion.js.map +1 -0
- package/dist/connectors/slack/dlq.d.ts +21 -0
- package/dist/connectors/slack/dlq.d.ts.map +1 -0
- package/dist/connectors/slack/dlq.js +23 -0
- package/dist/connectors/slack/dlq.js.map +1 -0
- package/dist/connectors/slack/idempotency.d.ts +5 -0
- package/dist/connectors/slack/idempotency.d.ts.map +1 -0
- package/dist/connectors/slack/idempotency.js +13 -0
- package/dist/connectors/slack/idempotency.js.map +1 -0
- package/dist/connectors/slack/ingest.d.ts +27 -0
- package/dist/connectors/slack/ingest.d.ts.map +1 -0
- package/dist/connectors/slack/ingest.js +48 -0
- package/dist/connectors/slack/ingest.js.map +1 -0
- package/dist/connectors/slack/ratelimit.d.ts +9 -0
- package/dist/connectors/slack/ratelimit.d.ts.map +1 -0
- package/dist/connectors/slack/ratelimit.js +18 -0
- package/dist/connectors/slack/ratelimit.js.map +1 -0
- package/dist/connectors/slack/scope.d.ts +16 -0
- package/dist/connectors/slack/scope.d.ts.map +1 -0
- package/dist/connectors/slack/scope.js +13 -0
- package/dist/connectors/slack/scope.js.map +1 -0
- package/dist/connectors/slack/signature.d.ts +12 -0
- package/dist/connectors/slack/signature.d.ts.map +1 -0
- package/dist/connectors/slack/signature.js +20 -0
- package/dist/connectors/slack/signature.js.map +1 -0
- package/dist/connectors/slack/tenant-routing.d.ts +13 -0
- package/dist/connectors/slack/tenant-routing.d.ts.map +1 -0
- package/dist/connectors/slack/tenant-routing.js +17 -0
- package/dist/connectors/slack/tenant-routing.js.map +1 -0
- package/dist/connectors/slack/transform.d.ts +20 -0
- package/dist/connectors/slack/transform.d.ts.map +1 -0
- package/dist/connectors/slack/transform.js +31 -0
- package/dist/connectors/slack/transform.js.map +1 -0
- package/dist/connectors/slack/types.d.ts +35 -0
- package/dist/connectors/slack/types.d.ts.map +1 -0
- package/dist/connectors/slack/types.js +23 -0
- package/dist/connectors/slack/types.js.map +1 -0
- package/dist/connectors/slack/web-client.d.ts +12 -0
- package/dist/connectors/slack/web-client.d.ts.map +1 -0
- package/dist/connectors/slack/web-client.js +43 -0
- package/dist/connectors/slack/web-client.js.map +1 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +46 -1
- package/dist/db.js.map +1 -1
- package/dist/importers.js +3 -3
- package/dist/importers.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +174 -2
- package/dist/server.js.map +1 -1
- package/dist/src/ambient.js +147 -0
- package/dist/src/ambient.js.map +1 -0
- package/dist/src/api.js +343 -0
- package/dist/src/api.js.map +1 -0
- package/dist/src/audit.js +152 -0
- package/dist/src/audit.js.map +1 -0
- package/dist/src/auth.js +65 -0
- package/dist/src/auth.js.map +1 -0
- package/dist/src/autolearn.js +143 -0
- package/dist/src/autolearn.js.map +1 -0
- package/dist/src/capture.js +512 -0
- package/dist/src/capture.js.map +1 -0
- package/dist/src/cli.js +4971 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/client.js +181 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/config.js +108 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/connectors/slack/backfill.js +76 -0
- package/dist/src/connectors/slack/backfill.js.map +1 -0
- package/dist/src/connectors/slack/deletion.js +46 -0
- package/dist/src/connectors/slack/deletion.js.map +1 -0
- package/dist/src/connectors/slack/dlq.js +23 -0
- package/dist/src/connectors/slack/dlq.js.map +1 -0
- package/dist/src/connectors/slack/idempotency.js +13 -0
- package/dist/src/connectors/slack/idempotency.js.map +1 -0
- package/dist/src/connectors/slack/ingest.js +48 -0
- package/dist/src/connectors/slack/ingest.js.map +1 -0
- package/dist/src/connectors/slack/ratelimit.js +18 -0
- package/dist/src/connectors/slack/ratelimit.js.map +1 -0
- package/dist/src/connectors/slack/scope.js +13 -0
- package/dist/src/connectors/slack/scope.js.map +1 -0
- package/dist/src/connectors/slack/signature.js +20 -0
- package/dist/src/connectors/slack/signature.js.map +1 -0
- package/dist/src/connectors/slack/tenant-routing.js +17 -0
- package/dist/src/connectors/slack/tenant-routing.js.map +1 -0
- package/dist/src/connectors/slack/transform.js +31 -0
- package/dist/src/connectors/slack/transform.js.map +1 -0
- package/dist/src/connectors/slack/types.js +23 -0
- package/dist/src/connectors/slack/types.js.map +1 -0
- package/dist/src/connectors/slack/web-client.js +43 -0
- package/dist/src/connectors/slack/web-client.js.map +1 -0
- package/dist/src/consolidate.js +517 -0
- package/dist/src/consolidate.js.map +1 -0
- package/dist/src/dag.js +104 -0
- package/dist/src/dag.js.map +1 -0
- package/dist/src/dashboard.js +409 -0
- package/dist/src/dashboard.js.map +1 -0
- package/dist/src/db.js +584 -0
- package/dist/src/db.js.map +1 -0
- package/dist/src/embeddings.js +344 -0
- package/dist/src/embeddings.js.map +1 -0
- package/dist/src/eval-suite.js +289 -0
- package/dist/src/eval-suite.js.map +1 -0
- package/dist/src/eval.js +187 -0
- package/dist/src/eval.js.map +1 -0
- package/dist/src/extract.js +87 -0
- package/dist/src/extract.js.map +1 -0
- package/dist/src/handoff.js +30 -0
- package/dist/src/handoff.js.map +1 -0
- package/dist/src/hooks.js +582 -0
- package/dist/src/hooks.js.map +1 -0
- package/dist/src/importers.js +399 -0
- package/dist/src/importers.js.map +1 -0
- package/dist/src/index.js +25 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/invalidation.js +94 -0
- package/dist/src/invalidation.js.map +1 -0
- package/dist/src/mcp/framing.js +45 -0
- package/dist/src/mcp/framing.js.map +1 -0
- package/dist/src/mcp/server.js +510 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/memory.js +280 -0
- package/dist/src/memory.js.map +1 -0
- package/dist/src/multihop.js +32 -0
- package/dist/src/multihop.js.map +1 -0
- package/dist/src/path-context.js +32 -0
- package/dist/src/path-context.js.map +1 -0
- package/dist/src/physics-config.js +26 -0
- package/dist/src/physics-config.js.map +1 -0
- package/dist/src/physics-state.js +163 -0
- package/dist/src/physics-state.js.map +1 -0
- package/dist/src/physics.js +361 -0
- package/dist/src/physics.js.map +1 -0
- package/dist/src/postinstall.js +68 -0
- package/dist/src/postinstall.js.map +1 -0
- package/dist/src/raw-archive.js +72 -0
- package/dist/src/raw-archive.js.map +1 -0
- package/dist/src/refine-llm.js +147 -0
- package/dist/src/refine-llm.js.map +1 -0
- package/dist/src/replay.js +117 -0
- package/dist/src/replay.js.map +1 -0
- package/dist/src/salience.js +74 -0
- package/dist/src/salience.js.map +1 -0
- package/dist/src/scheduler.js +67 -0
- package/dist/src/scheduler.js.map +1 -0
- package/dist/src/scope.js +35 -0
- package/dist/src/scope.js.map +1 -0
- package/dist/src/search.js +801 -0
- package/dist/src/search.js.map +1 -0
- package/dist/src/server-detect.js +70 -0
- package/dist/src/server-detect.js.map +1 -0
- package/dist/src/server.js +784 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/shared.js +309 -0
- package/dist/src/shared.js.map +1 -0
- package/dist/src/sso.js +22 -0
- package/dist/src/sso.js.map +1 -0
- package/dist/src/store.js +1390 -0
- package/dist/src/store.js.map +1 -0
- package/dist/src/tenant.js +17 -0
- package/dist/src/tenant.js.map +1 -0
- package/dist/src/trace.js +64 -0
- package/dist/src/trace.js.map +1 -0
- package/dist/src/working-memory.js +149 -0
- package/dist/src/working-memory.js.map +1 -0
- package/dist/src/yaml.js +98 -0
- package/dist/src/yaml.js.map +1 -0
- package/dist/store.d.ts +9 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +30 -2
- package/dist/store.js.map +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/dist/import.d.ts +0 -31
- package/dist/import.d.ts.map +0 -1
- package/dist/import.js +0 -307
- package/dist/import.js.map +0 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM-powered refinement of consolidated semantic memories.
|
|
3
|
+
*
|
|
4
|
+
* The rule-based `mergeContents` in consolidate.ts produces functional but
|
|
5
|
+
* ugly semantic memories — typically "[Consolidated from N related memories]"
|
|
6
|
+
* prepended to the longest source, or a bulleted list. `hippo refine` takes
|
|
7
|
+
* those and asks Claude to synthesize a clean, generalized principle.
|
|
8
|
+
*
|
|
9
|
+
* Design choices:
|
|
10
|
+
* - Separate command (not baked into `hippo sleep`) so API-key users opt in.
|
|
11
|
+
* - Idempotent via the `llm-refined` tag — re-running skips already-refined.
|
|
12
|
+
* - Uses fetch directly so no SDK dependency.
|
|
13
|
+
* - On failure (API error, bad response), the original memory is untouched.
|
|
14
|
+
*/
|
|
15
|
+
import { Layer } from './memory.js';
|
|
16
|
+
import { loadAllEntries, readEntry, writeEntry } from './store.js';
|
|
17
|
+
const REFINED_TAG = 'llm-refined';
|
|
18
|
+
const CONSOLIDATED_MARKERS = [
|
|
19
|
+
'[Consolidated from',
|
|
20
|
+
'[Consolidated pattern from',
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Ask Claude to synthesize a clean semantic memory from the merged content
|
|
24
|
+
* plus the original source memories. Returns the refined content string or
|
|
25
|
+
* `null` when the API call failed.
|
|
26
|
+
*/
|
|
27
|
+
export async function refineSemanticMemory(merged, sources, opts) {
|
|
28
|
+
const model = opts.model ?? 'claude-sonnet-4-6';
|
|
29
|
+
const fetchFn = opts.fetcher ?? fetch;
|
|
30
|
+
const sourceBlock = sources
|
|
31
|
+
.slice(0, 8)
|
|
32
|
+
.map((s, i) => `[source ${i + 1}] ${s.content.slice(0, 400)}`)
|
|
33
|
+
.join('\n\n');
|
|
34
|
+
const prompt = `You are refining a semantic memory in an agent's memory store. The rule-based consolidator merged several related episodic memories into one, but the output is clumsy. Produce a single coherent semantic memory that captures the underlying principle.
|
|
35
|
+
|
|
36
|
+
Rules:
|
|
37
|
+
- Output ONLY the refined content — no preamble, no quote marks, no "Here is...".
|
|
38
|
+
- Keep it concise: one paragraph, no headers, no bullet lists unless the sources are inherently a list.
|
|
39
|
+
- Preserve specific facts (names, numbers, paths, IDs) from the sources.
|
|
40
|
+
- Generalize: state the pattern, not each instance.
|
|
41
|
+
- Do NOT include the "[Consolidated from N ...]" marker.
|
|
42
|
+
|
|
43
|
+
Current merged content:
|
|
44
|
+
${merged}
|
|
45
|
+
|
|
46
|
+
Source memories (up to 8 shown):
|
|
47
|
+
${sourceBlock}`;
|
|
48
|
+
let res;
|
|
49
|
+
try {
|
|
50
|
+
res = await fetchFn('https://api.anthropic.com/v1/messages', {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: {
|
|
53
|
+
'content-type': 'application/json',
|
|
54
|
+
'x-api-key': opts.apiKey,
|
|
55
|
+
'anthropic-version': '2023-06-01',
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
model,
|
|
59
|
+
max_tokens: 800,
|
|
60
|
+
messages: [{ role: 'user', content: prompt }],
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
if (!res.ok)
|
|
68
|
+
return null;
|
|
69
|
+
try {
|
|
70
|
+
const data = await res.json();
|
|
71
|
+
const text = data.content?.[0]?.text?.trim() ?? '';
|
|
72
|
+
if (text.length < 10)
|
|
73
|
+
return null;
|
|
74
|
+
return text;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function isConsolidated(entry) {
|
|
81
|
+
if (entry.layer !== Layer.Semantic)
|
|
82
|
+
return false;
|
|
83
|
+
return CONSOLIDATED_MARKERS.some((m) => entry.content.startsWith(m));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Scan the store for consolidated semantic memories, refine each with the
|
|
87
|
+
* LLM, and write the refined content back. Tags with `llm-refined` so
|
|
88
|
+
* repeated runs are idempotent (unless `all` is set).
|
|
89
|
+
*/
|
|
90
|
+
export async function refineStore(hippoRoot, opts) {
|
|
91
|
+
const result = {
|
|
92
|
+
scanned: 0,
|
|
93
|
+
refined: 0,
|
|
94
|
+
skipped: 0,
|
|
95
|
+
failed: 0,
|
|
96
|
+
details: [],
|
|
97
|
+
};
|
|
98
|
+
const entries = loadAllEntries(hippoRoot);
|
|
99
|
+
let processed = 0;
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
if (!isConsolidated(entry))
|
|
102
|
+
continue;
|
|
103
|
+
result.scanned++;
|
|
104
|
+
if (!opts.all && entry.tags.includes(REFINED_TAG)) {
|
|
105
|
+
result.skipped++;
|
|
106
|
+
result.details.push({ id: entry.id, status: 'skipped', reason: 'already refined' });
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (opts.limit !== undefined && processed >= opts.limit)
|
|
110
|
+
break;
|
|
111
|
+
processed++;
|
|
112
|
+
// Best-effort: walk parents_json (schema v9) to fetch originals. When
|
|
113
|
+
// parents aren't recorded we still refine using just the merged content.
|
|
114
|
+
const sources = [];
|
|
115
|
+
const parentIds = Array.isArray(entry.parents) ? entry.parents : [];
|
|
116
|
+
for (const pid of parentIds) {
|
|
117
|
+
const p = readEntry(hippoRoot, pid);
|
|
118
|
+
if (p)
|
|
119
|
+
sources.push(p);
|
|
120
|
+
}
|
|
121
|
+
const refined = await refineSemanticMemory(entry.content, sources, {
|
|
122
|
+
apiKey: opts.apiKey,
|
|
123
|
+
model: opts.model,
|
|
124
|
+
fetcher: opts.fetcher,
|
|
125
|
+
});
|
|
126
|
+
if (refined === null) {
|
|
127
|
+
result.failed++;
|
|
128
|
+
result.details.push({ id: entry.id, status: 'failed', reason: 'api error or empty response' });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (opts.dryRun) {
|
|
132
|
+
result.refined++;
|
|
133
|
+
result.details.push({ id: entry.id, status: 'refined', reason: 'dry-run (no write)' });
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const updated = {
|
|
137
|
+
...entry,
|
|
138
|
+
content: refined,
|
|
139
|
+
tags: entry.tags.includes(REFINED_TAG) ? entry.tags : [...entry.tags, REFINED_TAG],
|
|
140
|
+
};
|
|
141
|
+
writeEntry(hippoRoot, updated);
|
|
142
|
+
result.refined++;
|
|
143
|
+
result.details.push({ id: entry.id, status: 'refined' });
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=refine-llm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refine-llm.js","sourceRoot":"","sources":["../../src/refine-llm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay — biologically-inspired rehearsal during consolidation.
|
|
3
|
+
*
|
|
4
|
+
* Hippocampal replay is internally-driven reactivation of memories during
|
|
5
|
+
* slow-wave sleep: the brain picks important recent experiences and "plays
|
|
6
|
+
* them back" without external input, strengthening them before they decay.
|
|
7
|
+
* In McClelland's complementary-learning-systems framing, replay is also
|
|
8
|
+
* how episodic memories train the neocortex (interleaved rehearsal); in
|
|
9
|
+
* reward-modulated STDP, replay is gated by dopamine so rewarded
|
|
10
|
+
* experiences get preferentially consolidated.
|
|
11
|
+
*
|
|
12
|
+
* This module implements a lightweight, deterministic analog. During each
|
|
13
|
+
* `hippo sleep`, we sample N surviving memories by a priority score that
|
|
14
|
+
* weighs reward feedback, emotional valence, under-rehearsal, and age —
|
|
15
|
+
* then apply the same retrieval-strengthening dynamics `markRetrieved`
|
|
16
|
+
* applies to real queries. The effect is that important memories stay
|
|
17
|
+
* strong even when the user hasn't explicitly queried them recently.
|
|
18
|
+
*
|
|
19
|
+
* Distinct from the other consolidation passes:
|
|
20
|
+
* - decay: removes what's too weak
|
|
21
|
+
* - physics: moves particles in embedding space
|
|
22
|
+
* - merge: collapses near-duplicate episodics into semantics
|
|
23
|
+
* - REPLAY: picks winners and rehearses them (this file)
|
|
24
|
+
*/
|
|
25
|
+
const VALENCE_WEIGHT = {
|
|
26
|
+
neutral: 1.0,
|
|
27
|
+
positive: 1.3,
|
|
28
|
+
negative: 1.5,
|
|
29
|
+
critical: 2.0,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Priority score used to rank survivors for replay. Higher = more likely
|
|
33
|
+
* to be sampled. Pure function of the entry and current time.
|
|
34
|
+
*/
|
|
35
|
+
export function replayPriority(entry, now) {
|
|
36
|
+
const pos = entry.outcome_positive ?? 0;
|
|
37
|
+
const neg = entry.outcome_negative ?? 0;
|
|
38
|
+
// Reward signal: neutral memories get 1, strongly-rewarded memories > 1,
|
|
39
|
+
// negative-dominated memories floor at 0.1 (so they're still eligible, just
|
|
40
|
+
// much less likely to be sampled than neutral peers). Clamp is required
|
|
41
|
+
// because sampleForReplay depends on all weights being positive.
|
|
42
|
+
const rewardSignal = Math.max(0.1, 1 + pos * 0.5 + (pos - neg) * 0.25);
|
|
43
|
+
const valence = VALENCE_WEIGHT[entry.emotional_valence] ?? 1.0;
|
|
44
|
+
// Under-rehearsed memories benefit most from replay.
|
|
45
|
+
const underRehearsed = 1 / (1 + (entry.retrieval_count ?? 0));
|
|
46
|
+
// Idle-time boost: memories that haven't been touched recently need rehearsal more.
|
|
47
|
+
const lastRetrieved = new Date(entry.last_retrieved);
|
|
48
|
+
const deltaMs = now.getTime() - lastRetrieved.getTime();
|
|
49
|
+
const ageHours = Number.isFinite(deltaMs) ? Math.max(0, deltaMs / 3_600_000) : 0;
|
|
50
|
+
const idleBoost = 1 + Math.log1p(ageHours) * 0.1;
|
|
51
|
+
// Weight by current strength so dead-and-decaying memories don't waste replay slots.
|
|
52
|
+
const rawStrength = Number.isFinite(entry.strength) ? entry.strength : 0;
|
|
53
|
+
const strengthFloor = Math.max(0.1, rawStrength);
|
|
54
|
+
return rewardSignal * valence * underRehearsed * idleBoost * strengthFloor;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Deterministic 32-bit RNG (Mulberry32). Same seed → same sequence.
|
|
58
|
+
* Keeps replay reproducible for tests and audit runs without bringing
|
|
59
|
+
* in a random-number dependency.
|
|
60
|
+
*/
|
|
61
|
+
function mulberry32(seed) {
|
|
62
|
+
let s = seed >>> 0;
|
|
63
|
+
return () => {
|
|
64
|
+
let t = (s += 0x6D2B79F5);
|
|
65
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
66
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
67
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Pick `count` memories for replay, weighted by `replayPriority`, without
|
|
72
|
+
* replacement. Deterministic given the same seed.
|
|
73
|
+
*
|
|
74
|
+
* The sampler is weighted but not greedy — picking by priority alone would
|
|
75
|
+
* always pick the top-N, which overfits. Biological replay shows both
|
|
76
|
+
* preferential-for-reward AND stochastic-exploration characteristics; we
|
|
77
|
+
* keep the stochastic element so adjacent survivors aren't always ignored.
|
|
78
|
+
*/
|
|
79
|
+
export function sampleForReplay(survivors, count, now, seed = Date.now() >>> 0) {
|
|
80
|
+
if (count <= 0 || survivors.length === 0)
|
|
81
|
+
return [];
|
|
82
|
+
const rng = mulberry32(seed);
|
|
83
|
+
// Stale memories have been deliberately marked as untrusted; rehearsing
|
|
84
|
+
// them would defeat the purpose of staleness. Skip them entirely.
|
|
85
|
+
const eligible = survivors.filter((e) => e.confidence !== 'stale');
|
|
86
|
+
if (eligible.length === 0)
|
|
87
|
+
return [];
|
|
88
|
+
const pool = eligible.map((entry, idx) => ({
|
|
89
|
+
entry,
|
|
90
|
+
idx,
|
|
91
|
+
weight: replayPriority(entry, now),
|
|
92
|
+
}));
|
|
93
|
+
const want = Math.min(count, pool.length);
|
|
94
|
+
const chosen = [];
|
|
95
|
+
const taken = new Set();
|
|
96
|
+
for (let k = 0; k < want; k++) {
|
|
97
|
+
let totalW = 0;
|
|
98
|
+
for (const p of pool)
|
|
99
|
+
if (!taken.has(p.idx))
|
|
100
|
+
totalW += p.weight;
|
|
101
|
+
if (totalW <= 0)
|
|
102
|
+
break;
|
|
103
|
+
let r = rng() * totalW;
|
|
104
|
+
for (const p of pool) {
|
|
105
|
+
if (taken.has(p.idx))
|
|
106
|
+
continue;
|
|
107
|
+
r -= p.weight;
|
|
108
|
+
if (r <= 0) {
|
|
109
|
+
chosen.push(p.entry);
|
|
110
|
+
taken.add(p.idx);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return chosen;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Salience gate — decides at memory creation time whether content is worth
|
|
3
|
+
* storing at full strength, should start weakened, or should be skipped.
|
|
4
|
+
*
|
|
5
|
+
* Inspired by the biological salience network (anterior insula + dACC):
|
|
6
|
+
* not everything that enters working memory deserves long-term storage.
|
|
7
|
+
*/
|
|
8
|
+
import { textOverlap } from './search.js';
|
|
9
|
+
const DEFAULTS = {
|
|
10
|
+
recentWindow: 20,
|
|
11
|
+
overlapThreshold: 0.6,
|
|
12
|
+
minContentLength: 5,
|
|
13
|
+
maxRepeatErrors: 4,
|
|
14
|
+
};
|
|
15
|
+
export function computeSalience(content, tags, recentMemories, options = {}) {
|
|
16
|
+
const opts = { ...DEFAULTS, ...options };
|
|
17
|
+
const trimmed = content.trim();
|
|
18
|
+
const isPinned = tags.includes('pinned');
|
|
19
|
+
if (isPinned) {
|
|
20
|
+
return { decision: 'store', reason: 'pinned', score: 1.0 };
|
|
21
|
+
}
|
|
22
|
+
if (trimmed.length < opts.minContentLength) {
|
|
23
|
+
return { decision: 'skip', reason: 'content_too_short', score: 0 };
|
|
24
|
+
}
|
|
25
|
+
const isError = tags.some(t => t === 'error' || t === 'critical' || t.startsWith('error:'));
|
|
26
|
+
const window = recentMemories.slice(-opts.recentWindow);
|
|
27
|
+
const duplicateMatch = findBestOverlap(trimmed, window);
|
|
28
|
+
if (duplicateMatch.overlap > opts.overlapThreshold) {
|
|
29
|
+
if (isError) {
|
|
30
|
+
const recentErrors = countRecentErrors(window);
|
|
31
|
+
if (recentErrors >= opts.maxRepeatErrors) {
|
|
32
|
+
return {
|
|
33
|
+
decision: 'start_weak',
|
|
34
|
+
reason: `repeat_error (${recentErrors} recent errors, overlap ${(duplicateMatch.overlap * 100).toFixed(0)}% with ${duplicateMatch.matchId})`,
|
|
35
|
+
score: 0.3,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return { decision: 'store', reason: 'error_despite_overlap', score: 0.7 };
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
decision: 'skip',
|
|
42
|
+
reason: `duplicate (${(duplicateMatch.overlap * 100).toFixed(0)}% overlap with ${duplicateMatch.matchId})`,
|
|
43
|
+
score: 0.1,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (isError) {
|
|
47
|
+
return { decision: 'store', reason: 'error_novel', score: 0.9 };
|
|
48
|
+
}
|
|
49
|
+
const hasStructuredTags = tags.some(t => t.startsWith('speaker:') || t.startsWith('topic:') || t.startsWith('scope:'));
|
|
50
|
+
let score = 0.5;
|
|
51
|
+
if (hasStructuredTags)
|
|
52
|
+
score += 0.15;
|
|
53
|
+
if (trimmed.length > 100)
|
|
54
|
+
score += 0.1;
|
|
55
|
+
if (trimmed.length > 300)
|
|
56
|
+
score += 0.1;
|
|
57
|
+
return { decision: 'store', reason: 'novel', score: Math.min(score, 1.0) };
|
|
58
|
+
}
|
|
59
|
+
function findBestOverlap(content, memories) {
|
|
60
|
+
let best = 0;
|
|
61
|
+
let matchId = null;
|
|
62
|
+
for (const m of memories) {
|
|
63
|
+
const overlap = textOverlap(content, m.content);
|
|
64
|
+
if (overlap > best) {
|
|
65
|
+
best = overlap;
|
|
66
|
+
matchId = m.id;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { overlap: best, matchId };
|
|
70
|
+
}
|
|
71
|
+
function countRecentErrors(memories) {
|
|
72
|
+
return memories.filter(m => m.emotional_valence === 'negative' || m.emotional_valence === 'critical').length;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=salience.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"salience.js","sourceRoot":"","sources":["../../src/salience.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
export const DAILY_TASK_NAME = 'hippo-daily-runner';
|
|
4
|
+
function defaultRegistry() {
|
|
5
|
+
return {
|
|
6
|
+
version: 1,
|
|
7
|
+
workspaces: [],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function workspaceRegistryPath(globalRoot) {
|
|
11
|
+
return path.join(globalRoot, 'workspaces.json');
|
|
12
|
+
}
|
|
13
|
+
function normalizeWorkspace(projectDir) {
|
|
14
|
+
return path.resolve(projectDir).replace(/\\/g, '/');
|
|
15
|
+
}
|
|
16
|
+
export function loadWorkspaceRegistry(globalRoot) {
|
|
17
|
+
const registryPath = workspaceRegistryPath(globalRoot);
|
|
18
|
+
if (!fs.existsSync(registryPath))
|
|
19
|
+
return defaultRegistry();
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
22
|
+
const workspaces = Array.isArray(parsed.workspaces)
|
|
23
|
+
? [...new Set(parsed.workspaces.map((entry) => normalizeWorkspace(String(entry))).filter(Boolean))].sort()
|
|
24
|
+
: [];
|
|
25
|
+
return {
|
|
26
|
+
version: 1,
|
|
27
|
+
workspaces,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return defaultRegistry();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function saveWorkspaceRegistry(globalRoot, registry) {
|
|
35
|
+
fs.mkdirSync(globalRoot, { recursive: true });
|
|
36
|
+
fs.writeFileSync(workspaceRegistryPath(globalRoot), JSON.stringify({
|
|
37
|
+
version: 1,
|
|
38
|
+
workspaces: [...new Set(registry.workspaces.map(normalizeWorkspace))].sort(),
|
|
39
|
+
}, null, 2) + '\n', 'utf8');
|
|
40
|
+
}
|
|
41
|
+
export function registerWorkspace(globalRoot, projectDir) {
|
|
42
|
+
const registry = loadWorkspaceRegistry(globalRoot);
|
|
43
|
+
registry.workspaces = [...new Set([...registry.workspaces, normalizeWorkspace(projectDir)])].sort();
|
|
44
|
+
saveWorkspaceRegistry(globalRoot, registry);
|
|
45
|
+
return registry;
|
|
46
|
+
}
|
|
47
|
+
export function listRegisteredWorkspaces(globalRoot) {
|
|
48
|
+
return loadWorkspaceRegistry(globalRoot).workspaces;
|
|
49
|
+
}
|
|
50
|
+
export function buildDailyRunnerCommand(projectDir, platform = process.platform) {
|
|
51
|
+
if (platform === 'win32') {
|
|
52
|
+
const resolved = path.win32.resolve(projectDir).replace(/\\/g, '/');
|
|
53
|
+
return `cd /d "${resolved}" && hippo daily-runner`;
|
|
54
|
+
}
|
|
55
|
+
const resolved = path.posix.resolve(projectDir.replace(/\\/g, '/'));
|
|
56
|
+
return `cd "${resolved}" && hippo daily-runner`;
|
|
57
|
+
}
|
|
58
|
+
export function runDailyMaintenance(workspaces, runCommand) {
|
|
59
|
+
for (const workspace of workspaces) {
|
|
60
|
+
const resolved = normalizeWorkspace(workspace);
|
|
61
|
+
if (!fs.existsSync(path.join(resolved, '.hippo')))
|
|
62
|
+
continue;
|
|
63
|
+
runCommand(resolved, ['learn', '--git', '--days', '1']);
|
|
64
|
+
runCommand(resolved, ['sleep']);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect the current operational scope from environment signals.
|
|
3
|
+
* Returns a scope name string (no prefix), or null if no scope detected.
|
|
4
|
+
* Priority: HIPPO_SCOPE > GSTACK_SKILL > OPENCLAW_SKILL.
|
|
5
|
+
*
|
|
6
|
+
* Pure env var reads: no I/O, safe to call from hot paths (e.g. UserPromptSubmit hook).
|
|
7
|
+
*/
|
|
8
|
+
export function detectScope() {
|
|
9
|
+
const explicit = process.env['HIPPO_SCOPE'];
|
|
10
|
+
if (explicit && explicit.trim())
|
|
11
|
+
return explicit.trim();
|
|
12
|
+
const gstackSkill = process.env['GSTACK_SKILL'];
|
|
13
|
+
if (gstackSkill && gstackSkill.trim())
|
|
14
|
+
return gstackSkill.trim();
|
|
15
|
+
const openclawSkill = process.env['OPENCLAW_SKILL'];
|
|
16
|
+
if (openclawSkill && openclawSkill.trim())
|
|
17
|
+
return openclawSkill.trim();
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Compute scope match between a memory's scope tags and the active scope.
|
|
22
|
+
* Returns: 1 if matching scope, -1 if mismatching scope, 0 if neutral (no scope on either side).
|
|
23
|
+
*/
|
|
24
|
+
export function scopeMatch(memoryTags, activeScope) {
|
|
25
|
+
const scopeTags = memoryTags.filter(t => t.startsWith('scope:'));
|
|
26
|
+
if (scopeTags.length === 0)
|
|
27
|
+
return 0; // memory has no scope: always neutral
|
|
28
|
+
if (!activeScope)
|
|
29
|
+
return 0; // no active scope: everything neutral
|
|
30
|
+
const scopeKey = `scope:${activeScope}`;
|
|
31
|
+
if (scopeTags.includes(scopeKey))
|
|
32
|
+
return 1; // match
|
|
33
|
+
return -1; // mismatch
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/scope.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC5C,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE;QAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IAExD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChD,IAAI,WAAW,IAAI,WAAW,CAAC,IAAI,EAAE;QAAE,OAAO,WAAW,CAAC,IAAI,EAAE,CAAC;IAEjE,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACpD,IAAI,aAAa,IAAI,aAAa,CAAC,IAAI,EAAE;QAAE,OAAO,aAAa,CAAC,IAAI,EAAE,CAAC;IAEvE,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,UAAoB,EAAE,WAA0B;IACzE,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC,CAAE,sCAAsC;IAC7E,IAAI,CAAC,WAAW;QAAE,OAAO,CAAC,CAAC,CAAY,sCAAsC;IAC7E,MAAM,QAAQ,GAAG,SAAS,WAAW,EAAE,CAAC;IACxC,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,CAAC,CAAG,QAAQ;IACtD,OAAO,CAAC,CAAC,CAAC,CAAuC,WAAW;AAC9D,CAAC"}
|