@gamaze/hicortex 0.20.7 → 0.20.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 +18 -41
- package/assets/dashboard.html +3989 -836
- package/dist/calibration.d.ts +293 -0
- package/dist/calibration.js +379 -0
- package/dist/capture-health.d.ts +87 -0
- package/dist/capture-health.js +106 -0
- package/dist/capture-pause.d.ts +86 -0
- package/dist/capture-pause.js +127 -0
- package/dist/capture.d.ts +24 -3
- package/dist/capture.js +11 -1
- package/dist/classify-domains.d.ts +6 -0
- package/dist/classify-domains.js +7 -1
- package/dist/cli.js +38 -3
- package/dist/config-read.d.ts +1 -1
- package/dist/config-read.js +96 -9
- package/dist/consolidate.d.ts +114 -68
- package/dist/consolidate.js +302 -182
- package/dist/dashboard.d.ts +326 -6
- package/dist/dashboard.js +592 -7
- package/dist/db.js +105 -0
- package/dist/dedup.d.ts +34 -26
- package/dist/dedup.js +91 -57
- package/dist/distiller.js +1 -1
- package/dist/domain-classify.d.ts +7 -6
- package/dist/domain-classify.js +12 -10
- package/dist/eval/decay-eval.d.ts +3 -3
- package/dist/eval/decay-eval.js +4 -4
- package/dist/eval/importance-eval.d.ts +85 -0
- package/dist/eval/importance-eval.js +286 -0
- package/dist/eval/planted-eval.d.ts +26 -0
- package/dist/eval/planted-eval.js +97 -0
- package/dist/eval/planted-fixtures.d.ts +107 -0
- package/dist/eval/planted-fixtures.js +283 -0
- package/dist/eval/planted-harness.d.ts +176 -0
- package/dist/eval/planted-harness.js +649 -0
- package/dist/eval/ranking-battery.d.ts +78 -0
- package/dist/eval/ranking-battery.js +181 -0
- package/dist/eval/ranking-eval.d.ts +41 -0
- package/dist/eval/ranking-eval.js +391 -0
- package/dist/eval/ranking-fixtures.d.ts +77 -0
- package/dist/eval/ranking-fixtures.js +226 -0
- package/dist/identity-store.d.ts +21 -0
- package/dist/identity-store.js +49 -0
- package/dist/index.js +4 -3
- package/dist/init.d.ts +23 -3
- package/dist/init.js +84 -9
- package/dist/llm.d.ts +43 -58
- package/dist/llm.js +87 -101
- package/dist/mcp-server.d.ts +12 -0
- package/dist/mcp-server.js +213 -32
- package/dist/nightly.d.ts +9 -1
- package/dist/nightly.js +164 -110
- package/dist/nofit.d.ts +4 -11
- package/dist/nofit.js +6 -23
- package/dist/prompts.d.ts +10 -0
- package/dist/prompts.js +28 -5
- package/dist/recall-index.d.ts +30 -28
- package/dist/recall-index.js +21 -18
- package/dist/recall-registry.d.ts +2 -1
- package/dist/recall-registry.js +35 -1
- package/dist/reconsolidation.d.ts +168 -87
- package/dist/reconsolidation.js +818 -377
- package/dist/relink.js +3 -4
- package/dist/rescore-importance.d.ts +80 -0
- package/dist/rescore-importance.js +236 -0
- package/dist/retrieval.d.ts +80 -35
- package/dist/retrieval.js +322 -105
- package/dist/run-deadline.d.ts +62 -0
- package/dist/run-deadline.js +73 -0
- package/dist/schema-prototypes.d.ts +3 -3
- package/dist/schema-prototypes.js +3 -3
- package/dist/stages.d.ts +37 -0
- package/dist/stages.js +51 -0
- package/dist/state.d.ts +34 -9
- package/dist/storage.d.ts +50 -18
- package/dist/storage.js +125 -30
- package/dist/telemetry.d.ts +8 -7
- package/dist/token-budget.js +3 -4
- package/dist/type-classify.js +4 -4
- package/dist/types.d.ts +143 -155
- package/domains.example.json +4 -5
- package/hermes-plugin/hicortex/README.md +2 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +4 -1
- package/pi-extension/hicortex/README.md +1 -1
- package/server.json +3 -3
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Operator capture pause (#423 phase 3, D3) — the server-side 200-skip.
|
|
4
|
+
*
|
|
5
|
+
* A pause is a row in `capture_pauses` (migration v18): machine × harness →
|
|
6
|
+
* paused_at. A row EXISTS = paused for that bundle; the /distill handler
|
|
7
|
+
* reads the table per POST, so a pause takes effect on the very next post —
|
|
8
|
+
* no restart — and answers 200 {skipped: true, paused: true}. The 200 is the
|
|
9
|
+
* whole point: capture.ts treats every 200 as confirmed and advances its
|
|
10
|
+
* cursor, so sessions that arrive while paused are deliberately NOT captured
|
|
11
|
+
* and are never re-sent or backfilled. Zero client changes.
|
|
12
|
+
*
|
|
13
|
+
* THE BUNDLE KEY. The pause's (machine, harness) must derive EXACTLY like
|
|
14
|
+
* the traffic it gates: machine via storage.sanitizeSourceMachine ('' when
|
|
15
|
+
* absent) and harness via harnessOfAgent below — the same normalization
|
|
16
|
+
* recordDistillActivity applies when it writes distill_activity, and the same
|
|
17
|
+
* "harness/profile" → "harness" split the console groups its bundles on. A
|
|
18
|
+
* key derived any other way would never match and the pause would silently
|
|
19
|
+
* not fire.
|
|
20
|
+
*
|
|
21
|
+
* LAST-SEEN (presence dots) derives ONLY from /distill activity — the one
|
|
22
|
+
* per-agent-identified traffic the server sees. Recall traffic (/search,
|
|
23
|
+
* /recall-index, /memory) carries no agent/machine identity on the wire, so
|
|
24
|
+
* attributing it would need new client fields — the heartbeats the spec
|
|
25
|
+
* forbids. Thresholds (green ≤36h — a nightly poster reads online through
|
|
26
|
+
* the following day; amber ≤7d — the distill_activity retention window; none
|
|
27
|
+
* beyond or with no rows) are page-side presentation; this module just
|
|
28
|
+
* reports the newest ts per bundle.
|
|
29
|
+
*
|
|
30
|
+
* Pure, unit-testable without express (the capture-health.ts layering):
|
|
31
|
+
* mcp-server.ts and dashboard.ts wire these functions to the live db.
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.harnessOfAgent = harnessOfAgent;
|
|
35
|
+
exports.capturePauseKey = capturePauseKey;
|
|
36
|
+
exports.isCapturePaused = isCapturePaused;
|
|
37
|
+
exports.setCapturePause = setCapturePause;
|
|
38
|
+
exports.listCapturePauses = listCapturePauses;
|
|
39
|
+
exports.readFleetLastSeen = readFleetLastSeen;
|
|
40
|
+
const storage_js_1 = require("./storage.js");
|
|
41
|
+
/**
|
|
42
|
+
* Derive the harness from a source_agent wire value — the bundle split the
|
|
43
|
+
* console already uses ("claude-code/main" → "claude-code"): the part before
|
|
44
|
+
* the first '/' when a slash is present at index > 0, else the whole trimmed
|
|
45
|
+
* string, capped at 128. Non-string/blank → "unknown" (mirrors how
|
|
46
|
+
* recordDistillActivity stores the agent when absent).
|
|
47
|
+
*/
|
|
48
|
+
function harnessOfAgent(sourceAgent) {
|
|
49
|
+
if (typeof sourceAgent !== "string")
|
|
50
|
+
return "unknown";
|
|
51
|
+
const t = sourceAgent.trim();
|
|
52
|
+
if (t.length === 0)
|
|
53
|
+
return "unknown";
|
|
54
|
+
const slash = t.indexOf("/");
|
|
55
|
+
return (slash > 0 ? t.slice(0, slash) : t).slice(0, 128);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Normalize the raw /distill wire fields into the pause key. MUST match the
|
|
59
|
+
* recordDistillActivity normalization (machine '' when absent, agent
|
|
60
|
+
* 'unknown' when absent) and the console's bundle grouping
|
|
61
|
+
* ((machine||'')+'|'+harness) — see the module doc.
|
|
62
|
+
*/
|
|
63
|
+
function capturePauseKey(machine, sourceAgent) {
|
|
64
|
+
return {
|
|
65
|
+
machine: (0, storage_js_1.sanitizeSourceMachine)(machine) ?? "",
|
|
66
|
+
harness: harnessOfAgent(sourceAgent),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** True when a pause row exists for the (machine, harness) bundle. */
|
|
70
|
+
function isCapturePaused(db, machine, harness) {
|
|
71
|
+
return (db
|
|
72
|
+
.prepare("SELECT 1 FROM capture_pauses WHERE machine = ? AND harness = ?")
|
|
73
|
+
.get(machine, harness) !== undefined);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Pause (upsert the row, timestamp now) or resume (delete it). Returns the
|
|
77
|
+
* persisted paused_at when pausing, null when resuming. No pruning, ever —
|
|
78
|
+
* see migration v18's provenance comment.
|
|
79
|
+
*/
|
|
80
|
+
function setCapturePause(db, machine, harness, paused) {
|
|
81
|
+
if (!paused) {
|
|
82
|
+
db.prepare("DELETE FROM capture_pauses WHERE machine = ? AND harness = ?").run(machine, harness);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const pausedAt = new Date().toISOString();
|
|
86
|
+
db.prepare("INSERT OR REPLACE INTO capture_pauses (machine, harness, paused_at) VALUES (?, ?, ?)").run(machine, harness, pausedAt);
|
|
87
|
+
return pausedAt;
|
|
88
|
+
}
|
|
89
|
+
/** Every paused bundle (newest first) — the dashboard fleet.pauses block. */
|
|
90
|
+
function listCapturePauses(db) {
|
|
91
|
+
return db
|
|
92
|
+
.prepare("SELECT machine, harness, paused_at FROM capture_pauses ORDER BY paused_at DESC")
|
|
93
|
+
.all();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The newest /distill activity per bundle: for each (machine, agent) take
|
|
97
|
+
* MAX(ts) with that latest row's outcome, derive the harness per agent, then
|
|
98
|
+
* merge same-bundle agents keeping the newest ts (one dot per bundle, not
|
|
99
|
+
* per profile). Reads only distill_activity, which the recorder prunes to
|
|
100
|
+
* 7 days — older-than-window bundles simply have no rows and no dot.
|
|
101
|
+
*/
|
|
102
|
+
function readFleetLastSeen(db) {
|
|
103
|
+
const rows = db
|
|
104
|
+
.prepare(`SELECT a.machine, a.agent, a.ts, a.outcome
|
|
105
|
+
FROM distill_activity a
|
|
106
|
+
JOIN (
|
|
107
|
+
SELECT machine, agent, MAX(ts) AS max_ts
|
|
108
|
+
FROM distill_activity
|
|
109
|
+
GROUP BY machine, agent
|
|
110
|
+
) latest
|
|
111
|
+
ON a.machine = latest.machine AND a.agent = latest.agent AND a.ts = latest.max_ts`)
|
|
112
|
+
.all();
|
|
113
|
+
const byBundle = new Map();
|
|
114
|
+
for (const r of rows) {
|
|
115
|
+
const key = `${r.machine}|${harnessOfAgent(r.agent)}`;
|
|
116
|
+
const prev = byBundle.get(key);
|
|
117
|
+
if (!prev || r.ts > prev.last_seen) {
|
|
118
|
+
byBundle.set(key, {
|
|
119
|
+
machine: r.machine,
|
|
120
|
+
harness: harnessOfAgent(r.agent),
|
|
121
|
+
last_seen: r.ts,
|
|
122
|
+
last_outcome: r.outcome,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return [...byBundle.values()].sort((a, b) => (a.last_seen < b.last_seen ? 1 : -1));
|
|
127
|
+
}
|
package/dist/capture.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { TranscriptBatch } from "./transcript-reader.js";
|
|
16
16
|
import type { CursorStore } from "./capture-cursors.js";
|
|
17
|
+
import type { RunDeadline } from "./run-deadline.js";
|
|
17
18
|
/**
|
|
18
19
|
* Max denoised chars per segment. Kept below the server's 80K distill cap
|
|
19
20
|
* (distiller.ts MAX_TRANSCRIPT_CHARS) with ~20K headroom so NO capture path can
|
|
@@ -56,6 +57,9 @@ export interface DistillBody {
|
|
|
56
57
|
source_agent_id?: string | null;
|
|
57
58
|
/** Client-declared topic/domain of the capturing agent. Provenance only. */
|
|
58
59
|
source_domain?: string | null;
|
|
60
|
+
/** Machine this capture ran on (#421 machine × harness): config
|
|
61
|
+
* `machineName` ?? os.hostname(), stamped by the nightly. */
|
|
62
|
+
source_machine?: string | null;
|
|
59
63
|
project: string;
|
|
60
64
|
session_id: string;
|
|
61
65
|
segment_id: string;
|
|
@@ -105,6 +109,21 @@ export interface CaptureOptions {
|
|
|
105
109
|
* `source_domain` provenance. Null when undeclared.
|
|
106
110
|
*/
|
|
107
111
|
sourceDomain?: string | null;
|
|
112
|
+
/**
|
|
113
|
+
* Machine stamp on every segment (#421 machine × harness): config
|
|
114
|
+
* `machineName` when set, else os.hostname() — resolved by the nightly
|
|
115
|
+
* caller. Null disables stamping.
|
|
116
|
+
*/
|
|
117
|
+
sourceMachine?: string | null;
|
|
118
|
+
/**
|
|
119
|
+
* The run-wide pipeline deadline (#405), checked BETWEEN segment POSTs —
|
|
120
|
+
* a boundary the per-session cursor discipline already guarantees is safe
|
|
121
|
+
* (the cursor only advances past server-confirmed segments, so a deadline
|
|
122
|
+
* stop holds every unconfirmed segment for the next run; dup-over-loss).
|
|
123
|
+
* Full and consolidate-only nightlies pass it; capture-only/watchdog runs
|
|
124
|
+
* keep their 30-min unit backstop instead.
|
|
125
|
+
*/
|
|
126
|
+
deadline?: RunDeadline;
|
|
108
127
|
}
|
|
109
128
|
export interface CaptureResult {
|
|
110
129
|
memoriesIngested: number;
|
|
@@ -113,10 +132,12 @@ export interface CaptureResult {
|
|
|
113
132
|
/**
|
|
114
133
|
* Set when the loop stopped early on a terminal server response: "limit"
|
|
115
134
|
* (token-budget 429, mcp-server.ts's `"token budget exceeded"` gate) or
|
|
116
|
-
* "auth" (401)
|
|
117
|
-
*
|
|
135
|
+
* "auth" (401); "deadline" (#405) when the run-wide pipeline deadline fired
|
|
136
|
+
* between segments (transient — the watermark holds, everything unconfirmed
|
|
137
|
+
* retries next run). A rate-limit 429 never sets this — it is transient
|
|
138
|
+
* (#327). The caller decides watermark handling.
|
|
118
139
|
*/
|
|
119
|
-
stopped?: "limit" | "auth";
|
|
140
|
+
stopped?: "limit" | "auth" | "deadline";
|
|
120
141
|
/**
|
|
121
142
|
* Run-global rate-429 latch (#327 CR): true when at least one session
|
|
122
143
|
* SURRENDERED to a rate-limit 429 (the transient kind — postWithRateRetry
|
package/dist/capture.js
CHANGED
|
@@ -204,7 +204,7 @@ async function postWithRateRetry(post, body) {
|
|
|
204
204
|
* re-paying the Retry-After ladder (#327).
|
|
205
205
|
*/
|
|
206
206
|
async function captureBatches(batches, opts) {
|
|
207
|
-
const { post, cursorStore, dryRun = false, segmentMaxChars = exports.SEGMENT_MAX_CHARS, sourceAgentId, sourceDomain } = opts;
|
|
207
|
+
const { post, cursorStore, dryRun = false, segmentMaxChars = exports.SEGMENT_MAX_CHARS, sourceAgentId, sourceDomain, sourceMachine, deadline } = opts;
|
|
208
208
|
let memoriesIngested = 0;
|
|
209
209
|
let sessionsSent = 0;
|
|
210
210
|
let hadTransientFailure = false;
|
|
@@ -249,6 +249,15 @@ async function captureBatches(batches, opts) {
|
|
|
249
249
|
let sessionPosted = false;
|
|
250
250
|
for (let s = 0; s < segments.length; s++) {
|
|
251
251
|
const seg = segments[s];
|
|
252
|
+
// #405: stop BETWEEN segments — a safe boundary by construction (the
|
|
253
|
+
// cursor below only advances past server-confirmed segments). The whole
|
|
254
|
+
// session loop breaks on `stopped` at the bottom; unconfirmed segments
|
|
255
|
+
// hold and retry next run.
|
|
256
|
+
if (!dryRun && deadline?.hit("capture")) {
|
|
257
|
+
console.warn(`[hicortex] Run deadline reached — capture stops after the last confirmed segment`);
|
|
258
|
+
stopped = "deadline";
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
252
261
|
// A segment advances the cursor to its segEnd only when it is the LAST
|
|
253
262
|
// segment ending at that boundary. Hard-split pieces (.p0,.p1,…) of one
|
|
254
263
|
// entry share the same segEnd; confirming an earlier piece must NOT move
|
|
@@ -267,6 +276,7 @@ async function captureBatches(batches, opts) {
|
|
|
267
276
|
source_agent: batch.sourceAgent ?? `claude-code/${batch.projectName}`,
|
|
268
277
|
source_agent_id: sourceAgentId ?? null,
|
|
269
278
|
source_domain: sourceDomain ?? null,
|
|
279
|
+
source_machine: sourceMachine ?? null,
|
|
270
280
|
project: batch.projectName,
|
|
271
281
|
session_id: batch.sessionId,
|
|
272
282
|
segment_id: `${genPrefix}${seg.segStart}-${seg.segEnd}${seg.idSuffix}`,
|
|
@@ -50,6 +50,12 @@ export interface ClassifyDomainsOptions {
|
|
|
50
50
|
llm?: LlmClient;
|
|
51
51
|
/** Config override (tests). Defaults to reading stateDir/config.json. */
|
|
52
52
|
config?: Record<string, unknown> | null;
|
|
53
|
+
/**
|
|
54
|
+
* Weak-primary floor (#408): release-managed default (calibration.ts via
|
|
55
|
+
* nofit's DEFAULT_WEAK_PRIMARY_FLOOR); this field is the eval/test seam —
|
|
56
|
+
* the config key is gone from the surface. Invalid → default.
|
|
57
|
+
*/
|
|
58
|
+
weakPrimaryFloor?: number;
|
|
53
59
|
/**
|
|
54
60
|
* Embedder override (tests). Used only for domain-description prototype
|
|
55
61
|
* seeds; defaults to the local ONNX embedder, loaded lazily on first need
|
package/dist/classify-domains.js
CHANGED
|
@@ -118,7 +118,13 @@ async function runClassifyDomains(options = {}) {
|
|
|
118
118
|
'{ "name": "Boating", "description": "..." }] } and re-run. ' +
|
|
119
119
|
"No fallback bucket is needed — no-fit memories are handled automatically.");
|
|
120
120
|
}
|
|
121
|
-
|
|
121
|
+
// #408: the floor is a release-managed calibration constant; the Options
|
|
122
|
+
// field is the eval/test seam (invalid values keep the default, the stage
|
|
123
|
+
// knob-validation style — silent fallback, no warn).
|
|
124
|
+
const floorRaw = Number(options.weakPrimaryFloor);
|
|
125
|
+
const weakPrimaryFloor = Number.isFinite(floorRaw) && floorRaw > 0 && floorRaw < 1
|
|
126
|
+
? floorRaw
|
|
127
|
+
: nofit_js_1.DEFAULT_WEAK_PRIMARY_FLOOR;
|
|
122
128
|
// Resolve the LLM (one model serves all phases — #231).
|
|
123
129
|
let llm;
|
|
124
130
|
if (options.llm) {
|
package/dist/cli.js
CHANGED
|
@@ -189,6 +189,36 @@ switch (command) {
|
|
|
189
189
|
});
|
|
190
190
|
break;
|
|
191
191
|
}
|
|
192
|
+
case "rescore-importance": {
|
|
193
|
+
// #425 — one-shot LLM backfill: re-judge the corpus under the
|
|
194
|
+
// re-anchored importance rubric. classify-domains shape (resumable
|
|
195
|
+
// cursor, --batch, --reset) + dedup discipline (dry-run default,
|
|
196
|
+
// --apply, DB backup before any write).
|
|
197
|
+
const args = process.argv.slice(3);
|
|
198
|
+
const intFlag = (name) => {
|
|
199
|
+
const idx = args.indexOf(name);
|
|
200
|
+
if (idx === -1)
|
|
201
|
+
return undefined;
|
|
202
|
+
const val = parseInt(args[idx + 1], 10);
|
|
203
|
+
if (isNaN(val)) {
|
|
204
|
+
console.error(`[hicortex] rescore-importance: ${name} requires an integer value`);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
return val;
|
|
208
|
+
};
|
|
209
|
+
const rescoreOptions = {
|
|
210
|
+
apply: args.includes("--apply"),
|
|
211
|
+
reset: args.includes("--reset"),
|
|
212
|
+
batchSize: intFlag("--batch"),
|
|
213
|
+
};
|
|
214
|
+
import("./rescore-importance.js").then(({ runRescoreImportance }) => {
|
|
215
|
+
runRescoreImportance(rescoreOptions).catch((err) => {
|
|
216
|
+
console.error(err instanceof Error ? err.message : `[hicortex] rescore-importance failed: ${err}`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
192
222
|
case "classify-types": {
|
|
193
223
|
const args = process.argv.slice(3);
|
|
194
224
|
const intFlag = (name) => {
|
|
@@ -421,6 +451,8 @@ Commands:
|
|
|
421
451
|
backup Snapshot the DB + identity + state to a tar.gz (online, WAL-safe)
|
|
422
452
|
classify-domains Backfill content-based domain tags over the corpus (server mode, needs config.domains)
|
|
423
453
|
classify-types Backfill episode→fact/decision type tags over the corpus (server mode)
|
|
454
|
+
rescore-importance Re-judge all memories' importance under the current rubric
|
|
455
|
+
(server mode; dry run by default — --apply executes; resumable)
|
|
424
456
|
learnings-identity Fetch identity + lessons and print Markdown to stdout (CC SessionStart hook)
|
|
425
457
|
(alias: lessons-context — the pre-#264 name, kept for backcompat)
|
|
426
458
|
recall-hook Pushed recall index for the current prompt (CC UserPromptSubmit/SessionStart hook)
|
|
@@ -445,9 +477,8 @@ Options:
|
|
|
445
477
|
dedup --apply Execute the merge (default: dry run, report only)
|
|
446
478
|
Losers are absorbed — hidden from recall, kept as
|
|
447
479
|
evidence (fetchable by id; dedup_log audit) — not deleted
|
|
448
|
-
dedup --threshold <t> Override the threshold for one run (default:
|
|
449
|
-
|
|
450
|
-
still honored; else 0.92)
|
|
480
|
+
dedup --threshold <t> Override the threshold for one run (default: the
|
|
481
|
+
release-managed calibration ceiling, 0.92)
|
|
451
482
|
dedup --db <path> DB path override (defaults to the configured DB)
|
|
452
483
|
history --rollback <row> Roll back history row <row>: restores prior content/status, un-absorbs triggers
|
|
453
484
|
history --db <path> DB path override (defaults to the configured DB)
|
|
@@ -459,6 +490,10 @@ Options:
|
|
|
459
490
|
classify-types --all Reclassify every memory (default: only episodes)
|
|
460
491
|
classify-types --batch <n> Memories per batch (default: 200)
|
|
461
492
|
classify-types --reset Restart from the beginning (ignore saved cursor)
|
|
493
|
+
rescore-importance --apply Execute the importance backfill (default: dry run, report only)
|
|
494
|
+
Takes a DB backup first; resumable via a state.json cursor
|
|
495
|
+
rescore-importance --batch <n> Rows per invocation (default: 500; LLM calls are 10 rows each)
|
|
496
|
+
rescore-importance --reset Restart from the beginning (ignore saved cursor)
|
|
462
497
|
identity show [name] Print all identity sections, or just <name> (raw, pipeable)
|
|
463
498
|
identity edit <name> Edit a section in $EDITOR; PUT only if changed
|
|
464
499
|
identity … --agent <id> Target a per-agent scope instead of the global set
|
package/dist/config-read.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare function readStrictBoolean(config: Record<string, unknown>, key:
|
|
|
27
27
|
/**
|
|
28
28
|
* Read a non-negative finite number (allows 0, unlike readPositiveConfig).
|
|
29
29
|
* Returns `def` when absent OR invalid. Used for keys where 0 is a valid "off"
|
|
30
|
-
* value (e.g.
|
|
30
|
+
* value (e.g. memorySoftCap's eviction opt-out, timerJitterSeconds).
|
|
31
31
|
*/
|
|
32
32
|
export declare function readNonNegativeConfig(config: Record<string, unknown>, key: string, def: number): number;
|
|
33
33
|
/**
|
package/dist/config-read.js
CHANGED
|
@@ -52,7 +52,7 @@ function readStrictBoolean(config, key) {
|
|
|
52
52
|
/**
|
|
53
53
|
* Read a non-negative finite number (allows 0, unlike readPositiveConfig).
|
|
54
54
|
* Returns `def` when absent OR invalid. Used for keys where 0 is a valid "off"
|
|
55
|
-
* value (e.g.
|
|
55
|
+
* value (e.g. memorySoftCap's eviction opt-out, timerJitterSeconds).
|
|
56
56
|
*/
|
|
57
57
|
function readNonNegativeConfig(config, key, def) {
|
|
58
58
|
const v = config[key];
|
|
@@ -152,6 +152,63 @@ const IGNORED_CONFIG_KEYS = [
|
|
|
152
152
|
"classifyModel", "classifyBaseUrl", "classifyApiKey", "classifyProvider",
|
|
153
153
|
"distillFallback",
|
|
154
154
|
];
|
|
155
|
+
/**
|
|
156
|
+
* Config keys REMOVED by the #405 budget simplification, each mapped to its
|
|
157
|
+
* replacement (or "removed" when there is none). The old single 0.16.8-style
|
|
158
|
+
* message does not fit — every key here needs to name where its job went.
|
|
159
|
+
* Warned at the config boundary alongside the 0.16.8 keys above.
|
|
160
|
+
*/
|
|
161
|
+
const REMOVED_CONFIG_KEYS = {
|
|
162
|
+
reconsolidationMaxMinutes: "removed — the ONE run deadline (nightlyTimeBudgetMinutes) bounds the stage",
|
|
163
|
+
reconsolidationMaxCalls: "removed — the ONE call budget (nightlyLlmCallBudget) caps all stages",
|
|
164
|
+
supersessionMaxCalls: "removed — the ONE call budget (nightlyLlmCallBudget) caps all stages",
|
|
165
|
+
dedupNightlyMaxMerges: "removed — the run deadline (nightlyTimeBudgetMinutes) bounds nightly merges",
|
|
166
|
+
classifyMaxTokens: "removed — maxTokens is the single output ceiling for every call",
|
|
167
|
+
llmBreakerThreshold: "removed — the breaker threshold is a constant (3)",
|
|
168
|
+
llmBreakerCooldownMs: "removed — the breaker cooldown is a constant (10 min)",
|
|
169
|
+
llmSingleFlightWaitMs: "removed — the wait derives from llmTimeoutMs",
|
|
170
|
+
preflightTimeoutMs: "removed — a constant (20 s per attempt)",
|
|
171
|
+
preflightAttempts: "removed — a constant (3 attempts)",
|
|
172
|
+
preflightRetryGapMs: "removed — a constant (60 s gap)",
|
|
173
|
+
moduleIndexTokenBudget: "removed — was documentation-only",
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Config keys that became RELEASE-MANAGED CALIBRATION constants (#408): the
|
|
177
|
+
* ~35 tuning keys of the 0.15–0.20 era. Their values are ignored — the
|
|
178
|
+
* constants ship with each release (src/calibration.ts) and change only in
|
|
179
|
+
* releases with eval evidence linked in the changelog. Like the #405 list,
|
|
180
|
+
* this is deliberately WIDER than the `HicortexConfig` type ever was: several
|
|
181
|
+
* keys (supersessionPenalty, the rrf/bm25 knob set, recallTitleChars, ...)
|
|
182
|
+
* were README-documented but never typed. Don't trim the list to match the
|
|
183
|
+
* interface — the warn exists precisely for the untyped surface.
|
|
184
|
+
*/
|
|
185
|
+
const RELEASE_MANAGED_CONFIG_KEYS = [
|
|
186
|
+
"decayHalfLifeDays", "searchLimit", "recentLimit", "recentWindowDays",
|
|
187
|
+
"coldExposureSlots", "recallMaxItems", "recallMinSimilarity",
|
|
188
|
+
"recallReshowTurns", "recallMinPromptChars", "recallTitleChars",
|
|
189
|
+
"sessionIntentWeight", "noveltyFloorSlots", "scoreSimilarityWeight",
|
|
190
|
+
"scoreStrengthWeight", "scoreConnectionsWeight", "scoreRecencyWeight",
|
|
191
|
+
"freshnessBoostDays", "freshnessBoostWeight", "supersededDemotion",
|
|
192
|
+
"projectAffinityWeight", "domainAffinityWeight", "rrfK",
|
|
193
|
+
"rrfCompositeWeight", "rrfFtsWeight", "rrfVectorWeight",
|
|
194
|
+
"bm25WeightBody", "bm25WeightProject", "bm25WeightDomain",
|
|
195
|
+
"dedupAutoMergeThreshold", "dedupMergeThreshold",
|
|
196
|
+
"supersessionMinSimilarity", "supersessionPenalty",
|
|
197
|
+
"correctionMinSimilarity", "correctionRewriteMinConfidence",
|
|
198
|
+
"weakPrimaryFloor",
|
|
199
|
+
];
|
|
200
|
+
/**
|
|
201
|
+
* Config keys MOVED to the diagnostic env tier (#408): the ollama-operational
|
|
202
|
+
* family left config.json and now resolves from environment variables
|
|
203
|
+
* (calibration.ts resolvers; env > constant, invalid env warns + falls back).
|
|
204
|
+
* Each entry maps the old config key to its env replacement so the warning
|
|
205
|
+
* can name the exact variable.
|
|
206
|
+
*/
|
|
207
|
+
const ENV_MOVED_CONFIG_KEYS = {
|
|
208
|
+
numCtx: "HICORTEX_NUM_CTX",
|
|
209
|
+
ollamaFlushEvery: "HICORTEX_OLLAMA_FLUSH_EVERY",
|
|
210
|
+
ollamaFlushWaitMs: "HICORTEX_OLLAMA_FLUSH_WAIT_MS",
|
|
211
|
+
};
|
|
155
212
|
/**
|
|
156
213
|
* Warn if the saved config carries keys that 0.16.8+ ignores. Call at every
|
|
157
214
|
* config read (daemon boot + nightly). The warning clears once the keys are
|
|
@@ -163,12 +220,42 @@ function warnIgnoredConfigKeys(savedConfig) {
|
|
|
163
220
|
return;
|
|
164
221
|
const present = IGNORED_CONFIG_KEYS.filter((k) => savedConfig[k] !== undefined);
|
|
165
222
|
const hasModelsBlock = savedConfig.models !== undefined;
|
|
166
|
-
if (present.length
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
223
|
+
if (present.length > 0 || hasModelsBlock) {
|
|
224
|
+
const detail = [...present, ...(hasModelsBlock ? ["models"] : [])].join(", ");
|
|
225
|
+
console.warn(`[hicortex] config has keys IGNORED since 0.16.8 (${detail}). They have no effect now. ` +
|
|
226
|
+
`Per-stage model keys / the \`models\` block: one model serves all phases — set ` +
|
|
227
|
+
`llmModel/llmBaseUrl/llmProvider (+ llmApiKey) to your intended model. ` +
|
|
228
|
+
`distillFallback: removed (strict mode is default — a failed distill retries next run). ` +
|
|
229
|
+
`Remove these keys to clear this warning. See the 0.16.8 changelog.`);
|
|
230
|
+
}
|
|
231
|
+
// #405: keys removed by the budget simplification — one line naming each
|
|
232
|
+
// present key and its replacement (or "removed").
|
|
233
|
+
const removed = Object.entries(REMOVED_CONFIG_KEYS)
|
|
234
|
+
.filter(([k]) => savedConfig[k] !== undefined);
|
|
235
|
+
if (removed.length > 0) {
|
|
236
|
+
const detail = removed.map(([k, v]) => `${k} (${v})`).join(", ");
|
|
237
|
+
console.warn(`[hicortex] config has keys REMOVED by the budget simplification (#405): ${detail}. ` +
|
|
238
|
+
`They have no effect. Remove them to clear this warning.`);
|
|
239
|
+
}
|
|
240
|
+
// #408: tuning keys that became release-managed calibration constants —
|
|
241
|
+
// one line naming every present key (their VALUES are ignored; the
|
|
242
|
+
// constants ship with each release and change only with published eval
|
|
243
|
+
// evidence linked in the changelog).
|
|
244
|
+
const releaseManaged = RELEASE_MANAGED_CONFIG_KEYS
|
|
245
|
+
.filter((k) => savedConfig[k] !== undefined);
|
|
246
|
+
if (releaseManaged.length > 0) {
|
|
247
|
+
console.warn(`[hicortex] config has keys that are now RELEASE-MANAGED CALIBRATION ` +
|
|
248
|
+
`(${releaseManaged.join(", ")}): their values are ignored — calibration ` +
|
|
249
|
+
`ships with each release and changes only with published eval evidence. ` +
|
|
250
|
+
`Remove them to clear this warning.`);
|
|
251
|
+
}
|
|
252
|
+
// #408: the diagnostic-tier keys that moved to environment variables —
|
|
253
|
+
// one line naming each old key AND its env replacement.
|
|
254
|
+
const envMoved = Object.entries(ENV_MOVED_CONFIG_KEYS)
|
|
255
|
+
.filter(([k]) => savedConfig[k] !== undefined);
|
|
256
|
+
if (envMoved.length > 0) {
|
|
257
|
+
const detail = envMoved.map(([k, env]) => `${k} → set ${env} instead`).join(", ");
|
|
258
|
+
console.warn(`[hicortex] config has keys that MOVED to environment variables (#408): ${detail}. ` +
|
|
259
|
+
`The config keys are ignored. Remove them to clear this warning.`);
|
|
260
|
+
}
|
|
174
261
|
}
|