@gamaze/hicortex 0.7.0 → 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.
- package/README.md +57 -39
- package/dist/claude-md.d.ts +9 -21
- package/dist/claude-md.js +9 -241
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +29 -11
- package/dist/consolidate.js +76 -26
- package/dist/db.js +24 -0
- package/dist/embedder.d.ts +11 -0
- package/dist/embedder.js +27 -0
- package/dist/extensions.d.ts +41 -88
- package/dist/extensions.js +36 -61
- package/dist/features.d.ts +21 -25
- package/dist/features.js +47 -83
- package/dist/graph.d.ts +1 -1
- package/dist/graph.js +13 -7
- package/dist/hermes-transcript-reader.d.ts +27 -0
- package/dist/hermes-transcript-reader.js +134 -0
- package/dist/index.d.ts +16 -4
- package/dist/index.js +252 -344
- package/dist/init.d.ts +41 -1
- package/dist/init.js +545 -190
- package/dist/lesson-selection.d.ts +62 -0
- package/dist/lesson-selection.js +159 -0
- package/dist/lessons-context.d.ts +17 -0
- package/dist/lessons-context.js +96 -0
- package/dist/llm.d.ts +42 -29
- package/dist/llm.js +89 -270
- package/dist/mcp-server.d.ts +0 -1
- package/dist/mcp-server.js +407 -88
- package/dist/nightly.d.ts +9 -6
- package/dist/nightly.js +197 -357
- package/dist/oc-transcript-reader.d.ts +20 -0
- package/dist/oc-transcript-reader.js +61 -0
- package/dist/pi-transcript-reader.d.ts +1 -0
- package/dist/prompts.d.ts +5 -0
- package/dist/prompts.js +29 -0
- package/dist/status.js +22 -2
- package/dist/storage.d.ts +7 -1
- package/dist/storage.js +28 -7
- package/dist/transcript-reader.d.ts +19 -0
- package/dist/transcript-reader.js +17 -3
- package/dist/types.d.ts +16 -0
- package/dist/types.js +7 -0
- package/dist/uninstall.js +31 -1
- package/hermes-plugin/hicortex/README.md +77 -0
- package/hermes-plugin/hicortex/__init__.py +17 -0
- package/hermes-plugin/hicortex/client.py +162 -0
- package/hermes-plugin/hicortex/config.py +105 -0
- package/hermes-plugin/hicortex/plugin.yaml +12 -0
- package/hermes-plugin/hicortex/provider.py +432 -0
- package/openclaw.plugin.json +17 -44
- package/package.json +7 -5
- package/dist/pro-loader.d.ts +0 -33
- package/dist/pro-loader.js +0 -187
|
@@ -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)
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.
|
|
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
|
-
"
|
|
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": "
|
|
26
|
-
"description": "
|
|
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
|
-
"
|
|
16
|
+
"authToken": {
|
|
29
17
|
"type": "string",
|
|
30
|
-
"
|
|
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
|
-
"
|
|
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": "
|
|
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.
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
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": "
|
|
50
|
+
"license": "PolyForm-Noncommercial-1.0.0",
|
|
49
51
|
"homepage": "https://hicortex.gamaze.com",
|
|
50
52
|
"repository": {
|
|
51
53
|
"type": "git",
|
package/dist/pro-loader.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pro extension loader.
|
|
3
|
-
*
|
|
4
|
-
* Called from features.ts at boot when a valid paid license is detected.
|
|
5
|
-
* Responsibilities:
|
|
6
|
-
* 1. Check ~/.hicortex/pro/installed.json for the currently-installed
|
|
7
|
-
* Pro version (if any).
|
|
8
|
-
* 2. Fetch GET /api/pro/meta from hicortex.gamaze.com to discover the
|
|
9
|
-
* latest available version for the caller's license tier.
|
|
10
|
-
* 3. If the installed version is older (or missing), download the
|
|
11
|
-
* tarball, verify the sha256 sidecar, and extract to ~/.hicortex/pro/.
|
|
12
|
-
* 4. Dynamic-import ~/.hicortex/pro/package/index.js and call activate()
|
|
13
|
-
* on its default export with a ProActivationContext.
|
|
14
|
-
*
|
|
15
|
-
* Failure modes (all soft — OSS host keeps running with defaults):
|
|
16
|
-
* - Network to /api/pro/meta fails → use whatever is already installed
|
|
17
|
-
* - No cached Pro and network fails → Pro not activated, OSS defaults apply
|
|
18
|
-
* - Downloaded tarball fails sha256 → abort download, keep old version
|
|
19
|
-
* - import() of activated module throws → log warning, keep defaults
|
|
20
|
-
*
|
|
21
|
-
* The loader is strictly best-effort. It must NEVER crash the OSS host.
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* Entry point called from features.ts at boot.
|
|
25
|
-
*
|
|
26
|
-
* @param licenseKey The Pro license key (hctx-...) from config
|
|
27
|
-
* @param stateDir Usually ~/.hicortex/
|
|
28
|
-
* @param hostVersion The version of the OSS host (from package.json) — passed
|
|
29
|
-
* to the Pro activate() for compatibility gating
|
|
30
|
-
* @param serverUrl Override for the Pro meta/download endpoint (defaults
|
|
31
|
-
* to https://hicortex.gamaze.com). Useful for testing.
|
|
32
|
-
*/
|
|
33
|
-
export declare function loadPro(licenseKey: string, stateDir: string, hostVersion: string, serverUrl?: string): Promise<void>;
|