@gamaze/hicortex 0.7.1 → 0.10.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 (49) hide show
  1. package/README.md +57 -39
  2. package/dist/claude-md.d.ts +9 -21
  3. package/dist/claude-md.js +9 -241
  4. package/dist/cli.d.ts +3 -2
  5. package/dist/cli.js +29 -11
  6. package/dist/consolidate.js +0 -7
  7. package/dist/db.js +24 -0
  8. package/dist/embedder.d.ts +11 -0
  9. package/dist/embedder.js +27 -0
  10. package/dist/extensions.d.ts +41 -88
  11. package/dist/extensions.js +36 -61
  12. package/dist/features.d.ts +21 -25
  13. package/dist/features.js +47 -83
  14. package/dist/hermes-transcript-reader.d.ts +27 -0
  15. package/dist/hermes-transcript-reader.js +134 -0
  16. package/dist/index.d.ts +16 -4
  17. package/dist/index.js +252 -344
  18. package/dist/init.d.ts +41 -1
  19. package/dist/init.js +545 -190
  20. package/dist/lesson-selection.d.ts +62 -0
  21. package/dist/lesson-selection.js +159 -0
  22. package/dist/lessons-context.d.ts +17 -0
  23. package/dist/lessons-context.js +96 -0
  24. package/dist/llm.d.ts +42 -29
  25. package/dist/llm.js +89 -270
  26. package/dist/mcp-server.d.ts +0 -1
  27. package/dist/mcp-server.js +404 -86
  28. package/dist/nightly.d.ts +9 -6
  29. package/dist/nightly.js +197 -357
  30. package/dist/oc-transcript-reader.d.ts +20 -0
  31. package/dist/oc-transcript-reader.js +61 -0
  32. package/dist/pi-transcript-reader.d.ts +1 -0
  33. package/dist/status.js +22 -2
  34. package/dist/storage.d.ts +7 -1
  35. package/dist/storage.js +28 -7
  36. package/dist/transcript-reader.d.ts +19 -0
  37. package/dist/transcript-reader.js +17 -3
  38. package/dist/types.d.ts +10 -0
  39. package/dist/uninstall.js +31 -1
  40. package/hermes-plugin/hicortex/README.md +77 -0
  41. package/hermes-plugin/hicortex/__init__.py +17 -0
  42. package/hermes-plugin/hicortex/client.py +162 -0
  43. package/hermes-plugin/hicortex/config.py +105 -0
  44. package/hermes-plugin/hicortex/plugin.yaml +12 -0
  45. package/hermes-plugin/hicortex/provider.py +432 -0
  46. package/openclaw.plugin.json +17 -44
  47. package/package.json +7 -5
  48. package/dist/pro-loader.d.ts +0 -33
  49. package/dist/pro-loader.js +0 -187
@@ -0,0 +1,105 @@
1
+ """Config schema + load/save for the Hicortex Hermes plugin.
2
+
3
+ Config lives at ``$HERMES_HOME/plugins/hicortex/config.json``. Environment
4
+ variables (``HICORTEX_URL``, ``HICORTEX_AUTH_TOKEN``) override the file, so the
5
+ plugin also works with env-only setup.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import os
12
+ from typing import Any, Dict, Optional
13
+
14
+ # Declarative config schema — drives `hermes memory setup` (see MemoryProvider
15
+ # .get_config_schema). Field shape per the Hermes MemoryProvider contract:
16
+ # key, label, description, default, required, secret, env_var, choices, url.
17
+ CONFIG_SCHEMA: list[dict[str, Any]] = [
18
+ {
19
+ "key": "hicortex_url",
20
+ "label": "Hicortex server URL",
21
+ "description": (
22
+ "URL of the Hicortex memory server. On the server host use "
23
+ "http://localhost:8787; on other machines use the server's "
24
+ "Tailscale hostname, e.g. http://memory-server:8787."
25
+ ),
26
+ "default": "http://localhost:8787",
27
+ "required": True,
28
+ },
29
+ {
30
+ "key": "hicortex_auth_token",
31
+ "label": "Auth token",
32
+ "description": (
33
+ "Bearer token for the server. Omit (leave blank) when targeting "
34
+ "localhost — the server bypasses auth there. Default token: "
35
+ "hctx-default-token."
36
+ ),
37
+ "secret": True,
38
+ "env_var": "HICORTEX_AUTH_TOKEN",
39
+ },
40
+ {
41
+ "key": "default_project",
42
+ "label": "Default project",
43
+ "description": "Optional project name to scope recall and capture.",
44
+ "required": False,
45
+ },
46
+ {
47
+ "key": "recall_limit",
48
+ "label": "Recall limit",
49
+ "description": "Max memories returned per recall (default 5).",
50
+ "default": "5",
51
+ "required": False,
52
+ },
53
+ {
54
+ "key": "privacy_filter",
55
+ "label": "Privacy filter",
56
+ "description": "Comma-separated privacy levels to include (e.g. WORK,PERSONAL).",
57
+ "default": "WORK,PERSONAL",
58
+ "required": False,
59
+ },
60
+ # NOTE: recall-only plugin — no capture config. Capture is handled by the
61
+ # nightly server-side reader of each agent's session store.
62
+ ]
63
+
64
+
65
+ def _config_path(hermes_home: Optional[str] = None) -> str:
66
+ home = hermes_home or os.environ.get("HERMES_HOME") or os.path.expanduser("~/.hermes")
67
+ return os.path.join(home, "plugins", "hicortex", "config.json")
68
+
69
+
70
+ def load_config() -> Dict[str, Any]:
71
+ """Load merged config: file <- env overrides <- defaults."""
72
+ path = _config_path()
73
+ cfg: Dict[str, Any] = {}
74
+ if os.path.exists(path):
75
+ try:
76
+ with open(path, encoding="utf-8") as f:
77
+ cfg = json.load(f) or {}
78
+ except Exception:
79
+ cfg = {}
80
+
81
+ # Env overrides
82
+ if os.environ.get("HICORTEX_URL"):
83
+ cfg["hicortex_url"] = os.environ["HICORTEX_URL"]
84
+ if os.environ.get("HICORTEX_AUTH_TOKEN"):
85
+ cfg["hicortex_auth_token"] = os.environ["HICORTEX_AUTH_TOKEN"]
86
+
87
+ # Defaults
88
+ cfg.setdefault("hicortex_url", "http://localhost:8787")
89
+ cfg.setdefault("recall_limit", 5)
90
+ cfg.setdefault("privacy_filter", "WORK,PERSONAL")
91
+ return cfg
92
+
93
+
94
+ def save_config(values: Dict[str, Any], hermes_home: str) -> None:
95
+ """Write non-secret config values to the plugin's config file.
96
+
97
+ Called by `hermes memory setup` after collecting user inputs. Secret fields
98
+ (hicortex_auth_token) are routed to the env store by Hermes, not written here.
99
+ """
100
+ path = _config_path(hermes_home)
101
+ os.makedirs(os.path.dirname(path), exist_ok=True)
102
+ # Don't persist secrets to the JSON file — Hermes stores them separately.
103
+ safe = {k: v for k, v in values.items() if k != "hicortex_auth_token"}
104
+ with open(path, "w", encoding="utf-8") as f:
105
+ json.dump(safe, f, indent=2)
@@ -0,0 +1,12 @@
1
+ name: hicortex
2
+ version: 0.4.0
3
+ description: "Self-learning memory for Hermes agents — every session is distilled into lessons overnight, and your agent wakes up wiser. Injects fresh lessons each turn and exposes the full 8-tool memory surface (search, context, ingest, lessons, index, graph, update, delete) via a shared Hicortex server. Stdlib-only."
4
+ pip_dependencies: []
5
+ hooks: []
6
+ requires_env:
7
+ - name: HICORTEX_URL
8
+ description: "Hicortex server URL. Leave empty for a local server (http://127.0.0.1:8787)."
9
+ secret: false
10
+ - name: HICORTEX_AUTH_TOKEN
11
+ description: "Auth token from the Hicortex server — run `hicortex status` on the server to see it. Leave empty when the server runs on this machine (localhost bypasses auth)."
12
+ secret: true
@@ -0,0 +1,432 @@
1
+ """Hicortex MemoryProvider for Hermes — recall-only.
2
+
3
+ Recall: prefetch() -> GET /search (relevant memories before each turn)
4
+ queue_prefetch() -> GET /search (background recall for the next turn)
5
+ tools -> hicortex_search / hicortex_recall_recent
6
+ system_prompt_block -> lessons + memory index injected into the prompt
7
+
8
+ Capture is NOT the plugin's job. A nightly reader on the Hicortex server
9
+ distills each agent's own session store (Hermes: ~/.hermes/profiles/<agent>/
10
+ state.db) centrally — see specs/2026-07-01-memory-capture-architecture.md. This
11
+ plugin has no local LLM, no spool, no timer, and no capture path.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import hashlib
17
+ import json
18
+ import logging
19
+ import threading
20
+ from typing import Any, Dict, List, Optional
21
+
22
+ from agent.memory_provider import MemoryProvider
23
+
24
+ from .client import HicortexClient
25
+ from .config import CONFIG_SCHEMA, load_config
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ _INJECT_CONTENT_CAP = 500
30
+
31
+
32
+ class HicortexProvider(MemoryProvider):
33
+ """Hicortex long-term memory backend for Hermes (recall-only)."""
34
+
35
+ def __init__(self):
36
+ self._client: Optional[HicortexClient] = None
37
+ self._project: Optional[str] = None
38
+ self._recall_limit: int = 5
39
+ self._privacy: Optional[str] = "WORK,PERSONAL"
40
+ self._prefetch_cache: Dict[str, str] = {}
41
+ self._bg_threads: List[threading.Thread] = []
42
+
43
+ @property
44
+ def name(self) -> str:
45
+ return "hicortex"
46
+
47
+ # ------------------------------------------------------------------ config
48
+ def _build_client(self) -> Optional[HicortexClient]:
49
+ cfg = load_config()
50
+ url = cfg.get("hicortex_url")
51
+ if not url:
52
+ return None
53
+ token = cfg.get("hicortex_auth_token")
54
+ return HicortexClient(url, auth_token=token or None)
55
+
56
+ def _client_or_none(self) -> Optional[HicortexClient]:
57
+ if self._client is None:
58
+ try:
59
+ self._client = self._build_client()
60
+ except Exception as e:
61
+ logger.warning("hicortex: failed to build client: %s", e)
62
+ return self._client
63
+
64
+ def is_available(self) -> bool:
65
+ """Configured and ready — NO network call (per MemoryProvider contract).
66
+
67
+ ``is_available`` runs at agent init to decide whether to activate this
68
+ provider. Pinging the server here would mean a slow or momentarily-down
69
+ server silently disables memory for the whole session. Per the contract
70
+ ("should not make network calls — just check config and installed deps")
71
+ we only verify a server URL is configured; per-request failures are
72
+ handled at use time.
73
+ """
74
+ return self._build_client() is not None
75
+
76
+ def initialize(self, session_id: str, **kwargs) -> None:
77
+ cfg = load_config()
78
+ self._project = cfg.get("default_project") or None
79
+ try:
80
+ self._recall_limit = int(cfg.get("recall_limit", 5))
81
+ except (TypeError, ValueError):
82
+ self._recall_limit = 5
83
+ self._privacy = cfg.get("privacy_filter", "WORK,PERSONAL")
84
+ try:
85
+ self._client = self._build_client()
86
+ except Exception as e:
87
+ logger.warning("hicortex: init client build failed: %s", e)
88
+
89
+ # ------------------------------------------------------------------- recall
90
+ def _format_hits(self, hits: list[dict]) -> str:
91
+ if not hits:
92
+ return ""
93
+ lines = [
94
+ "Relevant prior context from your long-term memory "
95
+ "(verify before relying on these — each shows date and project):"
96
+ ]
97
+ for h in hits[: self._recall_limit]:
98
+ date = (h.get("created_at") or "")[:10]
99
+ proj = h.get("project") or "global"
100
+ content = (h.get("content") or "").strip().replace("\n", " ")
101
+ if len(content) > _INJECT_CONTENT_CAP:
102
+ content = content[:_INJECT_CONTENT_CAP] + "…"
103
+ lines.append(f"- [{date}, {proj}] {content}")
104
+ return "\n".join(lines)
105
+
106
+ def prefetch(self, query: str, *, session_id: str = "") -> str:
107
+ key = hashlib.sha1(query.encode("utf-8")).hexdigest()
108
+ cached = self._prefetch_cache.pop(key, None)
109
+ if cached is not None:
110
+ return cached
111
+ client = self._client_or_none()
112
+ if client is None:
113
+ return ""
114
+ try:
115
+ hits = client.search(
116
+ query, limit=self._recall_limit, project=self._project, privacy=self._privacy
117
+ )
118
+ return self._format_hits(hits)
119
+ except Exception as e:
120
+ logger.debug("hicortex prefetch failed: %s", e)
121
+ return ""
122
+
123
+ def queue_prefetch(self, query: str, *, session_id: str = "") -> None:
124
+ client = self._client_or_none()
125
+ if client is None:
126
+ return
127
+ key = hashlib.sha1(query.encode("utf-8")).hexdigest()
128
+
129
+ def _bg() -> None:
130
+ try:
131
+ hits = client.search(
132
+ query, limit=self._recall_limit, project=self._project, privacy=self._privacy
133
+ )
134
+ self._prefetch_cache[key] = self._format_hits(hits)
135
+ except Exception as e:
136
+ logger.debug("hicortex queue_prefetch failed: %s", e)
137
+
138
+ self._spawn(_bg)
139
+
140
+ # ------------------------------------------------------ system prompt/tools
141
+ def system_prompt_block(self) -> str:
142
+ client = self._client_or_none()
143
+ if client is None:
144
+ return ""
145
+ try:
146
+ data = client.lessons()
147
+ except Exception as e:
148
+ logger.debug("hicortex lessons fetch failed: %s", e)
149
+ return ""
150
+ lessons = (data.get("lessons") or [])[:8]
151
+ idx = data.get("index") or {}
152
+ lines = [
153
+ "## Hicortex long-term memory",
154
+ "You have shared long-term memory across sessions. Use `hicortex_search` "
155
+ "for specific recall and `hicortex_recall_recent` for recent context.",
156
+ ]
157
+ if lessons:
158
+ lines.append("Lessons:")
159
+ for l in lessons:
160
+ c = (l.get("content") or "").strip().replace("\n", " ")
161
+ lines.append(f"- {c[:200]}")
162
+ if idx.get("total"):
163
+ lines.append(
164
+ f"({idx.get('total')} memories, {idx.get('lessonCount')} lessons "
165
+ f"across {idx.get('sourceCount')} agents)"
166
+ )
167
+ return "\n".join(lines)
168
+
169
+ def get_tool_schemas(self) -> List[Dict[str, Any]]:
170
+ return [
171
+ {
172
+ "name": "hicortex_search",
173
+ "description": (
174
+ "Search long-term memory using semantic similarity. Returns the most "
175
+ "relevant memories from past sessions."
176
+ ),
177
+ "parameters": {
178
+ "type": "object",
179
+ "properties": {
180
+ "query": {"type": "string", "description": "Search query text"},
181
+ "limit": {
182
+ "type": "number",
183
+ "description": "Max results (default 5)",
184
+ },
185
+ "project": {"type": "string", "description": "Filter by project name"},
186
+ },
187
+ "required": ["query"],
188
+ },
189
+ },
190
+ {
191
+ "name": "hicortex_recall_recent",
192
+ "description": "Recall recent context memories, optionally filtered by project.",
193
+ "parameters": {
194
+ "type": "object",
195
+ "properties": {
196
+ "project": {"type": "string"},
197
+ "limit": {
198
+ "type": "number",
199
+ "description": "Max results (default 10)",
200
+ },
201
+ },
202
+ },
203
+ },
204
+ {
205
+ "name": "hicortex_context",
206
+ "description": (
207
+ "Get recent context memories, optionally filtered by project. "
208
+ "Useful to recall what happened recently."
209
+ ),
210
+ "parameters": {
211
+ "type": "object",
212
+ "properties": {
213
+ "project": {"type": "string", "description": "Filter by project name"},
214
+ "limit": {"type": "number", "description": "Max results (default 10)"},
215
+ },
216
+ },
217
+ },
218
+ {
219
+ "name": "hicortex_ingest",
220
+ "description": (
221
+ "Store a new memory in long-term storage. "
222
+ "Use for important facts, decisions, or lessons."
223
+ ),
224
+ "parameters": {
225
+ "type": "object",
226
+ "properties": {
227
+ "content": {"type": "string", "description": "Memory content to store"},
228
+ "project": {"type": "string", "description": "Project this memory belongs to"},
229
+ "memory_type": {
230
+ "type": "string",
231
+ "enum": ["episode", "lesson", "fact", "decision"],
232
+ "description": "Type of memory (default: episode)",
233
+ },
234
+ },
235
+ "required": ["content"],
236
+ },
237
+ },
238
+ {
239
+ "name": "hicortex_lessons",
240
+ "description": (
241
+ "Get actionable lessons learned from past sessions. "
242
+ "Auto-generated insights about mistakes to avoid."
243
+ ),
244
+ "parameters": {
245
+ "type": "object",
246
+ "properties": {
247
+ "project": {"type": "string", "description": "Filter by project name"},
248
+ },
249
+ },
250
+ },
251
+ {
252
+ "name": "hicortex_index",
253
+ "description": (
254
+ "Get the knowledge domain index — shows what topics and projects "
255
+ "are stored in memory, grouped by domain."
256
+ ),
257
+ "parameters": {
258
+ "type": "object",
259
+ "properties": {},
260
+ },
261
+ },
262
+ {
263
+ "name": "hicortex_graph",
264
+ "description": (
265
+ "Query the memory knowledge graph — find connected memories, "
266
+ "hub nodes, or paths between memories."
267
+ ),
268
+ "parameters": {
269
+ "type": "object",
270
+ "properties": {
271
+ "operation": {
272
+ "type": "string",
273
+ "enum": ["neighbors", "hubs", "path"],
274
+ "description": "Graph operation to perform",
275
+ },
276
+ "id": {"type": "string", "description": "Memory ID (required for neighbors and path operations)"},
277
+ "target_id": {"type": "string", "description": "Target memory ID (required for path operation)"},
278
+ "limit": {"type": "number", "description": "Max results (default 10)"},
279
+ "domain": {"type": "string", "description": "Filter hubs by domain"},
280
+ "relationship": {
281
+ "type": "string",
282
+ "description": "Filter neighbors by relationship type (e.g., CONTRADICTS, SUPERSEDES, derives)",
283
+ },
284
+ },
285
+ "required": ["operation"],
286
+ },
287
+ },
288
+ {
289
+ "name": "hicortex_update",
290
+ "description": (
291
+ "Update an existing memory. Use after searching to fix incorrect information. "
292
+ "If content changes, the embedding is re-computed."
293
+ ),
294
+ "parameters": {
295
+ "type": "object",
296
+ "properties": {
297
+ "id": {"type": "string", "description": "Memory ID (from search results, first 8 chars or full UUID)"},
298
+ "content": {"type": "string", "description": "New content text"},
299
+ "project": {"type": "string", "description": "New project name"},
300
+ "memory_type": {
301
+ "type": "string",
302
+ "enum": ["episode", "lesson", "fact", "decision"],
303
+ "description": "New memory type",
304
+ },
305
+ },
306
+ "required": ["id"],
307
+ },
308
+ },
309
+ {
310
+ "name": "hicortex_delete",
311
+ "description": (
312
+ "Permanently delete a memory and its links. "
313
+ "Use when a memory is incorrect and should be removed entirely."
314
+ ),
315
+ "parameters": {
316
+ "type": "object",
317
+ "properties": {
318
+ "id": {"type": "string", "description": "Memory ID (from search results, first 8 chars or full UUID)"},
319
+ },
320
+ "required": ["id"],
321
+ },
322
+ },
323
+ ]
324
+
325
+ def handle_tool_call(self, tool_name: str, args: Dict[str, Any], **kwargs) -> str:
326
+ client = self._client_or_none()
327
+ if client is None:
328
+ return json.dumps({"error": "hicortex server not configured"})
329
+ try:
330
+ if tool_name == "hicortex_search":
331
+ hits = client.search(
332
+ args.get("query", ""),
333
+ limit=int(args.get("limit", 5)),
334
+ project=args.get("project") or self._project,
335
+ )
336
+ return json.dumps(hits)
337
+
338
+ elif tool_name in ("hicortex_recall_recent", "hicortex_context"):
339
+ hits = client.context(
340
+ project=args.get("project") or self._project,
341
+ limit=int(args.get("limit", 10)),
342
+ )
343
+ return json.dumps(hits)
344
+
345
+ elif tool_name == "hicortex_ingest":
346
+ content = args.get("content", "")
347
+ if not content:
348
+ return json.dumps({"error": "content is required"})
349
+ status, resp = client.ingest(
350
+ content=content,
351
+ source_agent="hermes/manual",
352
+ project=args.get("project") or self._project,
353
+ memory_type=args.get("memory_type", "episode"),
354
+ )
355
+ if status not in (200, 201):
356
+ return json.dumps({"error": resp.get("error", f"HTTP {status}")})
357
+ id_val = resp.get("id") or ""
358
+ return json.dumps({"id": id_val, "message": f"Memory stored (id: {id_val[:8]})"})
359
+
360
+ elif tool_name == "hicortex_lessons":
361
+ data = client.lessons()
362
+ lessons = (data.get("lessons") or [])
363
+ if not lessons:
364
+ return json.dumps({"message": "No lessons found."})
365
+ return json.dumps([{"content": l.get("content", "")[:500]} for l in lessons])
366
+
367
+ elif tool_name == "hicortex_index":
368
+ return json.dumps(client.index())
369
+
370
+ elif tool_name == "hicortex_graph":
371
+ op = args.get("operation", "")
372
+ result = client.graph(
373
+ op=op,
374
+ id=args.get("id"),
375
+ target_id=args.get("target_id"),
376
+ limit=args.get("limit"),
377
+ domain=args.get("domain"),
378
+ relationship=args.get("relationship"),
379
+ )
380
+ return json.dumps(result)
381
+
382
+ elif tool_name == "hicortex_update":
383
+ id_val = args.get("id", "")
384
+ if not id_val:
385
+ return json.dumps({"error": "id is required"})
386
+ status, resp = client.update(
387
+ id=id_val,
388
+ content=args.get("content"),
389
+ project=args.get("project"),
390
+ memory_type=args.get("memory_type"),
391
+ )
392
+ if status == 404:
393
+ return json.dumps({"error": f"Memory not found: {id_val}"})
394
+ if status not in (200, 201):
395
+ return json.dumps({"error": resp.get("error", f"HTTP {status}")})
396
+ return json.dumps({"updated": True, "id": resp.get("id", id_val)})
397
+
398
+ elif tool_name == "hicortex_delete":
399
+ id_val = args.get("id", "")
400
+ if not id_val:
401
+ return json.dumps({"error": "id is required"})
402
+ status, resp = client.delete(id=id_val)
403
+ if status == 404:
404
+ return json.dumps({"error": f"Memory not found: {id_val}"})
405
+ if status not in (200, 201):
406
+ return json.dumps({"error": resp.get("error", f"HTTP {status}")})
407
+ return json.dumps({"deleted": True, "id": resp.get("id", id_val)})
408
+
409
+ else:
410
+ return json.dumps({"error": f"unknown tool: {tool_name}"})
411
+
412
+ except Exception as e:
413
+ return json.dumps({"error": str(e)})
414
+
415
+ # ---------------------------------------------------------------- lifecycle
416
+ def _spawn(self, fn) -> None:
417
+ self._bg_threads = [t for t in self._bg_threads if t.is_alive()]
418
+ t = threading.Thread(target=fn, daemon=True)
419
+ t.start()
420
+ self._bg_threads.append(t)
421
+
422
+ def shutdown(self) -> None:
423
+ for t in self._bg_threads:
424
+ t.join(timeout=2.0)
425
+
426
+ def get_config_schema(self) -> List[Dict[str, Any]]:
427
+ return CONFIG_SCHEMA
428
+
429
+ def save_config(self, values: Dict[str, Any], hermes_home: str) -> None:
430
+ from .config import save_config as _save
431
+
432
+ _save(values, hermes_home)
@@ -2,69 +2,42 @@
2
2
  "id": "hicortex",
3
3
  "name": "Hicortex — Long-term Memory That Learns",
4
4
  "description": "Your agents remember past decisions, avoid repeated mistakes, and get smarter every day. Nightly reflection generates actionable lessons that automatically update agent behavior.",
5
- "version": "0.7.1",
5
+ "version": "0.10.0",
6
6
  "kind": "lifecycle",
7
7
  "skills": ["./skills/hicortex-memory", "./skills/hicortex-learn", "./skills/hicortex-activate"],
8
8
  "configSchema": {
9
9
  "type": "object",
10
10
  "properties": {
11
- "licenseKey": {
12
- "type": "string",
13
- "description": "Hicortex license key (hctx-...). Leave empty for free tier (250 memory cap)."
14
- },
15
- "llmBaseUrl": {
16
- "type": "string",
17
- "description": "LLM API base URL. Auto-detected from OpenClaw config or env vars if omitted."
18
- },
19
- "llmApiKey": {
20
- "type": "string",
21
- "description": "LLM API key. Auto-detected from OpenClaw config or env vars if omitted."
22
- },
23
- "llmModel": {
11
+ "serverUrl": {
24
12
  "type": "string",
25
- "default": "qwen3.5:4b",
26
- "description": "Model for importance scoring and distillation (local recommended)"
13
+ "default": "http://127.0.0.1:8787",
14
+ "description": "Hicortex server URL. Defaults to localhost (co-located server). For multi-machine setups, point this at the remote server (e.g. http://bedrock:8787 or a Tailscale HTTPS URL)."
27
15
  },
28
- "reflectModel": {
16
+ "authToken": {
29
17
  "type": "string",
30
- "default": "qwen3.5:cloud",
31
- "description": "Model for nightly reflection (cloud recommended)"
18
+ "description": "Bearer token for the Hicortex server. Localhost bypasses auth automatically. Required for remote servers — use the authToken from ~/.hicortex/config.json on the server."
32
19
  },
33
- "consolidateHour": {
34
- "type": "number",
35
- "default": 2,
36
- "description": "Hour (0-23, local time) to run nightly consolidation"
37
- },
38
- "dbPath": {
20
+ "licenseKey": {
39
21
  "type": "string",
40
- "description": "Custom path for the SQLite database file"
22
+ "description": "Hicortex license key (hctx-...). Leave empty for free tier (250 memory cap)."
41
23
  }
42
24
  },
43
25
  "required": []
44
26
  },
45
27
  "uiHints": {
28
+ "serverUrl": {
29
+ "label": "Server URL",
30
+ "placeholder": "http://127.0.0.1:8787"
31
+ },
32
+ "authToken": {
33
+ "label": "Auth Token",
34
+ "sensitive": true,
35
+ "placeholder": "Required for remote servers"
36
+ },
46
37
  "licenseKey": {
47
38
  "label": "License Key",
48
39
  "placeholder": "hctx-... (optional, free tier without key)",
49
40
  "sensitive": true
50
- },
51
- "llmBaseUrl": {
52
- "label": "LLM API URL",
53
- "placeholder": "Auto-detected if empty"
54
- },
55
- "llmApiKey": {
56
- "label": "LLM API Key",
57
- "sensitive": true,
58
- "placeholder": "Auto-detected if empty"
59
- },
60
- "llmModel": {
61
- "label": "Scoring Model"
62
- },
63
- "reflectModel": {
64
- "label": "Reflection Model"
65
- },
66
- "consolidateHour": {
67
- "label": "Consolidation Hour"
68
41
  }
69
42
  }
70
43
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gamaze/hicortex",
3
- "version": "0.7.1",
4
- "description": "Human-like memory for self-improving AI agents. Automatic capturing, nightly reflection, and cross-agent learning. Works with Claude Code and OpenClaw.",
3
+ "version": "0.10.0",
4
+ "description": "Self-learning memory for AI agents \u2014 experience captured automatically, distilled into lessons overnight, shared across your whole fleet. Works with Hermes, OpenClaw, Claude Code, and Pi.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "hicortex": "dist/cli.js"
@@ -26,14 +26,16 @@
26
26
  "files": [
27
27
  "dist/",
28
28
  "skills/",
29
+ "hermes-plugin/",
29
30
  "openclaw.plugin.json",
30
31
  "README.md"
31
32
  ],
32
33
  "scripts": {
33
- "build": "tsc",
34
+ "build": "rm -rf dist && tsc",
34
35
  "test": "vitest run",
35
36
  "test:watch": "vitest",
36
- "prepublishOnly": "npm run build"
37
+ "prepack": "npm run build && rm -rf ./hermes-plugin && mkdir -p ./hermes-plugin && cp -r ../../hermes-plugin/hicortex ./hermes-plugin/ && find ./hermes-plugin -name __pycache__ -type d -exec rm -rf {} + 2>/dev/null || true",
38
+ "prepublishOnly": "npm run build && rm -rf ./hermes-plugin && mkdir -p ./hermes-plugin && cp -r ../../hermes-plugin/hicortex ./hermes-plugin/ && find ./hermes-plugin -name __pycache__ -type d -exec rm -rf {} + 2>/dev/null || true"
37
39
  },
38
40
  "devDependencies": {
39
41
  "@types/better-sqlite3": "^7.6.0",
@@ -45,7 +47,7 @@
45
47
  "engines": {
46
48
  "node": ">=18"
47
49
  },
48
- "license": "MIT",
50
+ "license": "PolyForm-Noncommercial-1.0.0",
49
51
  "homepage": "https://hicortex.gamaze.com",
50
52
  "repository": {
51
53
  "type": "git",