@gamaze/hicortex 0.16.8 → 0.16.10
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 +2 -1
- package/dist/config-read.d.ts +7 -0
- package/dist/config-read.js +47 -0
- package/dist/features.d.ts +12 -2
- package/dist/features.js +14 -3
- package/dist/index.js +5 -1
- package/dist/lessons-context.d.ts +2 -0
- package/dist/lessons-context.js +8 -2
- package/dist/mcp-server.js +4 -0
- package/dist/nightly.js +12 -0
- package/dist/prompts.d.ts +6 -0
- package/dist/prompts.js +16 -3
- package/dist/telemetry.d.ts +9 -0
- package/dist/telemetry.js +3 -0
- package/dist/types.d.ts +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -203,7 +203,7 @@ Config at `~/.hicortex/config.json`. Created by `init`. Key options:
|
|
|
203
203
|
| `numCtx` | Context window for ollama (default 8192, one value for all phases). Scoring uses ~850 tokens, so 2048 is ample; distill/reflect/classify need more for `detectChunkSize`'s chunk sizing. |
|
|
204
204
|
| `enableThinking` | Toggle the model's internal reasoning ("thinking") stream for OpenAI-compatible endpoints (default false). Only meaningful for local chat-template-aware servers (ollama, mlx-lm); leave unset for cloud OpenAI/OpenRouter/Groq endpoints (they 400 on the unknown `chat_template_kwargs` field). |
|
|
205
205
|
| `maxTokens` | Max output tokens for all phases (default 8192). A ceiling, not a target — the model stops early when done. |
|
|
206
|
-
| `ollamaFlushEvery` | Flush ollama's accumulated memory every N scoring calls
|
|
206
|
+
| `ollamaFlushEvery` | Flush ollama's accumulated memory every N scoring calls. **Off by default (0)** — opt-in only for an **ollama** install whose runner RSS growth (~171 MB/call) swap-thrashes long consolidations on a RAM-constrained box; N=15 caps a cycle at ~2.5 GB. Gated on the provider being ollama (local **or** remote) — no effect for non-ollama providers. Only you can judge whether your ollama endpoint actually suffers the growth (a managed/cloud ollama host may not), so it stays off until you set it. |
|
|
207
207
|
| `ollamaFlushWaitMs` | Milliseconds to wait after an ollama flush for the runner to exit + release memory (default 180000 = 3 min). |
|
|
208
208
|
| `authToken` | Bearer token for endpoint auth. Generated on first `init` in server mode. Find the active token with `hicortex status` or in `~/.hicortex/config.json`. |
|
|
209
209
|
| `corsAllowedOrigins` | Browser origins allowed to read cross-origin responses, e.g. `["https://ui.example.com"]`. **Empty by default** — the server sends no `Access-Control-Allow-Origin` and never `Allow-Credentials`, so no external web page can read its data. The bundled `/viz` and `/context/ui` pages are same-origin and need no entry. |
|
|
@@ -211,6 +211,7 @@ Config at `~/.hicortex/config.json`. Created by `init`. Key options:
|
|
|
211
211
|
| `domains` | Your memory domain list (`[{name, description}]`). Scaffolded by `init`; edit freely — see [Memory Domains & Tags](#memory-domains--tags) |
|
|
212
212
|
| `weakPrimaryFloor` | Minimum similarity for a no-fit memory to keep a weak domain association (default: 0.45) |
|
|
213
213
|
| `moduleIndexTokenBudget` | Max tokens for domain index in lessons context (default: 500) |
|
|
214
|
+
| `lessonsLimit` | Max lessons injected into an agent's session-start context (default: 10). Lessons are ranked per session by project/domain affinity + recency + strength + access, so each session sees its most-relevant slice. Lower = leaner system prompts. |
|
|
214
215
|
| `contextClients` | Which harnesses inject the [context layer](#context-layer) at session start (default `["cc"]`; `"all"` or any subset of `cc`/`hermes`/`oc`) |
|
|
215
216
|
| `contextAgents` | Per-agent context modes (0.13): `{ "<id>": "override" \| "global" \| "off" }`. Absent + no `agents/<id>/` dir → every agent gets the global set. Boot-time (restart to apply) — see [Per-agent context](#per-agent-context-013) |
|
|
216
217
|
| `agentName` | This install's per-agent context id sent as `?agent=`. **Unset by default** (CC shares the global context — no `?agent=` sent). Explicit opt-in via `init --agent-name <name>`; `init --agent-name ""` clears it. An empty/whitespace value equals unset |
|
package/dist/config-read.d.ts
CHANGED
|
@@ -29,3 +29,10 @@ export declare function readStrictBoolean(config: Record<string, unknown>, key:
|
|
|
29
29
|
* value (e.g. ollamaFlushEvery).
|
|
30
30
|
*/
|
|
31
31
|
export declare function readNonNegativeConfig(config: Record<string, unknown>, key: string, def: number): number;
|
|
32
|
+
/**
|
|
33
|
+
* Warn if the saved config carries keys that 0.16.8+ ignores. Call at every
|
|
34
|
+
* config read (daemon boot + nightly). The warning clears once the keys are
|
|
35
|
+
* removed and (for the model keys) the model is consolidated into
|
|
36
|
+
* llmModel/llmBaseUrl/llmProvider.
|
|
37
|
+
*/
|
|
38
|
+
export declare function warnIgnoredConfigKeys(savedConfig: Record<string, unknown> | null | undefined): void;
|
package/dist/config-read.js
CHANGED
|
@@ -16,6 +16,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
16
16
|
exports.readPositiveConfig = readPositiveConfig;
|
|
17
17
|
exports.readStrictBoolean = readStrictBoolean;
|
|
18
18
|
exports.readNonNegativeConfig = readNonNegativeConfig;
|
|
19
|
+
exports.warnIgnoredConfigKeys = warnIgnoredConfigKeys;
|
|
19
20
|
/**
|
|
20
21
|
* Read a positive finite number from a config object. Returns `def` when the
|
|
21
22
|
* key is absent OR present-but-invalid (with a warn in the latter case).
|
|
@@ -58,3 +59,49 @@ function readNonNegativeConfig(config, key, def) {
|
|
|
58
59
|
console.warn(`[hicortex] config "${key}" = ${String(v)} is not a non-negative finite number — using default ${def}.`);
|
|
59
60
|
return def;
|
|
60
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Config keys that ≤0.16.7 accepted and 0.16.8+ IGNORES. Two groups:
|
|
64
|
+
* - per-stage model keys + the nested `models` block (#231): one model now
|
|
65
|
+
* serves all phases, so distillation/reflection/classification silently run
|
|
66
|
+
* on `llmModel` — a quality downgrade if a larger model was on a distill/
|
|
67
|
+
* reflect tier.
|
|
68
|
+
* - `distillFallback` (#232): removed; strict mode is default. A `"local"`
|
|
69
|
+
* value no longer falls back — failures retry next run.
|
|
70
|
+
* Both were public (documented in the README config table). Warn loudly at the
|
|
71
|
+
* config boundary so neither change is silent.
|
|
72
|
+
*
|
|
73
|
+
* NOTE: this list is deliberately WIDER than the `HicortexConfig` type. In
|
|
74
|
+
* ≤0.16.7 most of these keys (the flat `distill*`/`reflect*` set and
|
|
75
|
+
* `distillFallback`) were NEVER declared in the interface — they were
|
|
76
|
+
* accepted-but-untyped, read loosely off the config object, with the README
|
|
77
|
+
* config table as their only public contract. So deriving this list from the
|
|
78
|
+
* type would miss them. The list is the README's documented keys; don't trim it
|
|
79
|
+
* to match the interface (a future audit that did so would silently re-introduce
|
|
80
|
+
* the ignored-key gap this warning exists to close).
|
|
81
|
+
*/
|
|
82
|
+
const IGNORED_CONFIG_KEYS = [
|
|
83
|
+
"distillModel", "distillBaseUrl", "distillApiKey", "distillProvider",
|
|
84
|
+
"reflectModel", "reflectBaseUrl", "reflectApiKey", "reflectProvider",
|
|
85
|
+
"classifyModel", "classifyBaseUrl", "classifyApiKey", "classifyProvider",
|
|
86
|
+
"distillFallback",
|
|
87
|
+
];
|
|
88
|
+
/**
|
|
89
|
+
* Warn if the saved config carries keys that 0.16.8+ ignores. Call at every
|
|
90
|
+
* config read (daemon boot + nightly). The warning clears once the keys are
|
|
91
|
+
* removed and (for the model keys) the model is consolidated into
|
|
92
|
+
* llmModel/llmBaseUrl/llmProvider.
|
|
93
|
+
*/
|
|
94
|
+
function warnIgnoredConfigKeys(savedConfig) {
|
|
95
|
+
if (!savedConfig)
|
|
96
|
+
return;
|
|
97
|
+
const present = IGNORED_CONFIG_KEYS.filter((k) => savedConfig[k] !== undefined);
|
|
98
|
+
const hasModelsBlock = savedConfig.models !== undefined;
|
|
99
|
+
if (present.length === 0 && !hasModelsBlock)
|
|
100
|
+
return;
|
|
101
|
+
const detail = [...present, ...(hasModelsBlock ? ["models"] : [])].join(", ");
|
|
102
|
+
console.warn(`[hicortex] config has keys IGNORED since 0.16.8 (${detail}). They have no effect now. ` +
|
|
103
|
+
`Per-stage model keys / the \`models\` block: one model serves all phases — set ` +
|
|
104
|
+
`llmModel/llmBaseUrl/llmProvider (+ llmApiKey) to your intended model. ` +
|
|
105
|
+
`distillFallback: removed (strict mode is default — a failed distill retries next run). ` +
|
|
106
|
+
`Remove these keys to clear this warning. See the 0.16.8 changelog.`);
|
|
107
|
+
}
|
package/dist/features.d.ts
CHANGED
|
@@ -25,8 +25,18 @@ export declare function isPro(): boolean;
|
|
|
25
25
|
export declare function maxMemoriesAllowed(): number;
|
|
26
26
|
/** Always false — no cap is ever reached. */
|
|
27
27
|
export declare function memoryCapReached(_currentCount: number): boolean;
|
|
28
|
-
/**
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Max lessons injected into an agent's session-start context. Configurable via
|
|
30
|
+
* `config.lessonsLimit` (a positive integer); defaults to 10. Reuses
|
|
31
|
+
* `readPositiveConfig` so a present-but-invalid value (0, negative, NaN, wrong
|
|
32
|
+
* type) is rejected AT THE BOUNDARY with a warn — never silently. 0 is NOT
|
|
33
|
+
* honoured as "off" (use a real config switch if that's ever needed); it is
|
|
34
|
+
* invalid and falls back to 10 with a warning, so an operator can't
|
|
35
|
+
* accidentally silence lesson injection by typing 0.
|
|
36
|
+
*/
|
|
37
|
+
export declare function lessonsLimit(config?: {
|
|
38
|
+
lessonsLimit?: unknown;
|
|
39
|
+
} | null): number;
|
|
30
40
|
/** Always true — remote ingest is always allowed. */
|
|
31
41
|
export declare function remoteIngestAllowed(): boolean;
|
|
32
42
|
/** Direct read of the underlying features record. */
|
package/dist/features.js
CHANGED
|
@@ -24,6 +24,7 @@ exports.getCurrentFeatures = getCurrentFeatures;
|
|
|
24
24
|
const paths_js_1 = require("./paths.js");
|
|
25
25
|
const license_js_1 = require("./license.js");
|
|
26
26
|
const state_js_1 = require("./state.js");
|
|
27
|
+
const config_read_js_1 = require("./config-read.js");
|
|
27
28
|
const DEFAULT_STATE_DIR = (0, paths_js_1.hicortexHome)();
|
|
28
29
|
// A single canonical "full" feature set — no tiers.
|
|
29
30
|
const FULL_FEATURES = {
|
|
@@ -90,9 +91,19 @@ function maxMemoriesAllowed() {
|
|
|
90
91
|
function memoryCapReached(_currentCount) {
|
|
91
92
|
return false;
|
|
92
93
|
}
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Max lessons injected into an agent's session-start context. Configurable via
|
|
96
|
+
* `config.lessonsLimit` (a positive integer); defaults to 10. Reuses
|
|
97
|
+
* `readPositiveConfig` so a present-but-invalid value (0, negative, NaN, wrong
|
|
98
|
+
* type) is rejected AT THE BOUNDARY with a warn — never silently. 0 is NOT
|
|
99
|
+
* honoured as "off" (use a real config switch if that's ever needed); it is
|
|
100
|
+
* invalid and falls back to 10 with a warning, so an operator can't
|
|
101
|
+
* accidentally silence lesson injection by typing 0.
|
|
102
|
+
*/
|
|
103
|
+
function lessonsLimit(config) {
|
|
104
|
+
if (!config)
|
|
105
|
+
return 10;
|
|
106
|
+
return Math.floor((0, config_read_js_1.readPositiveConfig)({ lessonsLimit: config.lessonsLimit }, "lessonsLimit", 10));
|
|
96
107
|
}
|
|
97
108
|
/** Always true — remote ingest is always allowed. */
|
|
98
109
|
function remoteIngestAllowed() {
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,9 @@ const THIS_HARNESS = "oc";
|
|
|
50
50
|
let serverUrl = DEFAULT_SERVER_URL;
|
|
51
51
|
let authToken;
|
|
52
52
|
let hicortexHome = HICORTEX_HOME;
|
|
53
|
+
/** Resolved plugin config captured at service start — used for tunable knobs
|
|
54
|
+
* (e.g. lessonsLimit) that injected context blocks read at hook time. */
|
|
55
|
+
let pluginConfig = null;
|
|
53
56
|
/** Old-server guard (F2): 0 = not latched; otherwise the Date.now() epoch-ms
|
|
54
57
|
* until which /recall-index is skipped after a 404 (pre-0.14 server). The
|
|
55
58
|
* latch EXPIRES so a client-first rollout heals itself once the server is
|
|
@@ -152,7 +155,7 @@ async function buildLessonsBlock(project) {
|
|
|
152
155
|
const { data } = await serverGet("/lessons", LESSONS_TIMEOUT_MS);
|
|
153
156
|
if (!data || !data.lessons || data.lessons.length === 0)
|
|
154
157
|
return null;
|
|
155
|
-
const maxLessons = (0, features_js_1.lessonsLimit)();
|
|
158
|
+
const maxLessons = (0, features_js_1.lessonsLimit)(pluginConfig);
|
|
156
159
|
const state = (0, state_js_1.loadState)(hicortexHome);
|
|
157
160
|
const moduleIndex = data.moduleIndex ?? state.moduleIndex;
|
|
158
161
|
const selected = await (0, extensions_js_1.getLessonSelector)().select(data.lessons, {
|
|
@@ -264,6 +267,7 @@ exports.default = {
|
|
|
264
267
|
id: "hicortex-service",
|
|
265
268
|
async start(ctx) {
|
|
266
269
|
const config = (ctx.config ?? {});
|
|
270
|
+
pluginConfig = config;
|
|
267
271
|
const log = ctx.logger
|
|
268
272
|
? (msg) => ctx.logger.info(msg)
|
|
269
273
|
: console.log;
|
|
@@ -39,6 +39,8 @@ export interface ResolvedConfig {
|
|
|
39
39
|
home: string;
|
|
40
40
|
/** Per-agent context id sent as ?agent= (0.13); null → global (no param). */
|
|
41
41
|
agentName: string | null;
|
|
42
|
+
/** Max lessons to inject (config.lessonsLimit, default 10). */
|
|
43
|
+
lessonsLimit?: number;
|
|
42
44
|
}
|
|
43
45
|
/**
|
|
44
46
|
* Read ~/.hicortex/config.json and resolve the server URL + auth token, or
|
package/dist/lessons-context.js
CHANGED
|
@@ -64,7 +64,13 @@ function resolveConfig() {
|
|
|
64
64
|
// context. A configured agentName that sanitizes to null → agentId null too
|
|
65
65
|
// (NO ?agent=), never a 400 that the fail-soft hook would silently swallow.
|
|
66
66
|
const agentName = (0, context_store_js_1.resolveAgentIdentity)(config).agentId;
|
|
67
|
-
return {
|
|
67
|
+
return {
|
|
68
|
+
serverUrl,
|
|
69
|
+
authToken: config.authToken,
|
|
70
|
+
home,
|
|
71
|
+
agentName,
|
|
72
|
+
lessonsLimit: typeof config.lessonsLimit === "number" ? config.lessonsLimit : undefined,
|
|
73
|
+
};
|
|
68
74
|
}
|
|
69
75
|
function authHeaders(authToken) {
|
|
70
76
|
return authToken ? { "Authorization": `Bearer ${authToken}` } : {};
|
|
@@ -81,7 +87,7 @@ async function fetchLessonsBlock(cfg) {
|
|
|
81
87
|
if (!resp.ok)
|
|
82
88
|
return null;
|
|
83
89
|
const data = await resp.json();
|
|
84
|
-
const maxLessons = (0, features_js_1.lessonsLimit)();
|
|
90
|
+
const maxLessons = (0, features_js_1.lessonsLimit)(cfg);
|
|
85
91
|
const moduleIndex = data.moduleIndex ?? (0, state_js_1.loadState)(cfg.home).moduleIndex;
|
|
86
92
|
// The SessionStart hook runs in the session's working directory, whose last
|
|
87
93
|
// path component matches the capture-side CC project name convention
|
package/dist/mcp-server.js
CHANGED
|
@@ -57,6 +57,7 @@ const zod_1 = require("zod");
|
|
|
57
57
|
const db_js_1 = require("./db.js");
|
|
58
58
|
const llm_js_1 = require("./llm.js");
|
|
59
59
|
const features_js_1 = require("./features.js");
|
|
60
|
+
const config_read_js_1 = require("./config-read.js");
|
|
60
61
|
const state_js_1 = require("./state.js");
|
|
61
62
|
const embedder_js_1 = require("./embedder.js");
|
|
62
63
|
const storage = __importStar(require("./storage.js"));
|
|
@@ -373,6 +374,9 @@ async function startServer(options = {}) {
|
|
|
373
374
|
// If nothing is configured: start recall-only with an unmissable warning.
|
|
374
375
|
// One model serves all phases (#231) — no per-tier overlay here.
|
|
375
376
|
const savedConfig = readConfigFile(stateDir);
|
|
377
|
+
// 0.16.8 upgrade guard: per-stage keys are silently ignored now. Warn loudly
|
|
378
|
+
// so a carried-over distill/reflect model doesn't silently downgrade quality.
|
|
379
|
+
(0, config_read_js_1.warnIgnoredConfigKeys)(savedConfig);
|
|
376
380
|
// 0.16.2 activation gap: self-heal the agentId provenance field for
|
|
377
381
|
// pre-0.16.2 server installs on first boot after upgrade. The server's own
|
|
378
382
|
// nightly captures its sessions to localhost:8787/distill and needs this id;
|
package/dist/nightly.js
CHANGED
|
@@ -219,6 +219,8 @@ async function runNightly(options = {}) {
|
|
|
219
219
|
(0, state_js_1.migrateLegacyState)(stateDir);
|
|
220
220
|
// Check mode: client or server
|
|
221
221
|
const savedConfig = readNightlyConfig(stateDir);
|
|
222
|
+
// 0.16.8 upgrade guard: warn if ignored per-stage keys are still present.
|
|
223
|
+
(0, config_read_js_1.warnIgnoredConfigKeys)(savedConfig);
|
|
222
224
|
// 0.16.2 activation gap: pre-0.16.2 installs never re-run init, so their
|
|
223
225
|
// config has no agentId → capture sent source_agent_id: null forever (the
|
|
224
226
|
// provenance feature was inert for the whole existing fleet). Self-heal on
|
|
@@ -366,6 +368,7 @@ async function runNightly(options = {}) {
|
|
|
366
368
|
// Runs even if capture had transient failures (opens DB directly, independent
|
|
367
369
|
// of the HTTP capture path). Full nightly only — capture-only runs are
|
|
368
370
|
// intended to run more frequently than once daily.
|
|
371
|
+
let lessonsGenerated; // hoisted for telemetry; undefined when reflection didn't run (skipped) — bucketed apart from a real 0
|
|
369
372
|
if (!dryRun && !captureOnly) {
|
|
370
373
|
if (!llm || !llmConfig) {
|
|
371
374
|
console.error("[hicortex] consolidation skipped: no LLM configured — run npx @gamaze/hicortex init");
|
|
@@ -390,6 +393,14 @@ async function runNightly(options = {}) {
|
|
|
390
393
|
});
|
|
391
394
|
console.log(`[hicortex] Consolidation ${report.status} in ${report.elapsed_seconds}s` +
|
|
392
395
|
(report.stages.reflection ? ` (${report.stages.reflection.lessons_generated} lessons)` : ""));
|
|
396
|
+
// Only set when reflection actually RAN (not skipped). A skipped stage
|
|
397
|
+
// (e.g. endpoint offline, #232 fail-soft) must NOT collapse to 0 — that
|
|
398
|
+
// would make "endpoint down" indistinguishable from "prompt too tight"
|
|
399
|
+
// in the fleet aggregate. Leave undefined so the optional field is
|
|
400
|
+
// omitted and the aggregate buckets skipped runs separately.
|
|
401
|
+
const refl = report.stages.reflection;
|
|
402
|
+
if (refl && !refl.skipped)
|
|
403
|
+
lessonsGenerated = refl.lessons_generated;
|
|
393
404
|
}
|
|
394
405
|
}
|
|
395
406
|
// Step 4: Update last-run timestamp.
|
|
@@ -434,6 +445,7 @@ async function runNightly(options = {}) {
|
|
|
434
445
|
agent: agentType,
|
|
435
446
|
mem: storage.countMemories(db),
|
|
436
447
|
lessons: storage.getLessons(db, 365).length,
|
|
448
|
+
lessonsGenerated,
|
|
437
449
|
sessions: batches.length,
|
|
438
450
|
ok: !hadTransientFailure,
|
|
439
451
|
shown: adoption.shown,
|
package/dist/prompts.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LLM prompt templates for memory consolidation and distillation.
|
|
3
3
|
* Copied EXACTLY from the Python codebase (proven working prompts).
|
|
4
|
+
*
|
|
5
|
+
* SHIPPED SOURCE: this file is compiled into dist/ and published to npm + the
|
|
6
|
+
* public mirror (it is NOT in .publicignore). Any example, name, or detail in
|
|
7
|
+
* these prompts is therefore public. Use generic, anonymous examples — never
|
|
8
|
+
* real internal project/tool/host names or real incident specifics. The
|
|
9
|
+
* infra-name denylist cannot catch conceptual leaks, only known names.
|
|
4
10
|
*/
|
|
5
11
|
/**
|
|
6
12
|
* Importance scoring prompt. Takes a {memories_block} with indexed memories.
|
package/dist/prompts.js
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* LLM prompt templates for memory consolidation and distillation.
|
|
4
4
|
* Copied EXACTLY from the Python codebase (proven working prompts).
|
|
5
|
+
*
|
|
6
|
+
* SHIPPED SOURCE: this file is compiled into dist/ and published to npm + the
|
|
7
|
+
* public mirror (it is NOT in .publicignore). Any example, name, or detail in
|
|
8
|
+
* these prompts is therefore public. Use generic, anonymous examples — never
|
|
9
|
+
* real internal project/tool/host names or real incident specifics. The
|
|
10
|
+
* infra-name denylist cannot catch conceptual leaks, only known names.
|
|
5
11
|
*/
|
|
6
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
13
|
exports.importanceScoring = importanceScoring;
|
|
@@ -41,6 +47,9 @@ function reflection(memoriesBlock, recentLessons) {
|
|
|
41
47
|
|
|
42
48
|
Like human learning: we grow fastest when we reinforce what works AND correct what doesn't. A system that only learns from mistakes becomes overly cautious. A system that only learns from successes never improves. The combination multiplies.
|
|
43
49
|
|
|
50
|
+
GENERALITY BAR (read carefully — the most important rule):
|
|
51
|
+
Every lesson MUST be a generalizable operating principle that transfers across contexts, agents, and projects. It is NOT: an incident report, a changelog entry, a one-event fact, a tool-specific recipe, or a note about a named entity. If a memory is only interesting as "what happened today", it is an EPISODE — do not emit a lesson for it. Abstract away specific tool names, hostnames, and incident details from the lesson text; state the transferable rule.
|
|
52
|
+
|
|
44
53
|
Quality over quantity. 1-3 lessons is typical. An empty array [] is the CORRECT response when memories show routine competent work without noteworthy patterns, surprises, or friction. Do not manufacture lessons from nothing.
|
|
45
54
|
|
|
46
55
|
LESSON TYPES:
|
|
@@ -56,15 +65,19 @@ Good reinforce: "Bundling related changes into a single PR with clear narrative
|
|
|
56
65
|
Good reinforce: "When presenting multi-scenario analysis, show assumptions side-by-side so stakeholders evaluate trade-offs rather than reacting to isolated worst-cases"
|
|
57
66
|
Good correct: "Always verify ALL substitution targets by diffing output — partial fixes cause silent failures"
|
|
58
67
|
Good principle: "Gather evidence from logs before forming hypotheses — evidence-first debugging resolved issues 3x faster today"
|
|
68
|
+
Good principle: "When a long-running background agent survives a system migration as an orphan, explicitly unregister it before declaring the migration clean — orphans cause silent crash-loops"
|
|
59
69
|
Bad lesson: "The deploy script had a bug" (restatement, not actionable)
|
|
70
|
+
Bad (incident note / changelog entry): "The graph resolver was fixed by adding a computed-at marker column" — that is a changelog entry, not a lesson
|
|
71
|
+
Bad (tool-specific decision): "Tool X is banned because it broke Tool Y's settings" — a one-tool decision, not transferable
|
|
72
|
+
Bad (single-event): "The chat client crashed after a system restore because a background agent was left orphaned" — an incident report, not a principle
|
|
60
73
|
|
|
61
74
|
For each lesson, output a JSON object:
|
|
62
|
-
- "lesson": Concise, actionable rule in imperative voice
|
|
75
|
+
- "lesson": Concise, actionable rule in imperative voice. State the transferable rule — abstract away specific tool names, hostnames, and incident details.
|
|
63
76
|
- "type": "reinforce" | "correct" | "principle"
|
|
64
77
|
- "project": "global" unless genuinely project-specific (project-specific lessons are still valuable)
|
|
65
78
|
- "severity": "critical" | "important" | "minor"
|
|
66
79
|
- "confidence": "high" | "medium" | "low"
|
|
67
|
-
- "source_pattern": What triggered this (1 sentence, no personal data)
|
|
80
|
+
- "source_pattern": What triggered this (1 sentence, no personal data; may name the specific incident/tool here — but the \`lesson\` field itself stays generic)
|
|
68
81
|
|
|
69
82
|
Severity guide:
|
|
70
83
|
- "critical": Near-misses that could have caused data loss or security breach, even if caught in time. Also: recurring patterns that keep appearing despite prior corrections.
|
|
@@ -87,7 +100,7 @@ Focus on:
|
|
|
87
100
|
|
|
88
101
|
Privacy: Never include personal data (names, health, finances, credentials) in lesson text. Abstract to the process level.
|
|
89
102
|
|
|
90
|
-
Skip: isolated trivial actions, already-documented rules. However, if multiple small successes form a consistent pattern of quality, extract that pattern as a reinforcement.
|
|
103
|
+
Skip: isolated trivial actions, already-documented rules, incident notes, changelog entries, and tool-specific recipes. However, if multiple small successes form a consistent pattern of quality, extract that pattern as a reinforcement.
|
|
91
104
|
|
|
92
105
|
Respond with a JSON array. Empty array [] is a valid response.`;
|
|
93
106
|
}
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
* client is never miscounted as "cc".
|
|
14
14
|
* mem — total memory count
|
|
15
15
|
* lessons — total lesson count
|
|
16
|
+
* lessonsGenerated — lessons created THIS run (server mode only; the per-run
|
|
17
|
+
* delta, distinct from `lessons` which is the corpus total). Lets a
|
|
18
|
+
* silent decline in reflection output be seen in the fleet aggregate.
|
|
16
19
|
* sessions — sessions distilled this run
|
|
17
20
|
* ok — nightly succeeded (true/false)
|
|
18
21
|
* shown — sum of shown_count (server mode only)
|
|
@@ -76,6 +79,12 @@ export interface TelemetryPayload {
|
|
|
76
79
|
shown?: number;
|
|
77
80
|
uses?: number;
|
|
78
81
|
cold?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Lessons generated THIS run (server mode only). The per-run delta —
|
|
84
|
+
* `lessons` above is the corpus total, which drifts slowly via decay/prune
|
|
85
|
+
* and can't reveal a sudden drop in reflection output. 0.16.9+.
|
|
86
|
+
*/
|
|
87
|
+
lessonsGenerated?: number;
|
|
79
88
|
}
|
|
80
89
|
/**
|
|
81
90
|
* Check if telemetry is enabled. Disabled by:
|
package/dist/telemetry.js
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* client is never miscounted as "cc".
|
|
15
15
|
* mem — total memory count
|
|
16
16
|
* lessons — total lesson count
|
|
17
|
+
* lessonsGenerated — lessons created THIS run (server mode only; the per-run
|
|
18
|
+
* delta, distinct from `lessons` which is the corpus total). Lets a
|
|
19
|
+
* silent decline in reflection output be seen in the fleet aggregate.
|
|
17
20
|
* sessions — sessions distilled this run
|
|
18
21
|
* ok — nightly succeeded (true/false)
|
|
19
22
|
* shown — sum of shown_count (server mode only)
|
package/dist/types.d.ts
CHANGED
|
@@ -300,6 +300,13 @@ export interface HicortexConfig {
|
|
|
300
300
|
* doubled for margin). Only relevant when `ollamaFlushEvery` > 0.
|
|
301
301
|
*/
|
|
302
302
|
ollamaFlushWaitMs?: number;
|
|
303
|
+
/**
|
|
304
|
+
* Max lessons injected into an agent's session-start context (default 10).
|
|
305
|
+
* Lessons are ranked per-session by project/domain affinity + recency +
|
|
306
|
+
* strength + access, so each session sees its most-relevant slice. Lower =
|
|
307
|
+
* leaner system prompts.
|
|
308
|
+
*/
|
|
309
|
+
lessonsLimit?: number;
|
|
303
310
|
}
|
|
304
311
|
/** A config-owned life-sphere domain (see HicortexConfig.domains). */
|
|
305
312
|
export interface DomainDef {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.10",
|
|
4
4
|
"description": "Self-learning memory for AI agents — 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": {
|