@gamaze/hicortex 0.16.2 → 0.16.4
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 +3 -0
- package/dist/cli.js +1 -1
- package/dist/config-read.d.ts +25 -0
- package/dist/config-read.js +45 -0
- package/dist/consolidate.js +1 -1
- package/dist/llm.d.ts +19 -0
- package/dist/llm.js +84 -25
- package/dist/mcp-server.js +4 -0
- package/dist/nightly.js +69 -9
- package/dist/pi-transcript-reader.d.ts +1 -1
- package/dist/pi-transcript-reader.js +1 -1
- package/dist/recall-index.d.ts +2 -2
- package/dist/recall-index.js +2 -2
- package/dist/redact.d.ts +2 -2
- package/dist/redact.js +2 -2
- package/dist/telemetry.d.ts +13 -2
- package/dist/telemetry.js +5 -1
- package/dist/types.d.ts +44 -1
- package/hermes-plugin/hicortex/config.py +1 -1
- package/hermes-plugin/hicortex/provider.py +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -212,6 +212,9 @@ Config at `~/.hicortex/config.json`. Created by `init`. Key options:
|
|
|
212
212
|
| `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) |
|
|
213
213
|
| `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 |
|
|
214
214
|
| `nightlyHour` | Local hour (0–23) for the nightly job installed by `init` (defaults: client 2, server 3). Applied on fresh installs; existing schedules are never overwritten |
|
|
215
|
+
| `preflightTimeoutMs` | **Client mode only.** Per-attempt timeout for the nightly's server-reachability check before it starts capturing (default: 15000 ms) |
|
|
216
|
+
| `preflightAttempts` | **Client mode only.** Reachability-check retries before the nightly aborts (default: 3; floored at 1). `1` = single try, no retry |
|
|
217
|
+
| `preflightRetryGapMs` | **Client mode only.** Delay between reachability retries (default: 60000 ms). Note: timers don't advance while the machine is asleep, so on a sleeping laptop this gap counts awake-time, not wall-clock |
|
|
215
218
|
| `scoreSimilarityWeight` | Weight of semantic similarity in the ranking score (default: 0.50) |
|
|
216
219
|
| `scoreStrengthWeight` | Weight of effective strength — importance/use/recency of access (default: 0.20) |
|
|
217
220
|
| `scoreConnectionsWeight` | Weight of graph centrality (default: 0.15) |
|
package/dist/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ switch (command) {
|
|
|
40
40
|
agentName = (0, cli_args_js_1.readValueFlag)(process.argv, "--agent-name");
|
|
41
41
|
}
|
|
42
42
|
catch {
|
|
43
|
-
console.error("[hicortex] init: --agent-name requires a value, e.g. --agent-name
|
|
43
|
+
console.error("[hicortex] init: --agent-name requires a value, e.g. --agent-name my-agent");
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
|
46
46
|
const repairConfig = process.argv.includes("--repair-config");
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict config readers for primitive values. Pure functions — no LlmConfig
|
|
3
|
+
* dependency — shared by the nightly preflight knobs and the distill-tier
|
|
4
|
+
* overlay (llm.ts / mcp-server.ts).
|
|
5
|
+
*
|
|
6
|
+
* The point is to reject wrong-typed config values AT THE BOUNDARY (disk →
|
|
7
|
+
* runtime) with a warn, rather than casting them straight through. The trap
|
|
8
|
+
* these guard: a JSON slip like `"enableThinking": "false"` (string,
|
|
9
|
+
* not boolean) casts to a truthy value downstream — for the Qwen chat template
|
|
10
|
+
* a non-empty string flips thinking ON, which is precisely the failure the key
|
|
11
|
+
* exists to prevent, with no error anywhere. Same class as the #225
|
|
12
|
+
* preflight-knob validation.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Read a positive finite number from a config object. Returns `def` when the
|
|
16
|
+
* key is absent OR present-but-invalid (with a warn in the latter case).
|
|
17
|
+
*/
|
|
18
|
+
export declare function readPositiveConfig(config: Record<string, unknown>, key: string, def: number): number;
|
|
19
|
+
/**
|
|
20
|
+
* Read a strict boolean from a config object. Returns the boolean when valid,
|
|
21
|
+
* `undefined` when the key is absent OR present-but-not-a-boolean (with a warn
|
|
22
|
+
* in the latter case). Never coerces — `"false"` (string) is rejected, not
|
|
23
|
+
* treated as truthy.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readStrictBoolean(config: Record<string, unknown>, key: string): boolean | undefined;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Strict config readers for primitive values. Pure functions — no LlmConfig
|
|
4
|
+
* dependency — shared by the nightly preflight knobs and the distill-tier
|
|
5
|
+
* overlay (llm.ts / mcp-server.ts).
|
|
6
|
+
*
|
|
7
|
+
* The point is to reject wrong-typed config values AT THE BOUNDARY (disk →
|
|
8
|
+
* runtime) with a warn, rather than casting them straight through. The trap
|
|
9
|
+
* these guard: a JSON slip like `"enableThinking": "false"` (string,
|
|
10
|
+
* not boolean) casts to a truthy value downstream — for the Qwen chat template
|
|
11
|
+
* a non-empty string flips thinking ON, which is precisely the failure the key
|
|
12
|
+
* exists to prevent, with no error anywhere. Same class as the #225
|
|
13
|
+
* preflight-knob validation.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.readPositiveConfig = readPositiveConfig;
|
|
17
|
+
exports.readStrictBoolean = readStrictBoolean;
|
|
18
|
+
/**
|
|
19
|
+
* Read a positive finite number from a config object. Returns `def` when the
|
|
20
|
+
* key is absent OR present-but-invalid (with a warn in the latter case).
|
|
21
|
+
*/
|
|
22
|
+
function readPositiveConfig(config, key, def) {
|
|
23
|
+
const v = config[key];
|
|
24
|
+
if (v === undefined)
|
|
25
|
+
return def;
|
|
26
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 0)
|
|
27
|
+
return v;
|
|
28
|
+
console.warn(`[hicortex] config "${key}" = ${String(v)} is not a positive finite number — using default ${def}.`);
|
|
29
|
+
return def;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Read a strict boolean from a config object. Returns the boolean when valid,
|
|
33
|
+
* `undefined` when the key is absent OR present-but-not-a-boolean (with a warn
|
|
34
|
+
* in the latter case). Never coerces — `"false"` (string) is rejected, not
|
|
35
|
+
* treated as truthy.
|
|
36
|
+
*/
|
|
37
|
+
function readStrictBoolean(config, key) {
|
|
38
|
+
const v = config[key];
|
|
39
|
+
if (v === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (typeof v === "boolean")
|
|
42
|
+
return v;
|
|
43
|
+
console.warn(`[hicortex] config "${key}" = ${String(v)} is not a boolean — ignored.`);
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
package/dist/consolidate.js
CHANGED
|
@@ -832,7 +832,7 @@ function stageHubBoost(db, dryRun) {
|
|
|
832
832
|
//
|
|
833
833
|
// A later decision/correction can reverse, replace, or invalidate an earlier
|
|
834
834
|
// one — e.g. "chose Ollama for distillation" superseded a month later by
|
|
835
|
-
// "switched distillation to
|
|
835
|
+
// "switched distillation to a local 35B model over a mesh VPN". Left
|
|
836
836
|
// unlinked, retrieval and lesson selection can surface the stale one. This
|
|
837
837
|
// stage links OLD → NEW with relationship `superseded_by` and accelerates the
|
|
838
838
|
// old memory's decay, WITHOUT deleting it (unlike `hicortex dedup`'s merge —
|
package/dist/llm.d.ts
CHANGED
|
@@ -25,6 +25,10 @@ export interface LlmConfig {
|
|
|
25
25
|
distillBaseUrl?: string;
|
|
26
26
|
distillApiKey?: string;
|
|
27
27
|
distillProvider?: string;
|
|
28
|
+
/** Max output tokens for all phases (distill/reflect/classify/scoring). Heavy phases fall back to 8192, scoring to 2048. */
|
|
29
|
+
maxTokens?: number;
|
|
30
|
+
/** Toggle thinking on the openai-compat path for the heavy phases. Absent = no kwarg sent. */
|
|
31
|
+
enableThinking?: boolean;
|
|
28
32
|
/** Optional separate endpoint for reflect-tier LLM (e.g. remote Ollama with larger model). */
|
|
29
33
|
reflectBaseUrl?: string;
|
|
30
34
|
reflectApiKey?: string;
|
|
@@ -87,6 +91,21 @@ export type { ModelTierOverride } from "./types.js";
|
|
|
87
91
|
* a dead score apiKey/provider under an ollama base.
|
|
88
92
|
*/
|
|
89
93
|
export declare function applyModelsBlock(saved: Record<string, unknown> | null): Record<string, unknown> | null;
|
|
94
|
+
/**
|
|
95
|
+
* Validate + copy the heavy-phase tuning keys (#220: maxTokens + enableThinking)
|
|
96
|
+
* from the saved disk config onto a runtime LlmConfig. Called by BOTH LlmConfig
|
|
97
|
+
* construction sites — the daemon in mcp-server.ts (runs distill) AND
|
|
98
|
+
* resolveSavedLlmConfig below (the nightly runs reflect + classify) — so every
|
|
99
|
+
* process that runs a heavy phase honors the keys, and a future site calling
|
|
100
|
+
* this inherits them by construction.
|
|
101
|
+
*
|
|
102
|
+
* Both keys are optional; absent = call-site defaults (8192 / thinking kwarg
|
|
103
|
+
* omitted). Wrong-typed values warn and are dropped (readPositiveConfig /
|
|
104
|
+
* readStrictBoolean) — notably a JSON slip `"enableThinking": "false"` (string)
|
|
105
|
+
* is rejected rather than coerced to truthy thinking-on, which would silently
|
|
106
|
+
* invert the fix this key exists to apply.
|
|
107
|
+
*/
|
|
108
|
+
export declare function applyTierTuningOverlay(llmConfig: LlmConfig, savedConfig: Record<string, unknown> | null | undefined): void;
|
|
90
109
|
/**
|
|
91
110
|
* Resolve an LlmConfig from a saved ~/.hicortex/config.json object.
|
|
92
111
|
*
|
package/dist/llm.js
CHANGED
|
@@ -18,6 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
exports.LlmClient = exports.RateLimitError = exports.resolveLlmConfigForCC = void 0;
|
|
19
19
|
exports.resolveExplicitLlmConfig = resolveExplicitLlmConfig;
|
|
20
20
|
exports.applyModelsBlock = applyModelsBlock;
|
|
21
|
+
exports.applyTierTuningOverlay = applyTierTuningOverlay;
|
|
21
22
|
exports.resolveSavedLlmConfig = resolveSavedLlmConfig;
|
|
22
23
|
exports.resolveClassifyProbeTarget = resolveClassifyProbeTarget;
|
|
23
24
|
exports.findClaudeBinary = findClaudeBinary;
|
|
@@ -25,6 +26,7 @@ exports.claudeCliConfig = claudeCliConfig;
|
|
|
25
26
|
exports.probeOllama = probeOllama;
|
|
26
27
|
exports.probeOllamaModel = probeOllamaModel;
|
|
27
28
|
exports.resolveDistillFallback = resolveDistillFallback;
|
|
29
|
+
const config_read_js_1 = require("./config-read.js");
|
|
28
30
|
/**
|
|
29
31
|
* Resolve LLM configuration from explicit config-file overrides or
|
|
30
32
|
* Hicortex-specific env vars only. Returns null when nothing explicit is set.
|
|
@@ -182,6 +184,31 @@ function applyModelsBlock(saved) {
|
|
|
182
184
|
}
|
|
183
185
|
return { ...saved, ...mapped };
|
|
184
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Validate + copy the heavy-phase tuning keys (#220: maxTokens + enableThinking)
|
|
189
|
+
* from the saved disk config onto a runtime LlmConfig. Called by BOTH LlmConfig
|
|
190
|
+
* construction sites — the daemon in mcp-server.ts (runs distill) AND
|
|
191
|
+
* resolveSavedLlmConfig below (the nightly runs reflect + classify) — so every
|
|
192
|
+
* process that runs a heavy phase honors the keys, and a future site calling
|
|
193
|
+
* this inherits them by construction.
|
|
194
|
+
*
|
|
195
|
+
* Both keys are optional; absent = call-site defaults (8192 / thinking kwarg
|
|
196
|
+
* omitted). Wrong-typed values warn and are dropped (readPositiveConfig /
|
|
197
|
+
* readStrictBoolean) — notably a JSON slip `"enableThinking": "false"` (string)
|
|
198
|
+
* is rejected rather than coerced to truthy thinking-on, which would silently
|
|
199
|
+
* invert the fix this key exists to apply.
|
|
200
|
+
*/
|
|
201
|
+
function applyTierTuningOverlay(llmConfig, savedConfig) {
|
|
202
|
+
if (!savedConfig)
|
|
203
|
+
return;
|
|
204
|
+
if (savedConfig.maxTokens !== undefined) {
|
|
205
|
+
llmConfig.maxTokens = (0, config_read_js_1.readPositiveConfig)(savedConfig, "maxTokens", 8192);
|
|
206
|
+
}
|
|
207
|
+
const thinking = (0, config_read_js_1.readStrictBoolean)(savedConfig, "enableThinking");
|
|
208
|
+
if (thinking !== undefined) {
|
|
209
|
+
llmConfig.enableThinking = thinking;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
185
212
|
/**
|
|
186
213
|
* Resolve an LlmConfig from a saved ~/.hicortex/config.json object.
|
|
187
214
|
*
|
|
@@ -232,6 +259,12 @@ function resolveSavedLlmConfig(savedConfig, findBinary = findClaudeBinary) {
|
|
|
232
259
|
llmConfig.reflectApiKey = savedConfig.reflectApiKey ?? llmConfig.apiKey;
|
|
233
260
|
llmConfig.reflectProvider = savedConfig.reflectProvider ?? llmConfig.provider;
|
|
234
261
|
}
|
|
262
|
+
// Heavy-phase tuning (#220: maxTokens + enableThinking). The nightly runs
|
|
263
|
+
// reflect + classify (consolidation), the daemon runs distill — both need the
|
|
264
|
+
// keys, so the overlay is applied at both construction sites.
|
|
265
|
+
if (llmConfig) {
|
|
266
|
+
applyTierTuningOverlay(llmConfig, savedConfig);
|
|
267
|
+
}
|
|
235
268
|
// Optional classify tier (memory tag classification). Same overlay pattern
|
|
236
269
|
// as distillModel/distillBaseUrl: when absent, completeClassify falls back
|
|
237
270
|
// to the reflect tier — zero behavior change for existing installs.
|
|
@@ -504,28 +537,39 @@ class LlmClient {
|
|
|
504
537
|
/**
|
|
505
538
|
* Fast-tier completion (importance scoring, simple tasks).
|
|
506
539
|
*/
|
|
507
|
-
async completeFast(prompt, maxTokens
|
|
508
|
-
|
|
540
|
+
async completeFast(prompt, maxTokens) {
|
|
541
|
+
const tokens = maxTokens ?? this.config.maxTokens ?? 2048;
|
|
542
|
+
// No `thinking` arg: scoring is excluded from the thinking toggle (it's the
|
|
543
|
+
// local fast tier; on ollama it's think:false regardless). completeFast never
|
|
544
|
+
// threads thinking, so chat_template_kwargs is never sent for scoring.
|
|
545
|
+
return this.complete(this.config.model, prompt, tokens, 600_000);
|
|
509
546
|
}
|
|
510
547
|
/**
|
|
511
548
|
* Reflect-tier completion (nightly reflection, needs reasoning).
|
|
512
549
|
* Routes to reflectBaseUrl/reflectProvider if configured (e.g. remote Ollama with larger model).
|
|
513
550
|
*/
|
|
514
|
-
async completeReflect(prompt, maxTokens
|
|
551
|
+
async completeReflect(prompt, maxTokens) {
|
|
552
|
+
const tokens = maxTokens ?? this.config.maxTokens ?? 8192;
|
|
553
|
+
const thinking = this.config.enableThinking;
|
|
515
554
|
if (this.config.reflectBaseUrl) {
|
|
516
|
-
return this.completeWithOverride(this.config.reflectBaseUrl, this.config.reflectApiKey ?? this.config.apiKey, this.config.reflectProvider ?? this.config.provider, this.config.reflectModel, prompt,
|
|
555
|
+
return this.completeWithOverride(this.config.reflectBaseUrl, this.config.reflectApiKey ?? this.config.apiKey, this.config.reflectProvider ?? this.config.provider, this.config.reflectModel, prompt, tokens, 900_000, thinking);
|
|
517
556
|
}
|
|
518
|
-
return this.complete(this.config.reflectModel, prompt,
|
|
557
|
+
return this.complete(this.config.reflectModel, prompt, tokens, 900_000, thinking);
|
|
519
558
|
}
|
|
520
559
|
/**
|
|
521
560
|
* Distillation-tier completion (session knowledge extraction).
|
|
522
561
|
* Routes to distillBaseUrl/distillProvider if configured (e.g. remote Ollama with faster model).
|
|
523
562
|
*/
|
|
524
|
-
async completeDistill(prompt, maxTokens
|
|
563
|
+
async completeDistill(prompt, maxTokens) {
|
|
564
|
+
const tokens = maxTokens ?? this.config.maxTokens ?? 8192;
|
|
565
|
+
// enableThinking is threaded explicitly (not read inside the shared
|
|
566
|
+
// completeOpenAiCompat) so the chat_template_kwargs kwarg is scoped to the
|
|
567
|
+
// heavy phases — distill/reflect/classify — and never reaches scoring. See #220.
|
|
568
|
+
const thinking = this.config.enableThinking;
|
|
525
569
|
if (this.config.distillBaseUrl) {
|
|
526
|
-
return this.completeWithOverride(this.config.distillBaseUrl, this.config.distillApiKey ?? this.config.apiKey, this.config.distillProvider ?? this.config.provider, this.config.distillModel ?? this.config.model, prompt,
|
|
570
|
+
return this.completeWithOverride(this.config.distillBaseUrl, this.config.distillApiKey ?? this.config.apiKey, this.config.distillProvider ?? this.config.provider, this.config.distillModel ?? this.config.model, prompt, tokens, 900_000, thinking);
|
|
527
571
|
}
|
|
528
|
-
return this.complete(this.config.distillModel ?? this.config.model, prompt,
|
|
572
|
+
return this.complete(this.config.distillModel ?? this.config.model, prompt, tokens, 900_000, thinking);
|
|
529
573
|
}
|
|
530
574
|
/**
|
|
531
575
|
* Classification-tier completion (memory tag classification).
|
|
@@ -538,33 +582,35 @@ class LlmClient {
|
|
|
538
582
|
* - Only classifyModel set → the classify model on the reflect endpoint
|
|
539
583
|
* when one is configured, else on the base endpoint.
|
|
540
584
|
*/
|
|
541
|
-
async completeClassify(prompt, maxTokens
|
|
585
|
+
async completeClassify(prompt, maxTokens) {
|
|
586
|
+
const tokens = maxTokens ?? this.config.maxTokens ?? 8192;
|
|
587
|
+
const thinking = this.config.enableThinking;
|
|
542
588
|
if (!this.config.classifyModel && !this.config.classifyBaseUrl) {
|
|
543
|
-
return this.completeReflect(prompt,
|
|
589
|
+
return this.completeReflect(prompt, tokens);
|
|
544
590
|
}
|
|
545
591
|
const model = this.config.classifyModel ?? this.config.reflectModel;
|
|
546
592
|
if (this.config.classifyBaseUrl) {
|
|
547
|
-
return this.completeWithOverride(this.config.classifyBaseUrl, this.config.classifyApiKey ?? this.config.apiKey, this.config.classifyProvider ?? this.config.provider, model, prompt,
|
|
593
|
+
return this.completeWithOverride(this.config.classifyBaseUrl, this.config.classifyApiKey ?? this.config.apiKey, this.config.classifyProvider ?? this.config.provider, model, prompt, tokens, 900_000, thinking);
|
|
548
594
|
}
|
|
549
595
|
if (this.config.reflectBaseUrl) {
|
|
550
|
-
return this.completeWithOverride(this.config.reflectBaseUrl, this.config.reflectApiKey ?? this.config.apiKey, this.config.reflectProvider ?? this.config.provider, model, prompt,
|
|
596
|
+
return this.completeWithOverride(this.config.reflectBaseUrl, this.config.reflectApiKey ?? this.config.apiKey, this.config.reflectProvider ?? this.config.provider, model, prompt, tokens, 900_000, thinking);
|
|
551
597
|
}
|
|
552
|
-
return this.complete(model, prompt,
|
|
598
|
+
return this.complete(model, prompt, tokens, 900_000, thinking);
|
|
553
599
|
}
|
|
554
600
|
/**
|
|
555
601
|
* Complete with overridden baseUrl/apiKey/provider (used for reflect tier with separate endpoint).
|
|
556
602
|
* Creates a temporary LlmClient to avoid mutating shared config under concurrent calls.
|
|
557
603
|
*/
|
|
558
|
-
async completeWithOverride(baseUrl, apiKey, provider, model, prompt, maxTokens, timeoutMs) {
|
|
604
|
+
async completeWithOverride(baseUrl, apiKey, provider, model, prompt, maxTokens, timeoutMs, thinking) {
|
|
559
605
|
const tempClient = new LlmClient({
|
|
560
606
|
...this.config,
|
|
561
607
|
baseUrl,
|
|
562
608
|
apiKey,
|
|
563
609
|
provider,
|
|
564
610
|
});
|
|
565
|
-
return tempClient.complete(model, prompt, maxTokens, timeoutMs);
|
|
611
|
+
return tempClient.complete(model, prompt, maxTokens, timeoutMs, thinking);
|
|
566
612
|
}
|
|
567
|
-
async complete(model, prompt, maxTokens, timeoutMs) {
|
|
613
|
+
async complete(model, prompt, maxTokens, timeoutMs, thinking) {
|
|
568
614
|
if (this.isRateLimited) {
|
|
569
615
|
throw new RateLimitError(this.rateLimitedUntil - Date.now());
|
|
570
616
|
}
|
|
@@ -572,7 +618,7 @@ class LlmClient {
|
|
|
572
618
|
let lastErr;
|
|
573
619
|
for (let attempt = 0; attempt <= retryDelays.length; attempt++) {
|
|
574
620
|
try {
|
|
575
|
-
return await this.completeOnce(model, prompt, maxTokens, timeoutMs);
|
|
621
|
+
return await this.completeOnce(model, prompt, maxTokens, timeoutMs, thinking);
|
|
576
622
|
}
|
|
577
623
|
catch (err) {
|
|
578
624
|
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
@@ -589,7 +635,7 @@ class LlmClient {
|
|
|
589
635
|
}
|
|
590
636
|
throw lastErr;
|
|
591
637
|
}
|
|
592
|
-
async completeOnce(model, prompt, maxTokens, timeoutMs) {
|
|
638
|
+
async completeOnce(model, prompt, maxTokens, timeoutMs, thinking) {
|
|
593
639
|
if (this.config.provider === "claude-cli") {
|
|
594
640
|
return this.completeClaude(model, prompt, timeoutMs);
|
|
595
641
|
}
|
|
@@ -599,7 +645,10 @@ class LlmClient {
|
|
|
599
645
|
if (this.config.provider === "anthropic") {
|
|
600
646
|
return this.completeAnthropic(model, prompt, maxTokens, timeoutMs);
|
|
601
647
|
}
|
|
602
|
-
|
|
648
|
+
// thinking is threaded from each heavy phase (distill/reflect/classify), so
|
|
649
|
+
// the kwarg is emitted for those tiers on the openai-compat path; completeFast
|
|
650
|
+
// (scoring) never passes it (PR #227 review F1).
|
|
651
|
+
return this.completeOpenAiCompat(model, prompt, maxTokens, timeoutMs, thinking);
|
|
603
652
|
}
|
|
604
653
|
/**
|
|
605
654
|
* Claude CLI: shell out to `claude -p` for subscription users.
|
|
@@ -711,7 +760,7 @@ class LlmClient {
|
|
|
711
760
|
/**
|
|
712
761
|
* OpenAI-compatible /v1/chat/completions (works for OpenAI, OpenRouter, etc).
|
|
713
762
|
*/
|
|
714
|
-
async completeOpenAiCompat(model, prompt, maxTokens, timeoutMs) {
|
|
763
|
+
async completeOpenAiCompat(model, prompt, maxTokens, timeoutMs, thinking) {
|
|
715
764
|
const baseUrl = this.config.baseUrl.replace(/\/$/, "");
|
|
716
765
|
// Some providers include the API version in the base URL already
|
|
717
766
|
const hasVersion = /\/v\d+\/?$/.test(baseUrl);
|
|
@@ -724,14 +773,24 @@ class LlmClient {
|
|
|
724
773
|
if (this.config.apiKey) {
|
|
725
774
|
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
726
775
|
}
|
|
776
|
+
// Qwen3 thinking mode: when on, the model can burn the whole token budget on
|
|
777
|
+
// an unclosed <think> block and emit nothing (probed 2026-08-04). `thinking`
|
|
778
|
+
// is threaded here ONLY from completeDistill, so the kwarg is emitted solely
|
|
779
|
+
// for the distill tier — reflect/classify/fast never send it (PR #227 F1).
|
|
780
|
+
// mlx-lm forwards chat_template_kwargs to the chat template (mirrors the
|
|
781
|
+
// ollama path's think:false). See #220.
|
|
782
|
+
const body = {
|
|
783
|
+
model,
|
|
784
|
+
messages: [{ role: "user", content: prompt }],
|
|
785
|
+
max_tokens: maxTokens,
|
|
786
|
+
};
|
|
787
|
+
if (thinking !== undefined) {
|
|
788
|
+
body.chat_template_kwargs = { enable_thinking: thinking };
|
|
789
|
+
}
|
|
727
790
|
const resp = await fetch(url, {
|
|
728
791
|
method: "POST",
|
|
729
792
|
headers,
|
|
730
|
-
body: JSON.stringify(
|
|
731
|
-
model,
|
|
732
|
-
messages: [{ role: "user", content: prompt }],
|
|
733
|
-
max_tokens: maxTokens,
|
|
734
|
-
}),
|
|
793
|
+
body: JSON.stringify(body),
|
|
735
794
|
signal: AbortSignal.timeout(timeoutMs),
|
|
736
795
|
});
|
|
737
796
|
if (resp.status === 429)
|
package/dist/mcp-server.js
CHANGED
|
@@ -424,6 +424,10 @@ async function startServer(options = {}) {
|
|
|
424
424
|
llmConfig.distillApiKey = savedConfig.distillApiKey ?? llmConfig.apiKey;
|
|
425
425
|
llmConfig.distillProvider = savedConfig.distillProvider ?? llmConfig.provider;
|
|
426
426
|
}
|
|
427
|
+
// Heavy-phase tuning (#220): maxTokens + enableThinking, validated + copied
|
|
428
|
+
// via the shared overlay (also applied in resolveSavedLlmConfig for the
|
|
429
|
+
// nightly's reflect + classify). Wrong-typed values warn + drop.
|
|
430
|
+
(0, llm_js_1.applyTierTuningOverlay)(llmConfig, savedConfig);
|
|
427
431
|
// Apply separate reflect endpoint if configured (e.g. remote Ollama with larger model)
|
|
428
432
|
if (savedConfig?.reflectBaseUrl) {
|
|
429
433
|
llmConfig.reflectBaseUrl = savedConfig.reflectBaseUrl;
|
package/dist/nightly.js
CHANGED
|
@@ -55,6 +55,7 @@ try {
|
|
|
55
55
|
}
|
|
56
56
|
catch { }
|
|
57
57
|
const db_js_1 = require("./db.js");
|
|
58
|
+
const config_read_js_1 = require("./config-read.js");
|
|
58
59
|
const llm_js_1 = require("./llm.js");
|
|
59
60
|
const embedder_js_1 = require("./embedder.js");
|
|
60
61
|
const storage = __importStar(require("./storage.js"));
|
|
@@ -187,6 +188,9 @@ function captureLockWaitMs() {
|
|
|
187
188
|
const env = Number(process.env.HICORTEX_CAPTURE_LOCK_WAIT_MS);
|
|
188
189
|
return Number.isFinite(env) && env >= 0 ? env : CAPTURE_LOCK_WAIT_MS;
|
|
189
190
|
}
|
|
191
|
+
// readPositiveConfig moved to ./config-read.ts (shared with the distill-tier
|
|
192
|
+
// overlay in llm.ts / mcp-server.ts). Validates positive-number config knobs
|
|
193
|
+
// at the disk→runtime boundary with a warn-on-rejected-value.
|
|
190
194
|
const NIGHTLY_LOG_MAX_BYTES = 1024 * 1024; // 1 MB — years of normal runs
|
|
191
195
|
/**
|
|
192
196
|
* Keep ~/.hicortex/nightly.log bounded. The launchd plist and systemd unit
|
|
@@ -489,16 +493,72 @@ async function runClientNightly(config, dryRun, stateDir = HICORTEX_HOME, recapt
|
|
|
489
493
|
const authToken = config.authToken;
|
|
490
494
|
console.log(`[hicortex] Client nightly starting${dryRun ? " (dry run)" : ""}`);
|
|
491
495
|
console.log(`[hicortex] Server: ${serverUrl}`);
|
|
492
|
-
// Verify server is reachable
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
496
|
+
// Verify server is reachable. Retry so a client waking from sleep (its
|
|
497
|
+
// network link not yet re-established) or a transient blip doesn't abort
|
|
498
|
+
// the whole run — the pre-flight only needs the link back, which can take
|
|
499
|
+
// ~1 min after wake.
|
|
500
|
+
//
|
|
501
|
+
// Config-overridable (#163): a wired Pi vs a sleeping laptop want different
|
|
502
|
+
// values. Defaults: 15s per-attempt timeout, 3 attempts, 60s gap.
|
|
503
|
+
//
|
|
504
|
+
// WALL-CLOCK NOTE: setTimeout and AbortSignal.timeout do NOT advance while
|
|
505
|
+
// macOS is asleep, so the ~2m45s worst case (3×15s + 2×60s) is wall-clock-
|
|
506
|
+
// optimistic — a sleeping laptop can straddle sleep cycles and the real
|
|
507
|
+
// elapsed time can exceed it. Not a defect: the capture lock isn't held
|
|
508
|
+
// during the retry and the cursor design is dup-over-loss, so a late success
|
|
509
|
+
// is harmless. Just don't treat 2m45s as a hard wall-clock bound.
|
|
510
|
+
const PREFLIGHT_TIMEOUT_MS = (0, config_read_js_1.readPositiveConfig)(config, "preflightTimeoutMs", 15_000);
|
|
511
|
+
const PREFLIGHT_ATTEMPTS = Math.max(1, Math.floor((0, config_read_js_1.readPositiveConfig)(config, "preflightAttempts", 3)));
|
|
512
|
+
const PREFLIGHT_RETRY_GAP_MS = (0, config_read_js_1.readPositiveConfig)(config, "preflightRetryGapMs", 60_000);
|
|
513
|
+
let reachable = false;
|
|
514
|
+
for (let attempt = 1; attempt <= PREFLIGHT_ATTEMPTS; attempt++) {
|
|
515
|
+
try {
|
|
516
|
+
const resp = await fetch(`${serverUrl}/health`, { signal: AbortSignal.timeout(PREFLIGHT_TIMEOUT_MS) });
|
|
517
|
+
if (!resp.ok)
|
|
518
|
+
throw new Error(`HTTP ${resp.status}`);
|
|
519
|
+
const data = await resp.json();
|
|
520
|
+
console.log(`[hicortex] Server OK: v${data.version}, ${data.memories} memories`);
|
|
521
|
+
reachable = true;
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
catch (err) {
|
|
525
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
526
|
+
if (attempt < PREFLIGHT_ATTEMPTS) {
|
|
527
|
+
console.error(`[hicortex] Server unreachable at ${serverUrl} (attempt ${attempt}/${PREFLIGHT_ATTEMPTS}): ${msg} — retrying in ${PREFLIGHT_RETRY_GAP_MS / 1000}s`);
|
|
528
|
+
await new Promise((r) => setTimeout(r, PREFLIGHT_RETRY_GAP_MS));
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
console.error(`[hicortex] Server unreachable at ${serverUrl} after ${PREFLIGHT_ATTEMPTS} attempts: ${msg}`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
499
534
|
}
|
|
500
|
-
|
|
501
|
-
|
|
535
|
+
if (!reachable) {
|
|
536
|
+
// The abort was invisible for weeks once: a plain `return` let the oneshot
|
|
537
|
+
// exit 0, so systemd/launchd recorded success and the capture gap went
|
|
538
|
+
// unnoticed. Exit non-zero so `systemctl --user status` / launchd show the
|
|
539
|
+
// unit failed (safe — this is a timer-driven oneshot with no Restart=, so
|
|
540
|
+
// no loop), and fire the telemetry ping (ok=false) so the abort is
|
|
541
|
+
// distinguishable from "powered off / uninstalled" in the activity aggregate.
|
|
542
|
+
process.exitCode = 1;
|
|
543
|
+
if (!dryRun && (0, telemetry_js_1.isTelemetryEnabled)(config)) {
|
|
544
|
+
await (0, telemetry_js_1.sendTelemetry)({
|
|
545
|
+
id: (0, telemetry_js_1.getTelemetryId)(stateDir),
|
|
546
|
+
v: VERSION,
|
|
547
|
+
pv: telemetry_js_1.TELEMETRY_PAYLOAD_VERSION,
|
|
548
|
+
event: "nightly",
|
|
549
|
+
mode: "client",
|
|
550
|
+
// `agent` deliberately OMITTED: no transcripts have been read at
|
|
551
|
+
// pre-flight, so the type is genuinely unknown. The admin summary
|
|
552
|
+
// buckets a missing agent as "?" (distinct from cc/pi/oc/mixed) —
|
|
553
|
+
// sending "cc" here would miscount an aborting Hermes/OC-only client
|
|
554
|
+
// as a cc install. The success-path ping sends the real type once
|
|
555
|
+
// session sources are known.
|
|
556
|
+
mem: 0,
|
|
557
|
+
lessons: 0,
|
|
558
|
+
sessions: 0,
|
|
559
|
+
ok: false,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
502
562
|
console.error(`[hicortex] Aborting. Will retry next run.`);
|
|
503
563
|
return; // Don't update last-run so we retry
|
|
504
564
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
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
|
-
* --home-
|
|
22
|
+
* --home-user-projects-ExampleApp--/
|
|
23
23
|
* ...
|
|
24
24
|
*
|
|
25
25
|
* The encoded-cwd uses double-dash separators: /home/alice/projects/myagent
|
|
@@ -20,7 +20,7 @@
|
|
|
20
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
|
-
* --home-
|
|
23
|
+
* --home-user-projects-ExampleApp--/
|
|
24
24
|
* ...
|
|
25
25
|
*
|
|
26
26
|
* The encoded-cwd uses double-dash separators: /home/alice/projects/myagent
|
package/dist/recall-index.d.ts
CHANGED
|
@@ -75,8 +75,8 @@ export declare function formatIndexLine(r: MemorySearchResult & {
|
|
|
75
75
|
* NOTE: FTS hits BYPASS the similarity floor, so raising the floor shifts
|
|
76
76
|
* weight toward FTS-sourced entries. In practice FTS is currently inert on
|
|
77
77
|
* real prompts — eval #3 had 0 FTS rows / 2,208 (2,203 vector + 5 graph), and
|
|
78
|
-
* a
|
|
79
|
-
*
|
|
78
|
+
* a relevance sample returned 96/96 vector — so the floor change is safe as
|
|
79
|
+
* measured. But FTS quality is unmeasured; if FTS starts firing
|
|
80
80
|
* (e.g. as #205's fielded-BM25 retune beds in), give it its own eval.
|
|
81
81
|
*/
|
|
82
82
|
export declare function passesRelevanceGate(r: MemorySearchResult, minSimilarity: number): boolean;
|
package/dist/recall-index.js
CHANGED
|
@@ -129,8 +129,8 @@ function formatIndexLine(r, maxLen = DEFAULT_TITLE_CHARS) {
|
|
|
129
129
|
* NOTE: FTS hits BYPASS the similarity floor, so raising the floor shifts
|
|
130
130
|
* weight toward FTS-sourced entries. In practice FTS is currently inert on
|
|
131
131
|
* real prompts — eval #3 had 0 FTS rows / 2,208 (2,203 vector + 5 graph), and
|
|
132
|
-
* a
|
|
133
|
-
*
|
|
132
|
+
* a relevance sample returned 96/96 vector — so the floor change is safe as
|
|
133
|
+
* measured. But FTS quality is unmeasured; if FTS starts firing
|
|
134
134
|
* (e.g. as #205's fielded-BM25 retune beds in), give it its own eval.
|
|
135
135
|
*/
|
|
136
136
|
function passesRelevanceGate(r, minSimilarity) {
|
package/dist/redact.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Why this exists:
|
|
6
6
|
* - Session transcripts contain tool output: file reads, command output,
|
|
7
7
|
* env var dumps. These regularly contain API keys, tokens, and paths.
|
|
8
|
-
* - The distillation LLM is often remote (e.g.,
|
|
9
|
-
*
|
|
8
|
+
* - The distillation LLM is often remote (e.g., a mesh VPN link to a
|
|
9
|
+
* GPU box). Secrets in the transcript travel over the network.
|
|
10
10
|
* - Even if the LLM correctly classifies the memory as SENSITIVE, the
|
|
11
11
|
* secret is already stored and searchable via hicortex_search.
|
|
12
12
|
* - Redaction runs BEFORE the LLM sees the text, eliminating the risk.
|
package/dist/redact.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* Why this exists:
|
|
7
7
|
* - Session transcripts contain tool output: file reads, command output,
|
|
8
8
|
* env var dumps. These regularly contain API keys, tokens, and paths.
|
|
9
|
-
* - The distillation LLM is often remote (e.g.,
|
|
10
|
-
*
|
|
9
|
+
* - The distillation LLM is often remote (e.g., a mesh VPN link to a
|
|
10
|
+
* GPU box). Secrets in the transcript travel over the network.
|
|
11
11
|
* - Even if the LLM correctly classifies the memory as SENSITIVE, the
|
|
12
12
|
* secret is already stored and searchable via hicortex_search.
|
|
13
13
|
* - Redaction runs BEFORE the LLM sees the text, eliminating the risk.
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
* v — package version
|
|
7
7
|
* pv — payload schema version
|
|
8
8
|
* mode — server or client
|
|
9
|
-
* agent — cc, pi, oc, or mixed (detected from session sources)
|
|
9
|
+
* agent — cc, pi, oc, or mixed (detected from session sources); OMITTED
|
|
10
|
+
* when the agent type is genuinely unknown (pre-flight abort — no
|
|
11
|
+
* transcripts read yet). The admin summary buckets a missing agent
|
|
12
|
+
* as "?", distinct from any real type, so an aborting Hermes/OC
|
|
13
|
+
* client is never miscounted as "cc".
|
|
10
14
|
* mem — total memory count
|
|
11
15
|
* lessons — total lesson count
|
|
12
16
|
* sessions — sessions distilled this run
|
|
@@ -41,7 +45,14 @@ export interface TelemetryPayload {
|
|
|
41
45
|
id: string;
|
|
42
46
|
v: string;
|
|
43
47
|
mode: string;
|
|
44
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Agent type detected from session sources (cc/pi/oc/mixed). OMITTED when
|
|
50
|
+
* unknown — currently only the pre-flight abort path, where no transcripts
|
|
51
|
+
* have been read yet (sending "cc" there mislabelled aborting Hermes/OC
|
|
52
|
+
* clients in the admin aggregate). The admin summary buckets a missing agent
|
|
53
|
+
* as "?", which is the honest signal.
|
|
54
|
+
*/
|
|
55
|
+
agent?: string;
|
|
45
56
|
mem: number;
|
|
46
57
|
lessons: number;
|
|
47
58
|
sessions: number;
|
package/dist/telemetry.js
CHANGED
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
* v — package version
|
|
8
8
|
* pv — payload schema version
|
|
9
9
|
* mode — server or client
|
|
10
|
-
* agent — cc, pi, oc, or mixed (detected from session sources)
|
|
10
|
+
* agent — cc, pi, oc, or mixed (detected from session sources); OMITTED
|
|
11
|
+
* when the agent type is genuinely unknown (pre-flight abort — no
|
|
12
|
+
* transcripts read yet). The admin summary buckets a missing agent
|
|
13
|
+
* as "?", distinct from any real type, so an aborting Hermes/OC
|
|
14
|
+
* client is never miscounted as "cc".
|
|
11
15
|
* mem — total memory count
|
|
12
16
|
* lessons — total lesson count
|
|
13
17
|
* sessions — sessions distilled this run
|
package/dist/types.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export interface MemorySearchResult {
|
|
|
54
54
|
access_count: number;
|
|
55
55
|
memory_type: string;
|
|
56
56
|
project: string | null;
|
|
57
|
-
/** Origin agent (e.g. "hermes/
|
|
57
|
+
/** Origin agent (e.g. "hermes/profile-name", "cc/machine-name") — surfaced in the recall
|
|
58
58
|
* one-liner so agents can calibrate trust (#202 provenance). Optional on the
|
|
59
59
|
* result type (matches how `domain` is threaded) to avoid breaking fixtures. */
|
|
60
60
|
source_agent?: string | null;
|
|
@@ -260,6 +260,49 @@ export interface HicortexConfig {
|
|
|
260
260
|
* near its lower tail). See domains.example.json for a worked example.
|
|
261
261
|
*/
|
|
262
262
|
weakPrimaryFloor?: number;
|
|
263
|
+
/**
|
|
264
|
+
* Per-attempt timeout (ms) for the CLIENT nightly's pre-flight GET /health
|
|
265
|
+
* check before capturing (#163). Default 15000. Overridable per machine —
|
|
266
|
+
* a wired Pi vs a sleeping laptop want different values. See runClientNightly
|
|
267
|
+
* in nightly.ts. No effect in server mode (server capture is localhost).
|
|
268
|
+
*/
|
|
269
|
+
preflightTimeoutMs?: number;
|
|
270
|
+
/**
|
|
271
|
+
* Max attempts for the client nightly's pre-flight /health retry loop (#163).
|
|
272
|
+
* Default 3. Attempts are spaced preflightRetryGapMs apart; on exhaustion the
|
|
273
|
+
* run aborts with a non-zero exit code and an ok=false telemetry ping so the
|
|
274
|
+
* failure is visible to systemd/launchd and the activity aggregate.
|
|
275
|
+
*/
|
|
276
|
+
preflightAttempts?: number;
|
|
277
|
+
/**
|
|
278
|
+
* Gap (ms) between pre-flight /health attempts in the client nightly (#163).
|
|
279
|
+
* Default 60000. Wall-clock-optimistic on a sleeping laptop — setTimeout does
|
|
280
|
+
* NOT advance while macOS is asleep, so real elapsed time can exceed the
|
|
281
|
+
* nominal worst case. Not a defect (capture lock isn't held; cursor design is
|
|
282
|
+
* dup-over-loss); just don't treat the nominal sum as a hard bound.
|
|
283
|
+
*/
|
|
284
|
+
preflightRetryGapMs?: number;
|
|
285
|
+
/**
|
|
286
|
+
* Max output tokens for the LLM phases — distillation, reflection, classification,
|
|
287
|
+
* and scoring (completeDistill / completeReflect / completeClassify / completeFast).
|
|
288
|
+
* Default 8192 for the heavy phases, 2048 for scoring (the local fast tier); an
|
|
289
|
+
* explicit value overrides all of them. A ceiling, not a target: generation stops
|
|
290
|
+
* at the model's natural end (finish_reason stop), so a higher cap costs no latency
|
|
291
|
+
* when it finishes early. Read in llm.ts; see #220.
|
|
292
|
+
*/
|
|
293
|
+
maxTokens?: number;
|
|
294
|
+
/**
|
|
295
|
+
* Toggle the model's internal reasoning ("thinking") stream on the openai-compat
|
|
296
|
+
* path — applies to the heavy phases (distill / reflect / classify) that share an
|
|
297
|
+
* OpenAI-compatible endpoint (e.g. a reasoning model served via a local gateway).
|
|
298
|
+
* Default false. A thinking model with thinking ON can burn the entire token
|
|
299
|
+
* budget on an unclosed <think> block and emit nothing (probed 2026-08-04). When
|
|
300
|
+
* set (true or false), completeOpenAiCompat sends
|
|
301
|
+
* chat_template_kwargs:{enable_thinking}. Threaded structurally from each heavy
|
|
302
|
+
* phase, so scoring (ollama) never sends it. No effect on the anthropic path.
|
|
303
|
+
* See #220.
|
|
304
|
+
*/
|
|
305
|
+
enableThinking?: boolean;
|
|
263
306
|
}
|
|
264
307
|
/** A config-owned life-sphere domain (see HicortexConfig.domains). */
|
|
265
308
|
export interface DomainDef {
|
|
@@ -27,7 +27,7 @@ CONFIG_SCHEMA: list[dict[str, Any]] = [
|
|
|
27
27
|
"description": (
|
|
28
28
|
"URL of the Hicortex memory server. On the server host use "
|
|
29
29
|
"http://localhost:8787; on other machines use the server's "
|
|
30
|
-
"
|
|
30
|
+
"private hostname, e.g. http://memory-server:8787."
|
|
31
31
|
),
|
|
32
32
|
"default": "http://localhost:8787",
|
|
33
33
|
"required": True,
|
|
@@ -210,7 +210,8 @@ class HicortexProvider(MemoryProvider):
|
|
|
210
210
|
self._recall_limit = 5
|
|
211
211
|
self._privacy = cfg.get("privacy_filter", "WORK,PERSONAL")
|
|
212
212
|
# #203 scope: declared knowledge domains for this role-bound agent
|
|
213
|
-
# (e.g.
|
|
213
|
+
# (e.g. a health-focused agent → Health). Soft affinity boost on
|
|
214
|
+
# recall; never excludes.
|
|
214
215
|
_md_raw = cfg.get("mission_domains") or ""
|
|
215
216
|
self._mission_domains = [d.strip() for d in _md_raw.split(",") if d.strip()]
|
|
216
217
|
self._agent_name = _resolve_agent_name(cfg)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.4",
|
|
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": {
|