@gamaze/hicortex 0.4.3 → 0.4.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/dist/claude-md.d.ts +2 -2
- package/dist/claude-md.js +8 -6
- package/dist/consolidate.js +9 -22
- package/dist/db.d.ts +2 -0
- package/dist/db.js +88 -9
- package/dist/extensions.d.ts +126 -0
- package/dist/extensions.js +154 -0
- package/dist/features.d.ts +37 -0
- package/dist/features.js +127 -0
- package/dist/index.js +20 -35
- package/dist/license.d.ts +13 -3
- package/dist/license.js +30 -41
- package/dist/mcp-server.js +16 -18
- package/dist/nightly.js +28 -32
- package/dist/state.d.ts +64 -0
- package/dist/state.js +162 -0
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -42,7 +42,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
43
|
const node_path_1 = require("node:path");
|
|
44
44
|
const db_js_1 = require("./db.js");
|
|
45
|
-
const
|
|
45
|
+
const features_js_1 = require("./features.js");
|
|
46
|
+
const extensions_js_1 = require("./extensions.js");
|
|
47
|
+
const state_js_1 = require("./state.js");
|
|
46
48
|
const llm_js_1 = require("./llm.js");
|
|
47
49
|
const node_fs_1 = require("node:fs");
|
|
48
50
|
const node_os_1 = require("node:os");
|
|
@@ -87,8 +89,10 @@ exports.default = {
|
|
|
87
89
|
llm = new llm_js_1.LlmClient(llmConfig);
|
|
88
90
|
log(`[hicortex] LLM: ${llmConfig.provider}/${llmConfig.model} ` +
|
|
89
91
|
`(reflect: ${llmConfig.reflectModel})`);
|
|
90
|
-
//
|
|
91
|
-
(0,
|
|
92
|
+
// One-time migration of legacy state files (no-op if state.json exists)
|
|
93
|
+
(0, state_js_1.migrateLegacyState)(stateDir);
|
|
94
|
+
// License: init feature cache (sync after this returns)
|
|
95
|
+
await (0, features_js_1.initFeatures)(config.licenseKey, stateDir);
|
|
92
96
|
// Schedule nightly consolidation
|
|
93
97
|
const consolidateHour = config.consolidateHour ?? 2;
|
|
94
98
|
cancelConsolidation = (0, consolidate_js_1.scheduleConsolidation)(db, llm, embedder_js_1.embed, consolidateHour);
|
|
@@ -123,9 +127,13 @@ exports.default = {
|
|
|
123
127
|
const lessons = storage.getLessons(db, 7, ctx.project);
|
|
124
128
|
if (lessons.length === 0)
|
|
125
129
|
return {};
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
|
|
130
|
+
const maxLessons = (0, features_js_1.lessonsLimit)();
|
|
131
|
+
const selected = await (0, extensions_js_1.getLessonSelector)().select(lessons, {
|
|
132
|
+
maxLessons,
|
|
133
|
+
project: ctx.project,
|
|
134
|
+
agentId: ctx.agentId,
|
|
135
|
+
});
|
|
136
|
+
const formatted = selected.map((l) => {
|
|
129
137
|
// Extract just the lesson text from the structured content
|
|
130
138
|
const match = l.content.match(/## Lesson: (.+)/);
|
|
131
139
|
return match ? `- ${match[1]}` : `- ${l.content.slice(0, 200)}`;
|
|
@@ -136,9 +144,9 @@ exports.default = {
|
|
|
136
144
|
"\n";
|
|
137
145
|
// Daily friendly reminder when at memory cap
|
|
138
146
|
const memCount = storage.countMemories(db);
|
|
139
|
-
if (
|
|
147
|
+
if ((0, features_js_1.memoryCapReached)(memCount)) {
|
|
140
148
|
context +=
|
|
141
|
-
`\n---\nHicortex free tier: ${
|
|
149
|
+
`\n---\nHicortex free tier: ${(0, features_js_1.maxMemoriesAllowed)()} memories stored. ` +
|
|
142
150
|
`New memories can no longer be saved, and your agent has stopped learning and self-improving from new sessions. ` +
|
|
143
151
|
`Existing memories, lessons, and search still work. ` +
|
|
144
152
|
`Upgrade for unlimited usage: https://hicortex.gamaze.com/ ` +
|
|
@@ -169,10 +177,8 @@ exports.default = {
|
|
|
169
177
|
if (entries.length === 0)
|
|
170
178
|
return;
|
|
171
179
|
// Check license cap
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
storage.countMemories(db) >= features.maxMemories) {
|
|
175
|
-
console.warn(`[hicortex] Free tier limit reached (${features.maxMemories} memories). ` +
|
|
180
|
+
if ((0, features_js_1.memoryCapReached)(storage.countMemories(db))) {
|
|
181
|
+
console.warn(`[hicortex] Free tier limit reached (${(0, features_js_1.maxMemoriesAllowed)()} memories). ` +
|
|
176
182
|
`Search and lessons still work, but new memories won't be saved. ` +
|
|
177
183
|
`Upgrade for unlimited usage: https://hicortex.gamaze.com/`);
|
|
178
184
|
return;
|
|
@@ -299,12 +305,11 @@ exports.default = {
|
|
|
299
305
|
async execute(_callId, args, context) {
|
|
300
306
|
if (!db)
|
|
301
307
|
return { error: "Hicortex not initialized" };
|
|
302
|
-
|
|
303
|
-
if (features.maxMemories > 0 && storage.countMemories(db) >= features.maxMemories) {
|
|
308
|
+
if ((0, features_js_1.memoryCapReached)(storage.countMemories(db))) {
|
|
304
309
|
return {
|
|
305
310
|
content: [{
|
|
306
311
|
type: "text",
|
|
307
|
-
text: `Free tier limit reached (${
|
|
312
|
+
text: `Free tier limit reached (${(0, features_js_1.maxMemoriesAllowed)()} memories). ` +
|
|
308
313
|
`Your existing memories and lessons still work — search and recall are unaffected. ` +
|
|
309
314
|
`New memories won't be saved until you upgrade.\n\n` +
|
|
310
315
|
`Upgrade for unlimited usage: https://hicortex.gamaze.com/`
|
|
@@ -529,26 +534,6 @@ function persistProviderConfig(llmConfig, log) {
|
|
|
529
534
|
// Non-fatal — config works in memory even if we can't persist
|
|
530
535
|
}
|
|
531
536
|
}
|
|
532
|
-
/**
|
|
533
|
-
* Track when the memory cap was first hit. Returns days since cap was reached.
|
|
534
|
-
* Stores timestamp in stateDir/cap-hit.txt on first detection.
|
|
535
|
-
*/
|
|
536
|
-
function getDaysSinceCapHit(dir) {
|
|
537
|
-
const capFile = (0, node_path_1.join)(dir, "cap-hit.txt");
|
|
538
|
-
try {
|
|
539
|
-
const ts = (0, node_fs_1.readFileSync)(capFile, "utf-8").trim();
|
|
540
|
-
const hitDate = new Date(ts);
|
|
541
|
-
return Math.floor((Date.now() - hitDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
542
|
-
}
|
|
543
|
-
catch {
|
|
544
|
-
// First time hitting cap — record it
|
|
545
|
-
try {
|
|
546
|
-
(0, node_fs_1.writeFileSync)(capFile, new Date().toISOString());
|
|
547
|
-
}
|
|
548
|
-
catch { /* non-fatal */ }
|
|
549
|
-
return 0;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
537
|
const HICORTEX_TOOLS = [
|
|
553
538
|
"hicortex_search",
|
|
554
539
|
"hicortex_context",
|
package/dist/license.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* License API client.
|
|
3
|
+
*
|
|
4
|
+
* This module is a thin wrapper over the validation HTTP endpoint at
|
|
5
|
+
* https://hicortex.gamaze.com/api/validate. Persistence and feature gating
|
|
6
|
+
* live in features.ts + state.ts; this file does NOT touch disk.
|
|
7
|
+
*
|
|
8
|
+
* Offline grace: when the API is unreachable, we fall back to the cached
|
|
9
|
+
* tier in state.json (written by features.ts on the last successful
|
|
10
|
+
* validation). If the cached tier was validated within OFFLINE_GRACE_DAYS,
|
|
11
|
+
* we treat it as still valid.
|
|
12
|
+
*/
|
|
1
13
|
import type { LicenseInfo } from "./types.js";
|
|
2
|
-
/** Validate a license key against the Hicortex API */
|
|
14
|
+
/** Validate a license key against the Hicortex API. */
|
|
3
15
|
export declare function validateLicense(key: string | undefined, stateDir: string): Promise<LicenseInfo>;
|
|
4
|
-
/** Get current features, using cache or free tier defaults */
|
|
5
|
-
export declare function getFeatures(stateDir: string): LicenseInfo["features"];
|
package/dist/license.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* License API client.
|
|
4
|
+
*
|
|
5
|
+
* This module is a thin wrapper over the validation HTTP endpoint at
|
|
6
|
+
* https://hicortex.gamaze.com/api/validate. Persistence and feature gating
|
|
7
|
+
* live in features.ts + state.ts; this file does NOT touch disk.
|
|
8
|
+
*
|
|
9
|
+
* Offline grace: when the API is unreachable, we fall back to the cached
|
|
10
|
+
* tier in state.json (written by features.ts on the last successful
|
|
11
|
+
* validation). If the cached tier was validated within OFFLINE_GRACE_DAYS,
|
|
12
|
+
* we treat it as still valid.
|
|
13
|
+
*/
|
|
2
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
15
|
exports.validateLicense = validateLicense;
|
|
4
|
-
|
|
5
|
-
const node_fs_1 = require("node:fs");
|
|
6
|
-
const node_path_1 = require("node:path");
|
|
16
|
+
const state_js_1 = require("./state.js");
|
|
7
17
|
const VALIDATE_URL = "https://hicortex.gamaze.com/api/validate";
|
|
8
18
|
const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
9
19
|
const OFFLINE_GRACE_DAYS = 7;
|
|
10
|
-
// In-memory cache
|
|
20
|
+
// In-memory cache for the current process
|
|
11
21
|
let cachedLicense = null;
|
|
12
22
|
let cacheTimestamp = 0;
|
|
13
23
|
const FREE_LICENSE = {
|
|
@@ -21,12 +31,12 @@ const FREE_LICENSE = {
|
|
|
21
31
|
remoteIngest: true,
|
|
22
32
|
},
|
|
23
33
|
};
|
|
24
|
-
/** Validate a license key against the Hicortex API */
|
|
34
|
+
/** Validate a license key against the Hicortex API. */
|
|
25
35
|
async function validateLicense(key, stateDir) {
|
|
26
36
|
// No key = free tier
|
|
27
37
|
if (!key)
|
|
28
38
|
return FREE_LICENSE;
|
|
29
|
-
// Check in-memory cache
|
|
39
|
+
// Check in-memory cache (24h TTL)
|
|
30
40
|
if (cachedLicense && Date.now() - cacheTimestamp < CACHE_TTL_MS) {
|
|
31
41
|
return cachedLicense;
|
|
32
42
|
}
|
|
@@ -41,57 +51,36 @@ async function validateLicense(key, stateDir) {
|
|
|
41
51
|
throw new Error(`HTTP ${resp.status}`);
|
|
42
52
|
}
|
|
43
53
|
const data = (await resp.json());
|
|
44
|
-
// Cache result
|
|
45
54
|
cachedLicense = data;
|
|
46
55
|
cacheTimestamp = Date.now();
|
|
47
|
-
// Persist last successful validation timestamp for offline grace
|
|
48
|
-
if (data.valid) {
|
|
49
|
-
persistValidationTimestamp(stateDir);
|
|
50
|
-
}
|
|
51
56
|
return data;
|
|
52
57
|
}
|
|
53
58
|
catch {
|
|
54
|
-
// Network failure — check offline grace period
|
|
55
|
-
return offlineFallback(
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
/** Get current features, using cache or free tier defaults */
|
|
59
|
-
function getFeatures(stateDir) {
|
|
60
|
-
if (cachedLicense)
|
|
61
|
-
return cachedLicense.features;
|
|
62
|
-
return FREE_LICENSE.features;
|
|
63
|
-
}
|
|
64
|
-
function persistValidationTimestamp(stateDir) {
|
|
65
|
-
try {
|
|
66
|
-
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(stateDir, "license-validated.txt"), new Date().toISOString());
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
// Non-critical
|
|
59
|
+
// Network failure — check offline grace period via state.tier
|
|
60
|
+
return offlineFallback(stateDir);
|
|
70
61
|
}
|
|
71
62
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Offline fallback: if state.tier was validated within the grace period,
|
|
65
|
+
* return its cached features as if validation succeeded. Otherwise, free tier.
|
|
66
|
+
*/
|
|
67
|
+
function offlineFallback(stateDir) {
|
|
68
|
+
const persisted = (0, state_js_1.loadState)(stateDir).tier;
|
|
69
|
+
if (!persisted)
|
|
75
70
|
return FREE_LICENSE;
|
|
76
71
|
try {
|
|
77
|
-
const lastValidated = new Date(
|
|
72
|
+
const lastValidated = new Date(persisted.validatedAt);
|
|
78
73
|
const daysSince = (Date.now() - lastValidated.getTime()) / (1000 * 60 * 60 * 24);
|
|
79
74
|
if (daysSince <= OFFLINE_GRACE_DAYS) {
|
|
80
|
-
|
|
81
|
-
return cachedLicense ?? {
|
|
75
|
+
return {
|
|
82
76
|
valid: true,
|
|
83
|
-
tier:
|
|
84
|
-
features:
|
|
85
|
-
reflection: true,
|
|
86
|
-
vectorSearch: true,
|
|
87
|
-
maxMemories: -1,
|
|
88
|
-
crossAgent: true,
|
|
89
|
-
},
|
|
77
|
+
tier: persisted.tier,
|
|
78
|
+
features: persisted.features,
|
|
90
79
|
};
|
|
91
80
|
}
|
|
92
81
|
}
|
|
93
82
|
catch {
|
|
94
|
-
// Corrupted
|
|
83
|
+
// Corrupted timestamp — fall through
|
|
95
84
|
}
|
|
96
85
|
return FREE_LICENSE;
|
|
97
86
|
}
|
package/dist/mcp-server.js
CHANGED
|
@@ -55,7 +55,8 @@ const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js");
|
|
|
55
55
|
const zod_1 = require("zod");
|
|
56
56
|
const db_js_1 = require("./db.js");
|
|
57
57
|
const llm_js_1 = require("./llm.js");
|
|
58
|
-
const
|
|
58
|
+
const features_js_1 = require("./features.js");
|
|
59
|
+
const state_js_1 = require("./state.js");
|
|
59
60
|
const embedder_js_1 = require("./embedder.js");
|
|
60
61
|
const storage = __importStar(require("./storage.js"));
|
|
61
62
|
const retrieval = __importStar(require("./retrieval.js"));
|
|
@@ -121,12 +122,11 @@ function createMcpServer() {
|
|
|
121
122
|
}, async ({ content, project, memory_type }) => {
|
|
122
123
|
if (!db)
|
|
123
124
|
return { content: [{ type: "text", text: "Hicortex not initialized" }], isError: true };
|
|
124
|
-
|
|
125
|
-
if (features.maxMemories > 0 && storage.countMemories(db) >= features.maxMemories) {
|
|
125
|
+
if ((0, features_js_1.memoryCapReached)(storage.countMemories(db))) {
|
|
126
126
|
return {
|
|
127
127
|
content: [{
|
|
128
128
|
type: "text",
|
|
129
|
-
text: `Free tier limit reached (${
|
|
129
|
+
text: `Free tier limit reached (${(0, features_js_1.maxMemoriesAllowed)()} memories). ` +
|
|
130
130
|
`Your existing memories and lessons still work — search and recall are unaffected. ` +
|
|
131
131
|
`New memories won't be saved until you upgrade.\n\n` +
|
|
132
132
|
`Upgrade for unlimited usage: https://hicortex.gamaze.com/`
|
|
@@ -296,11 +296,13 @@ async function startServer(options = {}) {
|
|
|
296
296
|
? `${llmConfig.reflectProvider}/${llmConfig.reflectModel}@${llmConfig.reflectBaseUrl}`
|
|
297
297
|
: llmConfig.reflectModel;
|
|
298
298
|
console.log(`[hicortex] LLM fast: ${llmConfig.provider}/${llmConfig.model}${distillInfo ? `, distill: ${distillInfo}` : ""}, reflect: ${reflectInfo}`);
|
|
299
|
-
//
|
|
299
|
+
// One-time migration of legacy state files (no-op if state.json exists)
|
|
300
|
+
(0, state_js_1.migrateLegacyState)(stateDir);
|
|
301
|
+
// License: read from options, config file, or env var, init feature cache
|
|
300
302
|
const licenseKey = options.licenseKey
|
|
301
303
|
?? savedConfig?.licenseKey
|
|
302
304
|
?? process.env.HICORTEX_LICENSE_KEY;
|
|
303
|
-
(0,
|
|
305
|
+
await (0, features_js_1.initFeatures)(licenseKey, stateDir);
|
|
304
306
|
if (licenseKey) {
|
|
305
307
|
console.log(`[hicortex] License key configured`);
|
|
306
308
|
}
|
|
@@ -411,15 +413,12 @@ async function startServer(options = {}) {
|
|
|
411
413
|
// Pro license blocks remote ingest (upgrade to Team for multi-client)
|
|
412
414
|
const ip = req.ip ?? req.socket.remoteAddress ?? "";
|
|
413
415
|
const isLocal = ip === "127.0.0.1" || ip === "::1" || ip === "::ffff:127.0.0.1";
|
|
414
|
-
if (!isLocal) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
});
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
416
|
+
if (!isLocal && !(0, features_js_1.remoteIngestAllowed)()) {
|
|
417
|
+
res.status(403).json({
|
|
418
|
+
error: "Pro license is single-machine. Upgrade to Team for multi-client remote ingestion.",
|
|
419
|
+
upgrade: "https://hicortex.gamaze.com/",
|
|
420
|
+
});
|
|
421
|
+
return;
|
|
423
422
|
}
|
|
424
423
|
const { content, source_agent, project, memory_type, privacy, source_session, session_date } = req.body ?? {};
|
|
425
424
|
if (!content || typeof content !== "string") {
|
|
@@ -440,9 +439,8 @@ async function startServer(options = {}) {
|
|
|
440
439
|
}
|
|
441
440
|
}
|
|
442
441
|
// License check
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
res.status(429).json({ error: "Memory limit reached", limit: features.maxMemories });
|
|
442
|
+
if ((0, features_js_1.memoryCapReached)(storage.countMemories(db))) {
|
|
443
|
+
res.status(429).json({ error: "Memory limit reached", limit: (0, features_js_1.maxMemoriesAllowed)() });
|
|
446
444
|
return;
|
|
447
445
|
}
|
|
448
446
|
try {
|
package/dist/nightly.js
CHANGED
|
@@ -55,9 +55,10 @@ const distiller_js_1 = require("./distiller.js");
|
|
|
55
55
|
const consolidate_js_1 = require("./consolidate.js");
|
|
56
56
|
const transcript_reader_js_1 = require("./transcript-reader.js");
|
|
57
57
|
const claude_md_js_1 = require("./claude-md.js");
|
|
58
|
-
const
|
|
58
|
+
const features_js_1 = require("./features.js");
|
|
59
|
+
const extensions_js_1 = require("./extensions.js");
|
|
60
|
+
const state_js_1 = require("./state.js");
|
|
59
61
|
const HICORTEX_HOME = (0, node_path_1.join)((0, node_os_1.homedir)(), ".hicortex");
|
|
60
|
-
const LAST_RUN_PATH = (0, node_path_1.join)(HICORTEX_HOME, "nightly-last-run.txt");
|
|
61
62
|
function readNightlyConfig(stateDir) {
|
|
62
63
|
try {
|
|
63
64
|
const configPath = (0, node_path_1.join)(stateDir, "config.json");
|
|
@@ -78,25 +79,24 @@ function readConfigLicenseKey(stateDir) {
|
|
|
78
79
|
return undefined;
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
|
-
function readLastRun() {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
// No file — first run
|
|
90
|
-
}
|
|
91
|
-
return new Date(0); // Process everything
|
|
82
|
+
function readLastRun(stateDir = HICORTEX_HOME) {
|
|
83
|
+
const ts = (0, state_js_1.loadState)(stateDir).lastNightly;
|
|
84
|
+
if (!ts)
|
|
85
|
+
return new Date(0); // First run — process everything
|
|
86
|
+
const d = new Date(ts);
|
|
87
|
+
return isNaN(d.getTime()) ? new Date(0) : d;
|
|
92
88
|
}
|
|
93
|
-
function writeLastRun() {
|
|
94
|
-
(0,
|
|
95
|
-
|
|
89
|
+
function writeLastRun(stateDir = HICORTEX_HOME) {
|
|
90
|
+
(0, state_js_1.updateState)((s) => {
|
|
91
|
+
s.lastNightly = new Date().toISOString();
|
|
92
|
+
return s;
|
|
93
|
+
}, stateDir);
|
|
96
94
|
}
|
|
97
95
|
async function runNightly(options = {}) {
|
|
98
96
|
const dryRun = options.dryRun ?? false;
|
|
99
97
|
const stateDir = options.stateDir ?? HICORTEX_HOME;
|
|
98
|
+
// One-time migration of legacy state files (no-op if state.json exists)
|
|
99
|
+
(0, state_js_1.migrateLegacyState)(stateDir);
|
|
100
100
|
// Check mode: client or server
|
|
101
101
|
const savedConfig = readNightlyConfig(stateDir);
|
|
102
102
|
if (savedConfig?.mode === "client") {
|
|
@@ -109,9 +109,9 @@ async function runNightly(options = {}) {
|
|
|
109
109
|
// Init DB
|
|
110
110
|
const db = (0, db_js_1.initDb)(dbPath);
|
|
111
111
|
try {
|
|
112
|
-
// License: read from config file or env var
|
|
112
|
+
// License: read from config file or env var, init feature cache
|
|
113
113
|
const licenseKey = readConfigLicenseKey(stateDir) ?? process.env.HICORTEX_LICENSE_KEY;
|
|
114
|
-
await (0,
|
|
114
|
+
await (0, features_js_1.initFeatures)(licenseKey, stateDir);
|
|
115
115
|
// Init LLM: check config.json first, then auto-detect
|
|
116
116
|
let llmConfig;
|
|
117
117
|
const savedConfig = readNightlyConfig(stateDir);
|
|
@@ -182,7 +182,6 @@ async function runNightly(options = {}) {
|
|
|
182
182
|
}
|
|
183
183
|
// Step 2: Distill each session
|
|
184
184
|
let memoriesIngested = 0;
|
|
185
|
-
const features = (0, license_js_1.getFeatures)(stateDir);
|
|
186
185
|
// Detect safe chunk size based on model context window
|
|
187
186
|
const chunkSize = await (0, distiller_js_1.detectChunkSize)(llmConfig.provider, llmConfig.distillModel ?? llmConfig.model, llmConfig.baseUrl);
|
|
188
187
|
for (const batch of batches) {
|
|
@@ -197,8 +196,8 @@ async function runNightly(options = {}) {
|
|
|
197
196
|
continue;
|
|
198
197
|
}
|
|
199
198
|
// Check cap before distilling
|
|
200
|
-
if (
|
|
201
|
-
console.log(`[hicortex] Free tier limit (${
|
|
199
|
+
if ((0, features_js_1.memoryCapReached)(storage.countMemories(db))) {
|
|
200
|
+
console.log(`[hicortex] Free tier limit (${(0, features_js_1.maxMemoriesAllowed)()} memories). Skipping new ingestion. ` +
|
|
202
201
|
`Upgrade: https://hicortex.gamaze.com/`);
|
|
203
202
|
break;
|
|
204
203
|
}
|
|
@@ -238,7 +237,7 @@ async function runNightly(options = {}) {
|
|
|
238
237
|
}
|
|
239
238
|
// Step 4: Inject lessons into CLAUDE.md
|
|
240
239
|
if (!dryRun) {
|
|
241
|
-
const injection = (0, claude_md_js_1.injectLessons)(db, { stateDir });
|
|
240
|
+
const injection = await (0, claude_md_js_1.injectLessons)(db, { stateDir });
|
|
242
241
|
console.log(`[hicortex] CLAUDE.md updated: ${injection.lessonsCount} lessons at ${injection.path}`);
|
|
243
242
|
}
|
|
244
243
|
// Step 5: Update last-run timestamp
|
|
@@ -427,8 +426,8 @@ async function injectLessonsFromServer(serverUrl, authToken) {
|
|
|
427
426
|
return;
|
|
428
427
|
}
|
|
429
428
|
const data = await resp.json();
|
|
430
|
-
const maxLessons =
|
|
431
|
-
const selected = data.lessons
|
|
429
|
+
const maxLessons = (0, features_js_1.lessonsLimit)();
|
|
430
|
+
const selected = await (0, extensions_js_1.getLessonSelector)().select(data.lessons, { maxLessons });
|
|
432
431
|
// Format lessons
|
|
433
432
|
const lessonLines = selected.map((l) => {
|
|
434
433
|
const titleMatch = l.content.match(/## Lesson: (.+)/);
|
|
@@ -462,14 +461,11 @@ async function injectLessonsFromServer(serverUrl, authToken) {
|
|
|
462
461
|
}
|
|
463
462
|
blockParts.push(END_MARKER);
|
|
464
463
|
const block = blockParts.join("\n");
|
|
465
|
-
// Write to CLAUDE.md
|
|
466
|
-
const
|
|
467
|
-
const { join, dirname } = await import("node:path");
|
|
468
|
-
const { homedir } = await import("node:os");
|
|
469
|
-
const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
|
|
464
|
+
// Write to CLAUDE.md (uses fs/path/os already imported at top of file)
|
|
465
|
+
const claudeMdPath = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude", "CLAUDE.md");
|
|
470
466
|
let content = "";
|
|
471
467
|
try {
|
|
472
|
-
content = readFileSync(claudeMdPath, "utf-8");
|
|
468
|
+
content = (0, node_fs_1.readFileSync)(claudeMdPath, "utf-8");
|
|
473
469
|
}
|
|
474
470
|
catch { }
|
|
475
471
|
const startIdx = content.indexOf(START_MARKER);
|
|
@@ -484,7 +480,7 @@ async function injectLessonsFromServer(serverUrl, authToken) {
|
|
|
484
480
|
content += "\n";
|
|
485
481
|
content += block + "\n";
|
|
486
482
|
}
|
|
487
|
-
mkdirSync(dirname(claudeMdPath), { recursive: true });
|
|
488
|
-
writeFileSync(claudeMdPath, content);
|
|
483
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(claudeMdPath), { recursive: true });
|
|
484
|
+
(0, node_fs_1.writeFileSync)(claudeMdPath, content);
|
|
489
485
|
console.log(`[hicortex] CLAUDE.md updated: ${lessonLines.length} lessons, ${data.index.total} memories indexed`);
|
|
490
486
|
}
|
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized state management — single ~/.hicortex/state.json file.
|
|
3
|
+
*
|
|
4
|
+
* Replaces 4 separate state files used by previous versions:
|
|
5
|
+
* - nightly-last-run.txt → state.lastNightly
|
|
6
|
+
* - last-consolidated.txt → state.lastConsolidated
|
|
7
|
+
* - tier.json → state.tier
|
|
8
|
+
* - license-validated.txt → state.tier.validatedAt (subsumed)
|
|
9
|
+
*
|
|
10
|
+
* Why one file:
|
|
11
|
+
* - Atomic writes (write to temp + rename)
|
|
12
|
+
* - Easier debugging (one file to inspect)
|
|
13
|
+
* - No filesystem chatter from multiple separate writes
|
|
14
|
+
* - Single migration path going forward
|
|
15
|
+
*
|
|
16
|
+
* Note: ~/.hicortex/config.json is intentionally NOT merged here. Config is
|
|
17
|
+
* user-edited and tracked separately from machine state.
|
|
18
|
+
*/
|
|
19
|
+
import type { LicenseInfo } from "./types.js";
|
|
20
|
+
/** Persisted tier information — reflects the last successful validation. */
|
|
21
|
+
export interface PersistedTier {
|
|
22
|
+
/** Tier name from the validation API response. */
|
|
23
|
+
tier: LicenseInfo["tier"];
|
|
24
|
+
/** ISO timestamp of when this tier was validated against the API. */
|
|
25
|
+
validatedAt: string;
|
|
26
|
+
/** Cached features object — used by features.ts and offline fallback. */
|
|
27
|
+
features: LicenseInfo["features"];
|
|
28
|
+
}
|
|
29
|
+
export interface HicortexState {
|
|
30
|
+
/** ISO timestamp of the last nightly transcript scan watermark. */
|
|
31
|
+
lastNightly?: string;
|
|
32
|
+
/** ISO timestamp of the last consolidation pipeline run. */
|
|
33
|
+
lastConsolidated?: string;
|
|
34
|
+
/** Last-known license tier (replaces tier.json + license-validated.txt). */
|
|
35
|
+
tier?: PersistedTier;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load the state file. Returns an empty state if the file is missing
|
|
39
|
+
* or corrupted (callers should handle missing fields with defaults).
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadState(stateDir?: string): HicortexState;
|
|
42
|
+
/**
|
|
43
|
+
* Atomically write the state file. Uses write-to-temp + rename so a crash
|
|
44
|
+
* during write cannot leave a half-written state.json on disk.
|
|
45
|
+
*/
|
|
46
|
+
export declare function saveState(state: HicortexState, stateDir?: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Read-modify-write helper. The updater receives the current state and
|
|
49
|
+
* returns the next state (or void if it mutates in place).
|
|
50
|
+
*/
|
|
51
|
+
export declare function updateState(updater: (state: HicortexState) => HicortexState | void, stateDir?: string): HicortexState;
|
|
52
|
+
/**
|
|
53
|
+
* One-time migration from the 4 legacy state files to state.json.
|
|
54
|
+
*
|
|
55
|
+
* Behaviour:
|
|
56
|
+
* 1. If state.json already exists, do nothing (and clean up any leftover
|
|
57
|
+
* legacy files from a previously interrupted migration).
|
|
58
|
+
* 2. Otherwise, read whichever legacy files exist, build a HicortexState,
|
|
59
|
+
* write state.json, and delete the legacy files.
|
|
60
|
+
*
|
|
61
|
+
* Idempotent: safe to call on every boot.
|
|
62
|
+
* Returns true if migration ran, false if state.json already existed.
|
|
63
|
+
*/
|
|
64
|
+
export declare function migrateLegacyState(stateDir?: string): boolean;
|