@getplumb/core 0.1.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/LICENSE +21 -0
- package/README.md +48 -0
- package/dist/bm25.d.ts +23 -0
- package/dist/bm25.d.ts.map +1 -0
- package/dist/bm25.js +74 -0
- package/dist/bm25.js.map +1 -0
- package/dist/chunker.d.ts +35 -0
- package/dist/chunker.d.ts.map +1 -0
- package/dist/chunker.js +45 -0
- package/dist/chunker.js.map +1 -0
- package/dist/context-builder.d.ts +33 -0
- package/dist/context-builder.d.ts.map +1 -0
- package/dist/context-builder.js +101 -0
- package/dist/context-builder.js.map +1 -0
- package/dist/embedder.d.ts +34 -0
- package/dist/embedder.d.ts.map +1 -0
- package/dist/embedder.js +88 -0
- package/dist/embedder.js.map +1 -0
- package/dist/extractor.d.ts +21 -0
- package/dist/extractor.d.ts.map +1 -0
- package/dist/extractor.js +89 -0
- package/dist/extractor.js.map +1 -0
- package/dist/fact-search.d.ts +28 -0
- package/dist/fact-search.d.ts.map +1 -0
- package/dist/fact-search.js +155 -0
- package/dist/fact-search.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/llm-client.d.ts +28 -0
- package/dist/llm-client.d.ts.map +1 -0
- package/dist/llm-client.js +115 -0
- package/dist/llm-client.js.map +1 -0
- package/dist/local-store.d.ts +99 -0
- package/dist/local-store.d.ts.map +1 -0
- package/dist/local-store.js +292 -0
- package/dist/local-store.js.map +1 -0
- package/dist/raw-log-search.d.ts +33 -0
- package/dist/raw-log-search.d.ts.map +1 -0
- package/dist/raw-log-search.js +137 -0
- package/dist/raw-log-search.js.map +1 -0
- package/dist/read-path.d.ts +60 -0
- package/dist/read-path.d.ts.map +1 -0
- package/dist/read-path.js +76 -0
- package/dist/read-path.js.map +1 -0
- package/dist/schema.d.ts +34 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +119 -0
- package/dist/schema.js.map +1 -0
- package/dist/scorer.d.ts +30 -0
- package/dist/scorer.d.ts.map +1 -0
- package/dist/scorer.js +50 -0
- package/dist/scorer.js.map +1 -0
- package/dist/store.d.ts +25 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +2 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite schema for Plumb LocalStore.
|
|
3
|
+
*
|
|
4
|
+
* Two tables per user (scoped by user_id column, NOT separate tables per user):
|
|
5
|
+
* - facts: Layer 2 structured fact graph (subject-predicate-object triples)
|
|
6
|
+
* - raw_log: Layer 1 lossless conversation chunks
|
|
7
|
+
*
|
|
8
|
+
* Design principles:
|
|
9
|
+
* - Soft deletes only: deleted_at timestamp, never hard delete
|
|
10
|
+
* - Cross-session by design: session info stored as metadata, not as a boundary
|
|
11
|
+
* - Indexes on high-cardinality query axes: user_id, session_id, timestamp, deleted_at
|
|
12
|
+
*/
|
|
13
|
+
export declare const CREATE_FACTS_TABLE = "\n CREATE TABLE IF NOT EXISTS facts (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL,\n subject TEXT NOT NULL,\n predicate TEXT NOT NULL,\n object TEXT NOT NULL,\n confidence REAL NOT NULL,\n decay_rate TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n source_session_id TEXT NOT NULL,\n source_session_label TEXT,\n context TEXT,\n deleted_at TEXT\n )\n";
|
|
14
|
+
export declare const CREATE_FACTS_INDEXES: string[];
|
|
15
|
+
export declare const CREATE_RAW_LOG_TABLE = "\n CREATE TABLE IF NOT EXISTS raw_log (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL,\n session_id TEXT NOT NULL,\n session_label TEXT,\n user_message TEXT NOT NULL,\n agent_response TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n source TEXT NOT NULL,\n chunk_text TEXT NOT NULL,\n chunk_index INTEGER NOT NULL,\n vec_rowid INTEGER,\n content_hash TEXT,\n UNIQUE(user_id, content_hash)\n )\n";
|
|
16
|
+
export declare const CREATE_RAW_LOG_INDEXES: string[];
|
|
17
|
+
/**
|
|
18
|
+
* sqlite-vec virtual table for KNN vector search over raw_log chunks.
|
|
19
|
+
* Rowid mirrors the raw_log SQLite rowid for O(1) joins without a separate mapping table.
|
|
20
|
+
* FLOAT[384] matches BAAI/bge-small-en-v1.5 output dimension.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CREATE_VEC_RAW_LOG = "\n CREATE VIRTUAL TABLE IF NOT EXISTS vec_raw_log USING vec0(\n embedding FLOAT[384]\n )\n";
|
|
23
|
+
/**
|
|
24
|
+
* sqlite-vec virtual table for KNN vector search over facts.
|
|
25
|
+
* FLOAT[384] matches BAAI/bge-small-en-v1.5 output dimension.
|
|
26
|
+
*/
|
|
27
|
+
export declare const CREATE_VEC_FACTS = "\n CREATE VIRTUAL TABLE IF NOT EXISTS vec_facts USING vec0(\n embedding FLOAT[384]\n )\n";
|
|
28
|
+
/**
|
|
29
|
+
* Nudge log table for tracking one-time upgrade prompts.
|
|
30
|
+
* Each trigger type fires exactly once per install.
|
|
31
|
+
*/
|
|
32
|
+
export declare const CREATE_NUDGE_LOG_TABLE = "\n CREATE TABLE IF NOT EXISTS nudge_log (\n id TEXT PRIMARY KEY,\n trigger_type TEXT NOT NULL,\n fired_at TEXT NOT NULL\n )\n";
|
|
33
|
+
export declare function applySchema(db: import('better-sqlite3').Database): void;
|
|
34
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,kBAAkB,0fAe9B,CAAC;AAEF,eAAO,MAAM,oBAAoB,UAKhC,CAAC;AAEF,eAAO,MAAM,oBAAoB,8dAgBhC,CAAC;AAEF,eAAO,MAAM,sBAAsB,UAIlC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,oGAI9B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,kGAI5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,8JAMlC,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,EAAE,OAAO,gBAAgB,EAAE,QAAQ,GAAG,IAAI,CAkCvE"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite schema for Plumb LocalStore.
|
|
3
|
+
*
|
|
4
|
+
* Two tables per user (scoped by user_id column, NOT separate tables per user):
|
|
5
|
+
* - facts: Layer 2 structured fact graph (subject-predicate-object triples)
|
|
6
|
+
* - raw_log: Layer 1 lossless conversation chunks
|
|
7
|
+
*
|
|
8
|
+
* Design principles:
|
|
9
|
+
* - Soft deletes only: deleted_at timestamp, never hard delete
|
|
10
|
+
* - Cross-session by design: session info stored as metadata, not as a boundary
|
|
11
|
+
* - Indexes on high-cardinality query axes: user_id, session_id, timestamp, deleted_at
|
|
12
|
+
*/
|
|
13
|
+
export const CREATE_FACTS_TABLE = `
|
|
14
|
+
CREATE TABLE IF NOT EXISTS facts (
|
|
15
|
+
id TEXT PRIMARY KEY,
|
|
16
|
+
user_id TEXT NOT NULL,
|
|
17
|
+
subject TEXT NOT NULL,
|
|
18
|
+
predicate TEXT NOT NULL,
|
|
19
|
+
object TEXT NOT NULL,
|
|
20
|
+
confidence REAL NOT NULL,
|
|
21
|
+
decay_rate TEXT NOT NULL,
|
|
22
|
+
timestamp TEXT NOT NULL,
|
|
23
|
+
source_session_id TEXT NOT NULL,
|
|
24
|
+
source_session_label TEXT,
|
|
25
|
+
context TEXT,
|
|
26
|
+
deleted_at TEXT
|
|
27
|
+
)
|
|
28
|
+
`;
|
|
29
|
+
export const CREATE_FACTS_INDEXES = [
|
|
30
|
+
`CREATE INDEX IF NOT EXISTS idx_facts_user_id ON facts (user_id)`,
|
|
31
|
+
`CREATE INDEX IF NOT EXISTS idx_facts_session_id ON facts (source_session_id)`,
|
|
32
|
+
`CREATE INDEX IF NOT EXISTS idx_facts_timestamp ON facts (timestamp)`,
|
|
33
|
+
`CREATE INDEX IF NOT EXISTS idx_facts_deleted_at ON facts (deleted_at)`,
|
|
34
|
+
];
|
|
35
|
+
export const CREATE_RAW_LOG_TABLE = `
|
|
36
|
+
CREATE TABLE IF NOT EXISTS raw_log (
|
|
37
|
+
id TEXT PRIMARY KEY,
|
|
38
|
+
user_id TEXT NOT NULL,
|
|
39
|
+
session_id TEXT NOT NULL,
|
|
40
|
+
session_label TEXT,
|
|
41
|
+
user_message TEXT NOT NULL,
|
|
42
|
+
agent_response TEXT NOT NULL,
|
|
43
|
+
timestamp TEXT NOT NULL,
|
|
44
|
+
source TEXT NOT NULL,
|
|
45
|
+
chunk_text TEXT NOT NULL,
|
|
46
|
+
chunk_index INTEGER NOT NULL,
|
|
47
|
+
vec_rowid INTEGER,
|
|
48
|
+
content_hash TEXT,
|
|
49
|
+
UNIQUE(user_id, content_hash)
|
|
50
|
+
)
|
|
51
|
+
`;
|
|
52
|
+
export const CREATE_RAW_LOG_INDEXES = [
|
|
53
|
+
`CREATE INDEX IF NOT EXISTS idx_raw_log_user_id ON raw_log (user_id)`,
|
|
54
|
+
`CREATE INDEX IF NOT EXISTS idx_raw_log_session_id ON raw_log (session_id)`,
|
|
55
|
+
`CREATE INDEX IF NOT EXISTS idx_raw_log_timestamp ON raw_log (timestamp)`,
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* sqlite-vec virtual table for KNN vector search over raw_log chunks.
|
|
59
|
+
* Rowid mirrors the raw_log SQLite rowid for O(1) joins without a separate mapping table.
|
|
60
|
+
* FLOAT[384] matches BAAI/bge-small-en-v1.5 output dimension.
|
|
61
|
+
*/
|
|
62
|
+
export const CREATE_VEC_RAW_LOG = `
|
|
63
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS vec_raw_log USING vec0(
|
|
64
|
+
embedding FLOAT[384]
|
|
65
|
+
)
|
|
66
|
+
`;
|
|
67
|
+
/**
|
|
68
|
+
* sqlite-vec virtual table for KNN vector search over facts.
|
|
69
|
+
* FLOAT[384] matches BAAI/bge-small-en-v1.5 output dimension.
|
|
70
|
+
*/
|
|
71
|
+
export const CREATE_VEC_FACTS = `
|
|
72
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS vec_facts USING vec0(
|
|
73
|
+
embedding FLOAT[384]
|
|
74
|
+
)
|
|
75
|
+
`;
|
|
76
|
+
/**
|
|
77
|
+
* Nudge log table for tracking one-time upgrade prompts.
|
|
78
|
+
* Each trigger type fires exactly once per install.
|
|
79
|
+
*/
|
|
80
|
+
export const CREATE_NUDGE_LOG_TABLE = `
|
|
81
|
+
CREATE TABLE IF NOT EXISTS nudge_log (
|
|
82
|
+
id TEXT PRIMARY KEY,
|
|
83
|
+
trigger_type TEXT NOT NULL,
|
|
84
|
+
fired_at TEXT NOT NULL
|
|
85
|
+
)
|
|
86
|
+
`;
|
|
87
|
+
export function applySchema(db) {
|
|
88
|
+
db.exec(CREATE_FACTS_TABLE);
|
|
89
|
+
for (const idx of CREATE_FACTS_INDEXES) {
|
|
90
|
+
db.exec(idx);
|
|
91
|
+
}
|
|
92
|
+
db.exec(CREATE_RAW_LOG_TABLE);
|
|
93
|
+
for (const idx of CREATE_RAW_LOG_INDEXES) {
|
|
94
|
+
db.exec(idx);
|
|
95
|
+
}
|
|
96
|
+
db.exec(CREATE_VEC_RAW_LOG);
|
|
97
|
+
db.exec(CREATE_VEC_FACTS);
|
|
98
|
+
// Conditional migration: add vec_rowid column to facts if it doesn't exist yet.
|
|
99
|
+
const factsColumns = db.pragma('table_info(facts)');
|
|
100
|
+
const hasVecRowid = factsColumns.some((c) => c.name === 'vec_rowid');
|
|
101
|
+
if (!hasVecRowid) {
|
|
102
|
+
db.exec('ALTER TABLE facts ADD COLUMN vec_rowid INTEGER');
|
|
103
|
+
}
|
|
104
|
+
// Conditional migration: add content_hash column to raw_log if it doesn't exist yet.
|
|
105
|
+
const rawLogColumns = db.pragma('table_info(raw_log)');
|
|
106
|
+
const hasContentHash = rawLogColumns.some((c) => c.name === 'content_hash');
|
|
107
|
+
if (!hasContentHash) {
|
|
108
|
+
db.exec('ALTER TABLE raw_log ADD COLUMN content_hash TEXT');
|
|
109
|
+
// Create unique constraint on (user_id, content_hash).
|
|
110
|
+
// SQLite UNIQUE constraints ignore NULL values, so existing rows with NULL won't conflict.
|
|
111
|
+
db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_raw_log_content_hash ON raw_log(user_id, content_hash)');
|
|
112
|
+
}
|
|
113
|
+
// Conditional migration: create nudge_log table if it doesn't exist yet.
|
|
114
|
+
const tables = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='nudge_log'`).all();
|
|
115
|
+
if (tables.length === 0) {
|
|
116
|
+
db.exec(CREATE_NUDGE_LOG_TABLE);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;CAejC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,iEAAiE;IACjE,8EAA8E;IAC9E,qEAAqE;IACrE,uEAAuE;CACxE,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;CAgBnC,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,qEAAqE;IACrE,2EAA2E;IAC3E,yEAAyE;CAC1E,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;CAIjC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;CAI/B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;CAMrC,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,EAAqC;IAC/D,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC5B,KAAK,MAAM,GAAG,IAAI,oBAAoB,EAAE,CAAC;QACvC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;IACD,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,sBAAsB,EAAE,CAAC;QACzC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;IACD,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC5B,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAE1B,gFAAgF;IAChF,MAAM,YAAY,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAA4B,CAAC;IAC/E,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;IACrE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,EAAE,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;IAC5D,CAAC;IAED,qFAAqF;IACrF,MAAM,aAAa,GAAG,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAA4B,CAAC;IAClF,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC;IAC5E,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,EAAE,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QAC5D,uDAAuD;QACvD,2FAA2F;QAC3F,EAAE,CAAC,IAAI,CAAC,8FAA8F,CAAC,CAAC;IAC1G,CAAC;IAED,yEAAyE;IACzE,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,CAAC,wEAAwE,CAAC,CAAC,GAAG,EAAE,CAAC;IAC1G,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAClC,CAAC;AACH,CAAC"}
|
package/dist/scorer.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type Fact } from './types.js';
|
|
2
|
+
export interface ScoreResult {
|
|
3
|
+
readonly score: number;
|
|
4
|
+
readonly isCold: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Minimal shape required by scoreRawLog().
|
|
8
|
+
* raw_log rows carry a timestamp; other fields are not needed for scoring.
|
|
9
|
+
*/
|
|
10
|
+
export interface RawLogChunk {
|
|
11
|
+
readonly timestamp: Date;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Computes the exponential decay multiplier: e^(-lambda × age_in_days).
|
|
15
|
+
*/
|
|
16
|
+
export declare function computeDecay(lambda: number, ageInDays: number): number;
|
|
17
|
+
/**
|
|
18
|
+
* Scores a fact at read time using:
|
|
19
|
+
* score = base_confidence × e^(-lambda × age_in_days) × retrieval_frequency_boost
|
|
20
|
+
*
|
|
21
|
+
* Returns a ScoreResult with the computed score (0–1) and an isCold flag.
|
|
22
|
+
* Facts are never deleted — cold facts (score < 0.01) remain queryable.
|
|
23
|
+
*/
|
|
24
|
+
export declare function scoreFact(fact: Fact, now?: Date): ScoreResult;
|
|
25
|
+
/**
|
|
26
|
+
* Scores a raw log chunk using medium decay (lambda = 0.012).
|
|
27
|
+
* Raw chunks have no confidence field; decay is applied to a base of 1.0.
|
|
28
|
+
*/
|
|
29
|
+
export declare function scoreRawLog(chunk: RawLogChunk, now?: Date): ScoreResult;
|
|
30
|
+
//# sourceMappingURL=scorer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorer.d.ts","sourceRoot":"","sources":["../src/scorer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAqBlD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,GAAE,IAAiB,GAAG,WAAW,CAUzE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,WAAW,EAClB,GAAG,GAAE,IAAiB,GACrB,WAAW,CAKb"}
|
package/dist/scorer.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { DecayRate } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Lambda decay constants per decay rate.
|
|
4
|
+
* These control how quickly a fact loses relevance over time.
|
|
5
|
+
* slow (0.003): identity, stable preferences — half-life ~231 days
|
|
6
|
+
* medium (0.012): tool prefs, project context — half-life ~58 days
|
|
7
|
+
* fast (0.050): transient state — half-life ~14 days
|
|
8
|
+
*/
|
|
9
|
+
const LAMBDA = {
|
|
10
|
+
[DecayRate.slow]: 0.003,
|
|
11
|
+
[DecayRate.medium]: 0.012,
|
|
12
|
+
[DecayRate.fast]: 0.05,
|
|
13
|
+
};
|
|
14
|
+
/** Lambda applied to raw log chunks (medium decay). */
|
|
15
|
+
const RAW_LOG_LAMBDA = 0.012;
|
|
16
|
+
/** Cold score threshold — facts below this are flagged but never deleted. */
|
|
17
|
+
const COLD_THRESHOLD = 0.01;
|
|
18
|
+
/**
|
|
19
|
+
* Computes the exponential decay multiplier: e^(-lambda × age_in_days).
|
|
20
|
+
*/
|
|
21
|
+
export function computeDecay(lambda, ageInDays) {
|
|
22
|
+
return Math.exp(-lambda * ageInDays);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Scores a fact at read time using:
|
|
26
|
+
* score = base_confidence × e^(-lambda × age_in_days) × retrieval_frequency_boost
|
|
27
|
+
*
|
|
28
|
+
* Returns a ScoreResult with the computed score (0–1) and an isCold flag.
|
|
29
|
+
* Facts are never deleted — cold facts (score < 0.01) remain queryable.
|
|
30
|
+
*/
|
|
31
|
+
export function scoreFact(fact, now = new Date()) {
|
|
32
|
+
const ageInDays = (now.getTime() - fact.timestamp.getTime()) / (1_000 * 60 * 60 * 24);
|
|
33
|
+
const lambda = LAMBDA[fact.decayRate];
|
|
34
|
+
const decay = computeDecay(lambda, ageInDays);
|
|
35
|
+
// TODO: retrieval_frequency_boost — track access frequency in T-008 read path;
|
|
36
|
+
// for now we use 1.0 (no boost) as a placeholder.
|
|
37
|
+
const boost = 1.0;
|
|
38
|
+
const score = fact.confidence * decay * boost;
|
|
39
|
+
return { score, isCold: score < COLD_THRESHOLD };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Scores a raw log chunk using medium decay (lambda = 0.012).
|
|
43
|
+
* Raw chunks have no confidence field; decay is applied to a base of 1.0.
|
|
44
|
+
*/
|
|
45
|
+
export function scoreRawLog(chunk, now = new Date()) {
|
|
46
|
+
const ageInDays = (now.getTime() - chunk.timestamp.getTime()) / (1_000 * 60 * 60 * 24);
|
|
47
|
+
const score = computeDecay(RAW_LOG_LAMBDA, ageInDays);
|
|
48
|
+
return { score, isCold: score < COLD_THRESHOLD };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=scorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorer.js","sourceRoot":"","sources":["../src/scorer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,MAAM,GAA8B;IACxC,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK;IACvB,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK;IACzB,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI;CACvB,CAAC;AAEF,uDAAuD;AACvD,MAAM,cAAc,GAAG,KAAK,CAAC;AAE7B,6EAA6E;AAC7E,MAAM,cAAc,GAAG,IAAI,CAAC;AAe5B;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,SAAiB;IAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,IAAU,EAAE,MAAY,IAAI,IAAI,EAAE;IAC1D,MAAM,SAAS,GACb,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC9C,+EAA+E;IAC/E,wDAAwD;IACxD,MAAM,KAAK,GAAG,GAAG,CAAC;IAClB,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,GAAG,KAAK,GAAG,KAAK,CAAC;IAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAG,cAAc,EAAE,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,KAAkB,EAClB,MAAY,IAAI,IAAI,EAAE;IAEtB,MAAM,SAAS,GACb,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,YAAY,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IACtD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAG,cAAc,EAAE,CAAC;AACnD,CAAC"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Fact, IngestResult, MessageExchange, SearchResult, StoreStatus } from './types.js';
|
|
2
|
+
export interface MemoryStore {
|
|
3
|
+
/**
|
|
4
|
+
* Write a fact to the store. The store generates and returns the id.
|
|
5
|
+
*/
|
|
6
|
+
store(fact: Omit<Fact, 'id'>): Promise<string>;
|
|
7
|
+
/**
|
|
8
|
+
* Search for facts matching the query. Returns ranked results with score and age.
|
|
9
|
+
*/
|
|
10
|
+
search(query: string, limit?: number): Promise<readonly SearchResult[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Delete a fact by id.
|
|
13
|
+
*/
|
|
14
|
+
delete(id: string): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Return current store statistics.
|
|
17
|
+
*/
|
|
18
|
+
status(): Promise<StoreStatus>;
|
|
19
|
+
/**
|
|
20
|
+
* High-level entry point for ingesting a conversation exchange.
|
|
21
|
+
* Triggers both Layer 1 (raw log) and Layer 2 (fact extraction) writes.
|
|
22
|
+
*/
|
|
23
|
+
ingest(exchange: MessageExchange): Promise<IngestResult>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEjG,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/C;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC,CAAC;IAExE;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElC;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAE/B;;;OAGG;IACH,MAAM,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1D"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":""}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare enum DecayRate {
|
|
2
|
+
slow = "slow",
|
|
3
|
+
medium = "medium",
|
|
4
|
+
fast = "fast"
|
|
5
|
+
}
|
|
6
|
+
export interface Fact {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly subject: string;
|
|
9
|
+
readonly predicate: string;
|
|
10
|
+
readonly object: string;
|
|
11
|
+
readonly confidence: number;
|
|
12
|
+
readonly decayRate: DecayRate;
|
|
13
|
+
readonly timestamp: Date;
|
|
14
|
+
readonly sourceSessionId: string;
|
|
15
|
+
readonly sourceSessionLabel?: string;
|
|
16
|
+
readonly context?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface MessageExchange {
|
|
19
|
+
readonly userMessage: string;
|
|
20
|
+
readonly agentResponse: string;
|
|
21
|
+
readonly timestamp: Date;
|
|
22
|
+
readonly source: 'openclaw' | 'claude-code' | 'claude-desktop';
|
|
23
|
+
readonly sessionId: string;
|
|
24
|
+
readonly sessionLabel?: string;
|
|
25
|
+
readonly metadata?: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
export interface IngestResult {
|
|
28
|
+
readonly rawLogId: string;
|
|
29
|
+
readonly factsExtracted: number;
|
|
30
|
+
readonly factIds: readonly string[];
|
|
31
|
+
readonly skipped?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface StoreStatus {
|
|
34
|
+
readonly factCount: number;
|
|
35
|
+
readonly rawLogCount: number;
|
|
36
|
+
readonly lastIngestion: Date | null;
|
|
37
|
+
readonly storageBytes: number;
|
|
38
|
+
}
|
|
39
|
+
export interface SearchResult {
|
|
40
|
+
readonly fact: Fact;
|
|
41
|
+
readonly score: number;
|
|
42
|
+
readonly ageInDays: number;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oBAAY,SAAS;IACnB,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,IAAI,SAAS;CACd;AAED,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,aAAa,GAAG,gBAAgB,CAAC;IAC/D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,IAAI,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,SAIX;AAJD,WAAY,SAAS;IACnB,0BAAa,CAAA;IACb,8BAAiB,CAAA;IACjB,0BAAa,CAAA;AACf,CAAC,EAJW,SAAS,KAAT,SAAS,QAIpB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@getplumb/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Plumb memory engine — storage abstraction, types, and local SQLite driver",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"LICENSE",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
25
|
+
"tsx": "^4.0.0",
|
|
26
|
+
"typescript": "^5.4.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@xenova/transformers": "^2.17.2",
|
|
30
|
+
"better-sqlite3": "^12.6.2",
|
|
31
|
+
"openai": "^4.80.0",
|
|
32
|
+
"sqlite-vec": "0.1.7-alpha.2"
|
|
33
|
+
},
|
|
34
|
+
"optionalDependencies": {
|
|
35
|
+
"@anthropic-ai/sdk": "^0.78.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"test": "node --import tsx/esm --test src/**/*.test.ts",
|
|
40
|
+
"lint": "echo \"No lint yet\" && exit 0",
|
|
41
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
42
|
+
}
|
|
43
|
+
}
|