@gamaze/hicortex 0.13.0 → 0.13.2
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 +28 -7
- package/dist/capture-cursors.d.ts +73 -0
- package/dist/capture-cursors.js +133 -0
- package/dist/capture.d.ts +124 -0
- package/dist/capture.js +386 -0
- package/dist/cli.js +13 -1
- package/dist/distiller.d.ts +27 -1
- package/dist/distiller.js +81 -9
- package/dist/hermes-transcript-reader.d.ts +6 -2
- package/dist/hermes-transcript-reader.js +41 -4
- package/dist/init.d.ts +8 -0
- package/dist/init.js +15 -2
- package/dist/llm.d.ts +24 -1
- package/dist/llm.js +119 -2
- package/dist/mcp-server.js +57 -19
- package/dist/nightly-status.js +4 -1
- package/dist/nightly.d.ts +2 -0
- package/dist/nightly.js +224 -168
- package/dist/oc-transcript-reader.d.ts +3 -2
- package/dist/oc-transcript-reader.js +5 -3
- package/dist/pi-transcript-reader.d.ts +5 -8
- package/dist/pi-transcript-reader.js +36 -8
- package/dist/transcript-reader.d.ts +22 -1
- package/dist/transcript-reader.js +47 -14
- package/dist/types.d.ts +20 -0
- package/package.json +1 -1
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* The reader scans for new sessions since the last nightly run
|
|
8
8
|
* and feeds them to the existing distiller pipeline.
|
|
9
9
|
*/
|
|
10
|
+
import type { CursorMap } from "./capture-cursors.js";
|
|
11
|
+
export type { CursorMap };
|
|
10
12
|
export interface TranscriptBatch {
|
|
11
13
|
sessionId: string;
|
|
12
14
|
projectName: string;
|
|
@@ -18,6 +20,25 @@ export interface TranscriptBatch {
|
|
|
18
20
|
* `claude-code/<project>`. Lets per-harness readers stamp their own origin.
|
|
19
21
|
*/
|
|
20
22
|
sourceAgent?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Per-session cursor key (`<prefix>:<sessionId>`) — the capture-cursors.json
|
|
25
|
+
* key whose value gates and advances this session's incremental capture (#189).
|
|
26
|
+
*/
|
|
27
|
+
cursorKey: string;
|
|
28
|
+
/** Cursor value the delta starts from (entries already captured before this run). */
|
|
29
|
+
startCursor: number;
|
|
30
|
+
/**
|
|
31
|
+
* Shrink-guard generation for this session — woven into segment ids so
|
|
32
|
+
* post-reset segments can't collide with pre-reset ids on the content-blind
|
|
33
|
+
* server dedup. Advanced back to the store with the cursor.
|
|
34
|
+
*/
|
|
35
|
+
generation: number;
|
|
36
|
+
/**
|
|
37
|
+
* End-cursor value for each delta entry (length === entries.length). The
|
|
38
|
+
* packer uses these to land segment boundaries on exact entry boundaries.
|
|
39
|
+
* JSONL: startCursor + i + 1. Hermes: the row's messages.id.
|
|
40
|
+
*/
|
|
41
|
+
entryCursors: number[];
|
|
21
42
|
}
|
|
22
43
|
/**
|
|
23
44
|
* Cheap pre-filter: skip CC session FILES with fewer than this many raw JSONL
|
|
@@ -36,4 +57,4 @@ export declare const MIN_TRANSCRIPT_ENTRIES = 4;
|
|
|
36
57
|
* Read all CC transcripts modified since `since`.
|
|
37
58
|
* Returns one batch per session file.
|
|
38
59
|
*/
|
|
39
|
-
export declare function readCcTranscripts(since: Date, projectsDir?: string): TranscriptBatch[];
|
|
60
|
+
export declare function readCcTranscripts(since: Date, projectsDir?: string, cursors?: CursorMap): TranscriptBatch[];
|
|
@@ -32,7 +32,7 @@ const CC_PROJECTS_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude
|
|
|
32
32
|
* Read all CC transcripts modified since `since`.
|
|
33
33
|
* Returns one batch per session file.
|
|
34
34
|
*/
|
|
35
|
-
function readCcTranscripts(since, projectsDir = CC_PROJECTS_DIR) {
|
|
35
|
+
function readCcTranscripts(since, projectsDir = CC_PROJECTS_DIR, cursors = {}) {
|
|
36
36
|
const batches = [];
|
|
37
37
|
let projectDirs;
|
|
38
38
|
try {
|
|
@@ -74,7 +74,10 @@ function readCcTranscripts(since, projectsDir = CC_PROJECTS_DIR) {
|
|
|
74
74
|
// Skip files not modified since last run
|
|
75
75
|
if (fileStat.mtime <= since)
|
|
76
76
|
continue;
|
|
77
|
-
const
|
|
77
|
+
const sessionId = (0, node_path_1.basename)(filePath, ".jsonl");
|
|
78
|
+
const key = `cc:${sessionId}`;
|
|
79
|
+
const pos = cursors[key] ?? { cursor: 0, gen: 0 };
|
|
80
|
+
const batch = parseTranscriptFile(filePath, projectName, key, pos.cursor, pos.gen);
|
|
78
81
|
if (batch) {
|
|
79
82
|
batches.push(batch);
|
|
80
83
|
}
|
|
@@ -83,10 +86,15 @@ function readCcTranscripts(since, projectsDir = CC_PROJECTS_DIR) {
|
|
|
83
86
|
return batches;
|
|
84
87
|
}
|
|
85
88
|
/**
|
|
86
|
-
* Parse a single .jsonl transcript file into a batch.
|
|
87
|
-
*
|
|
89
|
+
* Parse a single .jsonl transcript file into a delta batch.
|
|
90
|
+
*
|
|
91
|
+
* Returns null if the file has too few meaningful entries (whole-file gate) or
|
|
92
|
+
* the cursor already covers everything (nothing new since last capture, #189).
|
|
93
|
+
*
|
|
94
|
+
* @param cursorKey capture-cursors.json key for this session
|
|
95
|
+
* @param startCursor entries already captured (delta = entries.slice(startCursor))
|
|
88
96
|
*/
|
|
89
|
-
function parseTranscriptFile(filePath, projectName) {
|
|
97
|
+
function parseTranscriptFile(filePath, projectName, cursorKey, startCursor, generation) {
|
|
90
98
|
let raw;
|
|
91
99
|
try {
|
|
92
100
|
raw = (0, node_fs_1.readFileSync)(filePath, "utf-8");
|
|
@@ -98,30 +106,55 @@ function parseTranscriptFile(filePath, projectName) {
|
|
|
98
106
|
if (lines.length < exports.MIN_TRANSCRIPT_ENTRIES)
|
|
99
107
|
return null; // degenerate/empty file
|
|
100
108
|
const entries = [];
|
|
101
|
-
|
|
109
|
+
const timestamps = [];
|
|
102
110
|
for (const line of lines) {
|
|
103
111
|
try {
|
|
104
112
|
const entry = JSON.parse(line);
|
|
105
113
|
entries.push(entry);
|
|
106
|
-
|
|
107
|
-
lastTimestamp = entry.timestamp;
|
|
108
|
-
}
|
|
114
|
+
timestamps.push(typeof entry.timestamp === "string" ? entry.timestamp : "");
|
|
109
115
|
}
|
|
110
116
|
catch {
|
|
111
|
-
// Skip malformed lines
|
|
117
|
+
// Skip malformed lines — a permanently-malformed line is skipped
|
|
118
|
+
// identically every run, and a partial trailing write fails JSON.parse
|
|
119
|
+
// now and parses (at the same index) once fully flushed.
|
|
112
120
|
}
|
|
113
121
|
}
|
|
122
|
+
// Whole-file degeneracy gate stays on the full parse, not the delta.
|
|
114
123
|
if (entries.length < exports.MIN_TRANSCRIPT_ENTRIES)
|
|
115
124
|
return null;
|
|
116
|
-
//
|
|
117
|
-
|
|
125
|
+
// Shrink guard: a truncated/rotated file with fewer entries than the stored
|
|
126
|
+
// cursor → reset to 0 AND bump the generation. The generation is woven into
|
|
127
|
+
// the segment id downstream so the fresh file's segments can never collide
|
|
128
|
+
// with the pre-reset ids on the server's content-blind dedup (fix 8).
|
|
129
|
+
let start = startCursor;
|
|
130
|
+
let gen = generation;
|
|
131
|
+
if (start > entries.length) {
|
|
132
|
+
start = 0;
|
|
133
|
+
gen = generation + 1;
|
|
134
|
+
}
|
|
135
|
+
const delta = entries.slice(start);
|
|
136
|
+
if (delta.length === 0)
|
|
137
|
+
return null; // cursor already covers the whole file
|
|
138
|
+
// Per-entry end cursors: entry i (0-based in the delta) ends at start+i+1.
|
|
139
|
+
const entryCursors = delta.map((_, i) => start + i + 1);
|
|
140
|
+
// Date from the LAST timestamped entry in the delta (per-night created_at for
|
|
141
|
+
// multi-day sessions), falling back to today.
|
|
142
|
+
let lastTimestamp = "";
|
|
143
|
+
for (let i = start; i < entries.length; i++) {
|
|
144
|
+
if (timestamps[i])
|
|
145
|
+
lastTimestamp = timestamps[i];
|
|
146
|
+
}
|
|
118
147
|
return {
|
|
119
|
-
sessionId,
|
|
148
|
+
sessionId: (0, node_path_1.basename)(filePath, ".jsonl"),
|
|
120
149
|
projectName,
|
|
121
150
|
date: lastTimestamp
|
|
122
151
|
? lastTimestamp.slice(0, 10)
|
|
123
152
|
: new Date().toISOString().slice(0, 10),
|
|
124
|
-
entries,
|
|
153
|
+
entries: delta,
|
|
154
|
+
cursorKey,
|
|
155
|
+
startCursor: start,
|
|
156
|
+
generation: gen,
|
|
157
|
+
entryCursors,
|
|
125
158
|
};
|
|
126
159
|
}
|
|
127
160
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -115,6 +115,16 @@ export interface ConsolidationReport {
|
|
|
115
115
|
calls_by_stage: Record<string, number>;
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* A single per-stage override inside the nested `models` server-config block.
|
|
120
|
+
* Consumed by `applyModelsBlock` (llm.ts), which re-exports this type.
|
|
121
|
+
*/
|
|
122
|
+
export interface ModelTierOverride {
|
|
123
|
+
model?: string;
|
|
124
|
+
baseUrl?: string;
|
|
125
|
+
apiKey?: string;
|
|
126
|
+
provider?: string;
|
|
127
|
+
}
|
|
118
128
|
/** Plugin configuration from openclaw.plugin.json configSchema. */
|
|
119
129
|
export interface HicortexConfig {
|
|
120
130
|
licenseKey?: string;
|
|
@@ -145,6 +155,16 @@ export interface HicortexConfig {
|
|
|
145
155
|
classifyApiKey?: string;
|
|
146
156
|
/** Optional provider for the classify endpoint (defaults to the base provider). */
|
|
147
157
|
classifyProvider?: string;
|
|
158
|
+
/**
|
|
159
|
+
* Server config (NOT an OC-plugin key): nested per-stage model overrides.
|
|
160
|
+
* `{ score|distill|reflect|classify: { model?, baseUrl?, apiKey?, provider? } }`.
|
|
161
|
+
* Normalized onto the flat `llm*` / `distill*` / `reflect*` / `classify*`
|
|
162
|
+
* keys at read time (see applyModelsBlock in llm.ts); nested wins, and the
|
|
163
|
+
* flat keys remain supported at lower precedence. Happy path is a single model via
|
|
164
|
+
* `llmModel`; use this block only for per-stage routing. `score.provider` is
|
|
165
|
+
* ignored (base provider comes from llmBackend).
|
|
166
|
+
*/
|
|
167
|
+
models?: Record<string, ModelTierOverride>;
|
|
148
168
|
/** @deprecated Consolidation is owned by the server nightly. */
|
|
149
169
|
consolidateHour?: number;
|
|
150
170
|
/** @deprecated The OC plugin no longer opens its own database. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "Self-learning memory for AI agents \u2014 experience captured automatically, distilled into lessons overnight, shared across your whole fleet. Works with Hermes, OpenClaw, Claude Code, and Pi.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|