@gamaze/hicortex 0.7.1 → 0.10.1
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 +72 -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 +0 -7
- 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/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 +404 -86
- 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/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 +10 -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,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermes transcript reader — the nightly capture path for Nous Research Hermes.
|
|
3
|
+
*
|
|
4
|
+
* Hermes stores conversation in a SQLite state DB, one per profile:
|
|
5
|
+
* ~/.hermes/profiles/<profile>/state.db (per-profile agents: lenny, raider, nano)
|
|
6
|
+
* ~/.hermes/state.db (global, non-profile setups)
|
|
7
|
+
*
|
|
8
|
+
* Schema (relevant columns):
|
|
9
|
+
* sessions(id TEXT PK, started_at REAL, ended_at REAL, ...)
|
|
10
|
+
* messages(session_id TEXT, role TEXT, content TEXT, tool_name TEXT, timestamp REAL, active INT, ...)
|
|
11
|
+
*
|
|
12
|
+
* Compaction is NON-DESTRUCTIVE (compacted-out turns kept with active=0) and
|
|
13
|
+
* sessions are retained ~90 days — so the full history of any ended session is
|
|
14
|
+
* readable here nightly. No runtime plugin capture is needed; the Hermes plugin
|
|
15
|
+
* is recall-only.
|
|
16
|
+
*
|
|
17
|
+
* We process only ENDED sessions (ended_at set) that ended since the last run.
|
|
18
|
+
* A live session is distilled after it ends — this avoids partial-session
|
|
19
|
+
* distillation and keeps per-session dedup clean (chunks are stored as
|
|
20
|
+
* `<sessionId>#<chunkIndex>`; see nightly.ts).
|
|
21
|
+
*/
|
|
22
|
+
import type { TranscriptBatch } from "./transcript-reader.js";
|
|
23
|
+
/**
|
|
24
|
+
* Read Hermes sessions that ended since `since`, across all profiles.
|
|
25
|
+
* Returns one batch per session, parallel to readCcTranscripts().
|
|
26
|
+
*/
|
|
27
|
+
export declare function readHermesSessions(since: Date, hermesHome?: string): TranscriptBatch[];
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hermes transcript reader — the nightly capture path for Nous Research Hermes.
|
|
4
|
+
*
|
|
5
|
+
* Hermes stores conversation in a SQLite state DB, one per profile:
|
|
6
|
+
* ~/.hermes/profiles/<profile>/state.db (per-profile agents: lenny, raider, nano)
|
|
7
|
+
* ~/.hermes/state.db (global, non-profile setups)
|
|
8
|
+
*
|
|
9
|
+
* Schema (relevant columns):
|
|
10
|
+
* sessions(id TEXT PK, started_at REAL, ended_at REAL, ...)
|
|
11
|
+
* messages(session_id TEXT, role TEXT, content TEXT, tool_name TEXT, timestamp REAL, active INT, ...)
|
|
12
|
+
*
|
|
13
|
+
* Compaction is NON-DESTRUCTIVE (compacted-out turns kept with active=0) and
|
|
14
|
+
* sessions are retained ~90 days — so the full history of any ended session is
|
|
15
|
+
* readable here nightly. No runtime plugin capture is needed; the Hermes plugin
|
|
16
|
+
* is recall-only.
|
|
17
|
+
*
|
|
18
|
+
* We process only ENDED sessions (ended_at set) that ended since the last run.
|
|
19
|
+
* A live session is distilled after it ends — this avoids partial-session
|
|
20
|
+
* distillation and keeps per-session dedup clean (chunks are stored as
|
|
21
|
+
* `<sessionId>#<chunkIndex>`; see nightly.ts).
|
|
22
|
+
*/
|
|
23
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
24
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
25
|
+
};
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.readHermesSessions = readHermesSessions;
|
|
28
|
+
const node_fs_1 = require("node:fs");
|
|
29
|
+
const node_path_1 = require("node:path");
|
|
30
|
+
const node_os_1 = require("node:os");
|
|
31
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
32
|
+
const HERMES_HOME = process.env.HERMES_HOME || (0, node_path_1.join)((0, node_os_1.homedir)(), ".hermes");
|
|
33
|
+
/**
|
|
34
|
+
* Session `source` values that are NOT primary conversations and must not be
|
|
35
|
+
* captured — capturing them would pollute long-term memory with automated,
|
|
36
|
+
* non-conversational runs. `cron` is Hermes' scheduled-task source (garmin
|
|
37
|
+
* syncs, daily scans, self-reflection loops). This mirrors the MemoryProvider
|
|
38
|
+
* contract's own guidance to skip non-primary `agent_context` (e.g. "cron").
|
|
39
|
+
* Everything else (cli, discord, telegram, slack, …) is a real conversation.
|
|
40
|
+
*/
|
|
41
|
+
const NON_PRIMARY_SOURCES = new Set(["cron"]);
|
|
42
|
+
/**
|
|
43
|
+
* Message roles that are metadata/noise, not conversation — mapped so the
|
|
44
|
+
* distiller's extractConversationText drops them (it skips "tool_result").
|
|
45
|
+
*/
|
|
46
|
+
const NOISE_ROLES = new Set(["tool", "session_meta"]);
|
|
47
|
+
/**
|
|
48
|
+
* Read Hermes sessions that ended since `since`, across all profiles.
|
|
49
|
+
* Returns one batch per session, parallel to readCcTranscripts().
|
|
50
|
+
*/
|
|
51
|
+
function readHermesSessions(since, hermesHome = HERMES_HOME) {
|
|
52
|
+
const batches = [];
|
|
53
|
+
const sinceEpoch = since.getTime() / 1000; // Hermes timestamps are unix seconds (REAL)
|
|
54
|
+
for (const { profile, dbPath } of discoverProfileDbs(hermesHome)) {
|
|
55
|
+
let db;
|
|
56
|
+
try {
|
|
57
|
+
db = new better_sqlite3_1.default(dbPath, { readonly: true, fileMustExist: true });
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
continue; // locked / unreadable / wrong owner — skip, retry next run
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const sessions = db
|
|
64
|
+
.prepare("SELECT id, ended_at, source FROM sessions WHERE ended_at IS NOT NULL AND ended_at > ? ORDER BY ended_at")
|
|
65
|
+
.all(sinceEpoch);
|
|
66
|
+
const msgStmt = db.prepare("SELECT role, content, tool_name, timestamp FROM messages WHERE session_id = ? ORDER BY timestamp, id");
|
|
67
|
+
for (const s of sessions) {
|
|
68
|
+
// Skip automated (non-primary) sessions — cron runs are not
|
|
69
|
+
// conversations and would pollute memory. Checked before pulling
|
|
70
|
+
// messages so we don't even read them.
|
|
71
|
+
if (NON_PRIMARY_SOURCES.has(s.source))
|
|
72
|
+
continue;
|
|
73
|
+
const rows = msgStmt.all(s.id);
|
|
74
|
+
// Skip only genuinely empty sessions. Do NOT gate on message count —
|
|
75
|
+
// a short 2-message exchange can carry a real decision. Meaningful-
|
|
76
|
+
// content is gated downstream by the post-denoise 200-char check in
|
|
77
|
+
// nightly.ts, so short-but-dense sessions aren't dropped here.
|
|
78
|
+
if (rows.length === 0)
|
|
79
|
+
continue;
|
|
80
|
+
// Map Hermes rows to the shape extractConversationText() understands
|
|
81
|
+
// (it reads m.role + m.content). Metadata/noise roles (tool results,
|
|
82
|
+
// session_meta) are relabelled "tool_result" so the distiller drops them.
|
|
83
|
+
const entries = rows.map((r) => ({
|
|
84
|
+
role: NOISE_ROLES.has(r.role) ? "tool_result" : r.role,
|
|
85
|
+
content: r.content ?? "",
|
|
86
|
+
}));
|
|
87
|
+
const endTs = s.ended_at ?? rows[rows.length - 1].timestamp;
|
|
88
|
+
batches.push({
|
|
89
|
+
sessionId: s.id,
|
|
90
|
+
projectName: profile,
|
|
91
|
+
sourceAgent: `hermes/${profile}`,
|
|
92
|
+
date: new Date(endTs * 1000).toISOString().slice(0, 10),
|
|
93
|
+
entries,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Query failed (schema drift on a Hermes upgrade) — skip this DB, don't crash the run.
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
db.close();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return batches;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Find every Hermes state DB: one per profile, plus the global DB for
|
|
108
|
+
* non-profile installs. The profile name becomes the provenance label.
|
|
109
|
+
*/
|
|
110
|
+
function discoverProfileDbs(hermesHome) {
|
|
111
|
+
const out = [];
|
|
112
|
+
const profilesDir = (0, node_path_1.join)(hermesHome, "profiles");
|
|
113
|
+
try {
|
|
114
|
+
for (const entry of (0, node_fs_1.readdirSync)(profilesDir)) {
|
|
115
|
+
if (entry.startsWith(".") || entry.startsWith("_"))
|
|
116
|
+
continue;
|
|
117
|
+
const dbPath = (0, node_path_1.join)(profilesDir, entry, "state.db");
|
|
118
|
+
try {
|
|
119
|
+
if ((0, node_fs_1.statSync)(dbPath).isFile())
|
|
120
|
+
out.push({ profile: entry, dbPath });
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// no state.db for this profile
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// no profiles dir
|
|
129
|
+
}
|
|
130
|
+
const globalDb = (0, node_path_1.join)(hermesHome, "state.db");
|
|
131
|
+
if ((0, node_fs_1.existsSync)(globalDb))
|
|
132
|
+
out.push({ profile: "default", dbPath: globalDb });
|
|
133
|
+
return out;
|
|
134
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Hicortex OpenClaw Plugin — Long-term Memory That Learns.
|
|
2
|
+
* Hicortex OpenClaw Plugin — Long-term Memory That Learns. (0.10.0)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Thin-client model: the plugin requires a Hicortex server (co-located at
|
|
5
|
+
* http://127.0.0.1:8787 by default, or a remote URL via `serverUrl` config).
|
|
6
|
+
* No local database, no local LLM, no embedder, no consolidation scheduler.
|
|
7
|
+
*
|
|
8
|
+
* Install once: `openclaw plugins install @gamaze/hicortex`
|
|
9
|
+
* Run server: `npx @gamaze/hicortex init`
|
|
10
|
+
*
|
|
11
|
+
* Responsibilities (recall-only adapter, like the Hermes plugin):
|
|
12
|
+
* - before_agent_start → GET /lessons (fail-soft, 3s timeout) → inject context
|
|
13
|
+
* - Tools → HTTP proxies to /search, /context, /ingest, /lessons
|
|
14
|
+
*
|
|
15
|
+
* CAPTURE IS NOT THIS PLUGIN'S JOB. OpenClaw persists sessions at
|
|
16
|
+
* ~/.openclaw/agents/<agentId>/sessions/*.jsonl in the Pi v3 format; the
|
|
17
|
+
* machine's Hicortex nightly reads them via oc-transcript-reader.ts —
|
|
18
|
+
* canonical nightly-from-logs, same as CC JSONL and Hermes state.db.
|
|
7
19
|
*/
|
|
8
20
|
declare const _default: {
|
|
9
21
|
id: string;
|