@psiclawops/hypermem 0.8.4 → 0.9.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/INSTALL.md +203 -23
  3. package/README.md +139 -216
  4. package/bench/README.md +42 -0
  5. package/bench/data-access-bench.mjs +380 -0
  6. package/bin/hypermem-bench.mjs +2 -0
  7. package/bin/hypermem-doctor.mjs +412 -0
  8. package/bin/hypermem-model-audit.mjs +339 -0
  9. package/bin/hypermem-status.mjs +491 -70
  10. package/dist/adaptive-lifecycle.d.ts +81 -0
  11. package/dist/adaptive-lifecycle.d.ts.map +1 -0
  12. package/dist/adaptive-lifecycle.js +190 -0
  13. package/dist/background-indexer.js +9 -9
  14. package/dist/budget-policy.d.ts +1 -1
  15. package/dist/budget-policy.d.ts.map +1 -1
  16. package/dist/budget-policy.js +10 -5
  17. package/dist/cache.d.ts +4 -0
  18. package/dist/cache.d.ts.map +1 -1
  19. package/dist/cache.js +2 -0
  20. package/dist/composition-snapshot-integrity.d.ts +36 -0
  21. package/dist/composition-snapshot-integrity.d.ts.map +1 -0
  22. package/dist/composition-snapshot-integrity.js +131 -0
  23. package/dist/composition-snapshot-runtime.d.ts +59 -0
  24. package/dist/composition-snapshot-runtime.d.ts.map +1 -0
  25. package/dist/composition-snapshot-runtime.js +250 -0
  26. package/dist/composition-snapshot-store.d.ts +44 -0
  27. package/dist/composition-snapshot-store.d.ts.map +1 -0
  28. package/dist/composition-snapshot-store.js +117 -0
  29. package/dist/compositor.d.ts +125 -1
  30. package/dist/compositor.d.ts.map +1 -1
  31. package/dist/compositor.js +692 -44
  32. package/dist/cross-agent.d.ts +1 -1
  33. package/dist/cross-agent.js +17 -17
  34. package/dist/doc-chunk-store.d.ts +19 -0
  35. package/dist/doc-chunk-store.d.ts.map +1 -1
  36. package/dist/doc-chunk-store.js +56 -6
  37. package/dist/dreaming-promoter.d.ts +1 -1
  38. package/dist/dreaming-promoter.js +2 -2
  39. package/dist/hybrid-retrieval.d.ts +38 -0
  40. package/dist/hybrid-retrieval.d.ts.map +1 -1
  41. package/dist/hybrid-retrieval.js +86 -1
  42. package/dist/index.d.ts +15 -6
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +33 -7
  45. package/dist/knowledge-store.d.ts +4 -1
  46. package/dist/knowledge-store.d.ts.map +1 -1
  47. package/dist/knowledge-store.js +27 -4
  48. package/dist/library-schema.d.ts +12 -8
  49. package/dist/library-schema.d.ts.map +1 -1
  50. package/dist/library-schema.js +22 -8
  51. package/dist/message-store.d.ts.map +1 -1
  52. package/dist/message-store.js +7 -3
  53. package/dist/metrics-dashboard.d.ts +18 -1
  54. package/dist/metrics-dashboard.d.ts.map +1 -1
  55. package/dist/metrics-dashboard.js +52 -14
  56. package/dist/reranker.d.ts +1 -1
  57. package/dist/reranker.js +2 -2
  58. package/dist/schema.d.ts +1 -1
  59. package/dist/schema.d.ts.map +1 -1
  60. package/dist/schema.js +28 -1
  61. package/dist/seed.d.ts +1 -1
  62. package/dist/seed.d.ts.map +1 -1
  63. package/dist/seed.js +3 -1
  64. package/dist/session-flusher.d.ts +2 -2
  65. package/dist/session-flusher.js +2 -2
  66. package/dist/spawn-context.d.ts +1 -1
  67. package/dist/spawn-context.js +1 -1
  68. package/dist/topic-store.js +5 -5
  69. package/dist/topic-synthesizer.d.ts +20 -0
  70. package/dist/topic-synthesizer.d.ts.map +1 -1
  71. package/dist/topic-synthesizer.js +114 -4
  72. package/dist/trigger-registry.d.ts +1 -1
  73. package/dist/trigger-registry.d.ts.map +1 -1
  74. package/dist/trigger-registry.js +14 -6
  75. package/dist/types.d.ts +273 -3
  76. package/dist/types.d.ts.map +1 -1
  77. package/dist/version.d.ts +7 -7
  78. package/dist/version.d.ts.map +1 -1
  79. package/dist/version.js +17 -7
  80. package/docs/DIAGNOSTICS.md +205 -0
  81. package/docs/INTEGRATION_VALIDATION.md +186 -0
  82. package/docs/MIGRATION.md +9 -6
  83. package/docs/MIGRATION_GUIDE.md +125 -101
  84. package/docs/ROADMAP.md +238 -20
  85. package/docs/TUNING.md +30 -6
  86. package/install.sh +159 -408
  87. package/memory-plugin/LICENSE +190 -0
  88. package/memory-plugin/README.md +20 -0
  89. package/memory-plugin/dist/index.js +50 -0
  90. package/memory-plugin/package.json +2 -2
  91. package/package.json +18 -4
  92. package/plugin/LICENSE +190 -0
  93. package/plugin/README.md +20 -0
  94. package/plugin/dist/index.d.ts +55 -0
  95. package/plugin/dist/index.d.ts.map +1 -1
  96. package/plugin/dist/index.js +362 -42
  97. package/plugin/dist/index.js.map +1 -1
  98. package/plugin/package.json +2 -2
  99. package/scripts/install-runtime.mjs +13 -3
package/install.sh CHANGED
@@ -1,11 +1,8 @@
1
1
  #!/usr/bin/env bash
2
- # HyperMem Installer
2
+ # HyperMem npm-first installer and upgrader
3
3
  # curl -fsSL https://raw.githubusercontent.com/PsiClawOps/hypermem/main/install.sh | bash
4
4
  set -euo pipefail
5
5
 
6
- # ─────────────────────────────────────────────
7
- # Colors
8
- # ─────────────────────────────────────────────
9
6
  RED='\033[0;31m'
10
7
  GREEN='\033[0;32m'
11
8
  YELLOW='\033[1;33m'
@@ -14,307 +11,144 @@ BOLD='\033[1m'
14
11
  DIM='\033[2m'
15
12
  NC='\033[0m'
16
13
 
17
- # ─────────────────────────────────────────────
18
- # Banner
19
- # ─────────────────────────────────────────────
20
- banner() {
21
- echo ""
22
- echo -e "${CYAN}${BOLD}"
23
- echo " ██╗ ██╗██╗ ██╗██████╗ ███████╗██████╗ ███╗ ███╗███████╗███╗ ███╗"
24
- echo " ██║ ██║╚██╗ ██╔╝██╔══██╗██╔════╝██╔══██╗████╗ ████║██╔════╝████╗ ████║"
25
- echo " ███████║ ╚████╔╝ ██████╔╝█████╗ ██████╔╝██╔████╔██║█████╗ ██╔████╔██║"
26
- echo " ██╔══██║ ╚██╔╝ ██╔═══╝ ██╔══╝ ██╔══██╗██║╚██╔╝██║██╔══╝ ██║╚██╔╝██║"
27
- echo " ██║ ██║ ██║ ██║ ███████╗██║ ██║██║ ╚═╝ ██║███████╗██║ ╚═╝ ██║"
28
- echo " ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝"
29
- echo -e "${NC}"
30
- echo -e " ${DIM}The memory layer for OpenClaw agents${NC}"
31
- echo ""
14
+ PACKAGE="${HYPERMEM_PACKAGE:-@psiclawops/hypermem@latest}"
15
+ INSTALL_DIR="${HYPERMEM_INSTALL_DIR:-$HOME/.hypermem}"
16
+ RUNTIME_DIR="${HYPERMEM_RUNTIME_DIR:-$HOME/.openclaw/plugins/hypermem}"
17
+ CONFIG_FILE="$HOME/.openclaw/hypermem/config.json"
18
+ ASSUME_YES=false
19
+ SKIP_NPM=false
20
+ SKIP_STAGE=false
21
+
22
+ usage() {
23
+ cat <<EOF
24
+ HyperMem installer
25
+
26
+ Usage: install.sh [options]
27
+
28
+ Options:
29
+ --yes non-interactive defaults
30
+ --package <spec> npm package spec, default: @psiclawops/hypermem@latest
31
+ --install-dir <p> npm install directory, default: ~/.hypermem
32
+ --runtime-dir <p> staged OpenClaw runtime dir, default: ~/.openclaw/plugins/hypermem
33
+ --skip-npm use existing package in install dir
34
+ --skip-stage install package only, do not run hypermem-install
35
+ --help show this help
36
+
37
+ Environment overrides:
38
+ HYPERMEM_PACKAGE, HYPERMEM_INSTALL_DIR, HYPERMEM_RUNTIME_DIR
39
+
40
+ This script stages HyperMem. It does not edit OpenClaw config and does not restart the gateway.
41
+ EOF
32
42
  }
33
43
 
34
- # ─────────────────────────────────────────────
35
- # Helpers
36
- # ─────────────────────────────────────────────
37
- info() { echo -e " ${CYAN}→${NC} $*"; }
38
- success() { echo -e " ${GREEN}✓${NC} $*"; }
39
- warn() { echo -e " ${YELLOW}⚠${NC} $*"; }
40
- error() { echo -e " ${RED}✗${NC} $*" >&2; }
41
- die() { error "$*"; exit 1; }
44
+ while [[ $# -gt 0 ]]; do
45
+ case "$1" in
46
+ --yes|-y) ASSUME_YES=true; shift ;;
47
+ --package) PACKAGE="$2"; shift 2 ;;
48
+ --install-dir) INSTALL_DIR="$2"; shift 2 ;;
49
+ --runtime-dir) RUNTIME_DIR="$2"; shift 2 ;;
50
+ --skip-npm) SKIP_NPM=true; shift ;;
51
+ --skip-stage) SKIP_STAGE=true; shift ;;
52
+ --help|-h) usage; exit 0 ;;
53
+ *) echo -e "${RED}Unknown option:${NC} $1" >&2; usage; exit 1 ;;
54
+ esac
55
+ done
42
56
 
43
- prompt() {
44
- # prompt <var_name> <question> [default]
45
- local var="$1" question="$2" default="${3:-}"
46
- if [[ -n "$default" ]]; then
47
- echo -ne " ${BOLD}${question}${NC} ${DIM}[${default}]${NC} "
48
- else
49
- echo -ne " ${BOLD}${question}${NC} "
50
- fi
51
- read -r reply </dev/tty || { [[ -n "$default" ]] && reply="$default" || die "Cannot read input (not a terminal?). Run: bash <(curl -fsSL ...) instead."; }
52
- [[ -z "$reply" && -n "$default" ]] && reply="$default"
53
- printf -v "$var" '%s' "$reply"
54
- }
57
+ info() { echo -e " ${CYAN}→${NC} $*"; }
58
+ success() { echo -e " ${GREEN}✓${NC} $*"; }
59
+ warn() { echo -e " ${YELLOW}⚠${NC} $*"; }
60
+ die() { echo -e " ${RED}✗${NC} $*" >&2; exit 1; }
55
61
 
56
62
  confirm() {
57
- # confirm <question> returns 0 for yes, 1 for no
63
+ if $ASSUME_YES; then return 0; fi
58
64
  echo -ne " ${BOLD}$1${NC} ${DIM}[y/N]${NC} "
59
65
  read -r reply </dev/tty || return 1
60
66
  [[ "$reply" =~ ^[Yy] ]]
61
67
  }
62
68
 
63
- # ─────────────────────────────────────────────
64
- # Preflight
65
- # ─────────────────────────────────────────────
66
- preflight() {
67
- echo -e "\n${BOLD} Preflight checks${NC}"
68
-
69
- # bash version
70
- if (( BASH_VERSINFO[0] < 4 )); then
71
- die "bash 4+ required (you have $BASH_VERSION)"
72
- fi
73
- success "bash $BASH_VERSION"
74
-
75
- # curl
76
- command -v curl &>/dev/null || die "curl is required"
77
- success "curl $(curl --version | head -1 | awk '{print $2}')"
78
-
79
- # git (optional — only needed for dev installs)
80
-
81
- # node
82
- command -v node &>/dev/null || die "Node.js is required (v22+)"
83
- NODE_VERSION=$(node --version | sed 's/v//')
84
- NODE_MAJOR=$(echo "$NODE_VERSION" | cut -d. -f1)
85
- (( NODE_MAJOR >= 22 )) || die "Node.js v22+ required (you have v$NODE_VERSION — HyperMem requires v22+)"
86
- success "node v$NODE_VERSION"
87
-
88
- # npm
89
- command -v npm &>/dev/null || die "npm is required"
90
- success "npm $(npm --version)"
69
+ banner() {
70
+ echo ""
71
+ echo -e "${CYAN}${BOLD} HyperMem installer${NC}"
72
+ echo -e " ${DIM}npm package install + OpenClaw runtime staging${NC}"
73
+ echo ""
91
74
  }
92
75
 
93
- # ─────────────────────────────────────────────
94
- # Hardware detection
95
- # ─────────────────────────────────────────────
96
- detect_hardware() {
97
- echo -e "\n${BOLD} Detecting hardware${NC}"
98
-
99
- HAS_NVIDIA=false
100
- HAS_AMD=false
101
- HAS_OLLAMA=false
102
- HAS_API_KEY=false
103
- DETECTED_TIER=1
104
-
105
- # NVIDIA GPU
106
- if command -v nvidia-smi &>/dev/null; then
107
- GPU_NAME=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 || echo "unknown")
108
- HAS_NVIDIA=true
109
- success "NVIDIA GPU: $GPU_NAME"
110
- else
111
- info "No NVIDIA GPU detected"
112
- fi
113
-
114
- # AMD GPU
115
- if command -v rocm-smi &>/dev/null || lspci 2>/dev/null | grep -qi 'amd.*display\|radeon'; then
116
- HAS_AMD=true
117
- success "AMD GPU detected"
118
- fi
119
-
120
- # Ollama
121
- if command -v ollama &>/dev/null; then
122
- OLLAMA_VERSION=$(ollama --version 2>/dev/null || echo "unknown")
123
- HAS_OLLAMA=true
124
- success "Ollama: $OLLAMA_VERSION"
125
- else
126
- info "Ollama not found"
127
- fi
128
-
129
- # API key (OpenRouter or OpenAI-compatible) — informational only, does NOT affect recommendation
130
- if [[ -n "${OPENROUTER_API_KEY:-}" || -n "${HYPERMEM_EMBED_API_KEY:-}" ]]; then
131
- HAS_API_KEY=true
132
- info "Embedding API key detected (Tier 4 available)"
133
- else
134
- info "No embedding API key in environment"
135
- fi
76
+ preflight() {
77
+ echo -e "${BOLD} Preflight${NC}"
78
+ command -v node >/dev/null 2>&1 || die "Node.js v22+ is required"
79
+ local node_version node_major
80
+ node_version="$(node --version | sed 's/^v//')"
81
+ node_major="${node_version%%.*}"
82
+ [[ "$node_major" =~ ^[0-9]+$ ]] || die "Cannot parse Node.js version: $node_version"
83
+ (( node_major >= 22 )) || die "Node.js v22+ required, found v$node_version"
84
+ success "node v$node_version"
85
+
86
+ command -v npm >/dev/null 2>&1 || die "npm is required"
87
+ success "npm $(npm --version)"
136
88
 
137
- # Recommend tier based on hardware only — operator opts into Tier 4 explicitly
138
- if $HAS_OLLAMA && ($HAS_NVIDIA || $HAS_AMD); then
139
- DETECTED_TIER=3
140
- elif $HAS_NVIDIA || $HAS_AMD; then
141
- # GPU present but no Ollama — recommend Tier 2, note Tier 3 needs Ollama
142
- DETECTED_TIER=2
89
+ if command -v openclaw >/dev/null 2>&1; then
90
+ success "openclaw CLI found"
91
+ openclaw gateway status >/dev/null 2>&1 || warn "OpenClaw gateway is not running or not onboarded yet. Complete OpenClaw setup before activation."
143
92
  else
144
- # CPU-only or unknown Tier 2 runs everywhere via WASM, no Ollama needed
145
- DETECTED_TIER=2
93
+ warn "openclaw CLI not found. HyperMem can be staged now, but activation requires OpenClaw."
146
94
  fi
147
95
  }
148
96
 
149
- # ─────────────────────────────────────────────
150
- # Tier selection
151
- # ─────────────────────────────────────────────
152
- select_tier() {
153
- echo -e "\n${BOLD} Memory tier selection${NC}\n"
154
-
155
- echo -e " ${DIM}Choose how HyperMem handles semantic memory retrieval:${NC}\n"
156
-
157
- echo -e " ${BOLD}1)${NC} ${GREEN}FTS5 + BM25${NC} ${DIM}(Tier 1 — keyword search only)${NC}"
158
- echo -e " No embedder. Fast, zero extra dependencies."
159
- echo -e " Best for: minimal setups, very low spec hardware.\n"
160
-
161
- echo -e " ${BOLD}2)${NC} ${GREEN}MiniLM-L6-v2${NC} ${DIM}(Tier 2 — lightweight semantic)${NC}"
162
- echo -e " 384-dimension embedder, runs in Node via WASM. No GPU, no Ollama."
163
- echo -e " Best for: CPU-only servers, Raspberry Pi, low-memory VMs.\n"
164
-
165
- echo -e " ${BOLD}3)${NC} ${GREEN}nomic-embed-text${NC} ${DIM}(Tier 3 — GPU-accelerated local)${NC}"
166
- echo -e " 768-dimension embedder via Ollama. GPU strongly recommended."
167
- echo -e " Best for: local workstations with a GPU, self-hosted setups.\n"
168
-
169
- echo -e " ${BOLD}4)${NC} ${GREEN}qwen3-embedding:8b${NC} ${DIM}(Tier 4 — API, top quality)${NC}"
170
- echo -e " 4096-dimension embedder via OpenRouter (or any OpenAI-compatible API)."
171
- echo -e " Best for: production deployments, highest retrieval quality.\n"
172
-
173
- echo -e " ${DIM}Recommended for your hardware: ${NC}${BOLD}Tier ${DETECTED_TIER}${NC}"
174
- $HAS_API_KEY && echo -e " ${DIM}(Tier 4 also available — API key detected in environment)${NC}"
175
- echo ""
176
-
177
- while true; do
178
- prompt TIER_INPUT "Select tier (1-4):" "$DETECTED_TIER"
179
- if [[ "$TIER_INPUT" =~ ^[1-4]$ ]]; then
180
- SELECTED_TIER="$TIER_INPUT"
181
- break
182
- fi
183
- warn "Enter a number between 1 and 4"
184
- done
185
-
186
- echo ""
187
- case "$SELECTED_TIER" in
188
- 1) success "Tier 1: FTS5+BM25 — no embedder needed" ;;
189
- 2) success "Tier 2: MiniLM-L6-v2 via @huggingface/transformers" ;;
190
- 3) success "Tier 3: nomic-embed-text via Ollama" ;;
191
- 4) success "Tier 4: qwen3-embedding:8b via OpenRouter" ;;
192
- esac
193
- }
194
-
195
- # ─────────────────────────────────────────────
196
- # Install HyperMem
197
- # ─────────────────────────────────────────────
198
- INSTALL_DIR="${HYPERMEM_INSTALL_DIR:-$HOME/.hypermem}"
199
-
200
- install_hypermem() {
201
- echo -e "\n${BOLD} Installing HyperMem${NC}"
202
-
97
+ install_package() {
98
+ echo -e "\n${BOLD} Package install${NC}"
203
99
  mkdir -p "$INSTALL_DIR"
204
-
205
- # Initialize package.json if this is a fresh install
206
100
  if [[ ! -f "$INSTALL_DIR/package.json" ]]; then
207
- info "Initializing install directory..."
208
- npm --prefix "$INSTALL_DIR" init -y --silent 2>/dev/null
101
+ info "initializing $INSTALL_DIR"
102
+ npm --prefix "$INSTALL_DIR" init -y --silent >/dev/null
209
103
  fi
210
104
 
211
- if [[ -d "$INSTALL_DIR/node_modules/@psiclawops/hypermem" ]]; then
212
- if confirm "HyperMem already found at $INSTALL_DIR update it?"; then
213
- info "Updating packages..."
214
- npm --prefix "$INSTALL_DIR" install --silent @psiclawops/hypermem@latest @psiclawops/hypercompositor@latest @psiclawops/hypermem-memory@latest
215
- else
216
- info "Using existing installation"
217
- fi
218
- else
219
- info "Installing @psiclawops/hypermem..."
220
- npm --prefix "$INSTALL_DIR" install --silent @psiclawops/hypermem@latest
221
-
222
- info "Installing @psiclawops/hypercompositor..."
223
- npm --prefix "$INSTALL_DIR" install --silent @psiclawops/hypercompositor@latest
224
-
225
- info "Installing @psiclawops/hypermem-memory..."
226
- npm --prefix "$INSTALL_DIR" install --silent @psiclawops/hypermem-memory@latest
105
+ if $SKIP_NPM; then
106
+ [[ -d "$INSTALL_DIR/node_modules/@psiclawops/hypermem" ]] || die "--skip-npm requested but package is missing in $INSTALL_DIR"
107
+ success "using existing package in $INSTALL_DIR"
108
+ return
227
109
  fi
228
110
 
229
- # Convenience vars for plugin registration
230
- PLUGIN_DIR="$INSTALL_DIR/node_modules/@psiclawops/hypercompositor"
231
- MEMORY_PLUGIN_DIR="$INSTALL_DIR/node_modules/@psiclawops/hypermem-memory"
232
-
233
- success "HyperMem installed at $INSTALL_DIR"
111
+ info "installing $PACKAGE"
112
+ npm --prefix "$INSTALL_DIR" install --silent "$PACKAGE"
113
+ success "package installed in $INSTALL_DIR"
234
114
  }
235
115
 
236
- # ─────────────────────────────────────────────
237
- # Tier-specific setup
238
- # ─────────────────────────────────────────────
239
- setup_tier() {
240
- echo -e "\n${BOLD} Setting up Tier ${SELECTED_TIER}${NC}"
241
-
242
- case "$SELECTED_TIER" in
243
- 1)
244
- # Nothing to install
245
- EMBED_PROVIDER="none"
246
- EMBED_MODEL="none"
247
- EMBED_DIMS=0
248
- success "No embedder required"
249
- ;;
250
-
251
- 2)
252
- info "Installing @huggingface/transformers (WASM embedder)..."
253
- npm --prefix "$INSTALL_DIR" install --silent @huggingface/transformers@3
254
- EMBED_PROVIDER="transformers"
255
- EMBED_MODEL="Xenova/all-MiniLM-L6-v2"
256
- EMBED_DIMS=384
257
- success "MiniLM-L6-v2 will download on first use (~90MB)"
258
- ;;
259
-
260
- 3)
261
- if ! command -v ollama &>/dev/null; then
262
- die "Ollama is required for Tier 3. Install it from https://ollama.com then re-run."
263
- fi
264
- info "Pulling nomic-embed-text via Ollama..."
265
- ollama pull nomic-embed-text
266
- EMBED_PROVIDER="ollama"
267
- EMBED_MODEL="nomic-embed-text"
268
- EMBED_DIMS=768
269
- success "nomic-embed-text ready"
270
- ;;
271
-
272
- 4)
273
- # Get API key
274
- API_KEY="${OPENROUTER_API_KEY:-${HYPERMEM_EMBED_API_KEY:-}}"
275
- if [[ -z "$API_KEY" ]]; then
276
- echo ""
277
- echo -e " ${DIM}Get a key at https://openrouter.ai/keys${NC}"
278
- prompt API_KEY "OpenRouter API key:"
279
- [[ -z "$API_KEY" ]] && die "API key required for Tier 4"
280
- else
281
- success "Using API key from environment"
282
- fi
283
- EMBED_PROVIDER="openai"
284
- EMBED_MODEL="qwen/qwen3-embedding:8b"
285
- EMBED_DIMS=4096
286
- EMBED_API_KEY="$API_KEY"
287
- EMBED_BASE_URL="https://openrouter.ai/api/v1"
288
- success "Tier 4 configured — qwen3-embedding:8b via OpenRouter"
289
- ;;
290
- esac
116
+ backup_runtime() {
117
+ [[ -e "$RUNTIME_DIR" ]] || return 0
118
+ local backup
119
+ backup="${RUNTIME_DIR}.backup.$(date +%Y%m%d-%H%M%S)"
120
+ if confirm "Existing runtime found at $RUNTIME_DIR. Back it up before replacing?"; then
121
+ cp -a "$RUNTIME_DIR" "$backup"
122
+ success "backup written to $backup"
123
+ else
124
+ warn "continuing without runtime backup"
125
+ fi
291
126
  }
292
127
 
293
- # ─────────────────────────────────────────────
294
- # Write config
295
- # ─────────────────────────────────────────────
296
- write_config() {
297
- echo -e "\n${BOLD} Writing config${NC}"
128
+ stage_runtime() {
129
+ echo -e "\n${BOLD} Runtime staging${NC}"
130
+ if $SKIP_STAGE; then
131
+ warn "runtime staging skipped"
132
+ return
133
+ fi
298
134
 
299
- CONFIG_DIR="$HOME/.openclaw/hypermem"
300
- mkdir -p "$CONFIG_DIR"
301
- CONFIG_FILE="$CONFIG_DIR/config.json"
135
+ local installer="$INSTALL_DIR/node_modules/@psiclawops/hypermem/scripts/install-runtime.mjs"
136
+ [[ -f "$installer" ]] || die "missing runtime installer: $installer"
137
+ backup_runtime
138
+ node "$installer" "$RUNTIME_DIR"
139
+ success "runtime staged to $RUNTIME_DIR"
140
+ }
302
141
 
303
- # Build embedding block
304
- if [[ "$SELECTED_TIER" == "1" ]]; then
305
- EMBED_BLOCK='"embedding": { "provider": "none" }'
306
- elif [[ "$SELECTED_TIER" == "2" ]]; then
307
- EMBED_BLOCK="\"embedding\": { \"provider\": \"transformers\", \"model\": \"$EMBED_MODEL\", \"dimensions\": $EMBED_DIMS }"
308
- elif [[ "$SELECTED_TIER" == "3" ]]; then
309
- EMBED_BLOCK="\"embedding\": { \"provider\": \"ollama\", \"model\": \"$EMBED_MODEL\", \"dimensions\": $EMBED_DIMS, \"ollamaUrl\": \"http://localhost:11434\" }"
310
- else
311
- EMBED_BLOCK="\"embedding\": { \"provider\": \"openai\", \"model\": \"$EMBED_MODEL\", \"dimensions\": $EMBED_DIMS, \"openaiBaseUrl\": \"$EMBED_BASE_URL\", \"openaiApiKey\": \"$EMBED_API_KEY\" }"
142
+ write_minimal_config_if_missing() {
143
+ echo -e "\n${BOLD} Config check${NC}"
144
+ if [[ -f "$CONFIG_FILE" ]]; then
145
+ success "existing config preserved: $CONFIG_FILE"
146
+ return
312
147
  fi
313
148
 
314
- cat > "$CONFIG_FILE" <<EOF
149
+ mkdir -p "$(dirname "$CONFIG_FILE")"
150
+ cat > "$CONFIG_FILE" <<'JSON'
315
151
  {
316
- "installDir": "$INSTALL_DIR",
317
- "tier": $SELECTED_TIER,
318
152
  "contextWindowSize": 128000,
319
153
  "contextWindowReserve": 0.25,
320
154
  "deferToolPruning": false,
@@ -322,32 +156,35 @@ write_config() {
322
156
  "contextWindowOverrides": {},
323
157
  "warmCacheReplayThresholdMs": 120000,
324
158
  "subagentWarming": "light",
159
+ "embedding": {
160
+ "provider": "none"
161
+ },
325
162
  "compositor": {
326
- "budgetFraction": 0.703,
163
+ "budgetFraction": 0.55,
327
164
  "reserveFraction": 0.25,
328
165
  "historyFraction": 0.4,
329
166
  "memoryFraction": 0.4,
330
167
  "defaultTokenBudget": 90000,
331
168
  "maxHistoryMessages": 500,
332
- "maxFacts": 30,
169
+ "maxFacts": 25,
333
170
  "maxExpertisePatterns": 6,
334
171
  "maxCrossSessionContext": 4000,
335
172
  "maxTotalTriggerTokens": 4000,
336
173
  "maxRecentToolPairs": 3,
337
174
  "maxProseToolPairs": 10,
338
- "warmHistoryBudgetFraction": 0.4,
175
+ "warmHistoryBudgetFraction": 0.27,
339
176
  "contextWindowReserve": 0.25,
340
177
  "dynamicReserveTurnHorizon": 5,
341
178
  "dynamicReserveMax": 0.5,
342
179
  "dynamicReserveEnabled": true,
343
- "keystoneHistoryFraction": 0.2,
344
- "keystoneMaxMessages": 15,
180
+ "keystoneHistoryFraction": 0.15,
181
+ "keystoneMaxMessages": 12,
345
182
  "keystoneMinSignificance": 0.5,
346
- "targetBudgetFraction": 0.65,
183
+ "targetBudgetFraction": 0.50,
347
184
  "enableFOS": true,
348
185
  "enableMOD": true,
349
186
  "hyperformProfile": "standard",
350
- "wikiTokenCap": 600,
187
+ "wikiTokenCap": 500,
351
188
  "zigzagOrdering": true
352
189
  },
353
190
  "eviction": {
@@ -363,154 +200,68 @@ write_config() {
363
200
  "recentConversationCooldownMs": 30000,
364
201
  "maxCandidatesPerPass": 200
365
202
  },
366
- $EMBED_BLOCK,
367
203
  "vectorStore": {
368
- "enabled": $([ "$SELECTED_TIER" -gt 1 ] && echo true || echo false)
204
+ "enabled": false
369
205
  }
370
206
  }
371
- EOF
372
-
373
- success "Config written to $CONFIG_FILE"
374
- }
375
-
376
- # ─────────────────────────────────────────────
377
- # OpenClaw plugin registration
378
- # ─────────────────────────────────────────────
379
- register_plugin() {
380
- if ! command -v openclaw &>/dev/null; then
381
- warn "OpenClaw CLI not found, skipping plugin registration"
382
- echo -e " ${DIM}Run these manually after installing OpenClaw:${NC}"
383
- echo -e " ${DIM} openclaw plugins install file:$PLUGIN_DIR${NC}"
384
- echo -e " ${DIM} openclaw plugins install file:$MEMORY_PLUGIN_DIR${NC}"
385
- return
386
- fi
387
-
388
- echo ""
389
- if confirm "Register HyperMem plugins with OpenClaw?"; then
390
- # Context engine plugin (hypercompositor)
391
- info "Registering context engine plugin (hypercompositor)..."
392
- if openclaw plugins install "file:$PLUGIN_DIR" 2>/dev/null; then
393
- success "hypercompositor registered"
394
- else
395
- warn "Context engine registration failed — run: openclaw plugins install file:$PLUGIN_DIR"
396
- fi
397
-
398
- # Memory plugin (hypermem)
399
- info "Registering memory plugin (hypermem)..."
400
- if openclaw plugins install "file:$MEMORY_PLUGIN_DIR" 2>/dev/null; then
401
- success "hypermem registered"
402
- else
403
- warn "Memory plugin registration failed — run: openclaw plugins install file:$MEMORY_PLUGIN_DIR"
404
- fi
405
-
406
- # Configure plugin slots
407
- info "Configuring plugin slots..."
408
- local SLOT_OK=true
409
- openclaw config set plugins.slots.contextEngine hypercompositor 2>/dev/null || SLOT_OK=false
410
- openclaw config set plugins.slots.memory hypermem 2>/dev/null || SLOT_OK=false
411
- if $SLOT_OK; then
412
- success "Plugin slots configured"
413
- else
414
- warn "Slot config failed — set manually:"
415
- echo -e " ${DIM} openclaw config set plugins.slots.contextEngine hypercompositor${NC}"
416
- echo -e " ${DIM} openclaw config set plugins.slots.memory hypermem${NC}"
417
- fi
418
-
419
- success "Restart OpenClaw to activate: openclaw gateway restart"
420
- fi
207
+ JSON
208
+ success "lightweight starter config written: $CONFIG_FILE"
421
209
  }
422
210
 
423
- # ─────────────────────────────────────────────
424
- # Smoke test
425
- # ─────────────────────────────────────────────
426
- smoke_test() {
427
- echo -e "\n${BOLD} Smoke test${NC}"
428
-
429
- # Verify config parses and has required fields
430
- node --input-type=module <<EOF 2>/dev/null && success "Config loads cleanly" || warn "Config load failed — check $HOME/.openclaw/hypermem/config.json"
431
- import { readFileSync, existsSync } from 'fs';
432
- const cfg = JSON.parse(readFileSync('$HOME/.openclaw/hypermem/config.json', 'utf8'));
433
- if (!cfg.tier) throw new Error('missing tier');
434
- if (!cfg.installDir) throw new Error('missing installDir');
435
- if (!existsSync(cfg.installDir)) throw new Error('installDir does not exist: ' + cfg.installDir);
436
- EOF
437
-
438
- # Verify HyperMem core module loads
439
- node --input-type=module <<EOF 2>/dev/null \
440
- && success "HyperMem core module loads" \
441
- || warn "HyperMem core module load failed — check $INSTALL_DIR/node_modules/@psiclawops/hypermem"
442
- import { createRequire } from 'module';
443
- const require = createRequire(import.meta.url);
444
- require('$INSTALL_DIR/node_modules/@psiclawops/hypermem/dist/index.js');
445
- EOF
446
-
447
- # Verify context engine plugin dist exists
448
- [[ -f "$PLUGIN_DIR/dist/index.js" ]] \
449
- && success "hypercompositor plugin built" \
450
- || warn "hypercompositor plugin not built — reinstall: npm --prefix $INSTALL_DIR install @psiclawops/hypercompositor@latest"
451
-
452
- # Verify memory plugin dist exists
453
- [[ -f "$MEMORY_PLUGIN_DIR/dist/index.js" ]] \
454
- && success "hypermem memory plugin built" \
455
- || warn "hypermem memory plugin not built — reinstall: npm --prefix $INSTALL_DIR install @psiclawops/hypermem-memory@latest"
456
-
457
- # Tier 2: verify transformers package is present
458
- if [[ "$SELECTED_TIER" == "2" ]]; then
459
- [[ -d "$INSTALL_DIR/node_modules/@huggingface/transformers" ]] \
460
- && success "@huggingface/transformers present" \
461
- || warn "@huggingface/transformers missing — run: npm --prefix $INSTALL_DIR install @huggingface/transformers@3"
462
- fi
463
-
464
- # Tier 3: verify nomic model is available in Ollama
465
- if [[ "$SELECTED_TIER" == "3" ]] && command -v ollama &>/dev/null; then
466
- ollama list 2>/dev/null | grep -q 'nomic-embed-text' \
467
- && success "nomic-embed-text present in Ollama" \
468
- || warn "nomic-embed-text not found in Ollama — run: ollama pull nomic-embed-text"
469
- fi
211
+ verify_stage() {
212
+ echo -e "\n${BOLD} Stage verification${NC}"
213
+ [[ -d "$RUNTIME_DIR/dist" ]] || die "missing $RUNTIME_DIR/dist"
214
+ [[ -d "$RUNTIME_DIR/plugin/dist" ]] || die "missing $RUNTIME_DIR/plugin/dist"
215
+ [[ -d "$RUNTIME_DIR/memory-plugin/dist" ]] || die "missing $RUNTIME_DIR/memory-plugin/dist"
216
+ [[ -f "$RUNTIME_DIR/bin/hypermem-status.mjs" ]] || die "missing hypermem-status bin"
217
+ [[ -f "$RUNTIME_DIR/bin/hypermem-model-audit.mjs" ]] || die "missing hypermem-model-audit bin"
218
+ success "runtime payload complete"
470
219
  }
471
220
 
472
- # ─────────────────────────────────────────────
473
- # Summary
474
- # ─────────────────────────────────────────────
475
- summary() {
221
+ next_steps() {
476
222
  echo ""
477
- echo -e "${CYAN}${BOLD} ─────────────────────────────────────────${NC}"
478
- echo -e "${CYAN}${BOLD} HyperMem installed${NC}"
479
- echo -e "${CYAN}${BOLD} ─────────────────────────────────────────${NC}"
223
+ echo -e "${CYAN}${BOLD} HyperMem staged${NC}"
480
224
  echo ""
481
- echo -e " ${BOLD}Tier:${NC} $SELECTED_TIER"
225
+ echo -e " ${BOLD}Package:${NC} $PACKAGE"
482
226
  echo -e " ${BOLD}Install:${NC} $INSTALL_DIR"
483
- echo -e " ${BOLD}Config:${NC} $HOME/.openclaw/hypermem/config.json"
484
- echo -e " ${BOLD}Plugins:${NC} hypercompositor (context-engine) + hypermem (memory)"
227
+ echo -e " ${BOLD}Runtime:${NC} $RUNTIME_DIR"
228
+ echo -e " ${BOLD}Config:${NC} $CONFIG_FILE"
485
229
  echo ""
230
+ echo -e " ${BOLD}Activation commands:${NC}"
231
+ cat <<EOF
232
+ openclaw config get plugins.load.paths
233
+ openclaw config get plugins.allow
486
234
 
487
- case "$SELECTED_TIER" in
488
- 1) echo -e " ${DIM}FTS5+BM25 keyword search active. No embedder.${NC}" ;;
489
- 2) echo -e " ${DIM}MiniLM-L6-v2 will download on first embedding call.${NC}" ;;
490
- 3) echo -e " ${DIM}nomic-embed-text ready via Ollama.${NC}" ;;
491
- 4) echo -e " ${DIM}qwen3-embedding:8b via OpenRouter. API key stored in config.${NC}" ;;
492
- esac
235
+ HYPERMEM_PATHS="[\"${RUNTIME_DIR}/plugin\",\"${RUNTIME_DIR}/memory-plugin\"]"
236
+ openclaw config set plugins.load.paths "\$HYPERMEM_PATHS" --strict-json
237
+ openclaw config set plugins.slots.contextEngine hypercompositor
238
+ openclaw config set plugins.slots.memory hypermem
493
239
 
240
+ # Only if plugins.allow already contains an array, append hypercompositor and hypermem to that existing array.
241
+ # If plugins.allow is unset, null, or empty, skip the allowlist step.
242
+
243
+ openclaw gateway restart
244
+ EOF
494
245
  echo ""
495
- echo -e " ${DIM}Upgrade tier anytime: re-run this installer and select a higher tier.${NC}"
496
- echo -e " ${DIM}Docs: https://github.com/psiclawops/hypermem${NC}"
246
+ echo -e " ${BOLD}Verify:${NC}"
247
+ cat <<EOF
248
+ openclaw plugins list
249
+ openclaw logs --limit 100 | grep -E 'hypermem|context-engine|falling back'
250
+ node ${RUNTIME_DIR}/bin/hypermem-status.mjs --health
251
+ node ${RUNTIME_DIR}/bin/hypermem-model-audit.mjs --strict
252
+ EOF
497
253
  echo ""
254
+ echo -e " ${DIM}A staged runtime is not active until OpenClaw is wired and restarted.${NC}"
498
255
  }
499
256
 
500
- # ─────────────────────────────────────────────
501
- # Main
502
- # ─────────────────────────────────────────────
503
257
  main() {
504
258
  banner
505
259
  preflight
506
- detect_hardware
507
- select_tier
508
- install_hypermem
509
- setup_tier
510
- write_config
511
- register_plugin
512
- smoke_test
513
- summary
260
+ install_package
261
+ stage_runtime
262
+ write_minimal_config_if_missing
263
+ verify_stage
264
+ next_steps
514
265
  }
515
266
 
516
267
  main "$@"