@loreai/core 0.15.0 → 0.17.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 +11 -0
- package/dist/bun/agents-file.d.ts +13 -1
- package/dist/bun/agents-file.d.ts.map +1 -1
- package/dist/bun/config.d.ts +20 -1
- package/dist/bun/config.d.ts.map +1 -1
- package/dist/bun/data.d.ts +174 -0
- package/dist/bun/data.d.ts.map +1 -0
- package/dist/bun/db.d.ts +65 -0
- package/dist/bun/db.d.ts.map +1 -1
- package/dist/bun/distillation.d.ts +49 -6
- package/dist/bun/distillation.d.ts.map +1 -1
- package/dist/bun/embedding-vendor.d.ts +66 -0
- package/dist/bun/embedding-vendor.d.ts.map +1 -0
- package/dist/bun/embedding-worker-types.d.ts +66 -0
- package/dist/bun/embedding-worker-types.d.ts.map +1 -0
- package/dist/bun/embedding-worker.d.ts +16 -0
- package/dist/bun/embedding-worker.d.ts.map +1 -0
- package/dist/bun/embedding-worker.js +100 -0
- package/dist/bun/embedding-worker.js.map +7 -0
- package/dist/bun/embedding.d.ts +91 -8
- package/dist/bun/embedding.d.ts.map +1 -1
- package/dist/bun/git.d.ts +47 -0
- package/dist/bun/git.d.ts.map +1 -0
- package/dist/bun/gradient.d.ts +19 -1
- package/dist/bun/gradient.d.ts.map +1 -1
- package/dist/bun/index.d.ts +9 -6
- package/dist/bun/index.d.ts.map +1 -1
- package/dist/bun/index.js +13205 -11259
- package/dist/bun/index.js.map +4 -4
- package/dist/bun/lat-reader.d.ts +1 -1
- package/dist/bun/lat-reader.d.ts.map +1 -1
- package/dist/bun/ltm.d.ts.map +1 -1
- package/dist/bun/markdown.d.ts +11 -0
- package/dist/bun/markdown.d.ts.map +1 -1
- package/dist/bun/prompt.d.ts +1 -1
- package/dist/bun/prompt.d.ts.map +1 -1
- package/dist/bun/recall.d.ts +53 -0
- package/dist/bun/recall.d.ts.map +1 -1
- package/dist/bun/search.d.ts +29 -0
- package/dist/bun/search.d.ts.map +1 -1
- package/dist/bun/temporal.d.ts +2 -0
- package/dist/bun/temporal.d.ts.map +1 -1
- package/dist/bun/types.d.ts +15 -0
- package/dist/bun/types.d.ts.map +1 -1
- package/dist/bun/worker-model.d.ts +15 -80
- package/dist/bun/worker-model.d.ts.map +1 -1
- package/dist/node/agents-file.d.ts +13 -1
- package/dist/node/agents-file.d.ts.map +1 -1
- package/dist/node/config.d.ts +20 -1
- package/dist/node/config.d.ts.map +1 -1
- package/dist/node/data.d.ts +174 -0
- package/dist/node/data.d.ts.map +1 -0
- package/dist/node/db.d.ts +65 -0
- package/dist/node/db.d.ts.map +1 -1
- package/dist/node/distillation.d.ts +49 -6
- package/dist/node/distillation.d.ts.map +1 -1
- package/dist/node/embedding-vendor.d.ts +66 -0
- package/dist/node/embedding-vendor.d.ts.map +1 -0
- package/dist/node/embedding-worker-types.d.ts +66 -0
- package/dist/node/embedding-worker-types.d.ts.map +1 -0
- package/dist/node/embedding-worker.d.ts +16 -0
- package/dist/node/embedding-worker.d.ts.map +1 -0
- package/dist/node/embedding-worker.js +100 -0
- package/dist/node/embedding-worker.js.map +7 -0
- package/dist/node/embedding.d.ts +91 -8
- package/dist/node/embedding.d.ts.map +1 -1
- package/dist/node/git.d.ts +47 -0
- package/dist/node/git.d.ts.map +1 -0
- package/dist/node/gradient.d.ts +19 -1
- package/dist/node/gradient.d.ts.map +1 -1
- package/dist/node/index.d.ts +9 -6
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +13205 -11259
- package/dist/node/index.js.map +4 -4
- package/dist/node/lat-reader.d.ts +1 -1
- package/dist/node/lat-reader.d.ts.map +1 -1
- package/dist/node/ltm.d.ts.map +1 -1
- package/dist/node/markdown.d.ts +11 -0
- package/dist/node/markdown.d.ts.map +1 -1
- package/dist/node/prompt.d.ts +1 -1
- package/dist/node/prompt.d.ts.map +1 -1
- package/dist/node/recall.d.ts +53 -0
- package/dist/node/recall.d.ts.map +1 -1
- package/dist/node/search.d.ts +29 -0
- package/dist/node/search.d.ts.map +1 -1
- package/dist/node/temporal.d.ts +2 -0
- package/dist/node/temporal.d.ts.map +1 -1
- package/dist/node/types.d.ts +15 -0
- package/dist/node/types.d.ts.map +1 -1
- package/dist/node/worker-model.d.ts +15 -80
- package/dist/node/worker-model.d.ts.map +1 -1
- package/dist/types/agents-file.d.ts +13 -1
- package/dist/types/agents-file.d.ts.map +1 -1
- package/dist/types/config.d.ts +20 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/data.d.ts +174 -0
- package/dist/types/data.d.ts.map +1 -0
- package/dist/types/db.d.ts +65 -0
- package/dist/types/db.d.ts.map +1 -1
- package/dist/types/distillation.d.ts +49 -6
- package/dist/types/distillation.d.ts.map +1 -1
- package/dist/types/embedding-vendor.d.ts +66 -0
- package/dist/types/embedding-vendor.d.ts.map +1 -0
- package/dist/types/embedding-worker-types.d.ts +66 -0
- package/dist/types/embedding-worker-types.d.ts.map +1 -0
- package/dist/types/embedding-worker.d.ts +16 -0
- package/dist/types/embedding-worker.d.ts.map +1 -0
- package/dist/types/embedding.d.ts +91 -8
- package/dist/types/embedding.d.ts.map +1 -1
- package/dist/types/git.d.ts +47 -0
- package/dist/types/git.d.ts.map +1 -0
- package/dist/types/gradient.d.ts +19 -1
- package/dist/types/gradient.d.ts.map +1 -1
- package/dist/types/index.d.ts +9 -6
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lat-reader.d.ts +1 -1
- package/dist/types/lat-reader.d.ts.map +1 -1
- package/dist/types/ltm.d.ts.map +1 -1
- package/dist/types/markdown.d.ts +11 -0
- package/dist/types/markdown.d.ts.map +1 -1
- package/dist/types/prompt.d.ts +1 -1
- package/dist/types/prompt.d.ts.map +1 -1
- package/dist/types/recall.d.ts +53 -0
- package/dist/types/recall.d.ts.map +1 -1
- package/dist/types/search.d.ts +29 -0
- package/dist/types/search.d.ts.map +1 -1
- package/dist/types/temporal.d.ts +2 -0
- package/dist/types/temporal.d.ts.map +1 -1
- package/dist/types/types.d.ts +15 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/worker-model.d.ts +15 -80
- package/dist/types/worker-model.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/agents-file.ts +87 -4
- package/src/config.ts +68 -5
- package/src/curator.ts +2 -2
- package/src/data.ts +768 -0
- package/src/db.ts +386 -7
- package/src/distillation.ts +178 -35
- package/src/embedding-vendor.ts +102 -0
- package/src/embedding-worker-types.ts +82 -0
- package/src/embedding-worker.ts +185 -0
- package/src/embedding.ts +607 -61
- package/src/git.ts +144 -0
- package/src/gradient.ts +174 -17
- package/src/index.ts +20 -0
- package/src/lat-reader.ts +5 -11
- package/src/ltm.ts +17 -44
- package/src/markdown.ts +15 -0
- package/src/prompt.ts +1 -2
- package/src/recall.ts +401 -70
- package/src/search.ts +71 -1
- package/src/temporal.ts +42 -35
- package/src/types.ts +15 -0
- package/src/worker-model.ts +17 -363
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Worker model resolution.
|
|
3
3
|
*
|
|
4
|
-
* Background workers (distillation, curation, query expansion)
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Phase 2: LLM judge (session model rates candidate output vs reference)
|
|
4
|
+
* Background workers (distillation, curation, query expansion) default to
|
|
5
|
+
* sonnet-4-6 when the session model is more expensive ($1.50+/M input).
|
|
6
|
+
* Sonnet-4-6 produces equivalent-quality distillations at lower cost.
|
|
7
|
+
* An explicit `workerModel` config override takes priority over this default.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Resolution order:
|
|
10
|
+
* 1. Explicit config override (`workerModel`)
|
|
11
|
+
* 2. Cost-aware default (sonnet-4 for expensive session models)
|
|
12
|
+
* 3. Session model fallback (same model as the conversation)
|
|
12
13
|
*/
|
|
13
|
-
/** Minimal model info
|
|
14
|
+
/** Minimal model info — kept for downstream consumers. */
|
|
14
15
|
export type ModelInfo = {
|
|
15
16
|
id: string;
|
|
16
17
|
providerID: string;
|
|
@@ -26,85 +27,19 @@ export type ModelInfo = {
|
|
|
26
27
|
reasoning?: boolean;
|
|
27
28
|
};
|
|
28
29
|
};
|
|
29
|
-
/** Result of a worker model validation stored in kv_meta. */
|
|
30
|
-
export type WorkerModelResult = {
|
|
31
|
-
modelID: string;
|
|
32
|
-
providerID: string;
|
|
33
|
-
fingerprint: string;
|
|
34
|
-
validatedAt: number;
|
|
35
|
-
judgeScore: number | null;
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Select worker model candidates from the available models.
|
|
39
|
-
*
|
|
40
|
-
* Returns up to 2 candidates: cheapest overall + one tier below the session
|
|
41
|
-
* model. The session model itself is included (if it's the cheapest, the list
|
|
42
|
-
* has 1 entry and no comparison is needed).
|
|
43
|
-
*/
|
|
44
|
-
export declare function selectWorkerCandidates(sessionModel: {
|
|
45
|
-
id: string;
|
|
46
|
-
providerID: string;
|
|
47
|
-
cost: {
|
|
48
|
-
input: number;
|
|
49
|
-
};
|
|
50
|
-
}, providerModels: ModelInfo[]): ModelInfo[];
|
|
51
|
-
/**
|
|
52
|
-
* Compute a fingerprint from the model landscape. Changes when:
|
|
53
|
-
* - Models are added or removed from the provider
|
|
54
|
-
* - The session model changes
|
|
55
|
-
*/
|
|
56
|
-
export declare function computeModelFingerprint(providerID: string, sessionModelID: string, activeModelIDs: string[]): string;
|
|
57
|
-
export declare function getValidatedWorkerModel(providerID: string): WorkerModelResult | null;
|
|
58
|
-
export declare function storeValidatedWorkerModel(result: WorkerModelResult): void;
|
|
59
|
-
/** Clear a stored worker model validation (e.g. when the model is deprecated). */
|
|
60
|
-
export declare function clearValidatedWorkerModel(providerID: string): void;
|
|
61
|
-
/**
|
|
62
|
-
* Check whether the stored validation is stale (fingerprint mismatch).
|
|
63
|
-
*/
|
|
64
|
-
export declare function isValidationStale(stored: WorkerModelResult | null, currentFingerprint: string): boolean;
|
|
65
|
-
export type StructuralCheckResult = {
|
|
66
|
-
passed: boolean;
|
|
67
|
-
observationCount: number;
|
|
68
|
-
tokenCount: number;
|
|
69
|
-
reason?: string;
|
|
70
|
-
};
|
|
71
|
-
/**
|
|
72
|
-
* Structural quality check: does the candidate distillation output meet
|
|
73
|
-
* minimum quality thresholds relative to the reference?
|
|
74
|
-
*/
|
|
75
|
-
export declare function structuralCheck(candidateObservations: string | null, referenceObservations: string): StructuralCheckResult;
|
|
76
|
-
export declare const WORKER_JUDGE_SYSTEM = "You are evaluating distillation quality. You will be given a REFERENCE distillation (produced by a capable model) and a CANDIDATE distillation (produced by a cheaper model) of the same conversation segment.\n\nRate the candidate on a scale of 1-5:\n5 = Captures all key facts and decisions, equivalent to reference\n4 = Captures most facts, minor omissions\n3 = Captures the essential facts, some detail loss acceptable\n2 = Missing important facts or technical details\n1 = Significantly incomplete or inaccurate\n\nRespond with ONLY a single digit (1-5).";
|
|
77
|
-
export declare function workerJudgeUser(reference: string, candidate: string): string;
|
|
78
|
-
/** Parse the judge's score from a response. Returns null on parse failure. */
|
|
79
|
-
export declare function parseJudgeScore(response: string): number | null;
|
|
80
|
-
import type { LLMClient } from "./types";
|
|
81
|
-
export type ValidationInput = {
|
|
82
|
-
llm: LLMClient;
|
|
83
|
-
providerID: string;
|
|
84
|
-
sessionModelID: string;
|
|
85
|
-
candidates: ModelInfo[];
|
|
86
|
-
/** Recent gen-0 distillation to use as reference (observations text). */
|
|
87
|
-
referenceObservations: string;
|
|
88
|
-
/** Source messages text for re-running distillation with candidates. */
|
|
89
|
-
sourceMessagesText: string;
|
|
90
|
-
/** Date string for the distillation prompt. */
|
|
91
|
-
date: string;
|
|
92
|
-
};
|
|
93
|
-
/**
|
|
94
|
-
* Run the two-phase quality validation for worker model candidates.
|
|
95
|
-
* Returns the cheapest passing candidate, or null if none pass.
|
|
96
|
-
*/
|
|
97
|
-
export declare function runValidation(input: ValidationInput): Promise<WorkerModelResult | null>;
|
|
98
30
|
/**
|
|
99
31
|
* Resolve the effective worker model for a given provider.
|
|
100
|
-
* Priority: explicit config >
|
|
32
|
+
* Priority: explicit config override > cost-aware default > session model.
|
|
101
33
|
*/
|
|
102
|
-
export declare function resolveWorkerModel(
|
|
34
|
+
export declare function resolveWorkerModel(_providerID: string, configWorkerModel?: {
|
|
103
35
|
providerID: string;
|
|
104
36
|
modelID: string;
|
|
105
37
|
}, configModel?: {
|
|
106
38
|
providerID: string;
|
|
107
39
|
modelID: string;
|
|
40
|
+
}, costAwareDefault?: {
|
|
41
|
+
providerID: string;
|
|
42
|
+
modelID: string;
|
|
108
43
|
}): {
|
|
109
44
|
providerID: string;
|
|
110
45
|
modelID: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-model.d.ts","sourceRoot":"","sources":["../../src/worker-model.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"worker-model.d.ts","sourceRoot":"","sources":["../../src/worker-model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,0DAA0D;AAC1D,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE;QACZ,KAAK,EAAE;YAAE,IAAI,EAAE,OAAO,CAAA;SAAE,CAAC;QACzB,+DAA+D;QAC/D,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH,CAAC;AAMF;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,iBAAiB,CAAC,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC3D,WAAW,CAAC,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACrD,gBAAgB,CAAC,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAUrD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loreai/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"description": "Shared memory engine for Lore — three-tier storage, distillation, gradient context management",
|
|
@@ -24,11 +24,14 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@huggingface/hub": "2.11.0",
|
|
27
|
-
"
|
|
27
|
+
"micromark": "^4.0.0",
|
|
28
28
|
"remark": "^15.0.1",
|
|
29
29
|
"uuidv7": "^1.1.0",
|
|
30
30
|
"zod": "^4.3.6"
|
|
31
31
|
},
|
|
32
|
+
"optionalDependencies": {
|
|
33
|
+
"fastembed": "^2.1.0"
|
|
34
|
+
},
|
|
32
35
|
"devDependencies": {
|
|
33
36
|
"@types/mdast": "^4.0.4"
|
|
34
37
|
},
|
package/src/agents-file.ts
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
* without duplication.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync } from "fs";
|
|
12
12
|
import { dirname, join } from "path";
|
|
13
|
+
import { db } from "./db";
|
|
13
14
|
import * as ltm from "./ltm";
|
|
14
15
|
import { serialize, inline, h, ul, liph, strong, t, root, unescapeMarkdown } from "./markdown";
|
|
15
16
|
|
|
@@ -50,6 +51,51 @@ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
|
|
|
50
51
|
/** Matches `<!-- lore:UUID -->` tracking markers. */
|
|
51
52
|
const MARKER_RE = /^<!--\s*lore:([0-9a-f-]+)\s*-->$/;
|
|
52
53
|
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// File cache (kv_meta) — skip redundant import/export work
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
type LoreFileCache = {
|
|
59
|
+
/** File mtime (milliseconds) at last processing. */
|
|
60
|
+
mtimeMs: number;
|
|
61
|
+
/** hashSection() of file content at that time. */
|
|
62
|
+
hash: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const CACHE_PREFIX = "lore_file_cache:";
|
|
66
|
+
|
|
67
|
+
function getCache(fp: string): LoreFileCache | null {
|
|
68
|
+
const row = db()
|
|
69
|
+
.query("SELECT value FROM kv_meta WHERE key = ?")
|
|
70
|
+
.get(CACHE_PREFIX + fp) as { value: string } | null;
|
|
71
|
+
if (!row) return null;
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(row.value);
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function setCache(fp: string, entry: LoreFileCache): void {
|
|
80
|
+
const key = CACHE_PREFIX + fp;
|
|
81
|
+
const value = JSON.stringify(entry);
|
|
82
|
+
db()
|
|
83
|
+
.query(
|
|
84
|
+
"INSERT INTO kv_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = ?",
|
|
85
|
+
)
|
|
86
|
+
.run(key, value, value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Clear the cached mtime/hash for a project's `.lore.md`.
|
|
91
|
+
* Useful in tests or after data wipes to force a full re-check.
|
|
92
|
+
*/
|
|
93
|
+
export function clearLoreFileCache(projectPath: string): void {
|
|
94
|
+
db()
|
|
95
|
+
.query("DELETE FROM kv_meta WHERE key = ?")
|
|
96
|
+
.run(CACHE_PREFIX + join(projectPath, LORE_FILE));
|
|
97
|
+
}
|
|
98
|
+
|
|
53
99
|
// ---------------------------------------------------------------------------
|
|
54
100
|
// Types
|
|
55
101
|
// ---------------------------------------------------------------------------
|
|
@@ -453,24 +499,61 @@ export function loreFileExists(projectPath: string): boolean {
|
|
|
453
499
|
/**
|
|
454
500
|
* Export current knowledge entries to `.lore.md` in the project root.
|
|
455
501
|
* The entire file is lore-owned — no section markers, no content to preserve.
|
|
502
|
+
*
|
|
503
|
+
* Skips the write if the content hash matches the cached hash (DB state
|
|
504
|
+
* unchanged since last export), avoiding unnecessary filesystem writes
|
|
505
|
+
* and mtime bumps.
|
|
456
506
|
*/
|
|
457
507
|
export function exportLoreFile(projectPath: string): void {
|
|
458
508
|
const sectionBody = buildSection(projectPath);
|
|
459
509
|
const content = LORE_FILE_HEADER + "\n" + sectionBody;
|
|
460
|
-
|
|
510
|
+
const contentHash = hashSection(content);
|
|
511
|
+
|
|
512
|
+
const fp = join(projectPath, LORE_FILE);
|
|
513
|
+
|
|
514
|
+
// Skip write if content hash matches cached hash (DB state unchanged).
|
|
515
|
+
const cached = getCache(fp);
|
|
516
|
+
if (cached && cached.hash === contentHash) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Content changed — write and update cache.
|
|
521
|
+
writeFileSync(fp, content, "utf8");
|
|
522
|
+
const { mtimeMs } = statSync(fp);
|
|
523
|
+
setCache(fp, { mtimeMs, hash: contentHash });
|
|
461
524
|
}
|
|
462
525
|
|
|
463
526
|
/**
|
|
464
527
|
* Returns true if `.lore.md` needs to be imported:
|
|
465
|
-
* - File exists and its content differs from what lore would currently produce
|
|
528
|
+
* - File exists and its content differs from what lore would currently produce.
|
|
529
|
+
*
|
|
530
|
+
* Uses an mtime + content-hash cache to skip the expensive buildSection()
|
|
531
|
+
* call when the file hasn't been touched since we last processed it.
|
|
466
532
|
*/
|
|
467
533
|
export function shouldImportLoreFile(projectPath: string): boolean {
|
|
468
534
|
const fp = join(projectPath, LORE_FILE);
|
|
469
535
|
if (!existsSync(fp)) return false;
|
|
470
536
|
|
|
537
|
+
// Fast path: if mtime hasn't changed since last processing, skip entirely.
|
|
538
|
+
const { mtimeMs } = statSync(fp);
|
|
539
|
+
const cached = getCache(fp);
|
|
540
|
+
if (cached && cached.mtimeMs === mtimeMs) {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Slow path: mtime changed (or first check) — read file and compare content.
|
|
471
545
|
const fileContent = readFileSync(fp, "utf8");
|
|
546
|
+
const fileHash = hashSection(fileContent);
|
|
472
547
|
const expected = LORE_FILE_HEADER + "\n" + buildSection(projectPath);
|
|
473
|
-
|
|
548
|
+
const expectedHash = hashSection(expected);
|
|
549
|
+
|
|
550
|
+
if (fileHash === expectedHash) {
|
|
551
|
+
// File matches DB — update cache so next call fast-paths.
|
|
552
|
+
setCache(fp, { mtimeMs, hash: fileHash });
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return true;
|
|
474
557
|
}
|
|
475
558
|
|
|
476
559
|
/**
|
package/src/config.ts
CHANGED
|
@@ -35,8 +35,20 @@ export const LoreConfig = z.object({
|
|
|
35
35
|
* cost-aware formula from targetCacheReadCostPerTurn. 0 = disabled
|
|
36
36
|
* (no cap, use full context). Default: undefined (use cost-aware auto). */
|
|
37
37
|
maxLayer0Tokens: z.number().min(0).optional(),
|
|
38
|
+
/** Target maximum cost (USD) for a single full cache bust (cold write of
|
|
39
|
+
* the entire context). Controls the total-context token cap at layer 1+:
|
|
40
|
+
* maxContextTokens = targetBustCost / cacheWriteCostPerToken.
|
|
41
|
+
* For opus-4-6 ($6.25/M write): $1.00 → 160K cap.
|
|
42
|
+
* For sonnet-4 ($3.75/M write): $1.00 → 267K (effectively uncapped at 200K).
|
|
43
|
+
* The cap is further adjusted dynamically per session based on observed
|
|
44
|
+
* bust rate (EMA) and break frequency. Default: 1.00. Set to 0 to disable. */
|
|
45
|
+
targetBustCost: z.number().min(0).default(1.00),
|
|
46
|
+
/** Direct override for the total-context token cap at layer 1+. When set,
|
|
47
|
+
* bypasses the cost-aware formula from targetBustCost. 0 = disabled.
|
|
48
|
+
* Default: undefined (use cost-aware auto). */
|
|
49
|
+
maxContextTokens: z.number().min(0).optional(),
|
|
38
50
|
})
|
|
39
|
-
.default({ distilled: 0.25, raw: 0.4, output: 0.25, ltm: 0.05, targetCacheReadCostPerTurn: 0.10 }),
|
|
51
|
+
.default({ distilled: 0.25, raw: 0.4, output: 0.25, ltm: 0.05, targetCacheReadCostPerTurn: 0.10, targetBustCost: 1.00 }),
|
|
40
52
|
/**
|
|
41
53
|
* Cold-cache idle-resume handling.
|
|
42
54
|
*
|
|
@@ -62,8 +74,15 @@ export const LoreConfig = z.object({
|
|
|
62
74
|
distillation: z
|
|
63
75
|
.object({
|
|
64
76
|
minMessages: z.number().min(3).default(5),
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
/** Minimum total tokens for a segment to be worth distilling.
|
|
78
|
+
* Segments below this are deferred (normal mode) or absorbed without
|
|
79
|
+
* an LLM call (force/urgent mode). Default: 64. */
|
|
80
|
+
minSegmentTokens: z.number().min(16).default(64),
|
|
81
|
+
/** Maximum total tokens per distillation segment. Segments exceeding
|
|
82
|
+
* this are split at time-gap or token boundaries. Replaces the former
|
|
83
|
+
* message-count-based maxSegment. Default: 8192. */
|
|
84
|
+
maxSegmentTokens: z.number().min(256).default(16384),
|
|
85
|
+
metaThreshold: z.number().min(3).default(20),
|
|
67
86
|
/** Max chars per tool output when rendering temporal messages for distillation input.
|
|
68
87
|
* Outputs longer than this are replaced with a compact annotation preserving line
|
|
69
88
|
* count, error signals, and file paths. Default: 2000 (matches upstream OpenCode's
|
|
@@ -72,8 +91,9 @@ export const LoreConfig = z.object({
|
|
|
72
91
|
})
|
|
73
92
|
.default({
|
|
74
93
|
minMessages: 5,
|
|
75
|
-
|
|
76
|
-
|
|
94
|
+
minSegmentTokens: 64,
|
|
95
|
+
maxSegmentTokens: 16384,
|
|
96
|
+
metaThreshold: 20,
|
|
77
97
|
toolOutputMaxChars: 2_000,
|
|
78
98
|
}),
|
|
79
99
|
knowledge: z
|
|
@@ -148,12 +168,55 @@ export const LoreConfig = z.object({
|
|
|
148
168
|
model: "BGESmallENV15",
|
|
149
169
|
dimensions: 384,
|
|
150
170
|
}),
|
|
171
|
+
/** Recall output formatting — controls how search results are presented to the agent. */
|
|
172
|
+
recall: z
|
|
173
|
+
.object({
|
|
174
|
+
/** Total character budget for recall output. Controls how much context the
|
|
175
|
+
* recall results consume. ~2K tokens at 8000 chars. Default: 8000. */
|
|
176
|
+
charBudget: z.number().min(2000).max(20000).default(8000),
|
|
177
|
+
/** Minimum RRF score relative to top result. Results below
|
|
178
|
+
* topScore * relevanceFloor are dropped. Default: 0.15.
|
|
179
|
+
* Set to 0 to disable score-based cutoff. */
|
|
180
|
+
relevanceFloor: z.number().min(0).max(1).default(0.15),
|
|
181
|
+
/** Max results to show in recall output. Default: 15. */
|
|
182
|
+
maxResults: z.number().min(3).max(30).default(15),
|
|
183
|
+
})
|
|
184
|
+
.default({ charBudget: 8000, relevanceFloor: 0.15, maxResults: 15 }),
|
|
151
185
|
})
|
|
152
186
|
.default({
|
|
153
187
|
ftsWeights: { title: 6.0, content: 2.0, category: 3.0 },
|
|
154
188
|
recallLimit: 10,
|
|
155
189
|
queryExpansion: false,
|
|
156
190
|
embeddings: { enabled: true, provider: "local" as const, model: "BGESmallENV15", dimensions: 384 },
|
|
191
|
+
recall: { charBudget: 8000, relevanceFloor: 0.15, maxResults: 15 },
|
|
192
|
+
}),
|
|
193
|
+
cache: z
|
|
194
|
+
.object({
|
|
195
|
+
/** TTL for the conversation cache breakpoint.
|
|
196
|
+
* - "5m" — standard Anthropic ephemeral (5 min eviction, 1.25× write cost)
|
|
197
|
+
* - "1h" — extended 1-hour TTL (2× write cost, requires extended cache tier)
|
|
198
|
+
* - "auto" — auto-upgrade to 1h when frequent cold-cache turns are detected.
|
|
199
|
+
* Monitors rolling window of recent turns; upgrades when >40% are cold-cache,
|
|
200
|
+
* downgrades when <20%. Auto-syncs idleResumeMinutes to 60 when 1h is active.
|
|
201
|
+
* Default: "auto". */
|
|
202
|
+
conversationTTL: z.enum(["5m", "1h", "auto"]).default("auto"),
|
|
203
|
+
/** Speculative cache warming — sends max_tokens:0 keepalive requests to
|
|
204
|
+
* refresh the Anthropic prompt cache before it expires. Uses survival
|
|
205
|
+
* analysis on inter-turn gaps to predict whether the user will return. */
|
|
206
|
+
warming: z
|
|
207
|
+
.object({
|
|
208
|
+
/** Enable cache warming. Default: true. */
|
|
209
|
+
enabled: z.boolean().default(true),
|
|
210
|
+
/** Override the survival probability threshold below which warming is
|
|
211
|
+
* skipped. Default: auto-derived from cache read/write cost ratio
|
|
212
|
+
* (~0.08 for 5m TTL, ~0.05 for 1h TTL). */
|
|
213
|
+
minReturnProbability: z.number().min(0).max(1).optional(),
|
|
214
|
+
})
|
|
215
|
+
.default({ enabled: true }),
|
|
216
|
+
})
|
|
217
|
+
.default({
|
|
218
|
+
conversationTTL: "auto",
|
|
219
|
+
warming: { enabled: true },
|
|
157
220
|
}),
|
|
158
221
|
crossProject: z.boolean().default(false),
|
|
159
222
|
agentsFile: z
|
package/src/curator.ts
CHANGED
|
@@ -82,7 +82,7 @@ export async function run(input: {
|
|
|
82
82
|
const responseText = await input.llm.prompt(
|
|
83
83
|
CURATOR_SYSTEM,
|
|
84
84
|
userContent,
|
|
85
|
-
{ model, workerID: "lore-curator", thinking: false, sessionID: input.sessionID },
|
|
85
|
+
{ model, workerID: "lore-curator", thinking: false, sessionID: input.sessionID, maxTokens: 2048 },
|
|
86
86
|
);
|
|
87
87
|
if (!responseText) return { created: 0, updated: 0, deleted: 0 };
|
|
88
88
|
|
|
@@ -185,7 +185,7 @@ export async function consolidate(input: {
|
|
|
185
185
|
const responseText = await input.llm.prompt(
|
|
186
186
|
CONSOLIDATION_SYSTEM,
|
|
187
187
|
userContent,
|
|
188
|
-
{ model, workerID: "lore-curator", thinking: false, sessionID: input.sessionID },
|
|
188
|
+
{ model, workerID: "lore-curator", thinking: false, sessionID: input.sessionID, maxTokens: 4096 },
|
|
189
189
|
);
|
|
190
190
|
if (!responseText) return { updated: 0, deleted: 0 };
|
|
191
191
|
|