@gamaze/hicortex 0.23.2 → 0.24.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/dist/nightly.js CHANGED
@@ -77,6 +77,8 @@ const state_js_1 = require("./state.js");
77
77
  const identity_store_js_1 = require("./identity-store.js");
78
78
  const capture_cursors_js_1 = require("./capture-cursors.js");
79
79
  const capture_js_1 = require("./capture.js");
80
+ const distill_queue_js_1 = require("./distill-queue.js");
81
+ const token_budget_js_1 = require("./token-budget.js");
80
82
  const run_deadline_js_1 = require("./run-deadline.js");
81
83
  const dashboard_js_1 = require("./dashboard.js");
82
84
  const recall_precision_js_1 = require("./recall-precision.js");
@@ -224,6 +226,43 @@ function makeRemotePost(serverUrl, authToken, deadline) {
224
226
  return normalizePostResult(resp);
225
227
  };
226
228
  }
229
+ /**
230
+ * #529: the REAL yield-signal probe (the drain's is injected so the stage is
231
+ * unit-testable without HTTP). GETs the configured URL with a short timeout
232
+ * and classifies: busy = 2xx AND (a JSON body with a truthy `busy` field OR
233
+ * the plain body "busy"); a non-2xx answer means the endpoint ANSWERED and is
234
+ * simply not reporting busy → idle; a network error/timeout → unreachable
235
+ * (the drain proceeds fail-open and warns once). The URL lives in the nightly
236
+ * (not distill-queue.ts) so the drain module stays fetch-free.
237
+ */
238
+ function makeDrainYieldProbe() {
239
+ return async (url) => {
240
+ try {
241
+ const resp = await fetch(url, {
242
+ signal: AbortSignal.timeout(calibration_js_1.DRAIN_YIELD_PROBE_TIMEOUT_MS),
243
+ });
244
+ if (!resp.ok)
245
+ return "idle";
246
+ const body = (await resp.text()).trim();
247
+ try {
248
+ const parsed = JSON.parse(body);
249
+ if (typeof parsed === "object" &&
250
+ parsed !== null &&
251
+ "busy" in parsed &&
252
+ Boolean(parsed.busy)) {
253
+ return "busy";
254
+ }
255
+ }
256
+ catch {
257
+ // Not JSON — the plain-body form below is the whole remaining check.
258
+ }
259
+ return body.toLowerCase() === "busy" ? "busy" : "idle";
260
+ }
261
+ catch {
262
+ return "unreachable";
263
+ }
264
+ };
265
+ }
227
266
  /**
228
267
  * Per-POST timeout: 20 min (synchronous distillation of a large segment can
229
268
  * take minutes), clamped to the run deadline's remaining time when one is in
@@ -244,6 +283,9 @@ async function normalizePostResult(resp) {
244
283
  status: 201,
245
284
  distilled: data.distilled ?? 0,
246
285
  dropped: data.dropped ?? [],
286
+ // #529: a queue-mode server confirms durable storage, not distillation
287
+ // — the client only swaps its log line on this flag.
288
+ queued: data.queued === true,
247
289
  ...(usage ? { usage } : {}),
248
290
  };
249
291
  }
@@ -665,6 +707,51 @@ async function runNightly(options = {}) {
665
707
  }
666
708
  }
667
709
  } // end capture block (consolidateOnly else)
710
+ // Step 2.5 (#529): drain the distill inbox — distill everything the
711
+ // daemon queued since the last run, BEFORE consolidation so drained
712
+ // memories are scored/linked/reflected in the SAME run. Full AND
713
+ // consolidate-only runs drain (the same gate as consolidation — hosted
714
+ // tenants deliver through their daemon too); capture-only/watchdog and
715
+ // dry-run never do (compute stays scheduled; capture-only keeps its
716
+ // no-LLM contract). All LLM goes through the one LlmClient (ladder,
717
+ // breaker, single-flight, dispatcher); token gate + run deadline are
718
+ // checked between items; the optional yield signal (drainYieldUrl) waits
719
+ // out interactive use of the endpoint. Runs even when capture above was
720
+ // skipped (lock wait / consolidate-only) — the inbox is independent of
721
+ // this machine's capture.
722
+ if (!dryRun && !captureOnly) {
723
+ const drainReport = await (0, distill_queue_js_1.drainDistillQueue)(db, {
724
+ llm,
725
+ llmConfig,
726
+ embed: embedder_js_1.embed,
727
+ budgetExceeded: () => (0, token_budget_js_1.isTokenBudgetExceeded)(stateDir),
728
+ deadline,
729
+ yieldUrl: (0, config_read_js_1.readStringConfig)(savedConfig ?? {}, "drainYieldUrl") ?? undefined,
730
+ probeBusy: makeDrainYieldProbe(),
731
+ });
732
+ // #5 metering, same as the daemon's sync path: record tokens against
733
+ // the monthly budget (incl. a failed item's partial usage) and fold
734
+ // them into the snapshot's distill share so new_this_run.tokens stays
735
+ // the run's TRUE total.
736
+ if (drainReport.usage.total > 0) {
737
+ (0, token_budget_js_1.recordDistillUsage)(stateDir, drainReport.usage);
738
+ distillUsage = distillUsage
739
+ ? {
740
+ prompt: distillUsage.prompt + drainReport.usage.prompt,
741
+ completion: distillUsage.completion + drainReport.usage.completion,
742
+ total: distillUsage.total + drainReport.usage.total,
743
+ }
744
+ : drainReport.usage;
745
+ }
746
+ if (drainReport.outcome !== "empty" && drainReport.outcome !== "completed") {
747
+ // A non-clean drain is a health signal, not a run failure: processed
748
+ // items are durable and the rest retries next scheduled run. Make it
749
+ // visible next to the capture-complete line so the queue-depth/age
750
+ // warning in status has a log-side counterpart.
751
+ console.warn(`[hicortex] Distill drain ${drainReport.outcome}: ` +
752
+ `${drainReport.processed} processed, ${drainReport.remaining} still queued`);
753
+ }
754
+ }
668
755
  // Step 3: Consolidation — skipped in capture-only mode, dry-run, or no LLM.
669
756
  // Runs even if capture had transient failures (opens DB directly, independent
670
757
  // of the HTTP capture path). Full nightly only — capture-only runs are
package/dist/nofit.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * No-fit lifecycle — weak-primary floor + no-association decay
3
- * (owner amendment 07.07 to specs/2026-07-07-graded-schema-memory-tags.md).
3
+ * (owner amendment 07.07).
4
4
  *
5
5
  * THE MODEL
6
6
  * ---------
package/dist/nofit.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  /**
3
3
  * No-fit lifecycle — weak-primary floor + no-association decay
4
- * (owner amendment 07.07 to specs/2026-07-07-graded-schema-memory-tags.md).
4
+ * (owner amendment 07.07).
5
5
  *
6
6
  * THE MODEL
7
7
  * ---------
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Graded schema membership — domain prototypes + per-tag association weights
3
- * (spec: specs/2026-07-07-graded-schema-memory-tags.md).
3
+ *.
4
4
  *
5
5
  * MODEL (cognitive grounding → mechanism)
6
6
  * ---------------------------------------
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Graded schema membership — domain prototypes + per-tag association weights
4
- * (spec: specs/2026-07-07-graded-schema-memory-tags.md).
4
+ *.
5
5
  *
6
6
  * MODEL (cognitive grounding → mechanism)
7
7
  * ---------------------------------------
package/dist/status.d.ts CHANGED
@@ -18,4 +18,15 @@ export declare function statusAgentLine(config: Record<string, unknown>): string
18
18
  * the full status printer. Unknown keys pass through verbatim (forward-compat).
19
19
  */
20
20
  export declare function formatTypeBreakdown(byType: Record<string, number>): string;
21
+ /**
22
+ * The distill-inbox lines for `hicortex status` (#529). Pure on the
23
+ * {@link readQueueStats} shape so the rendering (incl. the 24 h stale-item
24
+ * warning, DRAIN_QUEUE_WARN_AGE_HOURS) is unit-testable without the full
25
+ * status printer. An empty inbox prints nothing — depth 0 is the healthy
26
+ * steady state, not worth a line.
27
+ */
28
+ export declare function formatDistillQueueLines(stats: {
29
+ depth: number;
30
+ oldest_age_hours: number | null;
31
+ }): string[];
21
32
  export declare function runStatus(): Promise<void>;
package/dist/status.js CHANGED
@@ -5,6 +5,7 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.statusAgentLine = statusAgentLine;
7
7
  exports.formatTypeBreakdown = formatTypeBreakdown;
8
+ exports.formatDistillQueueLines = formatDistillQueueLines;
8
9
  exports.runStatus = runStatus;
9
10
  const paths_js_1 = require("./paths.js");
10
11
  const node_fs_1 = require("node:fs");
@@ -16,6 +17,8 @@ const features_js_1 = require("./features.js");
16
17
  const state_js_1 = require("./state.js");
17
18
  const identity_store_js_1 = require("./identity-store.js");
18
19
  const type_labels_js_1 = require("./type-labels.js");
20
+ const distill_queue_js_1 = require("./distill-queue.js");
21
+ const calibration_js_1 = require("./calibration.js");
19
22
  const HICORTEX_HOME = (0, paths_js_1.hicortexHome)();
20
23
  const CC_SETTINGS = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude", "settings.json");
21
24
  const OC_CONFIG = (0, node_path_1.join)((0, node_os_1.homedir)(), ".openclaw", "openclaw.json");
@@ -50,6 +53,24 @@ function formatTypeBreakdown(byType) {
50
53
  .map(([k, v]) => `${(0, type_labels_js_1.labelForType)(k)}=${v}`)
51
54
  .join(", ");
52
55
  }
56
+ /**
57
+ * The distill-inbox lines for `hicortex status` (#529). Pure on the
58
+ * {@link readQueueStats} shape so the rendering (incl. the 24 h stale-item
59
+ * warning, DRAIN_QUEUE_WARN_AGE_HOURS) is unit-testable without the full
60
+ * status printer. An empty inbox prints nothing — depth 0 is the healthy
61
+ * steady state, not worth a line.
62
+ */
63
+ function formatDistillQueueLines(stats) {
64
+ if (stats.depth === 0)
65
+ return [];
66
+ const age = stats.oldest_age_hours ?? 0;
67
+ const lines = [`Distill queue: ${stats.depth} pending (oldest ${age.toFixed(1)}h)`];
68
+ if (age >= calibration_js_1.DRAIN_QUEUE_WARN_AGE_HOURS) {
69
+ lines.push(` ⚠ Distill inbox oldest item is ${age.toFixed(1)}h old (>${calibration_js_1.DRAIN_QUEUE_WARN_AGE_HOURS}h) — ` +
70
+ `check the drain outcome in the last nightly log`);
71
+ }
72
+ return lines;
73
+ }
53
74
  async function runStatus() {
54
75
  console.log("Hicortex Status");
55
76
  console.log("─".repeat(40));
@@ -66,6 +87,10 @@ async function runStatus() {
66
87
  console.log(`Memories: ${stats.memories} (${typeStr || "none"})`);
67
88
  console.log(`Links: ${stats.links}`);
68
89
  console.log(`DB size: ${(stats.db_size_bytes / 1024).toFixed(1)} KB`);
90
+ // #529: inbox visibility — depth + oldest-item age, warn above 24 h.
91
+ for (const line of formatDistillQueueLines((0, distill_queue_js_1.readQueueStats)(db))) {
92
+ console.log(line);
93
+ }
69
94
  // 0.21 migration detection (#425): pre-0.21 stores have inflated importance
70
95
  // scores (median ~0.80 vs the honest ~0.40). If the live median is high,
71
96
  // recommend the one-shot rescore.
package/dist/types.d.ts CHANGED
@@ -642,6 +642,26 @@ export interface HicortexConfig {
642
642
  * probe per window. Nightly runs are single-shot and never cache.
643
643
  */
644
644
  llmProbeTtlMs?: number;
645
+ /**
646
+ * #529 kill switch for the durable distill inbox. Default TRUE (queue
647
+ * mode): POST /distill stores the redacted segment durably and answers the
648
+ * SAME 201 shape with zeroed counts + `queued: true` — no LLM call — and
649
+ * the nightly's drain stage distills the inbox before consolidation, so
650
+ * all distill LLM traffic happens inside the scheduled runs. `false`
651
+ * restores synchronous distill-on-POST byte-for-byte (every delivery
652
+ * lands in the sync flow). Read through readStrictBoolean — a non-boolean
653
+ * value is ignored (warned), never coerced.
654
+ */
655
+ distillQueue?: boolean;
656
+ /**
657
+ * #529 optional interactive-yield signal: a URL the drain polls BETWEEN
658
+ * items. A 2xx answer whose body is JSON with a truthy `busy` field — or
659
+ * the plain text `busy` — makes the drain wait and re-check (bounded by
660
+ * the run deadline and a per-item cap); unset or unreachable proceeds
661
+ * fail-open (one warn per run). Seam only for now — the drain side ships
662
+ * with this contract, endpoints can adopt it whenever.
663
+ */
664
+ drainYieldUrl?: string;
645
665
  /**
646
666
  * Max lessons injected into an agent's session-start context (default 10).
647
667
  * Lessons are ranked per-session by project/domain affinity + recency +
@@ -2,11 +2,18 @@
2
2
 
3
3
  > **Install:** `hermes plugins install gamaze-labs/hicortex-hermes-plugin` → `hermes memory setup hicortex` → restart your gateway.
4
4
  >
5
- > The [gamaze-labs/hicortex-hermes-plugin](https://github.com/gamaze-labs/hicortex-hermes-plugin) repo is a **generated read-only mirror** of `hermes-plugin/hicortex/` in the main Hicortex repo — do not open PRs there. Requires a running [Hicortex server](https://hicortex.gamaze.com/docs/installation.html) (local or remote) for recall; capture of Hermes sessions is handled by the server machine's nightly job.
5
+ > The [gamaze-labs/hicortex-hermes-plugin](https://github.com/gamaze-labs/hicortex-hermes-plugin) repo is a **generated read-only mirror** of `hermes-plugin/hicortex/` in the main Hicortex repo — do not open PRs there. Requires a running [Hicortex server](https://hicortex.gamaze.com/docs/installation.html) (local or remote) for recall; capture of Hermes sessions is the nightly job's, not this plugin's (see [Data flow](#data-flow)).
6
6
 
7
- Gives [Hermes](https://github.com/nousresearch/hermes-agent) agents self-learning memory backed by a [Hicortex](https://hicortex.gamaze.com/) server: their experience is distilled into lessons overnight, and they wake up wiser. **Recall-only:** the plugin retrieves relevant memories on every turn and injects distilled lessons into the system prompt. It has **no local LLM, no capture, no cron** — it is a thin recall shim.
7
+ Gives [Hermes](https://github.com/nousresearch/hermes-agent) agents self-learning memory backed by a [Hicortex](https://hicortex.gamaze.com/) server: their experience is distilled into lessons overnight, and they wake up wiser. **Recall-only:** each turn the plugin sends the user's message to the configured server and injects the returned recall index, plus distilled lessons into the system prompt. It has **no local LLM, no capture, no cron** — it is a thin recall shim.
8
8
 
9
- **Capture happens centrally.** A nightly reader on the Hicortex server distills each agent's own session store (Hermes keeps full history in `~/.hermes/profiles/<agent>/state.db`), so nothing needs to be captured in real time. See `specs/2026-07-01-memory-capture-architecture.md` in the main repo.
9
+ ## Data flow
10
+
11
+ Both directions of the wire, stated plainly:
12
+
13
+ - **Recall — every turn.** Each user message is sent to your configured Hicortex server for recall (`POST /recall-index`, or `GET /search` on pre-0.14 servers), and session distillation runs on the server. The server therefore sees prompt text as it arrives — point `hicortex_url` at a server you trust (default `http://localhost:8787`).
14
+ - **Capture — nightly.** Session logs are read locally from each machine's own Hermes store (Hermes keeps full history in `~/.hermes/profiles/<agent>/state.db`); the nightly ships only denoised text to the server, and the server stores distilled memories — raw session logs stay on the capturing machine. Nothing is captured in real time.
15
+
16
+ The plugin warns once at startup when `hicortex_url` is plain `http://` on a non-loopback host while an auth token is set — credentials and prompts then cross the network in cleartext. Use `https://`, or keep the server on a trusted private network (plain http over a private/overlay network is a legitimate setup; the warning is advisory, not a rejection). The HTTP client also strips the `Authorization` header on any redirect that leaves the original host.
10
17
 
11
18
  ## How it works
12
19
 
@@ -26,9 +33,9 @@ Instead of injecting full memory content every turn, `prefetch` sends the user's
26
33
 
27
34
  ### Per-agent standing context (0.13)
28
35
 
29
- `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.
36
+ `system_prompt_block()` also injects the hand-edited **standing context layer** (`## Identity`, 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.
30
37
 
31
- > **Note (#264 rename):** the server-side layer was renamed Context → Identity in 0.18. The `/context` endpoint remains as an alias so this plugin keeps working unchanged; the heading is still rendered as `## Context` here and will switch to `## Identity` in a follow-up plugin release. No action needed.
38
+ > **Note (#264 rename):** the server-side layer was renamed Context → Identity in 0.18. The `/context` endpoint remains as an alias so this plugin keeps working unchanged, and the injected heading renders as `## Identity`. No action needed.
32
39
 
33
40
  The plugin sends its **profile name** as `?agent=`, resolved in this order:
34
41
 
@@ -91,7 +98,7 @@ Env overrides: `HICORTEX_URL`, `HICORTEX_AUTH_TOKEN`.
91
98
  ## Topology
92
99
 
93
100
  - **Server host:** runs Hicortex. Set `hicortex_url: http://localhost:8787` (localhost bypasses auth).
94
- - **Other Hermes boxes:** set `hicortex_url` to the server's hostname (e.g. `http://memory-server:8787`) and `HICORTEX_AUTH_TOKEN` to the server's token. Each box recalls from the same shared brain.
101
+ - **Other Hermes boxes:** set `hicortex_url` to the server's hostname (e.g. `http://memory-server:8787`) and `HICORTEX_AUTH_TOKEN` to the server's token. Each box recalls from the same shared brain — and every user message travels to that server each turn (see [Data flow](#data-flow)). Over plain `http://` the token and prompts cross the network in cleartext (one startup warning); prefer `https://` or a trusted private network.
95
102
 
96
103
  ## Notes
97
104
 
@@ -16,3 +16,10 @@ from agent.memory_provider import MemoryProvider # noqa: F401 (loader scans fo
16
16
  from .provider import HicortexProvider
17
17
 
18
18
  __all__ = ["HicortexProvider"]
19
+
20
+
21
+ def register(ctx) -> None:
22
+ """Hermes plugin entry point (memory-provider guide convention): hand this
23
+ provider to the host. Complements the direct import path — the loader
24
+ scans for ``MemoryProvider`` subclasses either way."""
25
+ ctx.register_memory_provider(HicortexProvider())
@@ -6,10 +6,55 @@ Stdlib-only (no pip dependencies) so the plugin installs with zero friction.
6
6
  from __future__ import annotations
7
7
 
8
8
  import json
9
+ import logging
9
10
  import urllib.error
10
11
  import urllib.parse
11
12
  import urllib.request
12
- from typing import Any, Optional
13
+ from typing import Any, Optional, Tuple
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ # One-shot-per-process guard for the cleartext-transport warning. Clients are
18
+ # rebuilt per provider/config read; warning per construction would spam the
19
+ # gateway log on every rebuild.
20
+ _cleartext_warned = False
21
+
22
+
23
+ def _origin(url: str) -> Tuple[Optional[str], Optional[int]]:
24
+ """(host, port) origin of a URL, default-port-normalized (http→80,
25
+ https→443) and lower-cased — the identity a redirect must preserve for the
26
+ Authorization header to be safe to keep. Unparseable URLs never compare
27
+ equal, so they fail CLOSED (token stripped)."""
28
+ try:
29
+ parsed = urllib.parse.urlparse(url)
30
+ port = parsed.port or (443 if parsed.scheme == "https" else 80)
31
+ return (parsed.hostname or "").lower(), port
32
+ except ValueError:
33
+ return (url, None)
34
+
35
+
36
+ class _AuthStrippingRedirectHandler(urllib.request.HTTPRedirectHandler):
37
+ """Strip ``Authorization`` on any redirect that leaves the original origin.
38
+
39
+ urllib's default redirect handler forwards the request's headers to the
40
+ redirect target (only content headers — length/type/encoding — are
41
+ dropped), so a cross-host 302 hands the bearer token to whoever answers
42
+ the redirect. Same-origin redirects (path changes, trailing-slash fixes)
43
+ keep authenticating as before."""
44
+
45
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
46
+ new_req = super().redirect_request(req, fp, code, msg, headers, newurl)
47
+ if new_req is not None and _origin(req.full_url) != _origin(newurl):
48
+ # Request headers are Capitalized by add_header ("Authorization").
49
+ new_req.headers.pop("Authorization", None)
50
+ new_req.unredirected_hdrs.pop("Authorization", None)
51
+ return new_req
52
+
53
+
54
+ # Shared opener: urllib's defaults plus the auth-stripping redirect handler.
55
+ # `urlopen` cannot be used for this — it goes through the process-global
56
+ # default opener, which we must not reconfigure from inside a plugin.
57
+ _OPENER = urllib.request.build_opener(_AuthStrippingRedirectHandler)
13
58
 
14
59
 
15
60
  class HicortexClient:
@@ -31,6 +76,23 @@ class HicortexClient:
31
76
  if host in ("127.0.0.1", "localhost", "::1", "::ffff:127.0.0.1")
32
77
  else auth_token
33
78
  )
79
+ # Cleartext transport warning (catalog review 130093): a plain http://
80
+ # URL to a non-loopback host while a token is set means the token AND
81
+ # every prompt sent for recall cross the network unencrypted. WARN,
82
+ # never reject — plain http on a trusted private network (overlay
83
+ # networks, private overlay meshes) is a legitimate setup.
84
+ if self.auth_token and urllib.parse.urlparse(self.base_url).scheme != "https":
85
+ global _cleartext_warned
86
+ if not _cleartext_warned:
87
+ _cleartext_warned = True
88
+ logger.warning(
89
+ "hicortex: %s uses plain http:// on a non-loopback host "
90
+ "while an auth token is set — the token and every prompt "
91
+ "sent for recall cross the network in cleartext. Use "
92
+ "https://, or keep the server on a trusted private "
93
+ "network. (This warning fires once per process.)",
94
+ self.base_url,
95
+ )
34
96
  self.timeout = timeout
35
97
 
36
98
  def _headers(self) -> dict[str, str]:
@@ -69,7 +131,7 @@ class HicortexClient:
69
131
  errors converted to statuses (never raised)."""
70
132
  req = urllib.request.Request(url, data=data, headers=self._headers(), method=method)
71
133
  try:
72
- with urllib.request.urlopen(req, timeout=timeout or self.timeout) as resp:
134
+ with _OPENER.open(req, timeout=timeout or self.timeout) as resp:
73
135
  return resp.status, json.loads(resp.read().decode("utf-8"))
74
136
  except urllib.error.HTTPError as e:
75
137
  return e.code, self._parse_http_error(e)
@@ -1,5 +1,5 @@
1
1
  name: hicortex
2
- version: 0.7.8
2
+ version: 0.7.10
3
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: []
@@ -26,7 +26,7 @@ Recall: prefetch() -> POST /recall-index (pushed recall index, 0.14
26
26
 
27
27
  Capture is NOT the plugin's job. A nightly reader on the Hicortex server
28
28
  distills each agent's own session store (Hermes: ~/.hermes/profiles/<agent>/
29
- state.db) centrally — see specs/2026-07-01-memory-capture-architecture.md. This
29
+ state.db) centrally. This
30
30
  plugin has no local LLM, no spool, no timer, and no capture path.
31
31
  """
32
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamaze/hicortex",
3
- "version": "0.23.2",
3
+ "version": "0.24.0",
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": {
package/server.json CHANGED
@@ -3,12 +3,12 @@
3
3
  "name": "io.github.gamaze-labs/hicortex",
4
4
  "title": "Hicortex \u2014 AI Fleet Memory",
5
5
  "description": "Shared fleet memory for AI agents: nightly self-correction, recall every prompt (supported agents).",
6
- "version": "0.23.2",
6
+ "version": "0.24.0",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "identifier": "@gamaze/hicortex",
11
- "version": "0.23.2",
11
+ "version": "0.24.0",
12
12
  "transport": {
13
13
  "type": "stdio",
14
14
  "command": "npx",