claude-memory-layer 1.0.23 → 1.0.25
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/.claude/settings.local.json +25 -0
- package/README.md +2 -0
- package/dist/cli/index.js +229 -978
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +59 -71
- package/dist/core/index.js.map +3 -3
- package/dist/hooks/post-tool-use.js +287 -976
- package/dist/hooks/post-tool-use.js.map +4 -4
- package/dist/hooks/semantic-daemon.js +6520 -0
- package/dist/hooks/semantic-daemon.js.map +7 -0
- package/dist/hooks/session-end.js +209 -973
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +293 -978
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +247 -975
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +406 -1036
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/server/api/index.js +209 -973
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +209 -973
- package/dist/server/index.js.map +4 -4
- package/dist/services/memory-service.js +209 -973
- package/dist/services/memory-service.js.map +4 -4
- package/dist/ui/app.js +48 -1
- package/dist/ui/index.html +11 -3
- package/memory/_index.md +1 -0
- package/memory/agent_response/uncategorized/2026-03-04.md +1314 -1
- package/memory/session_summary/uncategorized/2026-03-04.md +50 -0
- package/memory/tool_observation/uncategorized/2026-03-04.md +969 -1
- package/memory/user_prompt/uncategorized/2026-03-04.md +555 -1
- package/package.json +1 -2
- package/scripts/build.ts +2 -1
- package/specs/memory-utilization-improvements/context.md +145 -0
- package/specs/memory-utilization-improvements/plan.md +361 -0
- package/specs/memory-utilization-improvements/spec.md +308 -0
- package/specs/optional-duckdb/context.md +77 -0
- package/specs/optional-duckdb/plan.md +142 -0
- package/specs/optional-duckdb/spec.md +35 -0
- package/specs/selective-tool-observation/context.md +100 -0
- package/specs/selective-tool-observation/plan.md +158 -0
- package/specs/selective-tool-observation/spec.md +127 -0
- package/src/cli/index.ts +1 -0
- package/src/core/db-wrapper.ts +18 -73
- package/src/core/embedder.ts +13 -4
- package/src/core/sqlite-event-store.ts +40 -0
- package/src/core/turn-state.ts +48 -0
- package/src/core/types.ts +1 -0
- package/src/hooks/post-tool-use.ts +72 -2
- package/src/hooks/semantic-daemon-client.ts +208 -0
- package/src/hooks/semantic-daemon.ts +276 -0
- package/src/hooks/session-start.ts +11 -0
- package/src/hooks/stop.ts +33 -4
- package/src/hooks/user-prompt-submit.ts +48 -40
- package/src/services/memory-service.ts +112 -65
- package/src/services/session-history-importer.ts +18 -0
- package/src/ui/app.js +48 -1
- package/src/ui/index.html +11 -3
package/dist/core/index.js
CHANGED
|
@@ -124,6 +124,7 @@ var ConfigSchema = z.object({
|
|
|
124
124
|
toolObservation: z.object({
|
|
125
125
|
enabled: z.boolean().default(true),
|
|
126
126
|
excludedTools: z.array(z.string()).default(["TodoWrite", "TodoRead"]),
|
|
127
|
+
minOutputLength: z.number().default(100),
|
|
127
128
|
maxOutputLength: z.number().default(1e4),
|
|
128
129
|
maxOutputLines: z.number().default(100),
|
|
129
130
|
storeOnlyOnSuccess: z.boolean().default(false)
|
|
@@ -620,25 +621,7 @@ function parseEntityCanonicalKey(canonicalKey) {
|
|
|
620
621
|
import { randomUUID } from "crypto";
|
|
621
622
|
|
|
622
623
|
// src/core/db-wrapper.ts
|
|
623
|
-
import
|
|
624
|
-
function convertBigInts(obj) {
|
|
625
|
-
if (obj === null || obj === void 0)
|
|
626
|
-
return obj;
|
|
627
|
-
if (typeof obj === "bigint")
|
|
628
|
-
return Number(obj);
|
|
629
|
-
if (obj instanceof Date)
|
|
630
|
-
return obj;
|
|
631
|
-
if (Array.isArray(obj))
|
|
632
|
-
return obj.map(convertBigInts);
|
|
633
|
-
if (typeof obj === "object") {
|
|
634
|
-
const result = {};
|
|
635
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
636
|
-
result[key] = convertBigInts(value);
|
|
637
|
-
}
|
|
638
|
-
return result;
|
|
639
|
-
}
|
|
640
|
-
return obj;
|
|
641
|
-
}
|
|
624
|
+
import BetterSqlite3 from "better-sqlite3";
|
|
642
625
|
function toDate(value) {
|
|
643
626
|
if (value instanceof Date)
|
|
644
627
|
return value;
|
|
@@ -648,59 +631,19 @@ function toDate(value) {
|
|
|
648
631
|
return new Date(value);
|
|
649
632
|
return new Date(String(value));
|
|
650
633
|
}
|
|
651
|
-
function createDatabase(
|
|
652
|
-
|
|
653
|
-
return new duckdb.Database(path2, { access_mode: "READ_ONLY" });
|
|
654
|
-
}
|
|
655
|
-
return new duckdb.Database(path2);
|
|
634
|
+
function createDatabase(dbPath, options) {
|
|
635
|
+
return new BetterSqlite3(dbPath, { readonly: options?.readOnly });
|
|
656
636
|
}
|
|
657
637
|
function dbRun(db, sql, params = []) {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
db.run(sql, (err) => {
|
|
661
|
-
if (err)
|
|
662
|
-
reject(err);
|
|
663
|
-
else
|
|
664
|
-
resolve();
|
|
665
|
-
});
|
|
666
|
-
} else {
|
|
667
|
-
db.run(sql, ...params, (err) => {
|
|
668
|
-
if (err)
|
|
669
|
-
reject(err);
|
|
670
|
-
else
|
|
671
|
-
resolve();
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
});
|
|
638
|
+
db.prepare(sql).run(...params);
|
|
639
|
+
return Promise.resolve();
|
|
675
640
|
}
|
|
676
641
|
function dbAll(db, sql, params = []) {
|
|
677
|
-
return
|
|
678
|
-
if (params.length === 0) {
|
|
679
|
-
db.all(sql, (err, rows) => {
|
|
680
|
-
if (err)
|
|
681
|
-
reject(err);
|
|
682
|
-
else
|
|
683
|
-
resolve(convertBigInts(rows || []));
|
|
684
|
-
});
|
|
685
|
-
} else {
|
|
686
|
-
db.all(sql, ...params, (err, rows) => {
|
|
687
|
-
if (err)
|
|
688
|
-
reject(err);
|
|
689
|
-
else
|
|
690
|
-
resolve(convertBigInts(rows || []));
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
});
|
|
642
|
+
return Promise.resolve(db.prepare(sql).all(...params));
|
|
694
643
|
}
|
|
695
644
|
function dbClose(db) {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
if (err)
|
|
699
|
-
reject(err);
|
|
700
|
-
else
|
|
701
|
-
resolve();
|
|
702
|
-
});
|
|
703
|
-
});
|
|
645
|
+
db.close();
|
|
646
|
+
return Promise.resolve();
|
|
704
647
|
}
|
|
705
648
|
|
|
706
649
|
// src/core/event-store.ts
|
|
@@ -1902,6 +1845,29 @@ var SQLiteEventStore = class {
|
|
|
1902
1845
|
};
|
|
1903
1846
|
}
|
|
1904
1847
|
}
|
|
1848
|
+
/**
|
|
1849
|
+
* Get session IDs that have events but no session_summary event.
|
|
1850
|
+
* Used to backfill summaries for sessions that ended without Stop hook.
|
|
1851
|
+
*/
|
|
1852
|
+
async getSessionsWithoutSummary(currentSessionId, limit = 5) {
|
|
1853
|
+
await this.initialize();
|
|
1854
|
+
const rows = sqliteAll(
|
|
1855
|
+
this.db,
|
|
1856
|
+
`SELECT DISTINCT e.session_id
|
|
1857
|
+
FROM events e
|
|
1858
|
+
WHERE e.session_id != ?
|
|
1859
|
+
AND e.event_type != 'session_summary'
|
|
1860
|
+
AND e.session_id NOT IN (
|
|
1861
|
+
SELECT DISTINCT session_id FROM events WHERE event_type = 'session_summary'
|
|
1862
|
+
)
|
|
1863
|
+
GROUP BY e.session_id
|
|
1864
|
+
HAVING COUNT(*) >= 3
|
|
1865
|
+
ORDER BY MAX(e.timestamp) DESC
|
|
1866
|
+
LIMIT ?`,
|
|
1867
|
+
[currentSessionId, limit]
|
|
1868
|
+
);
|
|
1869
|
+
return rows.map((r) => r.session_id);
|
|
1870
|
+
}
|
|
1905
1871
|
/**
|
|
1906
1872
|
* Get events by session ID
|
|
1907
1873
|
*/
|
|
@@ -2418,6 +2384,21 @@ var SQLiteEventStore = class {
|
|
|
2418
2384
|
[id, eventId, sessionId, score, query.slice(0, 100)]
|
|
2419
2385
|
);
|
|
2420
2386
|
}
|
|
2387
|
+
/**
|
|
2388
|
+
* Get session IDs that have unevaluated retrievals (measured_at IS NULL).
|
|
2389
|
+
* Excludes the current session. Used to backfill sessions that ended without Stop hook.
|
|
2390
|
+
*/
|
|
2391
|
+
async getUnevaluatedSessions(currentSessionId, limit = 5) {
|
|
2392
|
+
await this.initialize();
|
|
2393
|
+
const rows = sqliteAll(
|
|
2394
|
+
this.db,
|
|
2395
|
+
`SELECT DISTINCT session_id FROM memory_helpfulness
|
|
2396
|
+
WHERE measured_at IS NULL AND session_id != ?
|
|
2397
|
+
ORDER BY created_at DESC LIMIT ?`,
|
|
2398
|
+
[currentSessionId, limit]
|
|
2399
|
+
);
|
|
2400
|
+
return rows.map((r) => r.session_id);
|
|
2401
|
+
}
|
|
2421
2402
|
/**
|
|
2422
2403
|
* Evaluate helpfulness for all retrievals in a session
|
|
2423
2404
|
* Called at session end - uses behavioral signals to compute score
|
|
@@ -3997,7 +3978,7 @@ var VectorStore = class {
|
|
|
3997
3978
|
|
|
3998
3979
|
// src/core/embedder.ts
|
|
3999
3980
|
import { pipeline } from "@huggingface/transformers";
|
|
4000
|
-
var Embedder = class {
|
|
3981
|
+
var Embedder = class _Embedder {
|
|
4001
3982
|
pipeline = null;
|
|
4002
3983
|
modelName;
|
|
4003
3984
|
activeModelName;
|
|
@@ -4028,6 +4009,11 @@ var Embedder = class {
|
|
|
4028
4009
|
this.initialized = true;
|
|
4029
4010
|
}
|
|
4030
4011
|
}
|
|
4012
|
+
// ~4 chars per token; 512 tokens * 4 = 2048, use 2000 to be safe
|
|
4013
|
+
static MAX_CHARS = 2e3;
|
|
4014
|
+
truncate(text) {
|
|
4015
|
+
return text.length > _Embedder.MAX_CHARS ? text.slice(0, _Embedder.MAX_CHARS) : text;
|
|
4016
|
+
}
|
|
4031
4017
|
/**
|
|
4032
4018
|
* Generate embedding for a single text
|
|
4033
4019
|
*/
|
|
@@ -4036,10 +4022,11 @@ var Embedder = class {
|
|
|
4036
4022
|
if (!this.pipeline) {
|
|
4037
4023
|
throw new Error("Embedding pipeline not initialized");
|
|
4038
4024
|
}
|
|
4039
|
-
const output = await this.pipeline(text, {
|
|
4025
|
+
const output = await this.pipeline(this.truncate(text), {
|
|
4040
4026
|
pooling: "mean",
|
|
4041
4027
|
normalize: true,
|
|
4042
|
-
truncation: true
|
|
4028
|
+
truncation: true,
|
|
4029
|
+
max_length: 512
|
|
4043
4030
|
});
|
|
4044
4031
|
const vector = Array.from(output.data);
|
|
4045
4032
|
return {
|
|
@@ -4061,10 +4048,11 @@ var Embedder = class {
|
|
|
4061
4048
|
for (let i = 0; i < texts.length; i += batchSize) {
|
|
4062
4049
|
const batch = texts.slice(i, i + batchSize);
|
|
4063
4050
|
for (const text of batch) {
|
|
4064
|
-
const output = await this.pipeline(text, {
|
|
4051
|
+
const output = await this.pipeline(this.truncate(text), {
|
|
4065
4052
|
pooling: "mean",
|
|
4066
4053
|
normalize: true,
|
|
4067
|
-
truncation: true
|
|
4054
|
+
truncation: true,
|
|
4055
|
+
max_length: 512
|
|
4068
4056
|
});
|
|
4069
4057
|
const vector = Array.from(output.data);
|
|
4070
4058
|
results.push({
|