@gamaze/hicortex 0.12.0 → 0.13.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 +20 -1
- package/assets/context.html +115 -6
- package/dist/classify-domains.js +2 -2
- package/dist/cli-args.d.ts +16 -0
- package/dist/cli-args.js +30 -0
- package/dist/cli.js +13 -1
- package/dist/context-cli.d.ts +14 -3
- package/dist/context-cli.js +73 -22
- package/dist/context-store.d.ts +108 -2
- package/dist/context-store.js +308 -16
- package/dist/db.js +2 -1
- package/dist/features.js +2 -3
- package/dist/index.js +75 -29
- package/dist/init.d.ts +40 -0
- package/dist/init.js +175 -4
- package/dist/lessons-context.d.ts +56 -0
- package/dist/lessons-context.js +79 -28
- package/dist/mcp-server.js +18 -4
- package/dist/nightly-status.js +2 -1
- package/dist/nightly.js +2 -2
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +17 -0
- package/dist/relink.js +2 -2
- package/dist/retrieval.d.ts +1 -1
- package/dist/retrieval.js +2 -2
- package/dist/state.js +2 -2
- package/dist/status.d.ts +9 -0
- package/dist/status.js +31 -5
- package/dist/uninstall.js +2 -1
- package/hermes-plugin/hicortex/README.md +15 -2
- package/hermes-plugin/hicortex/client.py +7 -0
- package/hermes-plugin/hicortex/config.py +10 -0
- package/hermes-plugin/hicortex/plugin.yaml +2 -2
- package/hermes-plugin/hicortex/provider.py +134 -1
- package/package.json +2 -2
package/dist/lessons-context.js
CHANGED
|
@@ -23,30 +23,27 @@
|
|
|
23
23
|
* CC session, and a broken /context fetch must never blank the whole output.
|
|
24
24
|
*/
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.titleCaseSection = titleCaseSection;
|
|
27
|
+
exports.orderSectionNames = orderSectionNames;
|
|
28
|
+
exports.renderContextBlock = renderContextBlock;
|
|
29
|
+
exports.gateAndRenderContext = gateAndRenderContext;
|
|
26
30
|
exports.fetchLessonsContext = fetchLessonsContext;
|
|
27
31
|
const node_fs_1 = require("node:fs");
|
|
28
32
|
const node_path_1 = require("node:path");
|
|
29
|
-
const
|
|
33
|
+
const context_store_js_1 = require("./context-store.js");
|
|
30
34
|
const features_js_1 = require("./features.js");
|
|
31
35
|
const extensions_js_1 = require("./extensions.js");
|
|
32
36
|
const state_js_1 = require("./state.js");
|
|
37
|
+
const paths_js_1 = require("./paths.js");
|
|
33
38
|
const DEFAULT_PORT = 8787;
|
|
34
39
|
/** Harness name this hook injects for — used to self-gate on GET /context `clients`. */
|
|
35
40
|
const THIS_HARNESS = "cc";
|
|
36
|
-
/**
|
|
37
|
-
* Resolve the Hicortex home. Honors HICORTEX_HOME (matches the HICORTEX_DB_PATH
|
|
38
|
-
* env convention in db.ts) so headless/scratch installs can point elsewhere;
|
|
39
|
-
* defaults to ~/.hicortex. Resolved per-call so tests and env changes apply.
|
|
40
|
-
*/
|
|
41
|
-
function hicortexHome() {
|
|
42
|
-
return process.env.HICORTEX_HOME ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".hicortex");
|
|
43
|
-
}
|
|
44
41
|
/**
|
|
45
42
|
* Read ~/.hicortex/config.json and resolve the server URL + auth token, or
|
|
46
43
|
* null when there is no usable config (server not set up yet — fail soft).
|
|
47
44
|
*/
|
|
48
45
|
function resolveConfig() {
|
|
49
|
-
const home = hicortexHome();
|
|
46
|
+
const home = (0, paths_js_1.hicortexHome)();
|
|
50
47
|
let config;
|
|
51
48
|
try {
|
|
52
49
|
config = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(home, "config.json"), "utf-8"));
|
|
@@ -58,7 +55,13 @@ function resolveConfig() {
|
|
|
58
55
|
const serverUrl = config.mode === "client" && typeof config.serverUrl === "string"
|
|
59
56
|
? config.serverUrl.replace(/\/+$/, "")
|
|
60
57
|
: `http://127.0.0.1:${config.port ?? DEFAULT_PORT}`;
|
|
61
|
-
|
|
58
|
+
// Per-agent context id (0.13) via the shared resolver, so the id sent here
|
|
59
|
+
// always matches what `hicortex status` reports. No configured agentName →
|
|
60
|
+
// agentId null → NO ?agent= (bare fetch): CC's default is the shared global
|
|
61
|
+
// context. A configured agentName that sanitizes to null → agentId null too
|
|
62
|
+
// (NO ?agent=), never a 400 that the fail-soft hook would silently swallow.
|
|
63
|
+
const agentName = (0, context_store_js_1.resolveAgentIdentity)(config).agentId;
|
|
64
|
+
return { serverUrl, authToken: config.authToken, home, agentName };
|
|
62
65
|
}
|
|
63
66
|
function authHeaders(authToken) {
|
|
64
67
|
return authToken ? { "Authorization": `Bearer ${authToken}` } : {};
|
|
@@ -120,6 +123,8 @@ async function fetchLessonsBlock(cfg) {
|
|
|
120
123
|
/**
|
|
121
124
|
* Title-case a section name for its heading: split on `-`/`_`, capitalize each
|
|
122
125
|
* word ("user" → "User", "my_notes" → "My Notes").
|
|
126
|
+
* Exported so the OC plugin (index.ts) renders the `## Context` block
|
|
127
|
+
* identically to the CC hook rather than duplicating the logic.
|
|
123
128
|
*/
|
|
124
129
|
function titleCaseSection(name) {
|
|
125
130
|
return name
|
|
@@ -132,7 +137,7 @@ function titleCaseSection(name) {
|
|
|
132
137
|
* Stable section ordering: `user` first, then `rules` (the seeded primary
|
|
133
138
|
* sections, spec §8), then every other section alphabetically. Server-side
|
|
134
139
|
* enumeration order (readdirSync) is FS-dependent, so we sort here for a
|
|
135
|
-
* deterministic injection block.
|
|
140
|
+
* deterministic injection block. Exported for reuse by the OC plugin.
|
|
136
141
|
*/
|
|
137
142
|
function orderSectionNames(names) {
|
|
138
143
|
const primaries = ["user", "rules"].filter((p) => names.includes(p));
|
|
@@ -140,23 +145,13 @@ function orderSectionNames(names) {
|
|
|
140
145
|
return [...primaries, ...rest];
|
|
141
146
|
}
|
|
142
147
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
148
|
+
* Render the `## Context` block from a resolved section map, or null when there
|
|
149
|
+
* is nothing to inject (no sections, or every section blank after trimming).
|
|
150
|
+
* Pure — no gating, no I/O. Shared verbatim by the CC hook and the OC plugin so
|
|
151
|
+
* both harnesses emit an identical block. Sections are ordered (user, rules,
|
|
152
|
+
* then alphabetical) and rendered under title-cased `###` headings.
|
|
146
153
|
*/
|
|
147
|
-
|
|
148
|
-
const resp = await fetch(`${cfg.serverUrl}/context`, {
|
|
149
|
-
headers: authHeaders(cfg.authToken),
|
|
150
|
-
signal: AbortSignal.timeout(3000),
|
|
151
|
-
});
|
|
152
|
-
if (!resp.ok)
|
|
153
|
-
return null;
|
|
154
|
-
const data = await resp.json();
|
|
155
|
-
// Self-gate: only inject when this harness is in the server-resolved list.
|
|
156
|
-
const clients = Array.isArray(data.clients) ? data.clients : [];
|
|
157
|
-
if (!clients.includes(THIS_HARNESS))
|
|
158
|
-
return null;
|
|
159
|
-
const sections = data.sections;
|
|
154
|
+
function renderContextBlock(sections) {
|
|
160
155
|
if (!sections || typeof sections !== "object" || Array.isArray(sections))
|
|
161
156
|
return null;
|
|
162
157
|
const names = orderSectionNames(Object.keys(sections));
|
|
@@ -171,6 +166,62 @@ async function fetchContextBlock(cfg) {
|
|
|
171
166
|
return null;
|
|
172
167
|
return ["## Context", "", ...bodyParts].join("\n");
|
|
173
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Gate a GET /context response and render the `## Context` block, or null when
|
|
171
|
+
* nothing should be injected: `harness` not in the server-resolved `clients`,
|
|
172
|
+
* an empty/blank section set, or — when `requireAgentEcho` — a response that
|
|
173
|
+
* does not echo `agent`. The SINGLE gate used by both CC and OC so the two can
|
|
174
|
+
* never drift (the Python Hermes plugin `provider.py::_context_block` mirrors
|
|
175
|
+
* this logic — keep them in sync).
|
|
176
|
+
*
|
|
177
|
+
* `requireAgentEcho` is the old-server guard, and it is the CALLER's decision:
|
|
178
|
+
* - OC passes `agentId !== null` — when it actually sent an id, a 0.12 server
|
|
179
|
+
* that ignores `?agent=` (200 global, no echo) must NOT leak global context
|
|
180
|
+
* into every persona; on a bare fetch (no id) the guard is off (amendment
|
|
181
|
+
* A2).
|
|
182
|
+
* - CC passes `false` ALWAYS and deliberately (see the call site): a thin CC
|
|
183
|
+
* client auto-upgrades via npx BEFORE bedrock does, so during the upgrade
|
|
184
|
+
* window it talks to a 0.12 server that cannot hold ANY per-agent config —
|
|
185
|
+
* global IS the operator's intended state there, and a guard would instead
|
|
186
|
+
* blank ALL context for every CC session in that window.
|
|
187
|
+
*/
|
|
188
|
+
function gateAndRenderContext(data, harness, opts) {
|
|
189
|
+
if (!data || typeof data !== "object")
|
|
190
|
+
return null;
|
|
191
|
+
const clients = Array.isArray(data.clients) ? data.clients : [];
|
|
192
|
+
if (!clients.includes(harness))
|
|
193
|
+
return null;
|
|
194
|
+
if (opts.requireAgentEcho && typeof data.agent !== "string")
|
|
195
|
+
return null;
|
|
196
|
+
return renderContextBlock(data.sections ?? {});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Fetch /context and build the `## Context` block, or null when nothing should
|
|
200
|
+
* be injected: non-2xx, this harness not in `clients`, no sections, or all
|
|
201
|
+
* sections empty. Throws propagate to the caller's fail-soft catch.
|
|
202
|
+
*/
|
|
203
|
+
async function fetchContextBlock(cfg) {
|
|
204
|
+
// Send ?agent= only when we have a valid id; the server does the merge and
|
|
205
|
+
// returns the resolved sections, so the hook stays dumb (no client-side mode
|
|
206
|
+
// logic). A null id (CC's default: no configured agentName, or a configured
|
|
207
|
+
// value that sanitizes to nothing) → bare /context → the shared global set.
|
|
208
|
+
const url = cfg.agentName
|
|
209
|
+
? `${cfg.serverUrl}/context?agent=${encodeURIComponent(cfg.agentName)}`
|
|
210
|
+
: `${cfg.serverUrl}/context`;
|
|
211
|
+
const resp = await fetch(url, {
|
|
212
|
+
headers: authHeaders(cfg.authToken),
|
|
213
|
+
signal: AbortSignal.timeout(3000),
|
|
214
|
+
});
|
|
215
|
+
if (!resp.ok)
|
|
216
|
+
return null;
|
|
217
|
+
const data = await resp.json();
|
|
218
|
+
// CC deliberately passes requireAgentEcho: false (NOT the OC/Hermes old-server
|
|
219
|
+
// guard). A thin CC client auto-upgrades via npx BEFORE bedrock does, so
|
|
220
|
+
// mid-upgrade it may hit a 0.12 server that returns global context with no
|
|
221
|
+
// `agent` echo — and a 0.12 server cannot hold per-agent config, so global is
|
|
222
|
+
// the intended state. Guarding here would blank ALL CC context in that window.
|
|
223
|
+
return gateAndRenderContext(data, THIS_HARNESS, { requireAgentEcho: false });
|
|
224
|
+
}
|
|
174
225
|
/**
|
|
175
226
|
* Fetch context + lessons concurrently and return the combined Markdown block,
|
|
176
227
|
* or null when neither yields anything (nothing to inject; caller prints
|
package/dist/mcp-server.js
CHANGED
|
@@ -81,6 +81,10 @@ let stateDir = "";
|
|
|
81
81
|
// Resolved contextClients list (spec §2) — the harness names allowed to inject
|
|
82
82
|
// the standing context layer. Echoed by GET /context so each hook self-gates.
|
|
83
83
|
let contextClients = ["cc"];
|
|
84
|
+
// Resolved contextAgents map (0.13) — agent id → mode (override/global/off).
|
|
85
|
+
// Read once at boot (like contextClients); the drop-in-a-dir presence path is
|
|
86
|
+
// per-request, so only explicit config entries need a daemon restart to apply.
|
|
87
|
+
let contextAgents = {};
|
|
84
88
|
// Cache detectChunkSize results keyed by "<provider>/<model>@<baseUrl>" so we
|
|
85
89
|
// probe each endpoint once per server boot rather than once per /distill request.
|
|
86
90
|
const chunkSizeCache = new Map();
|
|
@@ -122,7 +126,7 @@ function createMcpServer() {
|
|
|
122
126
|
if (!db)
|
|
123
127
|
return { content: [{ type: "text", text: "Hicortex not initialized" }], isError: true };
|
|
124
128
|
try {
|
|
125
|
-
const results = retrieval.
|
|
129
|
+
const results = retrieval.searchRecent(db, { project, limit });
|
|
126
130
|
return { content: [{ type: "text", text: formatResults(results) }] };
|
|
127
131
|
}
|
|
128
132
|
catch (err) {
|
|
@@ -436,6 +440,16 @@ async function startServer(options = {}) {
|
|
|
436
440
|
console.warn(`[hicortex] Ignoring unknown contextClients: ${resolvedClients.dropped.join(", ")} ` +
|
|
437
441
|
`(known: cc, hermes, oc)`);
|
|
438
442
|
}
|
|
443
|
+
// Per-agent context (0.13): resolve the config-declared modes. Warn once per
|
|
444
|
+
// boot on dropped entries (bad key or bad mode) so typos surface. NOTE: this
|
|
445
|
+
// map is boot-time; editing contextAgents needs a daemon restart. Dropping an
|
|
446
|
+
// agents/<id> dir onto disk takes effect immediately (per-request presence).
|
|
447
|
+
const resolvedAgents = (0, context_store_js_1.resolveContextAgents)(savedConfig?.contextAgents);
|
|
448
|
+
contextAgents = resolvedAgents.agents;
|
|
449
|
+
if (resolvedAgents.dropped.length > 0) {
|
|
450
|
+
console.warn(`[hicortex] Ignoring invalid contextAgents entries: ${resolvedAgents.dropped.join(", ")} ` +
|
|
451
|
+
`(keys must match ^[a-z0-9][a-z0-9_-]*$; modes must be override|global|off)`);
|
|
452
|
+
}
|
|
439
453
|
// Express app
|
|
440
454
|
const app = (0, express_1.default)();
|
|
441
455
|
// Raise the body limit — whole-session denoised transcripts exceed the 100 kB default.
|
|
@@ -593,7 +607,7 @@ async function startServer(options = {}) {
|
|
|
593
607
|
? req.query.privacy.split(",").map((s) => s.trim()).filter(Boolean)
|
|
594
608
|
: undefined;
|
|
595
609
|
try {
|
|
596
|
-
const results = retrieval.
|
|
610
|
+
const results = retrieval.searchRecent(db, { project, limit, privacy });
|
|
597
611
|
res.json({ results });
|
|
598
612
|
}
|
|
599
613
|
catch (err) {
|
|
@@ -620,7 +634,7 @@ async function startServer(options = {}) {
|
|
|
620
634
|
// which the tests exercise directly — no mirror-app drift.
|
|
621
635
|
app.get("/context", (req, res) => {
|
|
622
636
|
try {
|
|
623
|
-
const r = (0, context_store_js_1.handleContextGet)((0, node_path_1.join)(stateDir, "context"), contextClients, req.query);
|
|
637
|
+
const r = (0, context_store_js_1.handleContextGet)((0, node_path_1.join)(stateDir, "context"), contextClients, req.query, contextAgents);
|
|
624
638
|
res.status(r.status).json(r.body);
|
|
625
639
|
}
|
|
626
640
|
catch (err) {
|
|
@@ -629,7 +643,7 @@ async function startServer(options = {}) {
|
|
|
629
643
|
});
|
|
630
644
|
app.put("/context", (req, res) => {
|
|
631
645
|
try {
|
|
632
|
-
const r = (0, context_store_js_1.handleContextPut)((0, node_path_1.join)(stateDir, "context"), req.body);
|
|
646
|
+
const r = (0, context_store_js_1.handleContextPut)((0, node_path_1.join)(stateDir, "context"), req.body, req.query, contextAgents);
|
|
633
647
|
if (r.warn)
|
|
634
648
|
console.warn(`[hicortex] ${r.warn}`);
|
|
635
649
|
res.status(r.status).json(r.body);
|
package/dist/nightly-status.js
CHANGED
|
@@ -11,13 +11,14 @@
|
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.showNightlyStatus = showNightlyStatus;
|
|
14
|
+
const paths_js_1 = require("./paths.js");
|
|
14
15
|
const node_fs_1 = require("node:fs");
|
|
15
16
|
const node_path_1 = require("node:path");
|
|
16
17
|
const node_os_1 = require("node:os");
|
|
17
18
|
const node_child_process_1 = require("node:child_process");
|
|
18
19
|
const db_js_1 = require("./db.js");
|
|
19
20
|
const state_js_1 = require("./state.js");
|
|
20
|
-
const HICORTEX_HOME = (0,
|
|
21
|
+
const HICORTEX_HOME = (0, paths_js_1.hicortexHome)();
|
|
21
22
|
const CONFIG_PATH = (0, node_path_1.join)(HICORTEX_HOME, "config.json");
|
|
22
23
|
async function showNightlyStatus() {
|
|
23
24
|
console.log("Hicortex Nightly Pipeline Status");
|
package/dist/nightly.js
CHANGED
|
@@ -46,9 +46,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
46
46
|
})();
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
48
|
exports.runNightly = runNightly;
|
|
49
|
+
const paths_js_1 = require("./paths.js");
|
|
49
50
|
const node_fs_1 = require("node:fs");
|
|
50
51
|
const node_path_1 = require("node:path");
|
|
51
|
-
const node_os_1 = require("node:os");
|
|
52
52
|
let VERSION = "0.0.0";
|
|
53
53
|
try {
|
|
54
54
|
VERSION = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "..", "package.json"), "utf-8")).version;
|
|
@@ -69,7 +69,7 @@ const oc_transcript_reader_js_1 = require("./oc-transcript-reader.js");
|
|
|
69
69
|
const features_js_1 = require("./features.js");
|
|
70
70
|
const state_js_1 = require("./state.js");
|
|
71
71
|
const telemetry_js_1 = require("./telemetry.js");
|
|
72
|
-
const HICORTEX_HOME = (0,
|
|
72
|
+
const HICORTEX_HOME = (0, paths_js_1.hicortexHome)();
|
|
73
73
|
function readNightlyConfig(stateDir) {
|
|
74
74
|
try {
|
|
75
75
|
const configPath = (0, node_path_1.join)(stateDir, "config.json");
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function hicortexHome(): string;
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hicortexHome = hicortexHome;
|
|
4
|
+
/**
|
|
5
|
+
* Canonical Hicortex home resolution — the single source of truth (#174).
|
|
6
|
+
*
|
|
7
|
+
* Honors the HICORTEX_HOME env override (a headless/test seam, mirroring the
|
|
8
|
+
* HICORTEX_DB_PATH convention in db.ts); otherwise defaults to ~/.hicortex.
|
|
9
|
+
* Every module that needs the home dir routes through here, so the override
|
|
10
|
+
* behaves consistently across all commands instead of being honored by some
|
|
11
|
+
* (context-cli, lessons-context) and hardcoded away by others.
|
|
12
|
+
*/
|
|
13
|
+
const node_os_1 = require("node:os");
|
|
14
|
+
const node_path_1 = require("node:path");
|
|
15
|
+
function hicortexHome() {
|
|
16
|
+
return process.env.HICORTEX_HOME ?? (0, node_path_1.join)((0, node_os_1.homedir)(), ".hicortex");
|
|
17
|
+
}
|
package/dist/relink.js
CHANGED
|
@@ -74,14 +74,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
74
74
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
75
75
|
exports.getStoredEmbedding = getStoredEmbedding;
|
|
76
76
|
exports.runRelink = runRelink;
|
|
77
|
+
const paths_js_1 = require("./paths.js");
|
|
77
78
|
const node_fs_1 = require("node:fs");
|
|
78
79
|
const node_path_1 = require("node:path");
|
|
79
|
-
const node_os_1 = require("node:os");
|
|
80
80
|
const db_js_1 = require("./db.js");
|
|
81
81
|
const storage = __importStar(require("./storage.js"));
|
|
82
82
|
const consolidate_js_1 = require("./consolidate.js");
|
|
83
83
|
const state_js_1 = require("./state.js");
|
|
84
|
-
const HICORTEX_HOME = (0,
|
|
84
|
+
const HICORTEX_HOME = (0, paths_js_1.hicortexHome)();
|
|
85
85
|
function readConfig(stateDir) {
|
|
86
86
|
try {
|
|
87
87
|
return JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(stateDir, "config.json"), "utf-8"));
|
package/dist/retrieval.d.ts
CHANGED
|
@@ -55,7 +55,7 @@ export declare function retrieve(db: Database.Database, embedFn: EmbedFn, query:
|
|
|
55
55
|
/**
|
|
56
56
|
* Get recent context, optionally filtered by project and privacy.
|
|
57
57
|
*/
|
|
58
|
-
export declare function
|
|
58
|
+
export declare function searchRecent(db: Database.Database, options?: {
|
|
59
59
|
project?: string | null;
|
|
60
60
|
limit?: number;
|
|
61
61
|
privacy?: string[];
|
package/dist/retrieval.js
CHANGED
|
@@ -52,7 +52,7 @@ exports.l2ToCosine = l2ToCosine;
|
|
|
52
52
|
exports.effectiveStrength = effectiveStrength;
|
|
53
53
|
exports.computeScore = computeScore;
|
|
54
54
|
exports.retrieve = retrieve;
|
|
55
|
-
exports.
|
|
55
|
+
exports.searchRecent = searchRecent;
|
|
56
56
|
const storage = __importStar(require("./storage.js"));
|
|
57
57
|
const BASE_DECAY = 0.0005;
|
|
58
58
|
/**
|
|
@@ -315,7 +315,7 @@ async function retrieve(db, embedFn, query, options) {
|
|
|
315
315
|
/**
|
|
316
316
|
* Get recent context, optionally filtered by project and privacy.
|
|
317
317
|
*/
|
|
318
|
-
function
|
|
318
|
+
function searchRecent(db, options) {
|
|
319
319
|
const limit = options?.limit ?? 10;
|
|
320
320
|
const project = options?.project;
|
|
321
321
|
const privacy = options?.privacy;
|
package/dist/state.js
CHANGED
|
@@ -23,10 +23,10 @@ exports.saveState = saveState;
|
|
|
23
23
|
exports.updateState = updateState;
|
|
24
24
|
exports.migrateLegacyState = migrateLegacyState;
|
|
25
25
|
exports.describeLastNightly = describeLastNightly;
|
|
26
|
+
const paths_js_1 = require("./paths.js");
|
|
26
27
|
const node_fs_1 = require("node:fs");
|
|
27
28
|
const node_path_1 = require("node:path");
|
|
28
|
-
const
|
|
29
|
-
const HICORTEX_HOME = (0, node_path_1.join)((0, node_os_1.homedir)(), ".hicortex");
|
|
29
|
+
const HICORTEX_HOME = (0, paths_js_1.hicortexHome)();
|
|
30
30
|
const STATE_FILE = "state.json";
|
|
31
31
|
/**
|
|
32
32
|
* Load the state file. Returns an empty state if the file is missing
|
package/dist/status.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Hicortex status — show current configuration and stats.
|
|
3
3
|
*/
|
|
4
|
+
/**
|
|
5
|
+
* The value shown after "Agent name:" in `hicortex status` (#179, A3). Reports
|
|
6
|
+
* EXACTLY what the CC hook resolves (shared `resolveAgentIdentity`), so the
|
|
7
|
+
* operator never keys `contextAgents`/`agents/<id>/` on an id the install does
|
|
8
|
+
* not actually send. Unset → the install sends no `?agent=` and shares the
|
|
9
|
+
* global context (CC default). A configured-but-unsanitizable value is called
|
|
10
|
+
* out as invalid (the hook sends none) rather than silently accepted.
|
|
11
|
+
*/
|
|
12
|
+
export declare function statusAgentLine(config: Record<string, unknown>): string;
|
|
4
13
|
export declare function runStatus(): Promise<void>;
|
package/dist/status.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* Hicortex status — show current configuration and stats.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.statusAgentLine = statusAgentLine;
|
|
6
7
|
exports.runStatus = runStatus;
|
|
8
|
+
const paths_js_1 = require("./paths.js");
|
|
7
9
|
const node_fs_1 = require("node:fs");
|
|
8
10
|
const node_path_1 = require("node:path");
|
|
9
11
|
const node_os_1 = require("node:os");
|
|
@@ -11,9 +13,29 @@ const node_child_process_1 = require("node:child_process");
|
|
|
11
13
|
const db_js_1 = require("./db.js");
|
|
12
14
|
const features_js_1 = require("./features.js");
|
|
13
15
|
const state_js_1 = require("./state.js");
|
|
14
|
-
const
|
|
16
|
+
const context_store_js_1 = require("./context-store.js");
|
|
17
|
+
const HICORTEX_HOME = (0, paths_js_1.hicortexHome)();
|
|
15
18
|
const CC_SETTINGS = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude", "settings.json");
|
|
16
19
|
const OC_CONFIG = (0, node_path_1.join)((0, node_os_1.homedir)(), ".openclaw", "openclaw.json");
|
|
20
|
+
/**
|
|
21
|
+
* The value shown after "Agent name:" in `hicortex status` (#179, A3). Reports
|
|
22
|
+
* EXACTLY what the CC hook resolves (shared `resolveAgentIdentity`), so the
|
|
23
|
+
* operator never keys `contextAgents`/`agents/<id>/` on an id the install does
|
|
24
|
+
* not actually send. Unset → the install sends no `?agent=` and shares the
|
|
25
|
+
* global context (CC default). A configured-but-unsanitizable value is called
|
|
26
|
+
* out as invalid (the hook sends none) rather than silently accepted.
|
|
27
|
+
*/
|
|
28
|
+
function statusAgentLine(config) {
|
|
29
|
+
const id = (0, context_store_js_1.resolveAgentIdentity)(config);
|
|
30
|
+
switch (id.source) {
|
|
31
|
+
case "configured":
|
|
32
|
+
return id.agentId;
|
|
33
|
+
case "invalid-config":
|
|
34
|
+
return `(invalid configured value "${id.rawConfigured}" — fix config.agentName; hook sends none)`;
|
|
35
|
+
default: // unset
|
|
36
|
+
return "(not set — global context)";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
17
39
|
async function runStatus() {
|
|
18
40
|
console.log("Hicortex Status");
|
|
19
41
|
console.log("─".repeat(40));
|
|
@@ -41,11 +63,12 @@ async function runStatus() {
|
|
|
41
63
|
let licenseKey = "";
|
|
42
64
|
let savedAuthToken = "";
|
|
43
65
|
let isClientMode = false;
|
|
66
|
+
let parsedConfig = {};
|
|
44
67
|
try {
|
|
45
|
-
|
|
46
|
-
licenseKey =
|
|
47
|
-
savedAuthToken =
|
|
48
|
-
isClientMode =
|
|
68
|
+
parsedConfig = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
|
|
69
|
+
licenseKey = parsedConfig.licenseKey ?? "";
|
|
70
|
+
savedAuthToken = parsedConfig.authToken ?? "";
|
|
71
|
+
isClientMode = parsedConfig.mode === "client";
|
|
49
72
|
}
|
|
50
73
|
catch { /* no config */ }
|
|
51
74
|
const validated = (0, features_js_1.getValidatedLicense)();
|
|
@@ -64,6 +87,9 @@ async function runStatus() {
|
|
|
64
87
|
else if (!isClientMode && !savedAuthToken) {
|
|
65
88
|
console.log(`Auth token: not configured (run: npx @gamaze/hicortex init)`);
|
|
66
89
|
}
|
|
90
|
+
// Per-agent context id (#179) — the id this install sends as ?agent= and the
|
|
91
|
+
// key operators use for contextAgents / agents/<id>/ dirs.
|
|
92
|
+
console.log(`Agent name: ${statusAgentLine(parsedConfig)}`);
|
|
67
93
|
console.log();
|
|
68
94
|
// Adapters
|
|
69
95
|
console.log("Adapters:");
|
package/dist/uninstall.js
CHANGED
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.runUninstall = runUninstall;
|
|
8
|
+
const paths_js_1 = require("./paths.js");
|
|
8
9
|
const node_fs_1 = require("node:fs");
|
|
9
10
|
const node_path_1 = require("node:path");
|
|
10
11
|
const node_os_1 = require("node:os");
|
|
11
12
|
const node_child_process_1 = require("node:child_process");
|
|
12
13
|
const node_readline_1 = require("node:readline");
|
|
13
14
|
const claude_md_js_1 = require("./claude-md.js");
|
|
14
|
-
const HICORTEX_HOME = (0,
|
|
15
|
+
const HICORTEX_HOME = (0, paths_js_1.hicortexHome)();
|
|
15
16
|
const CC_SETTINGS = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude", "settings.json");
|
|
16
17
|
const CC_COMMANDS_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude", "commands");
|
|
17
18
|
const CLAUDE_MD = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude", "CLAUDE.md");
|
|
@@ -14,11 +14,24 @@ Gives [Hermes](https://github.com/nousresearch/hermes-agent) agents self-learnin
|
|
|
14
14
|
|---|---|---|
|
|
15
15
|
| `prefetch(query)` | recall relevant memories before each turn | `GET /search` |
|
|
16
16
|
| `queue_prefetch(query)` | background recall for the next turn | `GET /search` |
|
|
17
|
-
| `system_prompt_block()` | inject distilled lessons + memory index | `GET /lessons` |
|
|
17
|
+
| `system_prompt_block()` | inject per-agent standing context + distilled lessons + memory index | `GET /context`, `GET /lessons` |
|
|
18
18
|
| `get_tool_schemas()` | exposes the 8 unified tools | see tool table below |
|
|
19
19
|
|
|
20
20
|
That's the whole surface. No `sync_turn`, no compaction/session-end capture — those are intentionally absent.
|
|
21
21
|
|
|
22
|
+
### Per-agent standing context (0.13)
|
|
23
|
+
|
|
24
|
+
`system_prompt_block()` also injects the hand-edited **standing context layer** (`## Context`, above the lessons block) — "who you are + how to work", distinct from episodic memory. The server resolves it **per agent**: this profile's own sections override the global set (`override`), or it can be `global` or `off`. See the main repo's `/context` layer docs.
|
|
25
|
+
|
|
26
|
+
The plugin sends its **profile name** as `?agent=`, resolved in this order:
|
|
27
|
+
|
|
28
|
+
1. `agent_name` in the plugin config (explicit override);
|
|
29
|
+
2. the `HERMES_PROFILE` environment variable;
|
|
30
|
+
3. a `HERMES_HOME` ending in `profiles/<name>` (the per-profile install path);
|
|
31
|
+
4. none → the global context (backward compatible).
|
|
32
|
+
|
|
33
|
+
Leave `agent_name` blank to auto-derive (2–4). Context injection needs a Hicortex server **≥ 0.13**; against an older server the plugin detects the missing per-agent support and injects no context (lessons are unaffected). Context and lessons fail soft independently — a context failure never costs the lessons block.
|
|
34
|
+
|
|
22
35
|
### Tools (unified 8)
|
|
23
36
|
|
|
24
37
|
| Tool | REST call | Description |
|
|
@@ -57,7 +70,7 @@ hermes memory setup # select "hicortex", enter the server URL/token when promp
|
|
|
57
70
|
|
|
58
71
|
Run it once per profile if you use Hermes profiles. Hermes allows **one** external memory provider at a time, so disable Honcho (or any other) first, then restart the gateway.
|
|
59
72
|
|
|
60
|
-
Config fields (`hicortex_url`, `default_project`, `recall_limit`, `privacy_filter`) can also be written to `$HERMES_HOME/plugins/hicortex/config.json` directly. The auth token is a **secret** — set it via env, not the JSON file:
|
|
73
|
+
Config fields (`hicortex_url`, `default_project`, `recall_limit`, `privacy_filter`, `agent_name`) can also be written to `$HERMES_HOME/plugins/hicortex/config.json` directly. `agent_name` pins the per-agent context id for this profile (leave blank to auto-derive — see [Per-agent standing context](#per-agent-standing-context-013)). The auth token is a **secret** — set it via env, not the JSON file:
|
|
61
74
|
|
|
62
75
|
```bash
|
|
63
76
|
export HICORTEX_AUTH_TOKEN=hctx-default-token # or your custom token
|
|
@@ -96,6 +96,13 @@ class HicortexClient:
|
|
|
96
96
|
def lessons(self) -> dict[str, Any]:
|
|
97
97
|
return self._get("/lessons")
|
|
98
98
|
|
|
99
|
+
def context(self, agent: Optional[str] = None) -> dict[str, Any]:
|
|
100
|
+
"""Standing context layer (L2). When ``agent`` is set, the server
|
|
101
|
+
resolves the per-agent scope and echoes ``agent``/``mode`` (0.13); a
|
|
102
|
+
pre-0.13 server ignores the param and returns the global set with no
|
|
103
|
+
echo — the caller uses that echo as an old-server guard."""
|
|
104
|
+
return self._get("/context", {"agent": agent})
|
|
105
|
+
|
|
99
106
|
def index(self) -> dict[str, Any]:
|
|
100
107
|
return self._get("/index")
|
|
101
108
|
|
|
@@ -57,6 +57,16 @@ CONFIG_SCHEMA: list[dict[str, Any]] = [
|
|
|
57
57
|
"default": "WORK,PERSONAL",
|
|
58
58
|
"required": False,
|
|
59
59
|
},
|
|
60
|
+
{
|
|
61
|
+
"key": "agent_name",
|
|
62
|
+
"label": "Agent name (per-agent context)",
|
|
63
|
+
"description": (
|
|
64
|
+
"Identity sent as ?agent= when fetching the standing context layer, "
|
|
65
|
+
"so this profile gets its own context (0.13). Leave blank to "
|
|
66
|
+
"auto-derive from the running profile (HERMES_PROFILE / HERMES_HOME)."
|
|
67
|
+
),
|
|
68
|
+
"required": False,
|
|
69
|
+
},
|
|
60
70
|
# NOTE: recall-only plugin — no capture config. Capture is handled by the
|
|
61
71
|
# nightly server-side reader of each agent's session store.
|
|
62
72
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
name: hicortex
|
|
2
|
-
version: 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, recent, ingest, lessons, index, graph, update, delete) via a shared Hicortex server. Stdlib-only."
|
|
2
|
+
version: 0.6.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 plus a per-agent standing context block, and exposes the full 8-tool memory surface (search, recent, ingest, lessons, index, graph, update, delete) via a shared Hicortex server. Stdlib-only."
|
|
4
4
|
pip_dependencies: []
|
|
5
5
|
hooks: []
|
|
6
6
|
requires_env:
|