@gamaze/hicortex 0.4.3 → 0.4.5
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 +21 -57
- package/dist/init.d.ts +1 -1
- package/dist/init.js +4 -4
- package/dist/license.d.ts +13 -3
- package/dist/license.js +30 -41
- package/dist/llm.d.ts +8 -7
- package/dist/llm.js +33 -48
- 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/openclaw.plugin.json +2 -2
- package/package.json +7 -3
- package/skills/hicortex-learn/SKILL.md +2 -2
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/`
|
|
@@ -461,28 +466,7 @@ async function autoConfigureLlm(pluginConfig, log) {
|
|
|
461
466
|
const msg = err instanceof Error ? err.message : String(err);
|
|
462
467
|
log(`[hicortex] LLM test failed (${llmConfig.baseUrl}): ${msg}`);
|
|
463
468
|
}
|
|
464
|
-
// Step 4:
|
|
465
|
-
if (llmConfig.provider === "zai") {
|
|
466
|
-
const altUrl = llmConfig.baseUrl.includes("/coding/")
|
|
467
|
-
? llmConfig.baseUrl.replace("/coding/", "/")
|
|
468
|
-
: llmConfig.baseUrl.replace("/api/paas/", "/api/coding/paas/");
|
|
469
|
-
log(`[hicortex] Trying alternate z.ai endpoint: ${altUrl}`);
|
|
470
|
-
llmConfig.baseUrl = altUrl;
|
|
471
|
-
const altClient = new llm_js_1.LlmClient(llmConfig);
|
|
472
|
-
try {
|
|
473
|
-
const response = await altClient.completeFast("Respond with just the word OK", 10);
|
|
474
|
-
if (response && response.length > 0) {
|
|
475
|
-
log(`[hicortex] LLM connection verified on alternate endpoint`);
|
|
476
|
-
persistProviderConfig(llmConfig, log);
|
|
477
|
-
return llmConfig;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
catch (err) {
|
|
481
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
482
|
-
log(`[hicortex] Alternate z.ai endpoint also failed: ${msg}`);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
// Step 5: Fall back — return the config anyway, log instructions
|
|
469
|
+
// Step 4: Fall back — return the config anyway, log instructions
|
|
486
470
|
log(`[hicortex] WARNING: Could not verify LLM connection. ` +
|
|
487
471
|
`Distillation and consolidation may fail. ` +
|
|
488
472
|
`To fix: add models.providers.${llmConfig.provider}.baseUrl to ~/.openclaw/openclaw.json ` +
|
|
@@ -529,26 +513,6 @@ function persistProviderConfig(llmConfig, log) {
|
|
|
529
513
|
// Non-fatal — config works in memory even if we can't persist
|
|
530
514
|
}
|
|
531
515
|
}
|
|
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
516
|
const HICORTEX_TOOLS = [
|
|
553
517
|
"hicortex_search",
|
|
554
518
|
"hicortex_context",
|
package/dist/init.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Detection:
|
|
5
5
|
* 1. Local HC server running (localhost:8787)
|
|
6
|
-
* 2. Remote HC server (HICORTEX_SERVER_URL
|
|
6
|
+
* 2. Remote HC server (HICORTEX_SERVER_URL — any reachable host:port)
|
|
7
7
|
* 3. OC plugin installed (~/.openclaw/openclaw.json)
|
|
8
8
|
* 4. CC MCP already registered (~/.claude/settings.json)
|
|
9
9
|
* 5. Existing DB (~/.hicortex/ or ~/.openclaw/data/)
|
package/dist/init.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Detection:
|
|
6
6
|
* 1. Local HC server running (localhost:8787)
|
|
7
|
-
* 2. Remote HC server (HICORTEX_SERVER_URL
|
|
7
|
+
* 2. Remote HC server (HICORTEX_SERVER_URL — any reachable host:port)
|
|
8
8
|
* 3. OC plugin installed (~/.openclaw/openclaw.json)
|
|
9
9
|
* 4. CC MCP already registered (~/.claude/settings.json)
|
|
10
10
|
* 5. Existing DB (~/.hicortex/ or ~/.openclaw/data/)
|
|
@@ -185,10 +185,10 @@ When invoked with \`/learn <text>\`, store the learning in long-term memory via
|
|
|
185
185
|
|
|
186
186
|
## Example
|
|
187
187
|
|
|
188
|
-
\`/learn
|
|
188
|
+
\`/learn always check provider docs before assuming an API uses the same auth scheme as OpenAI\`
|
|
189
189
|
|
|
190
190
|
Becomes a call to hicortex_ingest with:
|
|
191
|
-
- content: "LEARNING:
|
|
191
|
+
- content: "LEARNING: always check provider docs before assuming an API uses the same auth scheme as OpenAI — header names and token formats vary widely (Bearer vs x-api-key vs custom)."
|
|
192
192
|
- memory_type: "lesson"
|
|
193
193
|
`;
|
|
194
194
|
const learnPath = (0, node_path_1.join)(CC_COMMANDS_DIR, "learn.md");
|
|
@@ -402,7 +402,7 @@ async function persistLlmConfig() {
|
|
|
402
402
|
options.push({
|
|
403
403
|
label: "Other provider (requires API key)",
|
|
404
404
|
save: async () => {
|
|
405
|
-
console.log("\n Providers: Anthropic, OpenAI, Google,
|
|
405
|
+
console.log("\n Providers: Anthropic, OpenAI, Google, OpenRouter, or any OpenAI-compatible endpoint");
|
|
406
406
|
const baseUrl = await ask(" Provider base URL: ");
|
|
407
407
|
if (!baseUrl) {
|
|
408
408
|
console.log(" ⚠ Cancelled.");
|
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/llm.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Multi-provider LLM client for consolidation and distillation.
|
|
3
|
-
* Ported from hicortex/consolidate/llm.py.
|
|
4
3
|
*
|
|
5
4
|
* Resolution for OC adapter (resolveLlmConfig):
|
|
6
5
|
* 1. Plugin config (llmBaseUrl, llmApiKey, llmModel)
|
|
@@ -10,12 +9,14 @@
|
|
|
10
9
|
*
|
|
11
10
|
* Resolution for CC adapter (resolveLlmConfigForCC):
|
|
12
11
|
* 1. Explicit env vars (HICORTEX_LLM_BASE_URL + HICORTEX_LLM_API_KEY + HICORTEX_LLM_MODEL)
|
|
13
|
-
* 2. ANTHROPIC_API_KEY → Haiku (cheap, CC users always have this)
|
|
14
|
-
* 3. OPENAI_API_KEY → gpt-
|
|
15
|
-
* 4. GOOGLE_API_KEY → gemini-2.
|
|
16
|
-
* 5.
|
|
12
|
+
* 2. ANTHROPIC_API_KEY → Claude Haiku (cheap, CC users always have this)
|
|
13
|
+
* 3. OPENAI_API_KEY → gpt-5.4-nano
|
|
14
|
+
* 4. GOOGLE_API_KEY → gemini-2.5-flash
|
|
15
|
+
* 5. Claude CLI fallback (uses subscription, no API key needed)
|
|
16
|
+
* 6. Fallback: Ollama at http://localhost:11434
|
|
17
17
|
*
|
|
18
|
-
* Supports
|
|
18
|
+
* Supports any OpenAI-compatible endpoint plus first-class support for
|
|
19
|
+
* OpenAI, Anthropic, Google, Ollama, OpenRouter, and Claude CLI.
|
|
19
20
|
*/
|
|
20
21
|
export interface LlmConfig {
|
|
21
22
|
baseUrl: string;
|
|
@@ -116,7 +117,7 @@ export declare class LlmClient {
|
|
|
116
117
|
*/
|
|
117
118
|
private completeOllama;
|
|
118
119
|
/**
|
|
119
|
-
* Anthropic Messages API (/v1/messages).
|
|
120
|
+
* Anthropic Messages API (/v1/messages).
|
|
120
121
|
* Auth via x-api-key header.
|
|
121
122
|
*/
|
|
122
123
|
private completeAnthropic;
|
package/dist/llm.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Multi-provider LLM client for consolidation and distillation.
|
|
4
|
-
* Ported from hicortex/consolidate/llm.py.
|
|
5
4
|
*
|
|
6
5
|
* Resolution for OC adapter (resolveLlmConfig):
|
|
7
6
|
* 1. Plugin config (llmBaseUrl, llmApiKey, llmModel)
|
|
@@ -11,12 +10,14 @@
|
|
|
11
10
|
*
|
|
12
11
|
* Resolution for CC adapter (resolveLlmConfigForCC):
|
|
13
12
|
* 1. Explicit env vars (HICORTEX_LLM_BASE_URL + HICORTEX_LLM_API_KEY + HICORTEX_LLM_MODEL)
|
|
14
|
-
* 2. ANTHROPIC_API_KEY → Haiku (cheap, CC users always have this)
|
|
15
|
-
* 3. OPENAI_API_KEY → gpt-
|
|
16
|
-
* 4. GOOGLE_API_KEY → gemini-2.
|
|
17
|
-
* 5.
|
|
13
|
+
* 2. ANTHROPIC_API_KEY → Claude Haiku (cheap, CC users always have this)
|
|
14
|
+
* 3. OPENAI_API_KEY → gpt-5.4-nano
|
|
15
|
+
* 4. GOOGLE_API_KEY → gemini-2.5-flash
|
|
16
|
+
* 5. Claude CLI fallback (uses subscription, no API key needed)
|
|
17
|
+
* 6. Fallback: Ollama at http://localhost:11434
|
|
18
18
|
*
|
|
19
|
-
* Supports
|
|
19
|
+
* Supports any OpenAI-compatible endpoint plus first-class support for
|
|
20
|
+
* OpenAI, Anthropic, Google, Ollama, OpenRouter, and Claude CLI.
|
|
20
21
|
*/
|
|
21
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
23
|
exports.LlmClient = exports.RateLimitError = void 0;
|
|
@@ -76,7 +77,7 @@ function resolveLlmConfigForCC(overrides) {
|
|
|
76
77
|
baseUrl: overrides.llmBaseUrl,
|
|
77
78
|
apiKey: overrides.llmApiKey,
|
|
78
79
|
model: overrides.llmModel ?? "claude-haiku-4-5-20251001",
|
|
79
|
-
reflectModel: overrides.reflectModel ?? overrides.llmModel ?? "claude-sonnet-4-
|
|
80
|
+
reflectModel: overrides.reflectModel ?? overrides.llmModel ?? "claude-sonnet-4-6",
|
|
80
81
|
provider,
|
|
81
82
|
};
|
|
82
83
|
}
|
|
@@ -90,7 +91,7 @@ function resolveLlmConfigForCC(overrides) {
|
|
|
90
91
|
baseUrl: hcBaseUrl,
|
|
91
92
|
apiKey: hcApiKey,
|
|
92
93
|
model: hcModel ?? "claude-haiku-4-5-20251001",
|
|
93
|
-
reflectModel: process.env.HICORTEX_REFLECT_MODEL ?? hcModel ?? "claude-sonnet-4-
|
|
94
|
+
reflectModel: process.env.HICORTEX_REFLECT_MODEL ?? hcModel ?? "claude-sonnet-4-6",
|
|
94
95
|
provider,
|
|
95
96
|
};
|
|
96
97
|
}
|
|
@@ -101,7 +102,7 @@ function resolveLlmConfigForCC(overrides) {
|
|
|
101
102
|
baseUrl: process.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com",
|
|
102
103
|
apiKey: anthropicKey,
|
|
103
104
|
model: "claude-haiku-4-5-20251001",
|
|
104
|
-
reflectModel: "claude-sonnet-4-
|
|
105
|
+
reflectModel: "claude-sonnet-4-6",
|
|
105
106
|
provider: "anthropic",
|
|
106
107
|
};
|
|
107
108
|
}
|
|
@@ -111,8 +112,8 @@ function resolveLlmConfigForCC(overrides) {
|
|
|
111
112
|
return {
|
|
112
113
|
baseUrl,
|
|
113
114
|
apiKey: openaiKey,
|
|
114
|
-
model: "gpt-
|
|
115
|
-
reflectModel: "gpt-
|
|
115
|
+
model: "gpt-5.4-nano",
|
|
116
|
+
reflectModel: "gpt-5.4-nano",
|
|
116
117
|
provider: detectProvider(baseUrl),
|
|
117
118
|
};
|
|
118
119
|
}
|
|
@@ -121,8 +122,8 @@ function resolveLlmConfigForCC(overrides) {
|
|
|
121
122
|
return {
|
|
122
123
|
baseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
|
123
124
|
apiKey: googleKey,
|
|
124
|
-
model: "gemini-2.
|
|
125
|
-
reflectModel: "gemini-2.
|
|
125
|
+
model: "gemini-2.5-flash",
|
|
126
|
+
reflectModel: "gemini-2.5-flash",
|
|
126
127
|
provider: "google",
|
|
127
128
|
};
|
|
128
129
|
}
|
|
@@ -150,8 +151,6 @@ function detectProvider(url) {
|
|
|
150
151
|
return "openrouter";
|
|
151
152
|
if (u.includes("googleapis") || u.includes("generativelanguage"))
|
|
152
153
|
return "google";
|
|
153
|
-
if (u.includes("z.ai") || u.includes("zai"))
|
|
154
|
-
return "zai";
|
|
155
154
|
return "openai";
|
|
156
155
|
}
|
|
157
156
|
function readOpenClawConfig() {
|
|
@@ -162,7 +161,7 @@ function readOpenClawConfig() {
|
|
|
162
161
|
const primary = config?.agents?.defaults?.model?.primary;
|
|
163
162
|
if (!primary)
|
|
164
163
|
return null;
|
|
165
|
-
// primary format is "provider/model" (e.g. "
|
|
164
|
+
// primary format is "provider/model" (e.g. "openai/gpt-5", "anthropic/claude-sonnet-4-6")
|
|
166
165
|
if (typeof primary === "string" && (primary.includes("/") || primary.includes(":"))) {
|
|
167
166
|
const sep = primary.includes("/") ? "/" : ":";
|
|
168
167
|
const [providerHint, ...rest] = primary.split(sep);
|
|
@@ -216,7 +215,7 @@ function readOcAuthKey(provider) {
|
|
|
216
215
|
const raw = (0, node_fs_1.readFileSync)(authPath, "utf-8");
|
|
217
216
|
const auth = JSON.parse(raw);
|
|
218
217
|
const profiles = auth?.profiles ?? {};
|
|
219
|
-
// Look for a profile matching the provider (e.g. "
|
|
218
|
+
// Look for a profile matching the provider (e.g. "openai:default")
|
|
220
219
|
for (const [profileId, profile] of Object.entries(profiles)) {
|
|
221
220
|
const p = profile;
|
|
222
221
|
if (p?.provider === provider ||
|
|
@@ -245,8 +244,8 @@ function resolveFromEnv() {
|
|
|
245
244
|
return {
|
|
246
245
|
baseUrl,
|
|
247
246
|
apiKey: openaiKey,
|
|
248
|
-
model: process.env.OPENAI_MODEL ?? "gpt-
|
|
249
|
-
reflectModel: process.env.OPENAI_MODEL ?? "gpt-
|
|
247
|
+
model: process.env.OPENAI_MODEL ?? "gpt-5.4-nano",
|
|
248
|
+
reflectModel: process.env.OPENAI_MODEL ?? "gpt-5.4-nano",
|
|
250
249
|
provider,
|
|
251
250
|
};
|
|
252
251
|
}
|
|
@@ -255,8 +254,8 @@ function resolveFromEnv() {
|
|
|
255
254
|
return {
|
|
256
255
|
baseUrl: process.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com",
|
|
257
256
|
apiKey: anthropicKey,
|
|
258
|
-
model: "claude-sonnet-4-
|
|
259
|
-
reflectModel: "claude-sonnet-4-
|
|
257
|
+
model: "claude-sonnet-4-6",
|
|
258
|
+
reflectModel: "claude-sonnet-4-6",
|
|
260
259
|
provider: "anthropic",
|
|
261
260
|
};
|
|
262
261
|
}
|
|
@@ -265,8 +264,8 @@ function resolveFromEnv() {
|
|
|
265
264
|
return {
|
|
266
265
|
baseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
|
267
266
|
apiKey: googleKey,
|
|
268
|
-
model: "gemini-2.
|
|
269
|
-
reflectModel: "gemini-2.
|
|
267
|
+
model: "gemini-2.5-flash",
|
|
268
|
+
reflectModel: "gemini-2.5-flash",
|
|
270
269
|
provider: "google",
|
|
271
270
|
};
|
|
272
271
|
}
|
|
@@ -280,33 +279,23 @@ function getEnvKeyForProvider(provider) {
|
|
|
280
279
|
return process.env.ANTHROPIC_API_KEY;
|
|
281
280
|
case "google":
|
|
282
281
|
return process.env.GOOGLE_API_KEY;
|
|
283
|
-
case "zai":
|
|
284
|
-
return process.env.ZAI_API_KEY ?? process.env.LLM_API_KEY;
|
|
285
282
|
default:
|
|
286
283
|
return undefined;
|
|
287
284
|
}
|
|
288
285
|
}
|
|
289
|
-
/**
|
|
286
|
+
/**
|
|
287
|
+
* Default base URLs for first-class supported providers.
|
|
288
|
+
*
|
|
289
|
+
* For any other provider, set llmBaseUrl explicitly in your config or use
|
|
290
|
+
* an OpenAI-compatible endpoint. The detectProvider() function will treat
|
|
291
|
+
* unknown URLs as openai-compatible by default.
|
|
292
|
+
*/
|
|
290
293
|
const PROVIDER_BASE_URLS = {
|
|
291
294
|
openai: "https://api.openai.com/v1",
|
|
292
295
|
anthropic: "https://api.anthropic.com",
|
|
293
296
|
google: "https://generativelanguage.googleapis.com/v1beta",
|
|
294
297
|
ollama: "http://localhost:11434",
|
|
295
298
|
openrouter: "https://openrouter.ai/api",
|
|
296
|
-
zai: "https://api.z.ai/api/anthropic",
|
|
297
|
-
groq: "https://api.groq.com/openai/v1",
|
|
298
|
-
deepseek: "https://api.deepseek.com",
|
|
299
|
-
mistral: "https://api.mistral.ai/v1",
|
|
300
|
-
together: "https://api.together.xyz/v1",
|
|
301
|
-
perplexity: "https://api.perplexity.ai",
|
|
302
|
-
nvidia: "https://integrate.api.nvidia.com/v1",
|
|
303
|
-
xai: "https://api.x.ai/v1",
|
|
304
|
-
venice: "https://api.venice.ai/api/v1",
|
|
305
|
-
minimax: "https://api.minimaxi.com/v1",
|
|
306
|
-
moonshot: "https://api.moonshot.ai/v1",
|
|
307
|
-
kimi: "https://api.kimi.com/coding",
|
|
308
|
-
chutes: "https://api.chutes.ai",
|
|
309
|
-
kilo: "https://api.kilo.ai/api/gateway",
|
|
310
299
|
};
|
|
311
300
|
function getDefaultUrlForProvider(provider) {
|
|
312
301
|
return PROVIDER_BASE_URLS[provider.toLowerCase()] ?? "https://api.openai.com/v1";
|
|
@@ -505,7 +494,7 @@ class LlmClient {
|
|
|
505
494
|
if (this.config.provider === "ollama") {
|
|
506
495
|
return this.completeOllama(model, prompt, maxTokens, timeoutMs);
|
|
507
496
|
}
|
|
508
|
-
if (this.config.provider === "anthropic"
|
|
497
|
+
if (this.config.provider === "anthropic") {
|
|
509
498
|
return this.completeAnthropic(model, prompt, maxTokens, timeoutMs);
|
|
510
499
|
}
|
|
511
500
|
return this.completeOpenAiCompat(model, prompt, maxTokens, timeoutMs);
|
|
@@ -586,7 +575,7 @@ class LlmClient {
|
|
|
586
575
|
return result.trim();
|
|
587
576
|
}
|
|
588
577
|
/**
|
|
589
|
-
* Anthropic Messages API (/v1/messages).
|
|
578
|
+
* Anthropic Messages API (/v1/messages).
|
|
590
579
|
* Auth via x-api-key header.
|
|
591
580
|
*/
|
|
592
581
|
async completeAnthropic(model, prompt, maxTokens, timeoutMs) {
|
|
@@ -622,8 +611,8 @@ class LlmClient {
|
|
|
622
611
|
*/
|
|
623
612
|
async completeOpenAiCompat(model, prompt, maxTokens, timeoutMs) {
|
|
624
613
|
const baseUrl = this.config.baseUrl.replace(/\/$/, "");
|
|
625
|
-
// Some providers
|
|
626
|
-
const hasVersion = /\/v\d+\/?$/.test(baseUrl)
|
|
614
|
+
// Some providers include the API version in the base URL already
|
|
615
|
+
const hasVersion = /\/v\d+\/?$/.test(baseUrl);
|
|
627
616
|
const url = hasVersion
|
|
628
617
|
? `${baseUrl}/chat/completions`
|
|
629
618
|
: `${baseUrl}/v1/chat/completions`;
|
|
@@ -647,10 +636,6 @@ class LlmClient {
|
|
|
647
636
|
this.handleRateLimit(resp);
|
|
648
637
|
if (!resp.ok) {
|
|
649
638
|
const text = await resp.text().catch(() => "");
|
|
650
|
-
// z.ai: "Insufficient balance" likely means wrong endpoint (coding vs paas)
|
|
651
|
-
if (text.includes("1113") || text.includes("Insufficient balance")) {
|
|
652
|
-
console.log(`[hicortex] LLM billing error. Check that llmBaseUrl matches your plan. Current: ${baseUrl}`);
|
|
653
|
-
}
|
|
654
639
|
throw new Error(`LLM API error ${resp.status}: ${text}`);
|
|
655
640
|
}
|
|
656
641
|
const data = (await resp.json());
|
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 {
|