@gamaze/hicortex 0.5.0 → 0.5.2
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/dist/consolidate.d.ts +1 -1
- package/dist/consolidate.js +11 -2
- package/dist/nightly.js +52 -1
- package/dist/state.d.ts +2 -0
- package/dist/telemetry.d.ts +48 -0
- package/dist/telemetry.js +77 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/consolidate.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export declare function parseJsonLenient<T>(text: string, fallback: T): T;
|
|
|
24
24
|
/**
|
|
25
25
|
* Run the full consolidation pipeline. Returns a structured report.
|
|
26
26
|
*/
|
|
27
|
-
export declare function runConsolidation(db: Database.Database, llm: LlmClient, embedFn: EmbedFn, dryRun?: boolean): Promise<ConsolidationReport>;
|
|
27
|
+
export declare function runConsolidation(db: Database.Database, llm: LlmClient, embedFn: EmbedFn, dryRun?: boolean, skipReflection?: boolean): Promise<ConsolidationReport>;
|
|
28
28
|
/**
|
|
29
29
|
* Calculate milliseconds until the next occurrence of a given hour (local time).
|
|
30
30
|
*/
|
package/dist/consolidate.js
CHANGED
|
@@ -388,7 +388,7 @@ function stageDecayPrune(db, dryRun) {
|
|
|
388
388
|
/**
|
|
389
389
|
* Run the full consolidation pipeline. Returns a structured report.
|
|
390
390
|
*/
|
|
391
|
-
async function runConsolidation(db, llm, embedFn, dryRun = false) {
|
|
391
|
+
async function runConsolidation(db, llm, embedFn, dryRun = false, skipReflection = false) {
|
|
392
392
|
const start = new Date();
|
|
393
393
|
const report = {
|
|
394
394
|
started_at: start.toISOString(),
|
|
@@ -424,7 +424,16 @@ async function runConsolidation(db, llm, embedFn, dryRun = false) {
|
|
|
424
424
|
// Stage 2: Importance Scoring
|
|
425
425
|
report.stages.importance = await stageImportance(db, scoreMemories, llm, budget, dryRun);
|
|
426
426
|
// Stage 2.5: Reflection
|
|
427
|
-
|
|
427
|
+
if (skipReflection) {
|
|
428
|
+
report.stages.reflection = {
|
|
429
|
+
lessons_generated: 0,
|
|
430
|
+
skipped: true,
|
|
431
|
+
reason: "reflect_endpoint_offline",
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
report.stages.reflection = await stageReflection(db, precheck.newMemories, llm, budget, embedFn, dryRun);
|
|
436
|
+
}
|
|
428
437
|
// Stage 3: Link Discovery
|
|
429
438
|
report.stages.links = await stageLinks(db, precheck.newMemories, embedFn, dryRun);
|
|
430
439
|
// Stage 4: Decay & Prune
|
package/dist/nightly.js
CHANGED
|
@@ -47,6 +47,11 @@ exports.runNightly = runNightly;
|
|
|
47
47
|
const node_fs_1 = require("node:fs");
|
|
48
48
|
const node_path_1 = require("node:path");
|
|
49
49
|
const node_os_1 = require("node:os");
|
|
50
|
+
let VERSION = "0.0.0";
|
|
51
|
+
try {
|
|
52
|
+
VERSION = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "..", "package.json"), "utf-8")).version;
|
|
53
|
+
}
|
|
54
|
+
catch { }
|
|
50
55
|
const db_js_1 = require("./db.js");
|
|
51
56
|
const llm_js_1 = require("./llm.js");
|
|
52
57
|
const embedder_js_1 = require("./embedder.js");
|
|
@@ -59,6 +64,7 @@ const claude_md_js_1 = require("./claude-md.js");
|
|
|
59
64
|
const features_js_1 = require("./features.js");
|
|
60
65
|
const extensions_js_1 = require("./extensions.js");
|
|
61
66
|
const state_js_1 = require("./state.js");
|
|
67
|
+
const telemetry_js_1 = require("./telemetry.js");
|
|
62
68
|
const HICORTEX_HOME = (0, node_path_1.join)((0, node_os_1.homedir)(), ".hicortex");
|
|
63
69
|
function readNightlyConfig(stateDir) {
|
|
64
70
|
try {
|
|
@@ -270,8 +276,24 @@ async function runNightly(options = {}) {
|
|
|
270
276
|
console.log(`[hicortex] Distillation complete: ${memoriesIngested} new memories`);
|
|
271
277
|
// Step 3: Consolidation
|
|
272
278
|
if (!dryRun) {
|
|
279
|
+
// Pre-flight health check for the reflect endpoint.
|
|
280
|
+
// If reflectBaseUrl points to a remote Ollama and it's down (MBP offline),
|
|
281
|
+
// skip reflection entirely instead of waiting through 3 retries (~3.5 min).
|
|
282
|
+
// Scoring + linking + decay still run (they use the local model or don't need LLM).
|
|
283
|
+
let skipReflection = false;
|
|
284
|
+
if (llmConfig.reflectBaseUrl && (llmConfig.reflectProvider ?? llmConfig.provider) === "ollama") {
|
|
285
|
+
const reflectModel = llmConfig.reflectModel ?? llmConfig.model;
|
|
286
|
+
const health = await (0, llm_js_1.probeOllamaModel)(llmConfig.reflectBaseUrl, reflectModel);
|
|
287
|
+
if (!health.ok) {
|
|
288
|
+
const reason = health.reason === "unreachable"
|
|
289
|
+
? `reflect endpoint unreachable (${llmConfig.reflectBaseUrl})`
|
|
290
|
+
: `reflect model not loaded (${reflectModel} missing on ${llmConfig.reflectBaseUrl})`;
|
|
291
|
+
console.warn(`[hicortex] ${reason} — skipping reflection, scoring + linking will still run`);
|
|
292
|
+
skipReflection = true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
273
295
|
console.log(`[hicortex] Running consolidation...`);
|
|
274
|
-
const report = await (0, consolidate_js_1.runConsolidation)(db, llm, embedder_js_1.embed, dryRun);
|
|
296
|
+
const report = await (0, consolidate_js_1.runConsolidation)(db, llm, embedder_js_1.embed, dryRun, skipReflection);
|
|
275
297
|
console.log(`[hicortex] Consolidation ${report.status} in ${report.elapsed_seconds}s` +
|
|
276
298
|
(report.stages.reflection ? ` (${report.stages.reflection.lessons_generated} lessons)` : ""));
|
|
277
299
|
}
|
|
@@ -299,6 +321,22 @@ async function runNightly(options = {}) {
|
|
|
299
321
|
}
|
|
300
322
|
}
|
|
301
323
|
console.log(`[hicortex] Nightly pipeline complete.`);
|
|
324
|
+
// Step 6: Anonymous telemetry (fire-and-forget, opt-out via config)
|
|
325
|
+
if (!dryRun && (0, telemetry_js_1.isTelemetryEnabled)(savedConfig)) {
|
|
326
|
+
const agentType = piBatches.length > 0 && ccBatches.length > 0 ? "mixed"
|
|
327
|
+
: piBatches.length > 0 ? "pi"
|
|
328
|
+
: "cc";
|
|
329
|
+
await (0, telemetry_js_1.sendTelemetry)({
|
|
330
|
+
id: (0, telemetry_js_1.getTelemetryId)(stateDir),
|
|
331
|
+
v: VERSION,
|
|
332
|
+
mode: "server",
|
|
333
|
+
agent: agentType,
|
|
334
|
+
mem: storage.countMemories(db),
|
|
335
|
+
lessons: storage.getLessons(db, 365).length,
|
|
336
|
+
sessions: batches.length,
|
|
337
|
+
ok: !hadTransientFailure,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
302
340
|
}
|
|
303
341
|
finally {
|
|
304
342
|
db.close();
|
|
@@ -492,6 +530,19 @@ async function runClientNightly(config, dryRun) {
|
|
|
492
530
|
}
|
|
493
531
|
}
|
|
494
532
|
console.log(`[hicortex] Client nightly complete: ${memoriesIngested} memories from ${sessionsSent} sessions → ${serverUrl}`);
|
|
533
|
+
// Anonymous telemetry (fire-and-forget, opt-out via config)
|
|
534
|
+
if (!dryRun && (0, telemetry_js_1.isTelemetryEnabled)(config)) {
|
|
535
|
+
await (0, telemetry_js_1.sendTelemetry)({
|
|
536
|
+
id: (0, telemetry_js_1.getTelemetryId)(HICORTEX_HOME),
|
|
537
|
+
v: VERSION,
|
|
538
|
+
mode: "client",
|
|
539
|
+
agent: "cc", // client mode is always CC-originated currently
|
|
540
|
+
mem: memoriesIngested,
|
|
541
|
+
lessons: 0, // client doesn't know lesson count
|
|
542
|
+
sessions: batches.length,
|
|
543
|
+
ok: !hadTransientFailure,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
495
546
|
}
|
|
496
547
|
/**
|
|
497
548
|
* Fetch lessons + memory index from server and inject into CLAUDE.md.
|
package/dist/state.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export interface HicortexState {
|
|
|
33
33
|
lastConsolidated?: string;
|
|
34
34
|
/** Last-known license tier (replaces tier.json + license-validated.txt). */
|
|
35
35
|
tier?: PersistedTier;
|
|
36
|
+
/** Anonymous telemetry UUID — generated once, never linked to personal info. */
|
|
37
|
+
telemetryId?: string;
|
|
36
38
|
}
|
|
37
39
|
/**
|
|
38
40
|
* Load the state file. Returns an empty state if the file is missing
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anonymous telemetry — sends aggregate stats after each nightly run.
|
|
3
|
+
*
|
|
4
|
+
* What's sent (8 fields, all aggregate):
|
|
5
|
+
* id — random UUID, generated once on first run, stored in state.json
|
|
6
|
+
* v — package version
|
|
7
|
+
* mode — server or client
|
|
8
|
+
* agent — cc, pi, oc, or mixed (detected from session sources)
|
|
9
|
+
* mem — total memory count
|
|
10
|
+
* lessons — total lesson count
|
|
11
|
+
* sessions — sessions distilled this run
|
|
12
|
+
* ok — nightly succeeded (true/false)
|
|
13
|
+
*
|
|
14
|
+
* What's NOT sent:
|
|
15
|
+
* No personal data, no session content, no file paths, no IPs stored.
|
|
16
|
+
*
|
|
17
|
+
* Opt-out:
|
|
18
|
+
* Set "telemetry": false in ~/.hicortex/config.json
|
|
19
|
+
* OR set HICORTEX_TELEMETRY=off in the environment
|
|
20
|
+
*
|
|
21
|
+
* The ping is fire-and-forget with a 5s timeout. If it fails, nothing
|
|
22
|
+
* happens — the nightly result is unaffected.
|
|
23
|
+
*/
|
|
24
|
+
export interface TelemetryPayload {
|
|
25
|
+
id: string;
|
|
26
|
+
v: string;
|
|
27
|
+
mode: string;
|
|
28
|
+
agent: string;
|
|
29
|
+
mem: number;
|
|
30
|
+
lessons: number;
|
|
31
|
+
sessions: number;
|
|
32
|
+
ok: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if telemetry is enabled. Disabled by:
|
|
36
|
+
* - config.telemetry === false
|
|
37
|
+
* - HICORTEX_TELEMETRY env var set to "off", "false", or "0"
|
|
38
|
+
*/
|
|
39
|
+
export declare function isTelemetryEnabled(config: Record<string, unknown> | null): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Get or create the anonymous telemetry ID.
|
|
42
|
+
* Generated once, stored in state.json, never linked to any personal info.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getTelemetryId(stateDir: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Send anonymous telemetry. Fire-and-forget — failures are silently ignored.
|
|
47
|
+
*/
|
|
48
|
+
export declare function sendTelemetry(payload: TelemetryPayload, serverUrl?: string): Promise<void>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Anonymous telemetry — sends aggregate stats after each nightly run.
|
|
4
|
+
*
|
|
5
|
+
* What's sent (8 fields, all aggregate):
|
|
6
|
+
* id — random UUID, generated once on first run, stored in state.json
|
|
7
|
+
* v — package version
|
|
8
|
+
* mode — server or client
|
|
9
|
+
* agent — cc, pi, oc, or mixed (detected from session sources)
|
|
10
|
+
* mem — total memory count
|
|
11
|
+
* lessons — total lesson count
|
|
12
|
+
* sessions — sessions distilled this run
|
|
13
|
+
* ok — nightly succeeded (true/false)
|
|
14
|
+
*
|
|
15
|
+
* What's NOT sent:
|
|
16
|
+
* No personal data, no session content, no file paths, no IPs stored.
|
|
17
|
+
*
|
|
18
|
+
* Opt-out:
|
|
19
|
+
* Set "telemetry": false in ~/.hicortex/config.json
|
|
20
|
+
* OR set HICORTEX_TELEMETRY=off in the environment
|
|
21
|
+
*
|
|
22
|
+
* The ping is fire-and-forget with a 5s timeout. If it fails, nothing
|
|
23
|
+
* happens — the nightly result is unaffected.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.isTelemetryEnabled = isTelemetryEnabled;
|
|
27
|
+
exports.getTelemetryId = getTelemetryId;
|
|
28
|
+
exports.sendTelemetry = sendTelemetry;
|
|
29
|
+
const node_crypto_1 = require("node:crypto");
|
|
30
|
+
const state_js_1 = require("./state.js");
|
|
31
|
+
const TELEMETRY_URL = "https://hicortex.gamaze.com/api/telemetry";
|
|
32
|
+
/**
|
|
33
|
+
* Check if telemetry is enabled. Disabled by:
|
|
34
|
+
* - config.telemetry === false
|
|
35
|
+
* - HICORTEX_TELEMETRY env var set to "off", "false", or "0"
|
|
36
|
+
*/
|
|
37
|
+
function isTelemetryEnabled(config) {
|
|
38
|
+
// Config override
|
|
39
|
+
if (config?.telemetry === false)
|
|
40
|
+
return false;
|
|
41
|
+
// Env var override
|
|
42
|
+
const env = process.env.HICORTEX_TELEMETRY?.toLowerCase();
|
|
43
|
+
if (env === "off" || env === "false" || env === "0")
|
|
44
|
+
return false;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get or create the anonymous telemetry ID.
|
|
49
|
+
* Generated once, stored in state.json, never linked to any personal info.
|
|
50
|
+
*/
|
|
51
|
+
function getTelemetryId(stateDir) {
|
|
52
|
+
const state = (0, state_js_1.loadState)(stateDir);
|
|
53
|
+
if (state.telemetryId)
|
|
54
|
+
return state.telemetryId;
|
|
55
|
+
const id = (0, node_crypto_1.randomUUID)();
|
|
56
|
+
(0, state_js_1.updateState)((s) => {
|
|
57
|
+
s.telemetryId = id;
|
|
58
|
+
return s;
|
|
59
|
+
}, stateDir);
|
|
60
|
+
return id;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Send anonymous telemetry. Fire-and-forget — failures are silently ignored.
|
|
64
|
+
*/
|
|
65
|
+
async function sendTelemetry(payload, serverUrl = TELEMETRY_URL) {
|
|
66
|
+
try {
|
|
67
|
+
await fetch(serverUrl, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: { "Content-Type": "application/json" },
|
|
70
|
+
body: JSON.stringify(payload),
|
|
71
|
+
signal: AbortSignal.timeout(5_000),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Silently ignore — telemetry must never affect the nightly result
|
|
76
|
+
}
|
|
77
|
+
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "hicortex",
|
|
3
3
|
"name": "Hicortex — Long-term Memory That Learns",
|
|
4
4
|
"description": "Your agents remember past decisions, avoid repeated mistakes, and get smarter every day. Nightly reflection generates actionable lessons that automatically update agent behavior.",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.2",
|
|
6
6
|
"kind": "lifecycle",
|
|
7
7
|
"skills": ["./skills/hicortex-memory", "./skills/hicortex-learn", "./skills/hicortex-activate"],
|
|
8
8
|
"configSchema": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Human-like memory for self-improving AI agents. Automatic capturing, nightly reflection, and cross-agent learning. Works with Claude Code and OpenClaw.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|