@loreai/core 0.11.1 → 0.12.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/dist/bun/db.d.ts.map +1 -1
- package/dist/bun/distillation.d.ts +26 -0
- package/dist/bun/distillation.d.ts.map +1 -1
- package/dist/bun/index.js +107 -17
- package/dist/bun/index.js.map +3 -3
- package/dist/bun/recall.d.ts.map +1 -1
- package/dist/bun/temporal.d.ts +15 -0
- package/dist/bun/temporal.d.ts.map +1 -1
- package/dist/node/db.d.ts.map +1 -1
- package/dist/node/distillation.d.ts +26 -0
- package/dist/node/distillation.d.ts.map +1 -1
- package/dist/node/index.js +107 -17
- package/dist/node/index.js.map +3 -3
- package/dist/node/recall.d.ts.map +1 -1
- package/dist/node/temporal.d.ts +15 -0
- package/dist/node/temporal.d.ts.map +1 -1
- package/dist/types/db.d.ts.map +1 -1
- package/dist/types/distillation.d.ts +26 -0
- package/dist/types/distillation.d.ts.map +1 -1
- package/dist/types/recall.d.ts.map +1 -1
- package/dist/types/temporal.d.ts +15 -0
- package/dist/types/temporal.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/db.ts +64 -2
- package/src/distillation.ts +125 -19
- package/src/recall.ts +18 -0
- package/src/temporal.ts +39 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../src/recall.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAczC,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;AAEtE,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,mFAAmF;IACnF,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;CACrC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AA+JlC,wFAAwF;AACxF,wBAAsB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../src/recall.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAczC,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;AAEtE,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,mFAAmF;IACnF,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;CACrC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AA+JlC,wFAAwF;AACxF,wBAAsB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAuOzE;AAED,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,wiBAC8f,CAAC;AAEniB,mEAAmE;AACnE,eAAO,MAAM,yBAAyB;;;CAIrC,CAAC"}
|
package/dist/node/temporal.d.ts
CHANGED
|
@@ -68,6 +68,21 @@ export declare function searchScored(input: {
|
|
|
68
68
|
sessionID?: string;
|
|
69
69
|
limit?: number;
|
|
70
70
|
}): ScoredTemporalMessage[];
|
|
71
|
+
/**
|
|
72
|
+
* Normalized variance of relative-existence weights over message timestamps.
|
|
73
|
+
*
|
|
74
|
+
* Measures temporal attention imbalance: 0 means timestamps are evenly
|
|
75
|
+
* distributed (uniform attention), 1 means a single distant timestamp
|
|
76
|
+
* dominates (attention stuck in the past). Useful as a lightweight
|
|
77
|
+
* signal for distillation segmentation, recall time-biasing, and
|
|
78
|
+
* idle-resume awareness.
|
|
79
|
+
*
|
|
80
|
+
* Only meaningful for n ≥ 2. Returns 0 for 0 or 1 timestamps.
|
|
81
|
+
*
|
|
82
|
+
* Based on the "Temporal Clustering via Relative Existence" heuristic
|
|
83
|
+
* from D7x7z49/llm-context-idea.
|
|
84
|
+
*/
|
|
85
|
+
export declare function temporalCnorm(timestamps: number[], now?: number): number;
|
|
71
86
|
export declare function count(projectPath: string, sessionID?: string): number;
|
|
72
87
|
export declare function undistilledCount(projectPath: string, sessionID?: string): number;
|
|
73
88
|
export type PruneResult = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temporal.d.ts","sourceRoot":"","sources":["../../src/temporal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQrD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,WAAS,CAAC;AAEvC;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAarD;AAiBD,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,QAqCA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,WAAW,CACzB,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,eAAe,EAAE,CASnB;AAED,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,eAAe,EAAE,CAOnB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAQ1C;AA2BD,wBAAgB,MAAM,CAAC,KAAK,EAAE;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,eAAe,EAAE,CA0CpB;AAED,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,qBAAqB,EAAE,CAgC1B;AAED,wBAAgB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAWrE;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAWR;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,WAAW,CA0Ed"}
|
|
1
|
+
{"version":3,"file":"temporal.d.ts","sourceRoot":"","sources":["../../src/temporal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQrD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,WAAS,CAAC;AAEvC;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAarD;AAiBD,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,QAqCA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,WAAW,CACzB,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,eAAe,EAAE,CASnB;AAED,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,eAAe,EAAE,CAOnB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAQ1C;AA2BD,wBAAgB,MAAM,CAAC,KAAK,EAAE;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,eAAe,EAAE,CA0CpB;AAED,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,qBAAqB,EAAE,CAgC1B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAAE,EACpB,GAAG,GAAE,MAAmB,GACvB,MAAM,CAoBR;AAED,wBAAgB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAWrE;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAWR;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,WAAW,CA0Ed"}
|
package/dist/types/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAyVtC,wBAAgB,EAAE,IAAI,QAAQ,CAkC7B;
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAyVtC,wBAAgB,EAAE,IAAI,QAAQ,CAkC7B;AAmGD,wBAAgB,KAAK,SAKpB;AAGD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAYjE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK1D;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKrD;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAKpC;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAYxE"}
|
|
@@ -3,6 +3,32 @@ import { workerSessionIDs } from "./worker";
|
|
|
3
3
|
import type { LLMClient } from "./types";
|
|
4
4
|
export { workerSessionIDs };
|
|
5
5
|
type TemporalMessage = temporal.TemporalMessage;
|
|
6
|
+
/**
|
|
7
|
+
* Compression health ratio: k / √N.
|
|
8
|
+
*
|
|
9
|
+
* k = distilled token count, N = source token count.
|
|
10
|
+
* Values < 1.0 signal likely lossy compression (below the square-root
|
|
11
|
+
* boundary). Values > 1.0 signal relatively faithful compression.
|
|
12
|
+
*
|
|
13
|
+
* Based on the "LLM Context Square Root Theory" heuristic from
|
|
14
|
+
* D7x7z49/llm-context-idea. The specific threshold is unvalidated —
|
|
15
|
+
* use as a diagnostic signal, not a hard gate.
|
|
16
|
+
*/
|
|
17
|
+
export declare function compressionRatio(distilledTokens: number, sourceTokens: number): number;
|
|
18
|
+
/**
|
|
19
|
+
* Segment detection: group related messages into distillation-sized chunks.
|
|
20
|
+
*
|
|
21
|
+
* When the message count exceeds `maxSegment`, prefers splitting at the
|
|
22
|
+
* largest inter-message time gap (if it's ≥ 3× the median gap) to respect
|
|
23
|
+
* natural conversation boundaries. Falls back to count-based splitting at
|
|
24
|
+
* `maxSegment` when timestamps are uniform.
|
|
25
|
+
*
|
|
26
|
+
* Trailing segments with < 3 messages are merged into the previous segment
|
|
27
|
+
* to avoid tiny distillation inputs with too little context.
|
|
28
|
+
*
|
|
29
|
+
* Exported for testing; `run()` is the production caller.
|
|
30
|
+
*/
|
|
31
|
+
export declare function detectSegments(messages: TemporalMessage[], maxSegment: number): TemporalMessage[][];
|
|
6
32
|
/**
|
|
7
33
|
* Truncate tool outputs within a `TemporalMessage.content` string (produced
|
|
8
34
|
* by `temporal.partsToText`). Plain text and `[reasoning]` chunks pass
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"distillation.d.ts","sourceRoot":"","sources":["../../src/distillation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AAWvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGzC,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,KAAK,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"distillation.d.ts","sourceRoot":"","sources":["../../src/distillation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AAWvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGzC,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,KAAK,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;AAEhD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,GACnB,MAAM,CAGR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,eAAe,EAAE,EAC3B,UAAU,EAAE,MAAM,GACjB,eAAe,EAAE,EAAE,CAGrB;AAwGD;;;;;;;;;;;;GAYG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,MAAM,CAwBR;AAgBD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,eAAe,EAAE,EAC3B,kBAAkB,CAAC,EAAE,MAAM,GAC1B,MAAM,CAUR;AAED,KAAK,kBAAkB,GAAG;IACxB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAwBF;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,SAAS,CAEpB;AAwBD,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAQpD;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,eAAe,UAAQ,GACtB,YAAY,EAAE,CAqBhB;AA0ID,wBAAsB,GAAG,CAAC,KAAK,EAAE;IAC/B,GAAG,EAAE,SAAS,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA4DjD;AAmED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE;IACvC,GAAG,EAAE,SAAS,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACjD,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA+ErC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../src/recall.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAczC,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;AAEtE,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,mFAAmF;IACnF,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;CACrC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AA+JlC,wFAAwF;AACxF,wBAAsB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../../src/recall.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAczC,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;AAEtE,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,mFAAmF;IACnF,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;CACrC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AA+JlC,wFAAwF;AACxF,wBAAsB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAuOzE;AAED,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,wiBAC8f,CAAC;AAEniB,mEAAmE;AACnE,eAAO,MAAM,yBAAyB;;;CAIrC,CAAC"}
|
package/dist/types/temporal.d.ts
CHANGED
|
@@ -68,6 +68,21 @@ export declare function searchScored(input: {
|
|
|
68
68
|
sessionID?: string;
|
|
69
69
|
limit?: number;
|
|
70
70
|
}): ScoredTemporalMessage[];
|
|
71
|
+
/**
|
|
72
|
+
* Normalized variance of relative-existence weights over message timestamps.
|
|
73
|
+
*
|
|
74
|
+
* Measures temporal attention imbalance: 0 means timestamps are evenly
|
|
75
|
+
* distributed (uniform attention), 1 means a single distant timestamp
|
|
76
|
+
* dominates (attention stuck in the past). Useful as a lightweight
|
|
77
|
+
* signal for distillation segmentation, recall time-biasing, and
|
|
78
|
+
* idle-resume awareness.
|
|
79
|
+
*
|
|
80
|
+
* Only meaningful for n ≥ 2. Returns 0 for 0 or 1 timestamps.
|
|
81
|
+
*
|
|
82
|
+
* Based on the "Temporal Clustering via Relative Existence" heuristic
|
|
83
|
+
* from D7x7z49/llm-context-idea.
|
|
84
|
+
*/
|
|
85
|
+
export declare function temporalCnorm(timestamps: number[], now?: number): number;
|
|
71
86
|
export declare function count(projectPath: string, sessionID?: string): number;
|
|
72
87
|
export declare function undistilledCount(projectPath: string, sessionID?: string): number;
|
|
73
88
|
export type PruneResult = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temporal.d.ts","sourceRoot":"","sources":["../../src/temporal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQrD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,WAAS,CAAC;AAEvC;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAarD;AAiBD,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,QAqCA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,WAAW,CACzB,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,eAAe,EAAE,CASnB;AAED,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,eAAe,EAAE,CAOnB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAQ1C;AA2BD,wBAAgB,MAAM,CAAC,KAAK,EAAE;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,eAAe,EAAE,CA0CpB;AAED,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,qBAAqB,EAAE,CAgC1B;AAED,wBAAgB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAWrE;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAWR;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,WAAW,CA0Ed"}
|
|
1
|
+
{"version":3,"file":"temporal.d.ts","sourceRoot":"","sources":["../../src/temporal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQrD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,WAAS,CAAC;AAEvC;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAarD;AAiBD,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,QAqCA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,WAAW,CACzB,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,eAAe,EAAE,CASnB;AAED,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,eAAe,EAAE,CAOnB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAQ1C;AA2BD,wBAAgB,MAAM,CAAC,KAAK,EAAE;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,eAAe,EAAE,CA0CpB;AAED,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,qBAAqB,EAAE,CAgC1B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAAE,EACpB,GAAG,GAAE,MAAmB,GACvB,MAAM,CAoBR;AAED,wBAAgB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAWrE;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAWR;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,WAAW,CA0Ed"}
|
package/package.json
CHANGED
package/src/db.ts
CHANGED
|
@@ -396,7 +396,13 @@ function migrate(database: Database) {
|
|
|
396
396
|
}
|
|
397
397
|
)?.version ?? 0)
|
|
398
398
|
: 0;
|
|
399
|
-
if (current >= MIGRATIONS.length)
|
|
399
|
+
if (current >= MIGRATIONS.length) {
|
|
400
|
+
// Schema is at the expected version but a prior partial run may have left
|
|
401
|
+
// holes (e.g. ALTER TABLE succeeded but CREATE TABLE in the same migration
|
|
402
|
+
// string was skipped). Run idempotent recovery for known fragile objects.
|
|
403
|
+
recoverMissingObjects(database);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
400
406
|
for (let i = current; i < MIGRATIONS.length; i++) {
|
|
401
407
|
if (i === VACUUM_MIGRATION_INDEX) {
|
|
402
408
|
// VACUUM cannot run inside a transaction. Run it directly.
|
|
@@ -406,12 +412,68 @@ function migrate(database: Database) {
|
|
|
406
412
|
database.exec("PRAGMA auto_vacuum = INCREMENTAL");
|
|
407
413
|
database.exec("VACUUM");
|
|
408
414
|
} else {
|
|
409
|
-
|
|
415
|
+
try {
|
|
416
|
+
database.exec(MIGRATIONS[i]);
|
|
417
|
+
} catch (e: unknown) {
|
|
418
|
+
// Multi-statement migrations can partially fail when an early
|
|
419
|
+
// statement (e.g. ALTER TABLE ADD COLUMN) hits a duplicate-column
|
|
420
|
+
// error from a prior partial run. Swallow duplicate-column errors
|
|
421
|
+
// so the rest of the migration loop and the version bump proceed.
|
|
422
|
+
// Any genuinely new error is re-thrown.
|
|
423
|
+
if (
|
|
424
|
+
e instanceof Error &&
|
|
425
|
+
/duplicate column name/i.test(e.message)
|
|
426
|
+
) {
|
|
427
|
+
// The ALTER TABLE already applied — run remaining statements in
|
|
428
|
+
// this migration by stripping the offending ALTER and re-exec'ing.
|
|
429
|
+
// (Important: migrate() in db.ts runs each migration via database.exec()
|
|
430
|
+
// which stops at the first error in a multi-statement string.)
|
|
431
|
+
const stripped = stripAppliedAlters(MIGRATIONS[i], database);
|
|
432
|
+
if (stripped.trim()) database.exec(stripped);
|
|
433
|
+
} else {
|
|
434
|
+
throw e;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
410
437
|
}
|
|
411
438
|
}
|
|
412
439
|
// Update version to latest. Migration 0 inserts version=1 via its own INSERT,
|
|
413
440
|
// but subsequent migrations don't update it, so always normalize to MIGRATIONS.length.
|
|
414
441
|
database.exec(`UPDATE schema_version SET version = ${MIGRATIONS.length}`);
|
|
442
|
+
|
|
443
|
+
// Also run recovery for existing DBs that are already at the latest version
|
|
444
|
+
// but have holes from past partial runs.
|
|
445
|
+
recoverMissingObjects(database);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Strip ALTER TABLE ADD COLUMN statements for columns that already exist.
|
|
450
|
+
* Returns the migration string with those statements removed.
|
|
451
|
+
*/
|
|
452
|
+
function stripAppliedAlters(migration: string, database: Database): string {
|
|
453
|
+
return migration.replace(
|
|
454
|
+
/ALTER\s+TABLE\s+(\w+)\s+ADD\s+COLUMN\s+(\w+)\b[^;]*;/gi,
|
|
455
|
+
(match, table, column) => {
|
|
456
|
+
const cols = database
|
|
457
|
+
.query(`PRAGMA table_info(${table})`)
|
|
458
|
+
.all() as Array<{ name: string }>;
|
|
459
|
+
if (cols.some((c) => c.name === column)) return ""; // already exists
|
|
460
|
+
return match; // keep — this ALTER hasn't been applied
|
|
461
|
+
},
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Idempotent recovery for objects that may be missing due to multi-statement
|
|
467
|
+
* migration partial failures (e.g. ALTER TABLE throws duplicate-column,
|
|
468
|
+
* aborting the exec before a subsequent CREATE TABLE in the same string).
|
|
469
|
+
*/
|
|
470
|
+
function recoverMissingObjects(database: Database) {
|
|
471
|
+
database.exec(`
|
|
472
|
+
CREATE TABLE IF NOT EXISTS kv_meta (
|
|
473
|
+
key TEXT PRIMARY KEY,
|
|
474
|
+
value TEXT NOT NULL
|
|
475
|
+
);
|
|
476
|
+
`);
|
|
415
477
|
}
|
|
416
478
|
|
|
417
479
|
export function close() {
|
package/src/distillation.ts
CHANGED
|
@@ -19,32 +19,125 @@ export { workerSessionIDs };
|
|
|
19
19
|
|
|
20
20
|
type TemporalMessage = temporal.TemporalMessage;
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Compression health ratio: k / √N.
|
|
24
|
+
*
|
|
25
|
+
* k = distilled token count, N = source token count.
|
|
26
|
+
* Values < 1.0 signal likely lossy compression (below the square-root
|
|
27
|
+
* boundary). Values > 1.0 signal relatively faithful compression.
|
|
28
|
+
*
|
|
29
|
+
* Based on the "LLM Context Square Root Theory" heuristic from
|
|
30
|
+
* D7x7z49/llm-context-idea. The specific threshold is unvalidated —
|
|
31
|
+
* use as a diagnostic signal, not a hard gate.
|
|
32
|
+
*/
|
|
33
|
+
export function compressionRatio(
|
|
34
|
+
distilledTokens: number,
|
|
35
|
+
sourceTokens: number,
|
|
36
|
+
): number {
|
|
37
|
+
if (sourceTokens <= 0) return 0;
|
|
38
|
+
return distilledTokens / Math.sqrt(sourceTokens);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Segment detection: group related messages into distillation-sized chunks.
|
|
43
|
+
*
|
|
44
|
+
* When the message count exceeds `maxSegment`, prefers splitting at the
|
|
45
|
+
* largest inter-message time gap (if it's ≥ 3× the median gap) to respect
|
|
46
|
+
* natural conversation boundaries. Falls back to count-based splitting at
|
|
47
|
+
* `maxSegment` when timestamps are uniform.
|
|
48
|
+
*
|
|
49
|
+
* Trailing segments with < 3 messages are merged into the previous segment
|
|
50
|
+
* to avoid tiny distillation inputs with too little context.
|
|
51
|
+
*
|
|
52
|
+
* Exported for testing; `run()` is the production caller.
|
|
53
|
+
*/
|
|
54
|
+
export function detectSegments(
|
|
24
55
|
messages: TemporalMessage[],
|
|
25
56
|
maxSegment: number,
|
|
26
57
|
): TemporalMessage[][] {
|
|
27
58
|
if (messages.length <= maxSegment) return [messages];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
59
|
+
return splitSegments(messages, maxSegment);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Minimum segment size — segments smaller than this get merged. */
|
|
63
|
+
const MIN_SEGMENT = 3;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Multiplier for the median gap threshold: a time gap must be at least
|
|
67
|
+
* this many times the median gap to be used as a split point.
|
|
68
|
+
*/
|
|
69
|
+
const GAP_THRESHOLD_MULTIPLIER = 3;
|
|
70
|
+
|
|
71
|
+
function splitSegments(
|
|
72
|
+
messages: TemporalMessage[],
|
|
73
|
+
maxSegment: number,
|
|
74
|
+
): TemporalMessage[][] {
|
|
75
|
+
if (messages.length <= maxSegment) return [messages];
|
|
76
|
+
|
|
77
|
+
// Find the split point: prefer the largest time gap if it's significant
|
|
78
|
+
const splitIdx = findSplitIndex(messages, maxSegment);
|
|
79
|
+
|
|
80
|
+
const left = messages.slice(0, splitIdx);
|
|
81
|
+
const right = messages.slice(splitIdx);
|
|
82
|
+
|
|
83
|
+
// Recurse on both halves
|
|
84
|
+
const result = splitSegments(left, maxSegment);
|
|
85
|
+
|
|
86
|
+
if (right.length < MIN_SEGMENT) {
|
|
87
|
+
// Merge tiny trailing segment into the last segment
|
|
88
|
+
result[result.length - 1].push(...right);
|
|
89
|
+
} else {
|
|
90
|
+
result.push(...splitSegments(right, maxSegment));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Choose where to split an oversized message array.
|
|
98
|
+
*
|
|
99
|
+
* If there's a time gap ≥ 3× the median gap AND it falls within a range
|
|
100
|
+
* that would produce segments of at least MIN_SEGMENT size, use it.
|
|
101
|
+
* Otherwise fall back to the count-based boundary at `maxSegment`.
|
|
102
|
+
*/
|
|
103
|
+
function findSplitIndex(
|
|
104
|
+
messages: TemporalMessage[],
|
|
105
|
+
maxSegment: number,
|
|
106
|
+
): number {
|
|
107
|
+
// Compute consecutive time gaps
|
|
108
|
+
const gaps: Array<{ index: number; gap: number }> = [];
|
|
109
|
+
for (let i = 1; i < messages.length; i++) {
|
|
110
|
+
gaps.push({
|
|
111
|
+
index: i,
|
|
112
|
+
gap: messages[i].created_at - messages[i - 1].created_at,
|
|
113
|
+
});
|
|
38
114
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
115
|
+
|
|
116
|
+
if (gaps.length === 0) return maxSegment;
|
|
117
|
+
|
|
118
|
+
// Find median gap
|
|
119
|
+
const sortedGaps = gaps.map((g) => g.gap).sort((a, b) => a - b);
|
|
120
|
+
const medianGap = sortedGaps[Math.floor(sortedGaps.length / 2)];
|
|
121
|
+
|
|
122
|
+
// Find the largest gap that would produce viable segments (≥ MIN_SEGMENT on each side)
|
|
123
|
+
let bestGap = { index: -1, gap: 0 };
|
|
124
|
+
for (const g of gaps) {
|
|
125
|
+
if (
|
|
126
|
+
g.gap > bestGap.gap &&
|
|
127
|
+
g.index >= MIN_SEGMENT &&
|
|
128
|
+
messages.length - g.index >= MIN_SEGMENT
|
|
129
|
+
) {
|
|
130
|
+
bestGap = g;
|
|
45
131
|
}
|
|
46
132
|
}
|
|
47
|
-
|
|
133
|
+
|
|
134
|
+
// Use the time gap if it's significantly larger than median
|
|
135
|
+
if (bestGap.index > 0 && bestGap.gap >= medianGap * GAP_THRESHOLD_MULTIPLIER) {
|
|
136
|
+
return bestGap.index;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Fall back to count-based splitting
|
|
140
|
+
return maxSegment;
|
|
48
141
|
}
|
|
49
142
|
|
|
50
143
|
function formatTime(ms: number): string {
|
|
@@ -527,6 +620,19 @@ async function distillSegment(input: {
|
|
|
527
620
|
});
|
|
528
621
|
temporal.markDistilled(input.messages.map((m) => m.id));
|
|
529
622
|
|
|
623
|
+
// Diagnostic: log compression health and temporal clustering metrics.
|
|
624
|
+
// R_compression (k/√N): < 1.0 signals likely lossy distillation.
|
|
625
|
+
// C_norm: 0 = uniform timestamps, 1 = dominated by distant past.
|
|
626
|
+
const distilledTokens = Math.ceil(result.observations.length / 3);
|
|
627
|
+
const sourceTokens = input.messages.reduce((sum, m) => sum + m.tokens, 0);
|
|
628
|
+
const rComp = compressionRatio(distilledTokens, sourceTokens);
|
|
629
|
+
const cNorm = temporal.temporalCnorm(input.messages.map((m) => m.created_at));
|
|
630
|
+
log.info(
|
|
631
|
+
`distill segment: ${input.messages.length} msgs, ` +
|
|
632
|
+
`${sourceTokens}→${distilledTokens} tokens, ` +
|
|
633
|
+
`R=${rComp.toFixed(2)}, C_norm=${cNorm.toFixed(3)}`,
|
|
634
|
+
);
|
|
635
|
+
|
|
530
636
|
// Fire-and-forget: embed the distillation for vector search
|
|
531
637
|
if (embedding.isAvailable()) {
|
|
532
638
|
embedding.embedDistillation(distillId, result.observations);
|
package/src/recall.ts
CHANGED
|
@@ -322,6 +322,24 @@ export async function runRecall(input: RecallInput): Promise<RecallResult> {
|
|
|
322
322
|
key: (r) => `t:${r.item.id}`,
|
|
323
323
|
},
|
|
324
324
|
);
|
|
325
|
+
|
|
326
|
+
// Recency-biased list for temporal results: same candidates re-ranked
|
|
327
|
+
// by created_at (newest first). RRF naturally boosts messages that
|
|
328
|
+
// appear in both the BM25 and recency lists — i.e. results that are
|
|
329
|
+
// both semantically relevant AND recent. Uses the same `t:` key prefix
|
|
330
|
+
// so RRF merges rather than duplicates.
|
|
331
|
+
if (temporalResults.length > 0) {
|
|
332
|
+
const recencySorted = [...temporalResults].sort(
|
|
333
|
+
(a, b) => b.created_at - a.created_at,
|
|
334
|
+
);
|
|
335
|
+
allRrfLists.push({
|
|
336
|
+
items: recencySorted.map((item) => ({
|
|
337
|
+
source: "temporal" as const,
|
|
338
|
+
item,
|
|
339
|
+
})),
|
|
340
|
+
key: (r) => `t:${r.item.id}`,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
325
343
|
}
|
|
326
344
|
|
|
327
345
|
// Vector search on the original query (not expansions — avoid redundant embeds).
|
package/src/temporal.ts
CHANGED
|
@@ -280,6 +280,45 @@ export function searchScored(input: {
|
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Normalized variance of relative-existence weights over message timestamps.
|
|
285
|
+
*
|
|
286
|
+
* Measures temporal attention imbalance: 0 means timestamps are evenly
|
|
287
|
+
* distributed (uniform attention), 1 means a single distant timestamp
|
|
288
|
+
* dominates (attention stuck in the past). Useful as a lightweight
|
|
289
|
+
* signal for distillation segmentation, recall time-biasing, and
|
|
290
|
+
* idle-resume awareness.
|
|
291
|
+
*
|
|
292
|
+
* Only meaningful for n ≥ 2. Returns 0 for 0 or 1 timestamps.
|
|
293
|
+
*
|
|
294
|
+
* Based on the "Temporal Clustering via Relative Existence" heuristic
|
|
295
|
+
* from D7x7z49/llm-context-idea.
|
|
296
|
+
*/
|
|
297
|
+
export function temporalCnorm(
|
|
298
|
+
timestamps: number[],
|
|
299
|
+
now: number = Date.now(),
|
|
300
|
+
): number {
|
|
301
|
+
const n = timestamps.length;
|
|
302
|
+
if (n < 2) return 0;
|
|
303
|
+
|
|
304
|
+
// Existence durations: how long each piece has existed
|
|
305
|
+
const durations = timestamps.map((t) => now - t);
|
|
306
|
+
const totalDuration = durations.reduce((a, b) => a + b, 0);
|
|
307
|
+
if (totalDuration <= 0) return 0;
|
|
308
|
+
|
|
309
|
+
// Relative existence weights (positive, sum to 1)
|
|
310
|
+
const weights = durations.map((d) => d / totalDuration);
|
|
311
|
+
|
|
312
|
+
// Normalized variance: Var(w) / Var_max
|
|
313
|
+
// Var(w) = (1/n) * Σ(w_i - 1/n)²
|
|
314
|
+
// Var_max = (n-1) / n² (when one weight = 1, rest = 0)
|
|
315
|
+
const uniform = 1 / n;
|
|
316
|
+
const variance =
|
|
317
|
+
weights.reduce((sum, w) => sum + (w - uniform) ** 2, 0) / n;
|
|
318
|
+
const maxVariance = (n - 1) / (n * n);
|
|
319
|
+
return maxVariance === 0 ? 0 : variance / maxVariance;
|
|
320
|
+
}
|
|
321
|
+
|
|
283
322
|
export function count(projectPath: string, sessionID?: string): number {
|
|
284
323
|
const pid = ensureProject(projectPath);
|
|
285
324
|
const query = sessionID
|