@gamaze/hicortex 0.13.2 → 0.13.3

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.
@@ -12,7 +12,7 @@ import { type DomainDef } from "./domain-classify.js";
12
12
  * Minimum COSINE similarity for a link candidate.
13
13
  *
14
14
  * Calibration (2026-07): measured top-10 neighbor cosine histogram on the
15
- * 2945-memory production corpus (bedrock). Typical top-1 neighbor cosine:
15
+ * ~3000-memory production corpus. Typical top-1 neighbor cosine:
16
16
  * median 0.823, p10 0.743, p90 0.902. Threshold 0.75 combined with the
17
17
  * top-3 cap yields ≈ 2.2 candidate links/memory. The previous value (0.55)
18
18
  * lived on an accidental 1−L2 scale where it required cosine > 0.90 — a
@@ -65,7 +65,7 @@ const CONSOLIDATE_PRUNE_MIN_AGE_DAYS = 90;
65
65
  * Minimum COSINE similarity for a link candidate.
66
66
  *
67
67
  * Calibration (2026-07): measured top-10 neighbor cosine histogram on the
68
- * 2945-memory production corpus (bedrock). Typical top-1 neighbor cosine:
68
+ * ~3000-memory production corpus. Typical top-1 neighbor cosine:
69
69
  * median 0.823, p10 0.743, p90 0.902. Threshold 0.75 combined with the
70
70
  * top-3 cap yields ≈ 2.2 candidate links/memory. The previous value (0.55)
71
71
  * lived on an accidental 1−L2 scale where it required cosine > 0.90 — a
@@ -261,7 +261,7 @@ function extractAgentFlag(args) {
261
261
  // A missing value (end of args) or the next token being another flag is a
262
262
  // typo — never let it silently fall through to the global scope.
263
263
  if (val === undefined || val.startsWith("-")) {
264
- throw new ContextCliError("--agent requires a value, e.g. --agent lenny");
264
+ throw new ContextCliError("--agent requires a value, e.g. --agent alice");
265
265
  }
266
266
  agent = val;
267
267
  i++;
@@ -5,13 +5,13 @@
5
5
  * ---------------
6
6
  * The nightly's legacy `stageDomainCuration` groups PROJECTS into domains and
7
7
  * assigns every memory its project's domain. For an owner whose "projects" are
8
- * often AGENT names (lenny, nano, ...), one agent produces memories spanning
8
+ * often AGENT names (alice, bob, ...), one agent produces memories spanning
9
9
  * many life areas, so life-memories get smeared under the agent. This module
10
10
  * classifies a single memory into life-spheres by its CONTENT, drawn from a
11
11
  * user-curated vocabulary in ~/.hicortex/config.json (`domains`).
12
12
  *
13
13
  * GRADED SCHEMA TAGS (spec 2026-07-07, supersedes the LLM-picked primary from
14
- * PR #152/#153): a memory genuinely spans spheres — "set up bedrock for the
14
+ * PR #152/#153): a memory genuinely spans spheres — "set up the server for the
15
15
  * agent fleet" is both Hardware AND Ventures. The classifier now returns ONLY
16
16
  * the discrete part:
17
17
  * - `tags`: 0..N vocabulary names that genuinely apply, MOST-RELEVANT FIRST
@@ -31,7 +31,7 @@
31
31
  *
32
32
  * The `project` name is passed to the classifier as a HINT (content wins;
33
33
  * project only breaks ties). This rescues terse technical memories from
34
- * projects like raider/hiops/catalyst whose content alone reads as ambiguous.
34
+ * projects whose content alone reads as ambiguous.
35
35
  *
36
36
  * The classifier makes ONE constrained LLM call per memory (via the classify
37
37
  * tier — classifyModel/classifyBaseUrl when configured, else the reflect
@@ -6,13 +6,13 @@
6
6
  * ---------------
7
7
  * The nightly's legacy `stageDomainCuration` groups PROJECTS into domains and
8
8
  * assigns every memory its project's domain. For an owner whose "projects" are
9
- * often AGENT names (lenny, nano, ...), one agent produces memories spanning
9
+ * often AGENT names (alice, bob, ...), one agent produces memories spanning
10
10
  * many life areas, so life-memories get smeared under the agent. This module
11
11
  * classifies a single memory into life-spheres by its CONTENT, drawn from a
12
12
  * user-curated vocabulary in ~/.hicortex/config.json (`domains`).
13
13
  *
14
14
  * GRADED SCHEMA TAGS (spec 2026-07-07, supersedes the LLM-picked primary from
15
- * PR #152/#153): a memory genuinely spans spheres — "set up bedrock for the
15
+ * PR #152/#153): a memory genuinely spans spheres — "set up the server for the
16
16
  * agent fleet" is both Hardware AND Ventures. The classifier now returns ONLY
17
17
  * the discrete part:
18
18
  * - `tags`: 0..N vocabulary names that genuinely apply, MOST-RELEVANT FIRST
@@ -32,7 +32,7 @@
32
32
  *
33
33
  * The `project` name is passed to the classifier as a HINT (content wins;
34
34
  * project only breaks ties). This rescues terse technical memories from
35
- * projects like raider/hiops/catalyst whose content alone reads as ambiguous.
35
+ * projects whose content alone reads as ambiguous.
36
36
  *
37
37
  * The classifier makes ONE constrained LLM call per memory (via the classify
38
38
  * tier — classifyModel/classifyBaseUrl when configured, else the reflect
@@ -2,7 +2,7 @@
2
2
  * Hermes transcript reader — the nightly capture path for Nous Research Hermes.
3
3
  *
4
4
  * Hermes stores conversation in a SQLite state DB, one per profile:
5
- * ~/.hermes/profiles/<profile>/state.db (per-profile agents: lenny, raider, nano)
5
+ * ~/.hermes/profiles/<profile>/state.db (per-profile agents: alice, bob, carol)
6
6
  * ~/.hermes/state.db (global, non-profile setups)
7
7
  *
8
8
  * Schema (relevant columns):
@@ -3,7 +3,7 @@
3
3
  * Hermes transcript reader — the nightly capture path for Nous Research Hermes.
4
4
  *
5
5
  * Hermes stores conversation in a SQLite state DB, one per profile:
6
- * ~/.hermes/profiles/<profile>/state.db (per-profile agents: lenny, raider, nano)
6
+ * ~/.hermes/profiles/<profile>/state.db (per-profile agents: alice, bob, carol)
7
7
  * ~/.hermes/state.db (global, non-profile setups)
8
8
  *
9
9
  * Schema (relevant columns):
@@ -69,7 +69,7 @@ export declare function renderContextBlock(sections: Record<string, string>): st
69
69
  * into every persona; on a bare fetch (no id) the guard is off (amendment
70
70
  * A2).
71
71
  * - CC passes `false` ALWAYS and deliberately (see the call site): a thin CC
72
- * client auto-upgrades via npx BEFORE bedrock does, so during the upgrade
72
+ * client auto-upgrades via npx BEFORE the server does, so during the upgrade
73
73
  * window it talks to a 0.12 server that cannot hold ANY per-agent config —
74
74
  * global IS the operator's intended state there, and a guard would instead
75
75
  * blank ALL context for every CC session in that window.
@@ -180,7 +180,7 @@ function renderContextBlock(sections) {
180
180
  * into every persona; on a bare fetch (no id) the guard is off (amendment
181
181
  * A2).
182
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
183
+ * client auto-upgrades via npx BEFORE the server does, so during the upgrade
184
184
  * window it talks to a 0.12 server that cannot hold ANY per-agent config —
185
185
  * global IS the operator's intended state there, and a guard would instead
186
186
  * blank ALL context for every CC session in that window.
@@ -216,7 +216,7 @@ async function fetchContextBlock(cfg) {
216
216
  return null;
217
217
  const data = await resp.json();
218
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
219
+ // guard). A thin CC client auto-upgrades via npx BEFORE the server does, so
220
220
  // mid-upgrade it may hit a 0.12 server that returns global context with no
221
221
  // `agent` echo — and a 0.12 server cannot hold per-agent config, so global is
222
222
  // the intended state. Guarding here would blank ALL CC context in that window.
@@ -16,14 +16,14 @@
16
16
  *
17
17
  * Directory layout:
18
18
  * ~/.pi/agent/sessions/
19
- * --home-agents-Agents-raider--/
19
+ * --home-alice-projects-myagent--/
20
20
  * 2026-04-10T18-37-44-615Z_<uuid>.jsonl
21
21
  * 2026-04-11T07-51-28-282Z_<uuid>.jsonl
22
22
  * --home-agents-Development-MAIC--/
23
23
  * ...
24
24
  *
25
- * The encoded-cwd uses double-dash separators: /home/agents/Agents/raider
26
- * becomes --home-agents-Agents-raider--. The session header's `cwd` field
25
+ * The encoded-cwd uses double-dash separators: /home/alice/projects/myagent
26
+ * becomes --home-alice-projects-myagent--. The session header's `cwd` field
27
27
  * is the canonical path; the directory name is a filesystem-safe encoding.
28
28
  */
29
29
  import type { TranscriptBatch, CursorMap } from "./transcript-reader.js";
@@ -17,14 +17,14 @@
17
17
  *
18
18
  * Directory layout:
19
19
  * ~/.pi/agent/sessions/
20
- * --home-agents-Agents-raider--/
20
+ * --home-alice-projects-myagent--/
21
21
  * 2026-04-10T18-37-44-615Z_<uuid>.jsonl
22
22
  * 2026-04-11T07-51-28-282Z_<uuid>.jsonl
23
23
  * --home-agents-Development-MAIC--/
24
24
  * ...
25
25
  *
26
- * The encoded-cwd uses double-dash separators: /home/agents/Agents/raider
27
- * becomes --home-agents-Agents-raider--. The session header's `cwd` field
26
+ * The encoded-cwd uses double-dash separators: /home/alice/projects/myagent
27
+ * becomes --home-alice-projects-myagent--. The session header's `cwd` field
28
28
  * is the canonical path; the directory name is a filesystem-safe encoding.
29
29
  */
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -157,7 +157,7 @@ function readPiTranscripts(since, sessionsDir = DEFAULT_PI_SESSIONS_DIR, cursors
157
157
  }
158
158
  /**
159
159
  * Extract the last path segment from a cwd as the project name.
160
- * /home/agents/Agents/raider → "raider"
160
+ * /home/alice/projects/myagent → "myagent"
161
161
  * Falls back to decoding the directory name if cwd is empty.
162
162
  */
163
163
  function deriveProjectName(cwd, encodedDir) {
@@ -165,7 +165,7 @@ function deriveProjectName(cwd, encodedDir) {
165
165
  const segments = cwd.split("/").filter(Boolean);
166
166
  return segments[segments.length - 1] ?? "unknown";
167
167
  }
168
- // Decode the Pi directory encoding: --home-agents-Agents-raider-- → raider
168
+ // Decode the Pi directory encoding: --home-alice-projects-myagent-- → myagent
169
169
  const decoded = encodedDir.replace(/^--/, "").replace(/--$/, "").split("-");
170
170
  return decoded[decoded.length - 1] ?? "unknown";
171
171
  }
@@ -15,7 +15,7 @@ export interface TranscriptBatch {
15
15
  date: string;
16
16
  entries: unknown[];
17
17
  /**
18
- * Optional source-agent label (e.g. "hermes/lenny"). When set, the nightly
18
+ * Optional source-agent label (e.g. "hermes/alice"). When set, the nightly
19
19
  * pipeline uses it verbatim for provenance instead of the default
20
20
  * `claude-code/<project>`. Lets per-harness readers stamp their own origin.
21
21
  */
@@ -159,12 +159,12 @@ function parseTranscriptFile(filePath, projectName, cursorKey, startCursor, gene
159
159
  }
160
160
  /**
161
161
  * Decode CC project directory name to a human-readable project name.
162
- * CC uses path-based hashing: "-Users-mattias-Development-Tools-hicortex"
162
+ * CC uses path-based hashing: "-Users-alice-Development-Tools-hicortex"
163
163
  * becomes "hicortex" (last path component).
164
164
  */
165
165
  function decodeProjectDirName(dirName) {
166
166
  // CC encodes paths by replacing / with -
167
- // e.g. "-Users-mattias-Development-Tools-hicortex"
167
+ // e.g. "-Users-alice-Development-Tools-hicortex"
168
168
  const parts = dirName.split("-").filter(Boolean);
169
169
  if (parts.length === 0)
170
170
  return dirName;
@@ -9,11 +9,26 @@
9
9
  "Backfill an existing corpus with: hicortex classify-domains"
10
10
  ],
11
11
  "domains": [
12
- { "name": "Work", "description": "Your job and professional life — employer, clients, workstreams" },
13
- { "name": "Personal", "description": "Private life — home, hobbies, everyday matters" },
14
- { "name": "People", "description": "Relationshipsfamily, friends, social life, network" },
15
- { "name": "Health", "description": "Fitness, wellbeing, medical" },
16
- { "name": "Finance", "description": "Money — budgeting, spending, investing" }
12
+ {
13
+ "name": "Work",
14
+ "description": "Your job and professional life employer, clients, workstreams"
15
+ },
16
+ {
17
+ "name": "Personal",
18
+ "description": "Private life — home, hobbies, everyday matters"
19
+ },
20
+ {
21
+ "name": "People",
22
+ "description": "Relationships — family, friends, social life, network"
23
+ },
24
+ {
25
+ "name": "Health",
26
+ "description": "Fitness, wellbeing, medical"
27
+ },
28
+ {
29
+ "name": "Finance",
30
+ "description": "Money — budgeting, spending, investing"
31
+ }
17
32
  ],
18
33
  "_powerUserExample": {
19
34
  "_readme": [
@@ -22,14 +37,39 @@
22
37
  "`weakPrimaryFloor` (default 0.45) is the minimum embedding similarity for a no-fit memory to earn a weak primary; tune it from your corpus."
23
38
  ],
24
39
  "domains": [
25
- { "name": "Work", "description": "Employer, day job, client projects, workstreams", "compartment": true },
26
- { "name": "Personal", "description": "Private life — home, hobbies, everyday matters" },
27
- { "name": "People", "description": "Relationships — family, friends, social life, network" },
28
- { "name": "Health", "description": "Fitness, wellbeing, medical" },
29
- { "name": "Finance", "description": "Money — budgeting, spending, investing" },
30
- { "name": "Boating", "description": "Boats — maintenance, gear, trips, harbour life" },
31
- { "name": "Property", "description": "House and land — renovation, upkeep, garden" },
32
- { "name": "Vehicles", "description": "Cars and other vehicles service, repairs, purchases" }
40
+ {
41
+ "name": "Work",
42
+ "description": "Employer, day job, client projects, workstreams",
43
+ "compartment": true
44
+ },
45
+ {
46
+ "name": "Personal",
47
+ "description": "Private lifehome, hobbies, everyday matters"
48
+ },
49
+ {
50
+ "name": "People",
51
+ "description": "Relationships — family, friends, social life, network"
52
+ },
53
+ {
54
+ "name": "Health",
55
+ "description": "Fitness, wellbeing, medical"
56
+ },
57
+ {
58
+ "name": "Finance",
59
+ "description": "Money — budgeting, spending, investing"
60
+ },
61
+ {
62
+ "name": "Photography",
63
+ "description": "Camera gear, shoots, editing workflow, photo projects"
64
+ },
65
+ {
66
+ "name": "Home",
67
+ "description": "House, renovation projects, maintenance, garden"
68
+ },
69
+ {
70
+ "name": "Travel",
71
+ "description": "Trips, destinations, bookings, travel plans"
72
+ }
33
73
  ],
34
74
  "weakPrimaryFloor": 0.5
35
75
  }
@@ -33,8 +33,8 @@ _INJECT_CONTENT_CAP = 500
33
33
 
34
34
  # Agent ids are joined into a filesystem path server-side, so they share the
35
35
  # section-name allowlist. \Z (NOT $) anchors the END OF STRING: Python's $ also
36
- # matches just before a trailing "\n", so "nano\n" would pass and go out as
37
- # agent=nano%0A → a 400 the fail-soft path silently swallows.
36
+ # matches just before a trailing "\n", so "alice\n" would pass and go out as
37
+ # agent=alice%0A → a 400 the fail-soft path silently swallows.
38
38
  _AGENT_ID_RE = re.compile(r"^[a-z0-9][a-z0-9_-]*\Z")
39
39
 
40
40
 
@@ -48,7 +48,7 @@ def _sanitize_agent_id(raw: Optional[str]) -> Optional[str]:
48
48
  EXACTLY so a profile resolves to the SAME id on both harnesses (a mismatch
49
49
  would make one honor the persona firewall and the other leak global context
50
50
  into an ``off``/``override`` persona): lowercase → collapse invalid runs to
51
- "-" → strip leading -/_ → truncate 64 → validate. "Lenny" → "lenny";
51
+ "-" → strip leading -/_ → truncate 64 → validate. "Alice" → "alice";
52
52
  "MacBook-Pro.local" → "macbook-pro-local"; all-symbols → None."""
53
53
  if not isinstance(raw, str):
54
54
  return None
@@ -73,7 +73,7 @@ def _resolve_agent_name(cfg: Dict[str, Any]) -> Optional[str]:
73
73
  2. ``HERMES_PROFILE`` env;
74
74
  3. parse ``HERMES_HOME`` when it ends ``profiles/<name>``;
75
75
  4. None → bare fetch → the global set.
76
- Each source is stripped then SANITIZED (not rejected) so "Lenny" → "lenny"
76
+ Each source is stripped then SANITIZED (not rejected) so "Alice" → "alice"
77
77
  matches the TS contract; a source that sanitizes to None yields None (bare
78
78
  fetch), never a fall-through to another identity."""
79
79
  configured = (cfg.get("agent_name") or "").strip()
@@ -11,7 +11,7 @@
11
11
  "serverUrl": {
12
12
  "type": "string",
13
13
  "default": "http://127.0.0.1:8787",
14
- "description": "Hicortex server URL. Defaults to localhost (co-located server). For multi-machine setups, point this at the remote server (e.g. http://bedrock:8787 or a Tailscale HTTPS URL)."
14
+ "description": "Hicortex server URL. Defaults to localhost (co-located server). For multi-machine setups, point this at the remote server (e.g. http://your-server:8787 or an HTTPS URL)."
15
15
  },
16
16
  "authToken": {
17
17
  "type": "string",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamaze/hicortex",
3
- "version": "0.13.2",
3
+ "version": "0.13.3",
4
4
  "description": "Self-learning memory for AI agents \u2014 experience captured automatically, distilled into lessons overnight, shared across your whole fleet. Works with Hermes, OpenClaw, Claude Code, and Pi.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {