@gamaze/hicortex 0.7.0 → 0.10.0
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 +57 -39
- package/dist/claude-md.d.ts +9 -21
- package/dist/claude-md.js +9 -241
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +29 -11
- package/dist/consolidate.js +76 -26
- package/dist/db.js +24 -0
- package/dist/embedder.d.ts +11 -0
- package/dist/embedder.js +27 -0
- package/dist/extensions.d.ts +41 -88
- package/dist/extensions.js +36 -61
- package/dist/features.d.ts +21 -25
- package/dist/features.js +47 -83
- package/dist/graph.d.ts +1 -1
- package/dist/graph.js +13 -7
- package/dist/hermes-transcript-reader.d.ts +27 -0
- package/dist/hermes-transcript-reader.js +134 -0
- package/dist/index.d.ts +16 -4
- package/dist/index.js +252 -344
- package/dist/init.d.ts +41 -1
- package/dist/init.js +545 -190
- package/dist/lesson-selection.d.ts +62 -0
- package/dist/lesson-selection.js +159 -0
- package/dist/lessons-context.d.ts +17 -0
- package/dist/lessons-context.js +96 -0
- package/dist/llm.d.ts +42 -29
- package/dist/llm.js +89 -270
- package/dist/mcp-server.d.ts +0 -1
- package/dist/mcp-server.js +407 -88
- package/dist/nightly.d.ts +9 -6
- package/dist/nightly.js +197 -357
- package/dist/oc-transcript-reader.d.ts +20 -0
- package/dist/oc-transcript-reader.js +61 -0
- package/dist/pi-transcript-reader.d.ts +1 -0
- package/dist/prompts.d.ts +5 -0
- package/dist/prompts.js +29 -0
- package/dist/status.js +22 -2
- package/dist/storage.d.ts +7 -1
- package/dist/storage.js +28 -7
- package/dist/transcript-reader.d.ts +19 -0
- package/dist/transcript-reader.js +17 -3
- package/dist/types.d.ts +16 -0
- package/dist/types.js +7 -0
- package/dist/uninstall.js +31 -1
- package/hermes-plugin/hicortex/README.md +77 -0
- package/hermes-plugin/hicortex/__init__.py +17 -0
- package/hermes-plugin/hicortex/client.py +162 -0
- package/hermes-plugin/hicortex/config.py +105 -0
- package/hermes-plugin/hicortex/plugin.yaml +12 -0
- package/hermes-plugin/hicortex/provider.py +432 -0
- package/openclaw.plugin.json +17 -44
- package/package.json +7 -5
- package/dist/pro-loader.d.ts +0 -33
- package/dist/pro-loader.js +0 -187
package/dist/consolidate.js
CHANGED
|
@@ -43,6 +43,7 @@ exports.parseJsonLenient = parseJsonLenient;
|
|
|
43
43
|
exports.runConsolidation = runConsolidation;
|
|
44
44
|
exports.msUntilHour = msUntilHour;
|
|
45
45
|
exports.scheduleConsolidation = scheduleConsolidation;
|
|
46
|
+
const types_js_1 = require("./types.js");
|
|
46
47
|
const retrieval_js_1 = require("./retrieval.js");
|
|
47
48
|
const storage = __importStar(require("./storage.js"));
|
|
48
49
|
const prompts_js_1 = require("./prompts.js");
|
|
@@ -267,13 +268,6 @@ async function stageReflection(db, memories, llm, budget, embedFn, dryRun) {
|
|
|
267
268
|
minor: 0.6,
|
|
268
269
|
};
|
|
269
270
|
try {
|
|
270
|
-
// Check memory cap before storing lesson
|
|
271
|
-
if ((0, features_js_1.memoryCapReached)(storage.countMemories(db))) {
|
|
272
|
-
console.log(`[hicortex] Free tier limit (${(0, features_js_1.maxMemoriesAllowed)()} memories). ` +
|
|
273
|
-
`Existing memories and lessons still work. New lessons won't be saved. ` +
|
|
274
|
-
`Upgrade for unlimited usage: https://hicortex.gamaze.com/`);
|
|
275
|
-
break;
|
|
276
|
-
}
|
|
277
271
|
const embedding = await embedFn(content);
|
|
278
272
|
// Contradiction check: find semantically similar existing lessons.
|
|
279
273
|
// If a very similar lesson exists, ask the LLM whether the new one
|
|
@@ -487,12 +481,17 @@ async function stageDomainCuration(db, llm, budget, dryRun, stateDir) {
|
|
|
487
481
|
console.log(`[hicortex] Domain curation: ${domains.length} domains from ${projectRows.length} projects`);
|
|
488
482
|
return { curated: true, domains: domains.length };
|
|
489
483
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
484
|
+
/** Batch size for LLM edge classification calls. */
|
|
485
|
+
const EDGE_CLASSIFICATION_BATCH_SIZE = 8;
|
|
486
|
+
/** Valid relationship type set for fast lookup. */
|
|
487
|
+
const VALID_REL_SET = new Set(types_js_1.VALID_RELATIONSHIP_TYPES);
|
|
488
|
+
async function stageLinks(db, memories, embedFn, dryRun, llm, budget) {
|
|
494
489
|
let autoLinked = 0;
|
|
490
|
+
let llmClassified = 0;
|
|
491
|
+
let heuristicFallback = 0;
|
|
495
492
|
let failed = 0;
|
|
493
|
+
// Phase A: Discovery — collect candidates via vector similarity
|
|
494
|
+
const candidates = [];
|
|
496
495
|
for (const mem of memories) {
|
|
497
496
|
try {
|
|
498
497
|
const embedding = await embedFn(mem.content);
|
|
@@ -500,27 +499,78 @@ async function stageLinks(db, memories, embedFn, dryRun) {
|
|
|
500
499
|
for (const neighbor of neighbors) {
|
|
501
500
|
const similarity = 1.0 - neighbor.distance;
|
|
502
501
|
if (similarity > CONSOLIDATE_LINK_THRESHOLD) {
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
502
|
+
const heuristicType = classifyRelationship(mem, neighbor, similarity);
|
|
503
|
+
candidates.push({ source: mem, target: neighbor, similarity, heuristicType });
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
508
|
+
failed++;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (candidates.length === 0) {
|
|
512
|
+
return { auto_linked: 0, llm_classified: 0, heuristic_fallback: 0, failed };
|
|
513
|
+
}
|
|
514
|
+
// Phase B: LLM batch classification
|
|
515
|
+
// Build batches and classify with LLM where budget allows
|
|
516
|
+
const classifiedTypes = new Array(candidates.length);
|
|
517
|
+
for (let i = 0; i < candidates.length; i += EDGE_CLASSIFICATION_BATCH_SIZE) {
|
|
518
|
+
const batch = candidates.slice(i, i + EDGE_CLASSIFICATION_BATCH_SIZE);
|
|
519
|
+
// Attempt LLM classification if budget allows
|
|
520
|
+
if (budget.use("edge_classification")) {
|
|
521
|
+
try {
|
|
522
|
+
const pairsBlock = batch.map((c, idx) => {
|
|
523
|
+
const srcContent = c.source.content.slice(0, 200);
|
|
524
|
+
const tgtContent = c.target.content.slice(0, 200);
|
|
525
|
+
return `[${idx}] SOURCE: ${c.source.memory_type} | ${c.source.project ?? "global"} | ${srcContent}\n TARGET: ${c.target.memory_type} | ${c.target.project ?? "global"} | ${tgtContent}\n similarity: ${c.similarity.toFixed(2)}`;
|
|
526
|
+
}).join("\n\n");
|
|
527
|
+
const prompt = (0, prompts_js_1.edgeClassification)(pairsBlock);
|
|
528
|
+
const raw = await llm.completeFast(prompt, 512);
|
|
529
|
+
const parsed = parseJsonLenient(raw, []);
|
|
530
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
531
|
+
for (let j = 0; j < batch.length; j++) {
|
|
532
|
+
const llmType = parsed[j];
|
|
533
|
+
if (typeof llmType === "string" && VALID_REL_SET.has(llmType)) {
|
|
534
|
+
classifiedTypes[i + j] = llmType;
|
|
535
|
+
llmClassified++;
|
|
508
536
|
}
|
|
509
|
-
|
|
510
|
-
|
|
537
|
+
else {
|
|
538
|
+
// Invalid type from LLM — fall back to heuristic
|
|
539
|
+
classifiedTypes[i + j] = batch[j].heuristicType;
|
|
540
|
+
heuristicFallback++;
|
|
511
541
|
}
|
|
512
542
|
}
|
|
513
|
-
|
|
514
|
-
autoLinked++;
|
|
515
|
-
}
|
|
543
|
+
continue;
|
|
516
544
|
}
|
|
517
545
|
}
|
|
546
|
+
catch {
|
|
547
|
+
// LLM call failed — fall through to heuristic for this batch
|
|
548
|
+
}
|
|
518
549
|
}
|
|
519
|
-
|
|
520
|
-
|
|
550
|
+
// Budget exhausted or LLM failed — use heuristic for entire batch
|
|
551
|
+
for (let j = 0; j < batch.length; j++) {
|
|
552
|
+
classifiedTypes[i + j] = batch[j].heuristicType;
|
|
553
|
+
heuristicFallback++;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
// Phase C: Store all classified links
|
|
557
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
558
|
+
const c = candidates[i];
|
|
559
|
+
const relationship = classifiedTypes[i];
|
|
560
|
+
if (!dryRun) {
|
|
561
|
+
try {
|
|
562
|
+
storage.addLink(db, c.source.id, c.target.id, relationship, c.similarity);
|
|
563
|
+
autoLinked++;
|
|
564
|
+
}
|
|
565
|
+
catch {
|
|
566
|
+
failed++;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
autoLinked++;
|
|
521
571
|
}
|
|
522
572
|
}
|
|
523
|
-
return { auto_linked: autoLinked, failed };
|
|
573
|
+
return { auto_linked: autoLinked, llm_classified: llmClassified, heuristic_fallback: heuristicFallback, failed };
|
|
524
574
|
}
|
|
525
575
|
/**
|
|
526
576
|
* Classify the relationship between two memories based on type, temporal ordering, and similarity.
|
|
@@ -660,8 +710,8 @@ async function runConsolidation(db, llm, embedFn, dryRun = false, skipReflection
|
|
|
660
710
|
}
|
|
661
711
|
// Stage 2.7: Domain Curation
|
|
662
712
|
report.stages.domain_curation = await stageDomainCuration(db, llm, budget, dryRun, stateDir);
|
|
663
|
-
// Stage 3: Link Discovery
|
|
664
|
-
report.stages.links = await stageLinks(db, precheck.newMemories, embedFn, dryRun);
|
|
713
|
+
// Stage 3: Link Discovery (with LLM-assisted edge classification)
|
|
714
|
+
report.stages.links = await stageLinks(db, precheck.newMemories, embedFn, dryRun, llm, budget);
|
|
665
715
|
// Stage 3.5: Hub Detection — boost highly-connected memories
|
|
666
716
|
report.stages.hub_boost = stageHubBoost(db, dryRun);
|
|
667
717
|
// Stage 4: Decay & Prune
|
package/dist/db.js
CHANGED
|
@@ -226,6 +226,30 @@ const MIGRATIONS = [
|
|
|
226
226
|
db.exec("CREATE INDEX IF NOT EXISTS idx_memories_domain ON memories(domain)");
|
|
227
227
|
},
|
|
228
228
|
},
|
|
229
|
+
{
|
|
230
|
+
version: 4,
|
|
231
|
+
name: "unique_source_session",
|
|
232
|
+
up: (db) => {
|
|
233
|
+
// De-duplicate any pre-existing source_session values (e.g. from the
|
|
234
|
+
// /ingest + /distill race before this migration): keep the oldest row per
|
|
235
|
+
// source_session, NULL the rest so they lose their dedup key (the memory
|
|
236
|
+
// itself is preserved). Then add a UNIQUE partial index so the server can
|
|
237
|
+
// idempotently re-distill a segment without double-inserting.
|
|
238
|
+
db.exec(`
|
|
239
|
+
UPDATE memories SET source_session = NULL
|
|
240
|
+
WHERE rowid NOT IN (
|
|
241
|
+
SELECT MIN(rowid) FROM memories
|
|
242
|
+
WHERE source_session IS NOT NULL
|
|
243
|
+
GROUP BY source_session
|
|
244
|
+
)
|
|
245
|
+
AND source_session IS NOT NULL
|
|
246
|
+
`);
|
|
247
|
+
db.exec(`
|
|
248
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_memories_source_session_unique
|
|
249
|
+
ON memories(source_session) WHERE source_session IS NOT NULL
|
|
250
|
+
`);
|
|
251
|
+
},
|
|
252
|
+
},
|
|
229
253
|
];
|
|
230
254
|
/**
|
|
231
255
|
* Run all pending migrations against the database.
|
package/dist/embedder.d.ts
CHANGED
|
@@ -6,6 +6,17 @@
|
|
|
6
6
|
* installed. The model is lazy-loaded on first call.
|
|
7
7
|
*/
|
|
8
8
|
export declare const EMBEDDING_DIMENSIONS = 384;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the directory where @huggingface/transformers caches model weights.
|
|
11
|
+
*
|
|
12
|
+
* Exported as a pure function so it can be unit-tested without mocking the
|
|
13
|
+
* dynamic import. The default (`~/.hicortex/models`) is stable across package
|
|
14
|
+
* upgrades and writable by the installing user even under global npm installs
|
|
15
|
+
* (where the package dir is root-owned).
|
|
16
|
+
*
|
|
17
|
+
* @param home Override for the user home directory (used in tests).
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveModelCacheDir(home?: string): string;
|
|
9
20
|
/**
|
|
10
21
|
* Embed a single text string. Returns a Float32Array of 384 dimensions.
|
|
11
22
|
*/
|
package/dist/embedder.js
CHANGED
|
@@ -8,14 +8,31 @@
|
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.EMBEDDING_DIMENSIONS = void 0;
|
|
11
|
+
exports.resolveModelCacheDir = resolveModelCacheDir;
|
|
11
12
|
exports.embed = embed;
|
|
12
13
|
exports.embedBatch = embedBatch;
|
|
13
14
|
exports.dimensions = dimensions;
|
|
15
|
+
const node_fs_1 = require("node:fs");
|
|
16
|
+
const node_path_1 = require("node:path");
|
|
17
|
+
const node_os_1 = require("node:os");
|
|
14
18
|
exports.EMBEDDING_DIMENSIONS = 384;
|
|
15
19
|
const MODEL_NAME = "Xenova/bge-small-en-v1.5";
|
|
16
20
|
// Pipeline is lazy-loaded on first use
|
|
17
21
|
let pipeline = null;
|
|
18
22
|
let initPromise = null;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the directory where @huggingface/transformers caches model weights.
|
|
25
|
+
*
|
|
26
|
+
* Exported as a pure function so it can be unit-tested without mocking the
|
|
27
|
+
* dynamic import. The default (`~/.hicortex/models`) is stable across package
|
|
28
|
+
* upgrades and writable by the installing user even under global npm installs
|
|
29
|
+
* (where the package dir is root-owned).
|
|
30
|
+
*
|
|
31
|
+
* @param home Override for the user home directory (used in tests).
|
|
32
|
+
*/
|
|
33
|
+
function resolveModelCacheDir(home) {
|
|
34
|
+
return (0, node_path_1.join)(home ?? (0, node_os_1.homedir)(), ".hicortex", "models");
|
|
35
|
+
}
|
|
19
36
|
/**
|
|
20
37
|
* Initialize the embedding pipeline (called lazily on first embed call).
|
|
21
38
|
* Throws with a clear error if @huggingface/transformers is not available.
|
|
@@ -32,6 +49,16 @@ async function ensureInit() {
|
|
|
32
49
|
// Dynamic import — package may not be installed (it's optional)
|
|
33
50
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
51
|
const transformers = await Function('return import("@huggingface/transformers")')();
|
|
52
|
+
// Point the model cache at a stable user-writable directory so the
|
|
53
|
+
// ~130 MB weights survive package upgrades and work under global installs
|
|
54
|
+
// (the package dir is root-owned; defaulting to it causes EACCES).
|
|
55
|
+
const cacheDir = resolveModelCacheDir();
|
|
56
|
+
(0, node_fs_1.mkdirSync)(cacheDir, { recursive: true });
|
|
57
|
+
const env = transformers.env ?? transformers.default?.env;
|
|
58
|
+
if (env) {
|
|
59
|
+
env.cacheDir = cacheDir;
|
|
60
|
+
}
|
|
61
|
+
console.log(`[hicortex] Model cache: ${cacheDir}`);
|
|
35
62
|
const pipelineFn = transformers.pipeline ?? transformers.default?.pipeline;
|
|
36
63
|
if (!pipelineFn) {
|
|
37
64
|
throw new Error("Could not find pipeline function in @huggingface/transformers");
|
package/dist/extensions.d.ts
CHANGED
|
@@ -1,39 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Extension interfaces
|
|
2
|
+
* Extension interfaces + default implementations.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Originally the seam for the OSS/Pro split (Pro retired in #122 — the
|
|
5
|
+
* product is fully featured for everyone). The interfaces and the
|
|
6
|
+
* setExtensions() override hook remain so alternative implementations can
|
|
7
|
+
* still be swapped in (tests, future experiments).
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* LessonSelector — lesson selection sites:
|
|
10
|
+
* lessons-context.ts:fetchLessonsContext (CC SessionStart hook),
|
|
11
|
+
* index.ts:before_agent_start (OC in-process plugin).
|
|
12
|
+
* The default is the domain-aware scoring selector (lesson-selection.ts):
|
|
13
|
+
* ranks lessons by project match + domain affinity + recency +
|
|
14
|
+
* base_strength + access affinity, with prefix-based dedup. Restored
|
|
15
|
+
* into core in #123 after being deleted with the Pro loader in #122.
|
|
10
16
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* selector ranks lessons against project context, recency, and
|
|
16
|
-
* effectiveness scores instead of dumb-truncating.
|
|
17
|
-
*
|
|
18
|
-
* PromptStrategy — the current prompts.ts exports three pure functions
|
|
19
|
-
* (distillation, reflection, importanceScoring). The Pro variant has
|
|
20
|
-
* prescriptive prompts (`when X do Y` format) and re-trained reflection
|
|
21
|
-
* that produces a richer schema. The strategy bundles the prompt WITH
|
|
22
|
-
* its parser so a Pro prompt with a different output schema cannot
|
|
23
|
-
* silently fail when the OSS consumer parses it with the wrong shape.
|
|
17
|
+
* PromptStrategy — prompts.ts exports three pure functions
|
|
18
|
+
* (distillation, reflection, importanceScoring). The strategy bundles
|
|
19
|
+
* the prompt WITH its parser so a prompt with a different output schema
|
|
20
|
+
* cannot silently fail when the consumer parses it with the wrong shape.
|
|
24
21
|
*
|
|
25
22
|
* NOT in this file (deliberately deferred):
|
|
26
23
|
* - ContextAssembler — over-abstracted; the real seam is buildProjectContext
|
|
27
24
|
* in claude-md.ts, not the whole assembler. Will add when needed.
|
|
28
|
-
* - LessonValidator — speculative; no validation exists today.
|
|
29
|
-
* when the first Pro use case demands it.
|
|
25
|
+
* - LessonValidator — speculative; no validation exists today.
|
|
30
26
|
*/
|
|
31
27
|
import type { ModuleIndex } from "./types.js";
|
|
32
28
|
/**
|
|
33
29
|
* Minimum fields a lesson must have for the selector to work.
|
|
34
30
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
31
|
+
* Only `content` is required. The default (domain-aware) selector uses the
|
|
32
|
+
* optional fields when present — project for in-project/domain weighting,
|
|
33
|
+
* created_at for recency, base_strength for importance, access_count for
|
|
34
|
+
* proven-usefulness — and falls back to neutral scores when absent.
|
|
37
35
|
*
|
|
38
36
|
* Memory satisfies this interface (it's a structural subset), and the
|
|
39
37
|
* client-mode HTTP shape `{content, created_at, base_strength, access_count}`
|
|
@@ -52,20 +50,21 @@ export interface SelectableLesson {
|
|
|
52
50
|
export interface LessonSelectorContext {
|
|
53
51
|
/** Maximum number of lessons to return. Caller decides this from features.lessonsLimit(). */
|
|
54
52
|
maxLessons: number;
|
|
55
|
-
/** Current project, if known.
|
|
53
|
+
/** Current project, if known. The default selector weights in-project lessons higher. */
|
|
56
54
|
project?: string | null;
|
|
57
|
-
/** Optional: agent id, for cross-agent learning context
|
|
55
|
+
/** Optional: agent id, for cross-agent learning context. */
|
|
58
56
|
agentId?: string;
|
|
59
|
-
/** Optional: current task description, for relevance scoring
|
|
57
|
+
/** Optional: current task description, for relevance scoring. */
|
|
60
58
|
currentTask?: string;
|
|
61
|
-
/** MODULE_INDEX for domain-aware lesson selection (
|
|
59
|
+
/** MODULE_INDEX for domain-aware lesson selection (same-domain projects score 0.5). */
|
|
62
60
|
moduleIndex?: ModuleIndex;
|
|
63
61
|
}
|
|
64
62
|
export interface LessonSelector {
|
|
65
63
|
/**
|
|
66
64
|
* Pick `ctx.maxLessons` lessons from the candidate pool.
|
|
67
|
-
* Default impl:
|
|
68
|
-
*
|
|
65
|
+
* Default impl: domain-aware scoring (lesson-selection.ts) — ranks by
|
|
66
|
+
* project match, domain affinity, recency, base_strength, and access
|
|
67
|
+
* affinity, with prefix-based dedup.
|
|
69
68
|
*
|
|
70
69
|
* Generic so the output type matches the input type — Memory[] in returns
|
|
71
70
|
* Memory[] out, partial-shape in returns partial-shape out.
|
|
@@ -73,8 +72,13 @@ export interface LessonSelector {
|
|
|
73
72
|
select<T extends SelectableLesson>(lessons: T[], ctx: LessonSelectorContext): T[] | Promise<T[]>;
|
|
74
73
|
}
|
|
75
74
|
/**
|
|
76
|
-
* Default LessonSelector —
|
|
77
|
-
*
|
|
75
|
+
* Default LessonSelector — the domain-aware scoring selector.
|
|
76
|
+
*
|
|
77
|
+
* Ranks lessons by project match (1.0 exact / 0.5 same-domain via moduleIndex
|
|
78
|
+
* / 0.3 global), recency (30-day half-life), base_strength, and access
|
|
79
|
+
* affinity; dedups near-identical lessons by normalized prefix. When lessons
|
|
80
|
+
* carry no metadata, all scores tie and stable sort preserves input order
|
|
81
|
+
* (equivalent to the old slice(0, N) behaviour).
|
|
78
82
|
*/
|
|
79
83
|
export declare const defaultLessonSelector: LessonSelector;
|
|
80
84
|
/** Output schema produced by the reflection prompt and consumed by consolidate.ts. */
|
|
@@ -108,69 +112,18 @@ export interface PromptStrategy {
|
|
|
108
112
|
}
|
|
109
113
|
export declare const defaultPromptStrategy: PromptStrategy;
|
|
110
114
|
/**
|
|
111
|
-
* Holder for the active extension implementations.
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* Wiring will happen via setExtensions() called from src/pro/ after license
|
|
116
|
-
* validation (not yet implemented in OSS). Until Pro code exists and is loaded,
|
|
117
|
-
* every call site uses the defaults — zero behavioural change for OSS users.
|
|
115
|
+
* Holder for the active extension implementations. Every install uses the
|
|
116
|
+
* defaults; setExtensions() remains as an override hook for tests and future
|
|
117
|
+
* alternative implementations.
|
|
118
118
|
*/
|
|
119
119
|
declare let activeExtensions: {
|
|
120
120
|
selector: LessonSelector;
|
|
121
121
|
prompts: PromptStrategy;
|
|
122
122
|
};
|
|
123
|
-
/** Replace the active extensions (
|
|
123
|
+
/** Replace the active extensions (override hook — tests, experiments). */
|
|
124
124
|
export declare function setExtensions(ext: Partial<typeof activeExtensions>): void;
|
|
125
|
-
/** Get the active LessonSelector
|
|
125
|
+
/** Get the active LessonSelector. */
|
|
126
126
|
export declare function getLessonSelector(): LessonSelector;
|
|
127
|
-
/** Get the active PromptStrategy
|
|
127
|
+
/** Get the active PromptStrategy. */
|
|
128
128
|
export declare function getPromptStrategy(): PromptStrategy;
|
|
129
|
-
/**
|
|
130
|
-
* The object passed to a Pro package's `activate()` function at boot.
|
|
131
|
-
* Pro packages receive this to register their extensions and access OSS
|
|
132
|
-
* runtime APIs they need.
|
|
133
|
-
*
|
|
134
|
-
* Design rationale: Pro code never STATICALLY imports from the OSS client.
|
|
135
|
-
* All runtime access is through this context object. This has two benefits:
|
|
136
|
-
* 1. Pro bundles are self-contained — they don't have `require("../...")`
|
|
137
|
-
* calls that would break when the tarball is installed to
|
|
138
|
-
* ~/.hicortex/pro/ at runtime.
|
|
139
|
-
* 2. Pro code can only access what the OSS client exposes here, so the
|
|
140
|
-
* blast radius of a malicious/buggy Pro release is contained.
|
|
141
|
-
*
|
|
142
|
-
* Type-only imports of `LessonSelector`, `PromptStrategy` etc. in Pro code
|
|
143
|
-
* are fine — they're erased at compile time and produce no runtime imports.
|
|
144
|
-
*/
|
|
145
|
-
export interface ProActivationContext {
|
|
146
|
-
/** Register a lesson selector implementation. */
|
|
147
|
-
setSelector(selector: LessonSelector): void;
|
|
148
|
-
/** Register a prompt strategy implementation. */
|
|
149
|
-
setPrompts(prompts: PromptStrategy): void;
|
|
150
|
-
/** The version of the OSS host (from package.json). Pro can use this
|
|
151
|
-
* to gate features against host compatibility. */
|
|
152
|
-
hostVersion: string;
|
|
153
|
-
/** Log through the OSS logging surface so Pro logs get the [hicortex]
|
|
154
|
-
* prefix and unified formatting. */
|
|
155
|
-
log(message: string): void;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* The shape Pro packages must export as their default export.
|
|
159
|
-
* See `packages/hicortex/src/pro/index.ts` for the reference impl.
|
|
160
|
-
*/
|
|
161
|
-
export interface ProPackage {
|
|
162
|
-
/** Called once at OSS boot if a Pro license is valid and the Pro
|
|
163
|
-
* tarball has been downloaded + extracted. Should register extensions
|
|
164
|
-
* via the context and return. Errors abort Pro activation but do not
|
|
165
|
-
* abort the OSS host. */
|
|
166
|
-
activate(ctx: ProActivationContext): void | Promise<void>;
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Build an activation context for a Pro package. Called from the Pro
|
|
170
|
-
* loader in features.ts / pro-loader.ts.
|
|
171
|
-
*
|
|
172
|
-
* Keep this function small — it's the ONLY surface a Pro package gets.
|
|
173
|
-
* Expanding it expands the attack surface, so add fields deliberately.
|
|
174
|
-
*/
|
|
175
|
-
export declare function createProActivationContext(hostVersion: string): ProActivationContext;
|
|
176
129
|
export {};
|
package/dist/extensions.js
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Extension interfaces
|
|
3
|
+
* Extension interfaces + default implementations.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Originally the seam for the OSS/Pro split (Pro retired in #122 — the
|
|
6
|
+
* product is fully featured for everyone). The interfaces and the
|
|
7
|
+
* setExtensions() override hook remain so alternative implementations can
|
|
8
|
+
* still be swapped in (tests, future experiments).
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* LessonSelector — lesson selection sites:
|
|
11
|
+
* lessons-context.ts:fetchLessonsContext (CC SessionStart hook),
|
|
12
|
+
* index.ts:before_agent_start (OC in-process plugin).
|
|
13
|
+
* The default is the domain-aware scoring selector (lesson-selection.ts):
|
|
14
|
+
* ranks lessons by project match + domain affinity + recency +
|
|
15
|
+
* base_strength + access affinity, with prefix-based dedup. Restored
|
|
16
|
+
* into core in #123 after being deleted with the Pro loader in #122.
|
|
11
17
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* selector ranks lessons against project context, recency, and
|
|
17
|
-
* effectiveness scores instead of dumb-truncating.
|
|
18
|
-
*
|
|
19
|
-
* PromptStrategy — the current prompts.ts exports three pure functions
|
|
20
|
-
* (distillation, reflection, importanceScoring). The Pro variant has
|
|
21
|
-
* prescriptive prompts (`when X do Y` format) and re-trained reflection
|
|
22
|
-
* that produces a richer schema. The strategy bundles the prompt WITH
|
|
23
|
-
* its parser so a Pro prompt with a different output schema cannot
|
|
24
|
-
* silently fail when the OSS consumer parses it with the wrong shape.
|
|
18
|
+
* PromptStrategy — prompts.ts exports three pure functions
|
|
19
|
+
* (distillation, reflection, importanceScoring). The strategy bundles
|
|
20
|
+
* the prompt WITH its parser so a prompt with a different output schema
|
|
21
|
+
* cannot silently fail when the consumer parses it with the wrong shape.
|
|
25
22
|
*
|
|
26
23
|
* NOT in this file (deliberately deferred):
|
|
27
24
|
* - ContextAssembler — over-abstracted; the real seam is buildProjectContext
|
|
28
25
|
* in claude-md.ts, not the whole assembler. Will add when needed.
|
|
29
|
-
* - LessonValidator — speculative; no validation exists today.
|
|
30
|
-
* when the first Pro use case demands it.
|
|
26
|
+
* - LessonValidator — speculative; no validation exists today.
|
|
31
27
|
*/
|
|
32
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
29
|
exports.defaultPromptStrategy = exports.defaultLessonSelector = void 0;
|
|
34
30
|
exports.setExtensions = setExtensions;
|
|
35
31
|
exports.getLessonSelector = getLessonSelector;
|
|
36
32
|
exports.getPromptStrategy = getPromptStrategy;
|
|
37
|
-
|
|
33
|
+
// The domain-aware scoring selector is THE default for every install.
|
|
34
|
+
// (lesson-selection.ts only imports types from this file, so there is no
|
|
35
|
+
// runtime import cycle — `import type` is erased at compile time.)
|
|
36
|
+
const lesson_selection_js_1 = require("./lesson-selection.js");
|
|
38
37
|
/**
|
|
39
|
-
* Default LessonSelector —
|
|
40
|
-
*
|
|
38
|
+
* Default LessonSelector — the domain-aware scoring selector.
|
|
39
|
+
*
|
|
40
|
+
* Ranks lessons by project match (1.0 exact / 0.5 same-domain via moduleIndex
|
|
41
|
+
* / 0.3 global), recency (30-day half-life), base_strength, and access
|
|
42
|
+
* affinity; dedups near-identical lessons by normalized prefix. When lessons
|
|
43
|
+
* carry no metadata, all scores tie and stable sort preserves input order
|
|
44
|
+
* (equivalent to the old slice(0, N) behaviour).
|
|
41
45
|
*/
|
|
42
|
-
exports.defaultLessonSelector =
|
|
43
|
-
select(lessons, ctx) {
|
|
44
|
-
return lessons.slice(0, ctx.maxLessons);
|
|
45
|
-
},
|
|
46
|
-
};
|
|
46
|
+
exports.defaultLessonSelector = lesson_selection_js_1.domainAwareLessonSelector;
|
|
47
47
|
// ---------------------------------------------------------------------------
|
|
48
48
|
// Default PromptStrategy — wraps the current prompts.ts and the lenient JSON
|
|
49
49
|
// parser from consolidate.ts. Preserves current OSS behaviour exactly.
|
|
@@ -126,51 +126,26 @@ exports.defaultPromptStrategy = {
|
|
|
126
126
|
},
|
|
127
127
|
};
|
|
128
128
|
// ---------------------------------------------------------------------------
|
|
129
|
-
// Loader — used by call sites to get
|
|
129
|
+
// Loader — used by call sites to get the active implementations
|
|
130
130
|
// ---------------------------------------------------------------------------
|
|
131
131
|
/**
|
|
132
|
-
* Holder for the active extension implementations.
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* Wiring will happen via setExtensions() called from src/pro/ after license
|
|
137
|
-
* validation (not yet implemented in OSS). Until Pro code exists and is loaded,
|
|
138
|
-
* every call site uses the defaults — zero behavioural change for OSS users.
|
|
132
|
+
* Holder for the active extension implementations. Every install uses the
|
|
133
|
+
* defaults; setExtensions() remains as an override hook for tests and future
|
|
134
|
+
* alternative implementations.
|
|
139
135
|
*/
|
|
140
136
|
let activeExtensions = {
|
|
141
137
|
selector: exports.defaultLessonSelector,
|
|
142
138
|
prompts: exports.defaultPromptStrategy,
|
|
143
139
|
};
|
|
144
|
-
/** Replace the active extensions (
|
|
140
|
+
/** Replace the active extensions (override hook — tests, experiments). */
|
|
145
141
|
function setExtensions(ext) {
|
|
146
142
|
activeExtensions = { ...activeExtensions, ...ext };
|
|
147
143
|
}
|
|
148
|
-
/** Get the active LessonSelector
|
|
144
|
+
/** Get the active LessonSelector. */
|
|
149
145
|
function getLessonSelector() {
|
|
150
146
|
return activeExtensions.selector;
|
|
151
147
|
}
|
|
152
|
-
/** Get the active PromptStrategy
|
|
148
|
+
/** Get the active PromptStrategy. */
|
|
153
149
|
function getPromptStrategy() {
|
|
154
150
|
return activeExtensions.prompts;
|
|
155
151
|
}
|
|
156
|
-
/**
|
|
157
|
-
* Build an activation context for a Pro package. Called from the Pro
|
|
158
|
-
* loader in features.ts / pro-loader.ts.
|
|
159
|
-
*
|
|
160
|
-
* Keep this function small — it's the ONLY surface a Pro package gets.
|
|
161
|
-
* Expanding it expands the attack surface, so add fields deliberately.
|
|
162
|
-
*/
|
|
163
|
-
function createProActivationContext(hostVersion) {
|
|
164
|
-
return {
|
|
165
|
-
setSelector(selector) {
|
|
166
|
-
activeExtensions = { ...activeExtensions, selector };
|
|
167
|
-
},
|
|
168
|
-
setPrompts(prompts) {
|
|
169
|
-
activeExtensions = { ...activeExtensions, prompts };
|
|
170
|
-
},
|
|
171
|
-
hostVersion,
|
|
172
|
-
log(message) {
|
|
173
|
-
console.log(`[hicortex][pro] ${message}`);
|
|
174
|
-
},
|
|
175
|
-
};
|
|
176
|
-
}
|
package/dist/features.d.ts
CHANGED
|
@@ -1,37 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Feature gating — all gates removed as of 0.10.0.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* dodge a circular import).
|
|
4
|
+
* Personal and noncommercial use is fully featured under the
|
|
5
|
+
* PolyForm Noncommercial License. Commercial use requires a per-seat
|
|
6
|
+
* license (see COMMERCIAL.md). There is no technical feature gating;
|
|
7
|
+
* the license key's only remaining role is the "licensed to <org>"
|
|
8
|
+
* display in `hicortex status`.
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* The functions below are kept as trivial wrappers so call sites
|
|
11
|
+
* compile without churn. They will be removed entirely in a future
|
|
12
|
+
* cleanup pass once callers have been audited.
|
|
13
13
|
*/
|
|
14
14
|
import type { LicenseInfo } from "./types.js";
|
|
15
15
|
/**
|
|
16
|
-
* Initialize
|
|
17
|
-
*
|
|
18
|
-
* 1. Synchronously load persisted tier from disk (instant, deterministic)
|
|
19
|
-
* 2. If no persisted tier and we have a key, AWAIT first validation
|
|
20
|
-
* 3. If persisted tier exists, kick off background re-validation
|
|
21
|
-
*
|
|
22
|
-
* After this returns, sync getters (isPro, lessonsLimit, etc.) are deterministic
|
|
23
|
-
* and reflect the user's actual tier — no more "free during validation window".
|
|
16
|
+
* Initialize license display. Call ONCE at process boot.
|
|
17
|
+
* No feature gates are applied regardless of the validation result.
|
|
24
18
|
*/
|
|
25
|
-
export declare function initFeatures(licenseKey: string | undefined, stateDir?: string,
|
|
26
|
-
/**
|
|
19
|
+
export declare function initFeatures(licenseKey: string | undefined, stateDir?: string, _hostVersion?: string): Promise<void>;
|
|
20
|
+
/** Returns the validated license info if a key was supplied and validated. */
|
|
21
|
+
export declare function getValidatedLicense(): LicenseInfo | null;
|
|
22
|
+
/** Always false — no memory cap. */
|
|
27
23
|
export declare function isPro(): boolean;
|
|
28
|
-
/**
|
|
24
|
+
/** Always -1 (unlimited). */
|
|
29
25
|
export declare function maxMemoriesAllowed(): number;
|
|
30
|
-
/**
|
|
31
|
-
export declare function memoryCapReached(
|
|
32
|
-
/**
|
|
26
|
+
/** Always false — no cap is ever reached. */
|
|
27
|
+
export declare function memoryCapReached(_currentCount: number): boolean;
|
|
28
|
+
/** Always 20. */
|
|
33
29
|
export declare function lessonsLimit(): number;
|
|
34
|
-
/**
|
|
30
|
+
/** Always true — remote ingest is always allowed. */
|
|
35
31
|
export declare function remoteIngestAllowed(): boolean;
|
|
36
|
-
/** Direct read of the underlying features
|
|
32
|
+
/** Direct read of the underlying features record. */
|
|
37
33
|
export declare function getCurrentFeatures(): LicenseInfo["features"];
|