@gamaze/hicortex 0.20.0 → 0.20.2
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 +7 -3
- package/dist/consolidate.js +31 -8
- package/dist/init.d.ts +7 -0
- package/dist/init.js +117 -21
- package/dist/nightly.js +5 -2
- package/hermes-plugin/hicortex/README.md +2 -2
- package/hermes-plugin/hicortex/config.py +17 -56
- package/hermes-plugin/hicortex/plugin.yaml +2 -2
- package/hermes-plugin/hicortex/provider.py +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
# @gamaze/hicortex —
|
|
1
|
+
# @gamaze/hicortex — Shared Fleet Memory for AI Agents
|
|
2
2
|
|
|
3
3
|
<img src="https://raw.githubusercontent.com/gamaze-labs/hicortex/main/docs/dashboard-composition.png" alt="Hicortex dashboard — live screenshot: memory-type composition (episodes / lessons / facts / decisions)" width="800">
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Many agents. One shared memory. Wisdom compounds.**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Hicortex is shared long-term memory for AI agents. Install once, and every agent you run — Claude Code, Hermes, OpenClaw, Pi, OpenCode, any MCP client — draws on one memory: sessions are captured automatically, distilled into knowledge, decisions and learnings overnight, and a compact recall index is pushed into each prompt in every supported coding agent. What one agent learns, the whole fleet knows.
|
|
8
|
+
|
|
9
|
+
**Install — one command, ~2 minutes:** `npx @gamaze/hicortex init`
|
|
10
|
+
|
|
11
|
+
Self-hosted: raw sessions never leave your machine. Zero LLM calls at recall. Free for personal use.
|
|
8
12
|
|
|
9
13
|
**Website:** [hicortex.gamaze.com](https://hicortex.gamaze.com) · **Docs:** [hicortex.gamaze.com/docs](https://hicortex.gamaze.com/docs/)
|
|
10
14
|
|
package/dist/consolidate.js
CHANGED
|
@@ -293,11 +293,16 @@ function parseJsonLenient(text, fallback) {
|
|
|
293
293
|
// ---------------------------------------------------------------------------
|
|
294
294
|
// Stage 1: Pre-check
|
|
295
295
|
// ---------------------------------------------------------------------------
|
|
296
|
-
function readLastConsolidated() {
|
|
297
|
-
|
|
296
|
+
function readLastConsolidated(stateDir) {
|
|
297
|
+
// stateDir-optional for backcompat; runConsolidation threads its own
|
|
298
|
+
// stateDir so the skip decision reads the SAME state the run writes
|
|
299
|
+
// (previously this read the ambient home — a split-brain where a test or
|
|
300
|
+
// CLI caller with an isolated stateDir decided "skip" from the operator's
|
|
301
|
+
// real ~/.hicortex watermark).
|
|
302
|
+
return (0, state_js_1.loadState)(stateDir).lastConsolidated ?? "";
|
|
298
303
|
}
|
|
299
|
-
function stagePrecheck(db) {
|
|
300
|
-
const lastTs = readLastConsolidated();
|
|
304
|
+
function stagePrecheck(db, stateDir) {
|
|
305
|
+
const lastTs = readLastConsolidated(stateDir);
|
|
301
306
|
const lastDt = lastTs || "1970-01-01T00:00:00.000Z";
|
|
302
307
|
const newMemories = storage.getMemoriesSince(db, lastDt);
|
|
303
308
|
if (newMemories.length === 0) {
|
|
@@ -1372,7 +1377,7 @@ memorySoftCap) {
|
|
|
1372
1377
|
stages: {},
|
|
1373
1378
|
};
|
|
1374
1379
|
// Stage 1: Pre-check
|
|
1375
|
-
const precheck = stagePrecheck(db);
|
|
1380
|
+
const precheck = stagePrecheck(db, stateDir);
|
|
1376
1381
|
// Also check for unscored memories
|
|
1377
1382
|
const unscored = storage.getUnscoredMemories(db);
|
|
1378
1383
|
const newIds = new Set(precheck.newMemories.map((m) => m.id));
|
|
@@ -1380,7 +1385,15 @@ memorySoftCap) {
|
|
|
1380
1385
|
...precheck.newMemories,
|
|
1381
1386
|
...unscored.filter((m) => !newIds.has(m.id)),
|
|
1382
1387
|
];
|
|
1383
|
-
|
|
1388
|
+
// #194 no-fit scope: untagged rows (domain IS NULL) stay in the
|
|
1389
|
+
// re-evaluation scope — the decay/re-attempt contract ("re-halves once per
|
|
1390
|
+
// run while still below the floor") is work even on a quiet night with
|
|
1391
|
+
// nothing new. The skip below gated on new+unscored only, which the tests
|
|
1392
|
+
// never caught because stagePrecheck used to read the AMBIENT (always
|
|
1393
|
+
// empty in the suite) state instead of the run's own watermark — threading
|
|
1394
|
+
// stateDir (#357) exposed the divergence between test and production.
|
|
1395
|
+
const nofitInScope = db.prepare("SELECT COUNT(*) AS n FROM memories WHERE domain IS NULL").get().n;
|
|
1396
|
+
const skip = scoreMemories.length === 0 && nofitInScope === 0;
|
|
1384
1397
|
report.stages.precheck = {
|
|
1385
1398
|
skip,
|
|
1386
1399
|
reason: skip
|
|
@@ -1450,8 +1463,18 @@ memorySoftCap) {
|
|
|
1450
1463
|
report.status = "failed";
|
|
1451
1464
|
console.error("[hicortex] Consolidation pipeline error:", err);
|
|
1452
1465
|
}
|
|
1453
|
-
// Update last-consolidated timestamp
|
|
1454
|
-
|
|
1466
|
+
// Update last-consolidated timestamp. #357: the stages fail SOFT, so a run
|
|
1467
|
+
// against an endpoint that died mid-way still reports status "completed"
|
|
1468
|
+
// here — advancing the timestamp made state.json disagree with the
|
|
1469
|
+
// nightly's breaker-open override to endpoint_down (observed 0.20.0 soak:
|
|
1470
|
+
// endpoint_down reported, lastConsolidated advanced anyway). Gate on the
|
|
1471
|
+
// SAME signal the nightly's override reads (llm.breakerOpen) so the two
|
|
1472
|
+
// sites agree for the breaker case — keep this condition and the override
|
|
1473
|
+
// in nightly.ts in sync if either ever grows. (Sibling soft-fail paths —
|
|
1474
|
+
// e.g. a sustained-429 run, which never accrues to the breaker because a
|
|
1475
|
+
// 429 proves the endpoint answers — still advance; that is the #337
|
|
1476
|
+
// taxonomy working as designed.)
|
|
1477
|
+
if (!dryRun && report.status === "completed" && !llm.breakerOpen) {
|
|
1455
1478
|
(0, state_js_1.updateState)((s) => {
|
|
1456
1479
|
s.lastConsolidated = new Date().toISOString();
|
|
1457
1480
|
return s;
|
package/dist/init.d.ts
CHANGED
|
@@ -25,6 +25,13 @@ import type { DomainDef } from "./types.js";
|
|
|
25
25
|
* - "registered" — listed but connection not confirmed (server down / not restarted)
|
|
26
26
|
*/
|
|
27
27
|
export declare function parseMcpListStatus(mcpListOutput: string): "connected" | "registered" | "missing";
|
|
28
|
+
/**
|
|
29
|
+
* The client config's serverUrl BEFORE this run re-points it — the value init
|
|
30
|
+
* last wrote into the Hermes plugin's config (so the plugin step can tell an
|
|
31
|
+
* init-owned URL from a user-set one). Exported for tests.
|
|
32
|
+
*/
|
|
33
|
+
export declare function readPreviousServerUrl(configPath: string): string | undefined;
|
|
34
|
+
export declare function setupHermes(serverUrl: string, authToken: string, previousServerUrl?: string): void;
|
|
28
35
|
/**
|
|
29
36
|
* Parse a KEY=VALUE env file (e.g. ~/.hermes/.env or ~/.claude/settings.json env block).
|
|
30
37
|
* Handles: comments (#), quoted values, empty lines.
|
package/dist/init.js
CHANGED
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
22
|
exports.GENERIC_DEFAULT_DOMAINS = void 0;
|
|
23
23
|
exports.parseMcpListStatus = parseMcpListStatus;
|
|
24
|
+
exports.readPreviousServerUrl = readPreviousServerUrl;
|
|
25
|
+
exports.setupHermes = setupHermes;
|
|
24
26
|
exports.parseEnvFile = parseEnvFile;
|
|
25
27
|
exports.isLlmConfigured = isLlmConfigured;
|
|
26
28
|
exports.persistLlmConfig = persistLlmConfig;
|
|
@@ -399,7 +401,21 @@ function setupOpencode() {
|
|
|
399
401
|
// ---------------------------------------------------------------------------
|
|
400
402
|
// Hermes setup
|
|
401
403
|
// ---------------------------------------------------------------------------
|
|
402
|
-
|
|
404
|
+
/**
|
|
405
|
+
* The client config's serverUrl BEFORE this run re-points it — the value init
|
|
406
|
+
* last wrote into the Hermes plugin's config (so the plugin step can tell an
|
|
407
|
+
* init-owned URL from a user-set one). Exported for tests.
|
|
408
|
+
*/
|
|
409
|
+
function readPreviousServerUrl(configPath) {
|
|
410
|
+
try {
|
|
411
|
+
const { config: prev } = loadConfigStrict(configPath);
|
|
412
|
+
if (typeof prev.serverUrl === "string" && prev.serverUrl)
|
|
413
|
+
return prev.serverUrl;
|
|
414
|
+
}
|
|
415
|
+
catch { /* no prior config, or unreadable — nothing init-owned to carry */ }
|
|
416
|
+
return undefined;
|
|
417
|
+
}
|
|
418
|
+
function setupHermes(serverUrl, authToken, previousServerUrl) {
|
|
403
419
|
const pluginSource = (0, node_path_1.join)(__dirname, "..", "hermes-plugin", "hicortex");
|
|
404
420
|
if (!(0, node_fs_1.existsSync)(pluginSource)) {
|
|
405
421
|
console.log(" ⚠ Hermes plugin not found in package — skipping Hermes setup");
|
|
@@ -407,23 +423,86 @@ function setupHermes(serverUrl, authToken) {
|
|
|
407
423
|
}
|
|
408
424
|
const pluginsDir = (0, node_path_1.join)(HERMES_HOME, "plugins", "hicortex");
|
|
409
425
|
(0, node_fs_1.mkdirSync)(pluginsDir, { recursive: true });
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
426
|
+
// If the plugin was installed via `hermes plugins install` (a git checkout
|
|
427
|
+
// that actually contains the plugin — .git alone could be an empty/partial
|
|
428
|
+
// dir), do NOT copy over it — that would mix a managed checkout with
|
|
429
|
+
// copied files and break its own update path. The config below is still
|
|
430
|
+
// written: Hermes' own `memory setup` writes config.json into that tree
|
|
431
|
+
// too, so the file is expected there regardless of install path.
|
|
432
|
+
const isGitInstall = (0, node_fs_1.existsSync)((0, node_path_1.join)(pluginsDir, ".git")) && (0, node_fs_1.existsSync)((0, node_path_1.join)(pluginsDir, "plugin.yaml"));
|
|
433
|
+
if (isGitInstall) {
|
|
434
|
+
console.log(" ✓ Hermes plugin already installed (git checkout) — leaving its files, updating config only");
|
|
417
435
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
436
|
+
else {
|
|
437
|
+
const pluginFiles = (0, node_fs_1.readdirSync)(pluginSource);
|
|
438
|
+
for (const f of pluginFiles) {
|
|
439
|
+
// NEVER copy a bundled config.json over the user's: a stray one in a
|
|
440
|
+
// developer tree ships via prepack, and clobbering here would defeat
|
|
441
|
+
// the merge logic below (it runs AFTER this loop).
|
|
442
|
+
if (f === "config.json")
|
|
443
|
+
continue;
|
|
444
|
+
const src = (0, node_path_1.join)(pluginSource, f);
|
|
445
|
+
if ((0, node_fs_1.statSync)(src).isFile()) {
|
|
446
|
+
(0, node_fs_1.copyFileSync)(src, (0, node_path_1.join)(pluginsDir, f));
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
console.log(` ✓ Copied Hermes plugin to ${pluginsDir}`);
|
|
450
|
+
}
|
|
451
|
+
// Plugin config.json: MERGE, never blindly clobber. The URL is overwritten
|
|
452
|
+
// when it is INIT-OWNED — unset, a local default (either host spelling:
|
|
453
|
+
// init has written both `localhost` and `127.0.0.1` variants historically),
|
|
454
|
+
// or exactly what init last wrote (previousServerUrl — the client config's
|
|
455
|
+
// serverUrl BEFORE this run re-pointed it). A value the USER set by hand to
|
|
456
|
+
// something else is preserved WITH A LOUD DIVERGENCE WARNING — silently
|
|
457
|
+
// keeping a stale endpoint on a re-point is the failure this guards
|
|
458
|
+
// (CR: re-point must reach the plugin like it reaches CC's MCP entry).
|
|
459
|
+
// The auth token is a SECRET and is deliberately NOT written here —
|
|
460
|
+
// `hermes memory setup` routes it to $HERMES_HOME/.env, and localhost
|
|
461
|
+
// bypasses auth entirely.
|
|
462
|
+
const stripTrailingSlash = (v) => v.replace(/\/+$/, "");
|
|
463
|
+
const localDefaults = [`http://localhost:${DEFAULT_PORT}`, `http://127.0.0.1:${DEFAULT_PORT}`];
|
|
464
|
+
// Normalize before comparing (trailing-slash variants of the same URL must
|
|
465
|
+
// not read as user-set — the client flow strips, the env-var flow does not).
|
|
466
|
+
const normalizedPrevious = previousServerUrl ? stripTrailingSlash(previousServerUrl) : undefined;
|
|
467
|
+
const isInitOwnedUrl = (v) => {
|
|
468
|
+
if (typeof v !== "string" || v === "")
|
|
469
|
+
return true;
|
|
470
|
+
const nv = stripTrailingSlash(v);
|
|
471
|
+
return localDefaults.includes(nv) || nv === normalizedPrevious;
|
|
472
|
+
};
|
|
424
473
|
const configPath = (0, node_path_1.join)(pluginsDir, "config.json");
|
|
425
|
-
|
|
426
|
-
|
|
474
|
+
let config = {};
|
|
475
|
+
if ((0, node_fs_1.existsSync)(configPath)) {
|
|
476
|
+
try {
|
|
477
|
+
config = loadConfigStrict(configPath).config;
|
|
478
|
+
}
|
|
479
|
+
catch {
|
|
480
|
+
// Corrupt/unreadable plugin config: REPLACE (this file holds no secrets
|
|
481
|
+
// by design — only URL/knobs; reset to defaults) — loudly, never
|
|
482
|
+
// silently, never fatally. (The underlying error's copy speaks of
|
|
483
|
+
// ~/.hicortex config semantics that do not apply to this file, so it
|
|
484
|
+
// is deliberately not echoed.)
|
|
485
|
+
console.log(" ⚠ Plugin config.json unreadable — replacing it (URL re-prefilled; optional knobs reset to defaults; no secrets live in this file)");
|
|
486
|
+
config = {};
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const normalizedServerUrl = stripTrailingSlash(serverUrl);
|
|
490
|
+
let keptExistingUrl = false;
|
|
491
|
+
if (isInitOwnedUrl(config.hicortex_url)) {
|
|
492
|
+
config.hicortex_url = normalizedServerUrl;
|
|
493
|
+
}
|
|
494
|
+
else if (stripTrailingSlash(config.hicortex_url) !== normalizedServerUrl) {
|
|
495
|
+
keptExistingUrl = true;
|
|
496
|
+
console.log(` ℹ Kept existing plugin URL ${config.hicortex_url} (differs from this install's ${normalizedServerUrl})`);
|
|
497
|
+
console.log(" Edit ~/.hermes/plugins/hicortex/config.json if that is not what you want.");
|
|
498
|
+
}
|
|
499
|
+
try {
|
|
500
|
+
(0, node_fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2));
|
|
501
|
+
console.log(keptExistingUrl ? ` ℹ Plugin config stays on ${config.hicortex_url}` : ` ✓ Plugin config → ${config.hicortex_url}`);
|
|
502
|
+
}
|
|
503
|
+
catch (e) {
|
|
504
|
+
console.log(` ⚠ Could not write plugin config (${e instanceof Error ? e.message : e}) — Hermes setup skipped config pre-fill`);
|
|
505
|
+
}
|
|
427
506
|
// For profile-based setups, symlink the shared plugin into each profile's
|
|
428
507
|
// plugin dir (non-destructive). Discovery scans $HERMES_HOME/plugins/, so
|
|
429
508
|
// this is belt-and-suspenders for profile-scoped installs.
|
|
@@ -460,14 +539,26 @@ function setupHermes(serverUrl, authToken) {
|
|
|
460
539
|
// Activation is left to Hermes' own tooling. We NEVER edit config.yaml —
|
|
461
540
|
// Hermes' `memory setup` discovers this plugin automatically and writes the
|
|
462
541
|
// config with its own YAML-aware writer (routing the token to .env).
|
|
463
|
-
|
|
464
|
-
|
|
542
|
+
// The token is PRINTED here for the remote case (the user's own terminal —
|
|
543
|
+
// same visibility as `hicortex status`); Hermes has no non-interactive
|
|
544
|
+
// setup path, so one paste is the floor. When a user-set URL was KEPT, the
|
|
545
|
+
// token hint names that server's token requirement instead (this install's
|
|
546
|
+
// token belongs to a different server).
|
|
547
|
+
console.log(" → Activate with: hermes memory setup hicortex");
|
|
465
548
|
if ((0, node_fs_1.existsSync)(profilesDir)) {
|
|
466
549
|
console.log(" Run once per profile if you use Hermes profiles.");
|
|
467
550
|
}
|
|
468
|
-
if (
|
|
469
|
-
console.log("
|
|
551
|
+
if (keptExistingUrl) {
|
|
552
|
+
console.log(" The kept URL's server needs ITS token — run `hicortex status` on that machine if you don't have it.");
|
|
553
|
+
}
|
|
554
|
+
else if (authToken) {
|
|
555
|
+
console.log(` When prompted for the token, paste: ${authToken}`);
|
|
556
|
+
console.log(" (stored by Hermes in $HERMES_HOME/.env — never in the plugin's config.json)");
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
console.log(" Local server: leave the token blank — localhost bypasses auth.");
|
|
470
560
|
}
|
|
561
|
+
console.log(" Installed Hermes AFTER Hicortex? Re-running `npx @gamaze/hicortex init` redoes this step.");
|
|
471
562
|
}
|
|
472
563
|
/**
|
|
473
564
|
* Parse a KEY=VALUE env file (e.g. ~/.hermes/.env or ~/.claude/settings.json env block).
|
|
@@ -1850,6 +1941,11 @@ async function runClientInit(serverUrl, agentName) {
|
|
|
1850
1941
|
// writeClientConfig: strict-load → apply overrides → save. Throws on a
|
|
1851
1942
|
// malformed existing config (0.16.x BLOCKER — never wipe the client's
|
|
1852
1943
|
// authToken/licenseKey). ENOENT → fresh client config.
|
|
1944
|
+
// The PRE-overwrite serverUrl is captured first: it is what init last
|
|
1945
|
+
// wrote into the Hermes plugin's config, so the plugin step can tell an
|
|
1946
|
+
// init-owned URL (update it — re-point must reach Hermes like it reaches
|
|
1947
|
+
// CC's MCP entry) from a user-set one (keep + warn).
|
|
1948
|
+
const previousServerUrl = readPreviousServerUrl(configPath);
|
|
1853
1949
|
const { config } = writeClientConfig(configPath, { serverUrl, authToken }, nameDecision);
|
|
1854
1950
|
console.log(` ✓ Client config saved to ${configPath}`);
|
|
1855
1951
|
if (typeof config.agentName === "string") {
|
|
@@ -1906,7 +2002,7 @@ async function runClientInit(serverUrl, agentName) {
|
|
|
1906
2002
|
// Step 8: Setup Hermes if detected
|
|
1907
2003
|
if ((0, node_fs_1.existsSync)(HERMES_HOME)) {
|
|
1908
2004
|
console.log("\nHermes detected — installing plugin...");
|
|
1909
|
-
setupHermes(serverUrl, authToken);
|
|
2005
|
+
setupHermes(serverUrl, authToken, previousServerUrl);
|
|
1910
2006
|
}
|
|
1911
2007
|
// Step 8b: Setup the Pi extension if detected (self-resolving — no config write)
|
|
1912
2008
|
if ((0, node_fs_1.existsSync)(PI_AGENT_DIR)) {
|
package/dist/nightly.js
CHANGED
|
@@ -741,8 +741,11 @@ async function runNightly(options = {}) {
|
|
|
741
741
|
consolidationStatus = report.status;
|
|
742
742
|
// #337: the stages fail soft, so a run against an endpoint that died
|
|
743
743
|
// MID-run would otherwise report "completed". An open breaker is the
|
|
744
|
-
// honest signal — override to endpoint_down
|
|
745
|
-
//
|
|
744
|
+
// honest signal — override to endpoint_down. lastConsolidated only
|
|
745
|
+
// advances on a clean completed AND a closed breaker (the same
|
|
746
|
+
// llm.breakerOpen signal, gated in consolidate.ts — keep the two
|
|
747
|
+
// sites in lockstep), so the importance/reflection/domain work is
|
|
748
|
+
// re-run on the next pass.
|
|
746
749
|
if (llm.breakerOpen) {
|
|
747
750
|
console.error(`[hicortex] LLM circuit breaker OPEN after consolidation — ` +
|
|
748
751
|
`overriding "${report.status}" to endpoint_down (stages failed ` +
|
|
@@ -78,7 +78,7 @@ hermes memory setup # select "hicortex", enter the server URL/token when promp
|
|
|
78
78
|
|
|
79
79
|
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.
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
Setup asks exactly two questions: the **server URL** and the **auth token**. Everything else has a correct default and is configured — if ever needed — directly in `$HERMES_HOME/plugins/hicortex/config.json`: `default_project` (blank = memories unattributed), `recall_limit` (default 5; sizes the tools and the legacy `/search` fallback only — the pushed recall index is sized by SERVER config `recallMaxItems`), `agent_name` (blank auto-derives from the running profile; pin it only for fleet re-installs — see [Per-agent standing context](#per-agent-standing-context-013)), and `mission_domains` (blank = off; an optional recall boost keyed to the server's domain vocabulary). The auth token is a **secret** — set it via env, not the JSON file:
|
|
82
82
|
|
|
83
83
|
```bash
|
|
84
84
|
export HICORTEX_AUTH_TOKEN=hctx-<your-token> # or your custom token
|
|
@@ -86,7 +86,7 @@ export HICORTEX_AUTH_TOKEN=hctx-<your-token> # or your custom token
|
|
|
86
86
|
|
|
87
87
|
Env overrides: `HICORTEX_URL`, `HICORTEX_AUTH_TOKEN`.
|
|
88
88
|
|
|
89
|
-
> **`privacy_filter` is
|
|
89
|
+
> **`privacy_filter` is REMOVED from setup** (plugin 0.7.4; deprecated since 0.7.2 / server 0.16.2). The server no longer filters on privacy — the `privacy` column is vestigial (stored, never filtered) — so the setting was a no-op the setup flow still prompted for. New setups never see the question; existing config.json files that still carry the key are tolerated (one-time warning in the gateway log; safe to delete the line). For work/personal isolation, run a **separate Hicortex server** per scope rather than relying on in-server privacy filtering.
|
|
90
90
|
|
|
91
91
|
## Topology
|
|
92
92
|
|
|
@@ -36,65 +36,25 @@ CONFIG_SCHEMA: list[dict[str, Any]] = [
|
|
|
36
36
|
"key": "hicortex_auth_token",
|
|
37
37
|
"label": "Auth token",
|
|
38
38
|
"description": (
|
|
39
|
-
"Bearer token for the server.
|
|
40
|
-
"localhost — the server bypasses auth there.
|
|
41
|
-
"
|
|
39
|
+
"Bearer token for the server. Leave blank when targeting "
|
|
40
|
+
"localhost — the server bypasses auth there. To find a remote "
|
|
41
|
+
"server's token, run `hicortex status` on the server machine "
|
|
42
|
+
"(or check ~/.hicortex/config.json there)."
|
|
42
43
|
),
|
|
43
44
|
"secret": True,
|
|
44
45
|
"env_var": "HICORTEX_AUTH_TOKEN",
|
|
45
46
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"tools and the legacy pre-0.14 /search prefetch fallback only — "
|
|
58
|
-
"the pushed recall index is sized by SERVER config (recallMaxItems)."
|
|
59
|
-
),
|
|
60
|
-
"default": "5",
|
|
61
|
-
"required": False,
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
"key": "privacy_filter",
|
|
65
|
-
"label": "Privacy filter (DEPRECATED)",
|
|
66
|
-
"description": (
|
|
67
|
-
"DEPRECATED since plugin 0.7.2 / server 0.16.2. The server no "
|
|
68
|
-
"longer filters on privacy — the column is vestigial. This setting "
|
|
69
|
-
"is now a harmless no-op: it is still accepted for backward "
|
|
70
|
-
"compat but ignored. For work/personal isolation, run a separate "
|
|
71
|
-
"Hicortex server per scope. (Historically: comma-separated privacy "
|
|
72
|
-
"levels to include, e.g. WORK,PERSONAL.)"
|
|
73
|
-
),
|
|
74
|
-
"default": "WORK,PERSONAL",
|
|
75
|
-
"required": False,
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
"key": "agent_name",
|
|
79
|
-
"label": "Agent name (per-agent context)",
|
|
80
|
-
"description": (
|
|
81
|
-
"Identity sent as ?agent= when fetching the standing context layer, "
|
|
82
|
-
"so this profile gets its own context (0.13). Leave blank to "
|
|
83
|
-
"auto-derive from the running profile (HERMES_PROFILE / HERMES_HOME)."
|
|
84
|
-
),
|
|
85
|
-
"required": False,
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
"key": "mission_domains",
|
|
89
|
-
"label": "Mission domains",
|
|
90
|
-
"description": (
|
|
91
|
-
"Comma-separated knowledge domains this agent works in (e.g. Health, "
|
|
92
|
-
"or Finance,Work). Recall boosts memories tagged into these domains "
|
|
93
|
-
"(soft — never excludes others). Pick from the domains in your "
|
|
94
|
-
"Hicortex config; leave blank for a general-purpose agent."
|
|
95
|
-
),
|
|
96
|
-
"required": False,
|
|
97
|
-
},
|
|
47
|
+
# 0.7.6: setup asks ONLY url + token. default_project joined the
|
|
48
|
+
# config-only knobs (0.7.5 removed recall_limit / agent_name /
|
|
49
|
+
# mission_domains) after live-install feedback: "no human will understand
|
|
50
|
+
# what it is" — a memory-attribution bucket is not a setup question.
|
|
51
|
+
# Config-only knobs (set directly in config.json, all have correct
|
|
52
|
+
# defaults): default_project (blank = unattributed), recall_limit (5;
|
|
53
|
+
# tools + legacy fallback only — the pushed index is SERVER-sized via
|
|
54
|
+
# recallMaxItems), agent_name (blank auto-derives from the running
|
|
55
|
+
# profile), mission_domains (blank = off).
|
|
56
|
+
# privacy_filter was removed outright in 0.7.4 (dead since server 0.16.2;
|
|
57
|
+
# tolerated at load with a one-time warning if an old file carries it).
|
|
98
58
|
# NOTE: recall-only plugin — no capture config. Capture is handled by the
|
|
99
59
|
# nightly server-side reader of each agent's session store.
|
|
100
60
|
]
|
|
@@ -130,7 +90,8 @@ def load_config() -> Dict[str, Any]:
|
|
|
130
90
|
# Defaults
|
|
131
91
|
cfg.setdefault("hicortex_url", "http://localhost:8787")
|
|
132
92
|
cfg.setdefault("recall_limit", 5)
|
|
133
|
-
|
|
93
|
+
# No privacy_filter default — the setting is dead (see schema note) and
|
|
94
|
+
# must not be injected into new configs.
|
|
134
95
|
|
|
135
96
|
# 0.16.2 deprecation: privacy_filter is a no-op now (server ignores privacy
|
|
136
97
|
# entirely). Warn once per process if the profile explicitly sets it.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
name: hicortex
|
|
2
|
-
version: 0.7.
|
|
3
|
-
description: "Self-learning memory for Hermes agents — every session is distilled into lessons overnight, and your agent wakes up wiser. Pushes a compact per-turn recall index (lazy-loaded with hicortex_get), injects fresh lessons plus a per-agent standing context block, and exposes the full 9-tool memory surface (search, get, recent, ingest, lessons, index, graph, update, delete) via a shared Hicortex server. Stdlib-only."
|
|
2
|
+
version: 0.7.8
|
|
3
|
+
description: "Self-learning memory for Hermes agents — every session is distilled into lessons overnight, and your agent wakes up wiser. Pushes a compact per-turn recall index (lazy-loaded with hicortex_get), injects fresh lessons plus a per-agent standing context block, and exposes the full 9-tool memory surface (search, get, recent, ingest, lessons, index, graph, update, delete) via a shared Hicortex server. Stdlib-only. AFTER INSTALL: run `hermes memory setup`, select hicortex, enter the server URL + token (see the token env var description for where to find it)."
|
|
4
4
|
pip_dependencies: []
|
|
5
5
|
hooks: []
|
|
6
6
|
requires_env:
|
|
@@ -224,7 +224,10 @@ class HicortexProvider(MemoryProvider):
|
|
|
224
224
|
self._recall_limit = int(cfg.get("recall_limit", 5))
|
|
225
225
|
except (TypeError, ValueError):
|
|
226
226
|
self._recall_limit = 5
|
|
227
|
-
|
|
227
|
+
# privacy_filter removed (0.7.4): the server ignores privacy entirely;
|
|
228
|
+
# the dead setting is no longer read. _privacy stays at its inert
|
|
229
|
+
# default for the (equally ignored) wire parameter.
|
|
230
|
+
self._privacy = "WORK,PERSONAL"
|
|
228
231
|
# #203 scope: declared knowledge domains for this role-bound agent
|
|
229
232
|
# (e.g. a health-focused agent → Health). Soft affinity boost on
|
|
230
233
|
# recall; never excludes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.2",
|
|
4
4
|
"description": "Persistent agent identity for AI agents \u2014 a hand-edited identity layer, nightly-distilled experience, and lessons injected every session, shared across your whole fleet. Works with Hermes, OpenClaw, Claude Code, Pi, and opencode.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|