@gamaze/hicortex 0.17.6 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -28
- package/assets/dashboard.html +121 -5
- package/assets/{context.html → identity.html} +18 -18
- package/assets/viz.html +19 -7
- package/dist/claude-md.d.ts +2 -1
- package/dist/claude-md.js +2 -1
- package/dist/cli-args.d.ts +9 -0
- package/dist/cli-args.js +16 -0
- package/dist/cli.js +29 -20
- package/dist/consolidate.d.ts +15 -0
- package/dist/consolidate.js +30 -3
- package/dist/dashboard.d.ts +58 -1
- package/dist/dashboard.js +27 -1
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/health.d.ts +68 -0
- package/dist/health.js +73 -0
- package/dist/identity-cli.d.ts +90 -0
- package/dist/{context-cli.js → identity-cli.js} +66 -48
- package/dist/{context-store.d.ts → identity-store.d.ts} +94 -31
- package/dist/{context-store.js → identity-store.js} +212 -71
- package/dist/index.d.ts +12 -5
- package/dist/index.js +57 -29
- package/dist/init.d.ts +44 -8
- package/dist/init.js +142 -37
- package/dist/learnings-identity.d.ts +149 -0
- package/dist/{lessons-context.js → learnings-identity.js} +96 -52
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +217 -68
- package/dist/memory-instructions.d.ts +6 -6
- package/dist/memory-instructions.js +6 -6
- package/dist/nightly.js +65 -6
- package/dist/paths.js +1 -1
- package/dist/recall-hook-cli.d.ts +1 -1
- package/dist/recall-hook-cli.js +3 -3
- package/dist/recall-index.js +5 -2
- package/dist/status.d.ts +2 -2
- package/dist/status.js +11 -9
- package/dist/telemetry.d.ts +10 -0
- package/dist/type-classify.js +4 -1
- package/dist/type-labels.d.ts +30 -0
- package/dist/type-labels.js +43 -0
- package/dist/types.d.ts +28 -0
- package/dist/uninstall.d.ts +12 -0
- package/dist/uninstall.js +21 -3
- package/dist/viz.d.ts +24 -11
- package/dist/viz.js +97 -32
- package/hermes-plugin/hicortex/README.md +4 -2
- package/package.json +2 -2
- package/dist/context-cli.d.ts +0 -69
- package/dist/lessons-context.d.ts +0 -102
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* learnings-identity — query-time identity + lessons injection for the CC
|
|
3
|
+
* SessionStart hook. (Canonical command name `learnings-identity` since #264;
|
|
4
|
+
* the legacy `lessons-context` subcommand is kept as a backcompat alias via
|
|
5
|
+
* resolveCommandAlias so existing installed hooks keep working. Fetches the
|
|
6
|
+
* identity layer + the lessons block.)
|
|
7
|
+
*
|
|
8
|
+
* Replaces file-based injection (injectLessons / injectLessonsFromServer).
|
|
9
|
+
* Reads ~/.hicortex/config.json to find the server URL, then fetches TWO
|
|
10
|
+
* endpoints concurrently and prints a compact Markdown block to stdout so CC
|
|
11
|
+
* picks it up as session context:
|
|
12
|
+
*
|
|
13
|
+
* GET /identity → the standing identity layer (user info + rules; 0.12,
|
|
14
|
+
* renamed from /context in 0.18 #264). Injected as a
|
|
15
|
+
* `## Identity` block ONLY when this harness ("cc") is in
|
|
16
|
+
* the server-resolved `clients` list (self-gate).
|
|
17
|
+
* GET /lessons → episodic memory lessons + memory index, rendered as the
|
|
18
|
+
* existing `## Hicortex Memory` block.
|
|
19
|
+
*
|
|
20
|
+
* The two fetches run in Promise.all, each with its OWN 3 s timeout and
|
|
21
|
+
* INDEPENDENT fail-soft: an /identity failure must never cost the lessons
|
|
22
|
+
* block, and vice versa. Sequential fetches would double worst-case
|
|
23
|
+
* SessionStart latency (~6 s) — see spec §7.
|
|
24
|
+
*
|
|
25
|
+
* Fail-soft by design: ANY failure (missing config, network error, non-2xx,
|
|
26
|
+
* parse error) results in silent exit-0. A broken hook must never block a
|
|
27
|
+
* CC session, and a broken /identity fetch must never blank the whole output.
|
|
28
|
+
*/
|
|
29
|
+
import { type AgentMode } from "./identity-store.js";
|
|
30
|
+
/**
|
|
31
|
+
* The GET /identity response shape, shared by the CC hook and the OC plugin so
|
|
32
|
+
* their gating cannot drift. `agent`/`mode` are echoed by a 0.13 server whenever
|
|
33
|
+
* `?agent=` was sent (in EVERY mode); a pre-0.13 server omits them.
|
|
34
|
+
*/
|
|
35
|
+
export interface IdentityResponse {
|
|
36
|
+
sections?: Record<string, string>;
|
|
37
|
+
updated_at?: string;
|
|
38
|
+
clients?: string[];
|
|
39
|
+
agent?: string;
|
|
40
|
+
mode?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ResolvedConfig {
|
|
43
|
+
serverUrl: string;
|
|
44
|
+
authToken: string | undefined;
|
|
45
|
+
home: string;
|
|
46
|
+
/** Per-agent identity id sent as ?agent= (0.13); null → global (no param). */
|
|
47
|
+
agentName: string | null;
|
|
48
|
+
/** Max lessons to inject (config.lessonsLimit, default 10). */
|
|
49
|
+
lessonsLimit?: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Read ~/.hicortex/config.json and resolve the server URL + auth token, or
|
|
53
|
+
* null when there is no usable config (server not set up yet — fail soft).
|
|
54
|
+
* Exported for reuse by the recall-hook CLI (#192) so the two CC hooks can
|
|
55
|
+
* never resolve the server differently.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveConfig(): ResolvedConfig | null;
|
|
58
|
+
/**
|
|
59
|
+
* Title-case a section name for its heading: split on `-`/`_`, capitalize each
|
|
60
|
+
* word ("user" → "User", "my_notes" → "My Notes").
|
|
61
|
+
* Exported so the OC plugin (index.ts) renders the `## Identity` block
|
|
62
|
+
* identically to the CC hook rather than duplicating the logic.
|
|
63
|
+
*/
|
|
64
|
+
export declare function titleCaseSection(name: string): string;
|
|
65
|
+
/**
|
|
66
|
+
* Stable section ordering: `user` first, then `rules` (the seeded primary
|
|
67
|
+
* sections, spec §8), then every other section alphabetically. Server-side
|
|
68
|
+
* enumeration order (readdirSync) is FS-dependent, so we sort here for a
|
|
69
|
+
* deterministic injection block. Exported for reuse by the OC plugin.
|
|
70
|
+
*/
|
|
71
|
+
export declare function orderSectionNames(names: string[]): string[];
|
|
72
|
+
/**
|
|
73
|
+
* Render the `## Identity` block from a resolved section map, or null when there
|
|
74
|
+
* is nothing to inject (no sections, or every section blank after trimming).
|
|
75
|
+
* Pure — no gating, no I/O. Shared verbatim by the CC hook and the OC plugin so
|
|
76
|
+
* both harnesses emit an identical block. Sections are ordered (user, rules,
|
|
77
|
+
* then alphabetical) and rendered under title-cased `###` headings.
|
|
78
|
+
*/
|
|
79
|
+
export declare function renderIdentityBlock(sections: Record<string, string>): string | null;
|
|
80
|
+
/**
|
|
81
|
+
* Gate a GET /identity response and render the `## Identity` block, or null when
|
|
82
|
+
* nothing should be injected: `harness` not in the server-resolved `clients`,
|
|
83
|
+
* an empty/blank section set, or — when `requireAgentEcho` — a response that
|
|
84
|
+
* does not echo `agent`. The SINGLE gate used by both CC and OC so the two can
|
|
85
|
+
* never drift (the Python Hermes plugin `provider.py::_context_block` mirrors
|
|
86
|
+
* this logic — keep them in sync).
|
|
87
|
+
*
|
|
88
|
+
* `requireAgentEcho` is the old-server guard, and it is the CALLER's decision:
|
|
89
|
+
* - OC passes `agentId !== null` — when it actually sent an id, a 0.12 server
|
|
90
|
+
* that ignores `?agent=` (200 global, no echo) must NOT leak global identity
|
|
91
|
+
* into every persona; on a bare fetch (no id) the guard is off (amendment
|
|
92
|
+
* A2).
|
|
93
|
+
* - CC passes `false` ALWAYS and deliberately (see the call site): a thin CC
|
|
94
|
+
* client auto-upgrades via npx BEFORE the server does, so during the upgrade
|
|
95
|
+
* window it talks to a 0.12 server that cannot hold ANY per-agent config —
|
|
96
|
+
* global IS the operator's intended state there, and a guard would instead
|
|
97
|
+
* blank ALL identity for every CC session in that window.
|
|
98
|
+
*/
|
|
99
|
+
/**
|
|
100
|
+
* Result of `buildIdentityToolResult` — the MCP tool handler maps this to its
|
|
101
|
+
* `{content:[{type:"text",text}],isError?}` shape. Pure value: no MCP SDK
|
|
102
|
+
* types leak here so the function is unit-testable with no harness.
|
|
103
|
+
*/
|
|
104
|
+
export interface IdentityToolResult {
|
|
105
|
+
text: string;
|
|
106
|
+
isError?: boolean;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Build the `hicortex_identity` MCP tool result from the SAME pure pipeline the
|
|
110
|
+
* REST `GET /identity` route and the SessionStart hook use (#264 CRITICAL fix:
|
|
111
|
+
* previously the tool was a closure inside `createMcpServer()` that tests
|
|
112
|
+
* re-implemented locally, so the production path was never exercised).
|
|
113
|
+
*
|
|
114
|
+
* Pipeline (kept identical to REST + hook by CONSTRUCTION, not by mirroring):
|
|
115
|
+
* 1. `handleIdentityGet` — the real GET /identity handler, with the optional
|
|
116
|
+
* `agent` param forwarded so per-agent installs resolve the right scope
|
|
117
|
+
* (WARNING-2: previously the tool always passed `{}` → global, so an agent
|
|
118
|
+
* with an override saw the wrong identity).
|
|
119
|
+
* 2. `injectMemorySection` — the synthetic product-owned `memory` section
|
|
120
|
+
* (WARNING-1: the REST route + SessionStart hook inject it; the tool did
|
|
121
|
+
* not, contradicting its "same data" docs).
|
|
122
|
+
* 3. optional `name` filter, then `renderIdentityBlock` for the `### <Title>`
|
|
123
|
+
* markdown the hook injects.
|
|
124
|
+
*
|
|
125
|
+
* Pure: no I/O of its own (the only I/O is `handleIdentityGet` reading the
|
|
126
|
+
* identity dir, which is the same I/O the REST route does). Takes the resolved
|
|
127
|
+
* `identityClients` / `identityAgents` the daemon already holds at boot.
|
|
128
|
+
*/
|
|
129
|
+
export declare function buildIdentityToolResult(identityDir: string, identityClients: string[], identityAgents: Record<string, AgentMode>, opts: {
|
|
130
|
+
name?: string;
|
|
131
|
+
agent?: string;
|
|
132
|
+
memoryInstructionsEnabled: boolean;
|
|
133
|
+
}): IdentityToolResult;
|
|
134
|
+
export declare function gateAndRenderIdentity(data: IdentityResponse, harness: string, opts: {
|
|
135
|
+
requireAgentEcho: boolean;
|
|
136
|
+
}): string | null;
|
|
137
|
+
/**
|
|
138
|
+
* Fetch identity + lessons concurrently and return the combined Markdown block,
|
|
139
|
+
* or null when neither yields anything (nothing to inject; caller prints
|
|
140
|
+
* nothing and exits 0). The `## Identity` block is prepended before the existing
|
|
141
|
+
* `## Hicortex Memory` block.
|
|
142
|
+
*/
|
|
143
|
+
export declare function fetchLessonsIdentity(): Promise<string | null>;
|
|
144
|
+
/** Backcompat alias (#264). */
|
|
145
|
+
export declare const fetchLessonsContext: typeof fetchLessonsIdentity;
|
|
146
|
+
/** Backcompat aliases (#264) for the renamed symbols. */
|
|
147
|
+
export declare const renderContextBlock: typeof renderIdentityBlock;
|
|
148
|
+
export declare const gateAndRenderContext: typeof gateAndRenderIdentity;
|
|
149
|
+
export type ContextResponse = IdentityResponse;
|
|
@@ -1,43 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* learnings-identity — query-time identity + lessons injection for the CC
|
|
4
|
+
* SessionStart hook. (Canonical command name `learnings-identity` since #264;
|
|
5
|
+
* the legacy `lessons-context` subcommand is kept as a backcompat alias via
|
|
6
|
+
* resolveCommandAlias so existing installed hooks keep working. Fetches the
|
|
7
|
+
* identity layer + the lessons block.)
|
|
4
8
|
*
|
|
5
9
|
* Replaces file-based injection (injectLessons / injectLessonsFromServer).
|
|
6
10
|
* Reads ~/.hicortex/config.json to find the server URL, then fetches TWO
|
|
7
11
|
* endpoints concurrently and prints a compact Markdown block to stdout so CC
|
|
8
12
|
* picks it up as session context:
|
|
9
13
|
*
|
|
10
|
-
* GET /
|
|
11
|
-
*
|
|
12
|
-
* ("cc") is in
|
|
14
|
+
* GET /identity → the standing identity layer (user info + rules; 0.12,
|
|
15
|
+
* renamed from /context in 0.18 #264). Injected as a
|
|
16
|
+
* `## Identity` block ONLY when this harness ("cc") is in
|
|
17
|
+
* the server-resolved `clients` list (self-gate).
|
|
13
18
|
* GET /lessons → episodic memory lessons + memory index, rendered as the
|
|
14
19
|
* existing `## Hicortex Memory` block.
|
|
15
20
|
*
|
|
16
21
|
* The two fetches run in Promise.all, each with its OWN 3 s timeout and
|
|
17
|
-
* INDEPENDENT fail-soft:
|
|
18
|
-
* and vice versa. Sequential fetches would double worst-case
|
|
19
|
-
* latency (~6 s) — see spec §7.
|
|
22
|
+
* INDEPENDENT fail-soft: an /identity failure must never cost the lessons
|
|
23
|
+
* block, and vice versa. Sequential fetches would double worst-case
|
|
24
|
+
* SessionStart latency (~6 s) — see spec §7.
|
|
20
25
|
*
|
|
21
26
|
* Fail-soft by design: ANY failure (missing config, network error, non-2xx,
|
|
22
27
|
* parse error) results in silent exit-0. A broken hook must never block a
|
|
23
|
-
* CC session, and a broken /
|
|
28
|
+
* CC session, and a broken /identity fetch must never blank the whole output.
|
|
24
29
|
*/
|
|
25
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.gateAndRenderContext = exports.renderContextBlock = exports.fetchLessonsContext = void 0;
|
|
26
32
|
exports.resolveConfig = resolveConfig;
|
|
27
33
|
exports.titleCaseSection = titleCaseSection;
|
|
28
34
|
exports.orderSectionNames = orderSectionNames;
|
|
29
|
-
exports.
|
|
30
|
-
exports.
|
|
31
|
-
exports.
|
|
35
|
+
exports.renderIdentityBlock = renderIdentityBlock;
|
|
36
|
+
exports.buildIdentityToolResult = buildIdentityToolResult;
|
|
37
|
+
exports.gateAndRenderIdentity = gateAndRenderIdentity;
|
|
38
|
+
exports.fetchLessonsIdentity = fetchLessonsIdentity;
|
|
32
39
|
const node_fs_1 = require("node:fs");
|
|
33
40
|
const node_path_1 = require("node:path");
|
|
34
|
-
const
|
|
41
|
+
const identity_store_js_1 = require("./identity-store.js");
|
|
42
|
+
const memory_instructions_js_1 = require("./memory-instructions.js");
|
|
35
43
|
const features_js_1 = require("./features.js");
|
|
36
44
|
const extensions_js_1 = require("./extensions.js");
|
|
37
45
|
const state_js_1 = require("./state.js");
|
|
38
46
|
const paths_js_1 = require("./paths.js");
|
|
39
47
|
const DEFAULT_PORT = 8787;
|
|
40
|
-
/** Harness name this hook injects for — used to self-gate on GET /
|
|
48
|
+
/** Harness name this hook injects for — used to self-gate on GET /identity `clients`. */
|
|
41
49
|
const THIS_HARNESS = "cc";
|
|
42
50
|
/**
|
|
43
51
|
* Read ~/.hicortex/config.json and resolve the server URL + auth token, or
|
|
@@ -58,12 +66,12 @@ function resolveConfig() {
|
|
|
58
66
|
const serverUrl = config.mode === "client" && typeof config.serverUrl === "string"
|
|
59
67
|
? config.serverUrl.replace(/\/+$/, "")
|
|
60
68
|
: `http://127.0.0.1:${config.port ?? DEFAULT_PORT}`;
|
|
61
|
-
// Per-agent
|
|
69
|
+
// Per-agent identity id (0.13) via the shared resolver, so the id sent here
|
|
62
70
|
// always matches what `hicortex status` reports. No configured agentName →
|
|
63
71
|
// agentId null → NO ?agent= (bare fetch): CC's default is the shared global
|
|
64
|
-
//
|
|
72
|
+
// identity. A configured agentName that sanitizes to null → agentId null too
|
|
65
73
|
// (NO ?agent=), never a 400 that the fail-soft hook would silently swallow.
|
|
66
|
-
const agentName = (0,
|
|
74
|
+
const agentName = (0, identity_store_js_1.resolveAgentIdentity)(config).agentId;
|
|
67
75
|
return {
|
|
68
76
|
serverUrl,
|
|
69
77
|
authToken: config.authToken,
|
|
@@ -133,7 +141,7 @@ async function fetchLessonsBlock(cfg) {
|
|
|
133
141
|
/**
|
|
134
142
|
* Title-case a section name for its heading: split on `-`/`_`, capitalize each
|
|
135
143
|
* word ("user" → "User", "my_notes" → "My Notes").
|
|
136
|
-
* Exported so the OC plugin (index.ts) renders the `##
|
|
144
|
+
* Exported so the OC plugin (index.ts) renders the `## Identity` block
|
|
137
145
|
* identically to the CC hook rather than duplicating the logic.
|
|
138
146
|
*/
|
|
139
147
|
function titleCaseSection(name) {
|
|
@@ -155,13 +163,13 @@ function orderSectionNames(names) {
|
|
|
155
163
|
return [...primaries, ...rest];
|
|
156
164
|
}
|
|
157
165
|
/**
|
|
158
|
-
* Render the `##
|
|
166
|
+
* Render the `## Identity` block from a resolved section map, or null when there
|
|
159
167
|
* is nothing to inject (no sections, or every section blank after trimming).
|
|
160
168
|
* Pure — no gating, no I/O. Shared verbatim by the CC hook and the OC plugin so
|
|
161
169
|
* both harnesses emit an identical block. Sections are ordered (user, rules,
|
|
162
170
|
* then alphabetical) and rendered under title-cased `###` headings.
|
|
163
171
|
*/
|
|
164
|
-
function
|
|
172
|
+
function renderIdentityBlock(sections) {
|
|
165
173
|
if (!sections || typeof sections !== "object" || Array.isArray(sections))
|
|
166
174
|
return null;
|
|
167
175
|
const names = orderSectionNames(Object.keys(sections));
|
|
@@ -174,28 +182,59 @@ function renderContextBlock(sections) {
|
|
|
174
182
|
}
|
|
175
183
|
if (bodyParts.length === 0)
|
|
176
184
|
return null;
|
|
177
|
-
return ["##
|
|
185
|
+
return ["## Identity", "", ...bodyParts].join("\n");
|
|
178
186
|
}
|
|
179
187
|
/**
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* never drift (the Python Hermes plugin `provider.py::_context_block` mirrors
|
|
185
|
-
* this logic — keep them in sync).
|
|
188
|
+
* Build the `hicortex_identity` MCP tool result from the SAME pure pipeline the
|
|
189
|
+
* REST `GET /identity` route and the SessionStart hook use (#264 CRITICAL fix:
|
|
190
|
+
* previously the tool was a closure inside `createMcpServer()` that tests
|
|
191
|
+
* re-implemented locally, so the production path was never exercised).
|
|
186
192
|
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
193
|
+
* Pipeline (kept identical to REST + hook by CONSTRUCTION, not by mirroring):
|
|
194
|
+
* 1. `handleIdentityGet` — the real GET /identity handler, with the optional
|
|
195
|
+
* `agent` param forwarded so per-agent installs resolve the right scope
|
|
196
|
+
* (WARNING-2: previously the tool always passed `{}` → global, so an agent
|
|
197
|
+
* with an override saw the wrong identity).
|
|
198
|
+
* 2. `injectMemorySection` — the synthetic product-owned `memory` section
|
|
199
|
+
* (WARNING-1: the REST route + SessionStart hook inject it; the tool did
|
|
200
|
+
* not, contradicting its "same data" docs).
|
|
201
|
+
* 3. optional `name` filter, then `renderIdentityBlock` for the `### <Title>`
|
|
202
|
+
* markdown the hook injects.
|
|
203
|
+
*
|
|
204
|
+
* Pure: no I/O of its own (the only I/O is `handleIdentityGet` reading the
|
|
205
|
+
* identity dir, which is the same I/O the REST route does). Takes the resolved
|
|
206
|
+
* `identityClients` / `identityAgents` the daemon already holds at boot.
|
|
197
207
|
*/
|
|
198
|
-
function
|
|
208
|
+
function buildIdentityToolResult(identityDir, identityClients, identityAgents, opts) {
|
|
209
|
+
// WARNING-2: forward `agent` so per-agent installs resolve the right scope.
|
|
210
|
+
// An invalid id makes handleIdentityGet return a 400 → surfaced as isError.
|
|
211
|
+
const query = opts.agent ? { agent: opts.agent } : {};
|
|
212
|
+
const r = (0, identity_store_js_1.handleIdentityGet)(identityDir, identityClients, query, identityAgents);
|
|
213
|
+
if (r.status !== 200) {
|
|
214
|
+
const errBody = r.body;
|
|
215
|
+
return {
|
|
216
|
+
text: `Identity fetch failed: ${JSON.stringify(errBody.error ?? r.body)}`,
|
|
217
|
+
isError: true,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
// WARNING-1: inject the synthetic `memory` section exactly like REST + the
|
|
221
|
+
// SessionStart hook. `injectMemorySection` is a no-op when disabled or when
|
|
222
|
+
// agent mode === "off".
|
|
223
|
+
(0, memory_instructions_js_1.injectMemorySection)(r.body, opts.memoryInstructionsEnabled);
|
|
224
|
+
const sections = r.body.sections ?? {};
|
|
225
|
+
const filtered = opts.name
|
|
226
|
+
? (sections[opts.name] !== undefined ? { [opts.name]: sections[opts.name] } : {})
|
|
227
|
+
: sections;
|
|
228
|
+
const block = renderIdentityBlock(filtered);
|
|
229
|
+
if (block === null) {
|
|
230
|
+
const text = opts.name
|
|
231
|
+
? `No identity section named '${opts.name}'.`
|
|
232
|
+
: "No identity sections configured.";
|
|
233
|
+
return { text };
|
|
234
|
+
}
|
|
235
|
+
return { text: block };
|
|
236
|
+
}
|
|
237
|
+
function gateAndRenderIdentity(data, harness, opts) {
|
|
199
238
|
if (!data || typeof data !== "object")
|
|
200
239
|
return null;
|
|
201
240
|
const clients = Array.isArray(data.clients) ? data.clients : [];
|
|
@@ -203,21 +242,21 @@ function gateAndRenderContext(data, harness, opts) {
|
|
|
203
242
|
return null;
|
|
204
243
|
if (opts.requireAgentEcho && typeof data.agent !== "string")
|
|
205
244
|
return null;
|
|
206
|
-
return
|
|
245
|
+
return renderIdentityBlock(data.sections ?? {});
|
|
207
246
|
}
|
|
208
247
|
/**
|
|
209
|
-
* Fetch /
|
|
248
|
+
* Fetch /identity and build the `## Identity` block, or null when nothing should
|
|
210
249
|
* be injected: non-2xx, this harness not in `clients`, no sections, or all
|
|
211
250
|
* sections empty. Throws propagate to the caller's fail-soft catch.
|
|
212
251
|
*/
|
|
213
|
-
async function
|
|
252
|
+
async function fetchIdentityBlock(cfg) {
|
|
214
253
|
// Send ?agent= only when we have a valid id; the server does the merge and
|
|
215
254
|
// returns the resolved sections, so the hook stays dumb (no client-side mode
|
|
216
255
|
// logic). A null id (CC's default: no configured agentName, or a configured
|
|
217
|
-
// value that sanitizes to nothing) → bare /
|
|
256
|
+
// value that sanitizes to nothing) → bare /identity → the shared global set.
|
|
218
257
|
const url = cfg.agentName
|
|
219
|
-
? `${cfg.serverUrl}/
|
|
220
|
-
: `${cfg.serverUrl}/
|
|
258
|
+
? `${cfg.serverUrl}/identity?agent=${encodeURIComponent(cfg.agentName)}`
|
|
259
|
+
: `${cfg.serverUrl}/identity`;
|
|
221
260
|
const resp = await fetch(url, {
|
|
222
261
|
headers: authHeaders(cfg.authToken),
|
|
223
262
|
signal: AbortSignal.timeout(3000),
|
|
@@ -227,30 +266,35 @@ async function fetchContextBlock(cfg) {
|
|
|
227
266
|
const data = await resp.json();
|
|
228
267
|
// CC deliberately passes requireAgentEcho: false (NOT the OC/Hermes old-server
|
|
229
268
|
// guard). A thin CC client auto-upgrades via npx BEFORE the server does, so
|
|
230
|
-
// mid-upgrade it may hit a 0.12 server that returns global
|
|
269
|
+
// mid-upgrade it may hit a 0.12 server that returns global identity with no
|
|
231
270
|
// `agent` echo — and a 0.12 server cannot hold per-agent config, so global is
|
|
232
|
-
// the intended state. Guarding here would blank ALL CC
|
|
233
|
-
return
|
|
271
|
+
// the intended state. Guarding here would blank ALL CC identity in that window.
|
|
272
|
+
return gateAndRenderIdentity(data, THIS_HARNESS, { requireAgentEcho: false });
|
|
234
273
|
}
|
|
235
274
|
/**
|
|
236
|
-
* Fetch
|
|
275
|
+
* Fetch identity + lessons concurrently and return the combined Markdown block,
|
|
237
276
|
* or null when neither yields anything (nothing to inject; caller prints
|
|
238
|
-
* nothing and exits 0). The `##
|
|
277
|
+
* nothing and exits 0). The `## Identity` block is prepended before the existing
|
|
239
278
|
* `## Hicortex Memory` block.
|
|
240
279
|
*/
|
|
241
|
-
async function
|
|
280
|
+
async function fetchLessonsIdentity() {
|
|
242
281
|
const cfg = resolveConfig();
|
|
243
282
|
if (!cfg)
|
|
244
283
|
return null;
|
|
245
284
|
// Independent fail-soft: each branch degrades to null without affecting the
|
|
246
285
|
// other. Promise.all runs them concurrently — each carries its own 3 s
|
|
247
286
|
// timeout, so worst-case latency stays ~3 s, not ~6 s (spec §7).
|
|
248
|
-
const [
|
|
249
|
-
|
|
287
|
+
const [identityBlock, lessonsBlock] = await Promise.all([
|
|
288
|
+
fetchIdentityBlock(cfg).catch(() => null),
|
|
250
289
|
fetchLessonsBlock(cfg).catch(() => null),
|
|
251
290
|
]);
|
|
252
|
-
const blocks = [
|
|
291
|
+
const blocks = [identityBlock, lessonsBlock].filter((b) => b !== null && b !== "");
|
|
253
292
|
if (blocks.length === 0)
|
|
254
293
|
return null;
|
|
255
294
|
return blocks.join("\n\n");
|
|
256
295
|
}
|
|
296
|
+
/** Backcompat alias (#264). */
|
|
297
|
+
exports.fetchLessonsContext = fetchLessonsIdentity;
|
|
298
|
+
/** Backcompat aliases (#264) for the renamed symbols. */
|
|
299
|
+
exports.renderContextBlock = renderIdentityBlock;
|
|
300
|
+
exports.gateAndRenderContext = gateAndRenderIdentity;
|
package/dist/mcp-server.d.ts
CHANGED
|
@@ -10,9 +10,11 @@
|
|
|
10
10
|
* GET /sse — SSE stream for MCP clients
|
|
11
11
|
* POST /messages — message endpoint for MCP clients
|
|
12
12
|
*/
|
|
13
|
+
import type { MemorySearchResult } from "./types.js";
|
|
13
14
|
export declare function startServer(options?: {
|
|
14
15
|
port?: number;
|
|
15
16
|
host?: string;
|
|
16
17
|
dbPath?: string;
|
|
17
18
|
licenseKey?: string;
|
|
18
19
|
}): Promise<void>;
|
|
20
|
+
export declare function formatResults(results: MemorySearchResult[]): string;
|