@duetso/agent 0.1.57 → 0.1.59
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 +41 -2
- package/dist/package.json +1 -1
- package/dist/src/cli/memory-db.d.ts +1 -1
- package/dist/src/cli/memory-db.js +3 -2
- package/dist/src/cli/memory-db.js.map +1 -1
- package/dist/src/memory/context-pack.d.ts +3 -3
- package/dist/src/memory/context-pack.d.ts.map +1 -1
- package/dist/src/memory/context-pack.js +2 -2
- package/dist/src/memory/loader.d.ts +12 -45
- package/dist/src/memory/loader.d.ts.map +1 -1
- package/dist/src/memory/loader.js +61 -77
- package/dist/src/memory/loader.js.map +1 -1
- package/dist/src/memory/migrations.d.ts.map +1 -1
- package/dist/src/memory/migrations.js +28 -0
- package/dist/src/memory/migrations.js.map +1 -1
- package/dist/src/memory/observational-prompts.d.ts.map +1 -1
- package/dist/src/memory/observational-prompts.js +3 -0
- package/dist/src/memory/observational-prompts.js.map +1 -1
- package/dist/src/memory/observational.d.ts +15 -3
- package/dist/src/memory/observational.d.ts.map +1 -1
- package/dist/src/memory/observational.js +117 -40
- package/dist/src/memory/observational.js.map +1 -1
- package/dist/src/memory/recall.js +2 -1
- package/dist/src/memory/recall.js.map +1 -1
- package/dist/src/memory/storage.d.ts +48 -27
- package/dist/src/memory/storage.d.ts.map +1 -1
- package/dist/src/memory/storage.js +65 -42
- package/dist/src/memory/storage.js.map +1 -1
- package/dist/src/memory/store.d.ts +20 -21
- package/dist/src/memory/store.d.ts.map +1 -1
- package/dist/src/memory/store.js +15 -99
- package/dist/src/memory/store.js.map +1 -1
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +224 -54
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/autocomplete.d.ts.map +1 -1
- package/dist/src/tui/autocomplete.js +5 -0
- package/dist/src/tui/autocomplete.js.map +1 -1
- package/dist/src/tui/clipboard.d.ts +30 -14
- package/dist/src/tui/clipboard.d.ts.map +1 -1
- package/dist/src/tui/clipboard.js +88 -11
- package/dist/src/tui/clipboard.js.map +1 -1
- package/dist/src/tui/theme.d.ts +18 -1
- package/dist/src/tui/theme.d.ts.map +1 -1
- package/dist/src/tui/theme.js +30 -1
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/tui/transcript-log.d.ts +1 -1
- package/dist/src/tui/transcript-log.js +1 -1
- package/dist/src/turn-runner/tools.d.ts +1 -1
- package/dist/src/turn-runner/turn-runner.d.ts +2 -2
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +9 -5
- package/dist/src/turn-runner/turn-runner.js.map +1 -1
- package/dist/src/types/memory.d.ts +20 -36
- package/dist/src/types/memory.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { rebuildMemoryContextPack } from "./context-pack.js";
|
|
4
3
|
import { DEFAULT_EMBEDDING_MODEL } from "./embedding.js";
|
|
5
4
|
import { EmbeddingBackfillWorker } from "./embedding-worker.js";
|
|
5
|
+
import { rebuildMemoryContextPack } from "./context-pack.js";
|
|
6
6
|
import { runMigrations } from "./migrations.js";
|
|
7
7
|
import { openPGlite } from "./pglite.js";
|
|
8
|
-
|
|
8
|
+
import { nanoid } from "nanoid";
|
|
9
|
+
export async function loadStoredMemory(memoryPath, cwd, options = {}) {
|
|
9
10
|
if (!memoryPath) {
|
|
10
11
|
const noop = async () => { };
|
|
11
|
-
return {
|
|
12
|
+
return { db: undefined, embed: undefined, dispose: noop };
|
|
12
13
|
}
|
|
13
14
|
const database = await openMemoryDatabase(resolveMemoryPath(memoryPath, cwd));
|
|
14
|
-
const snapshot = await readMemorySnapshot(database);
|
|
15
|
-
await store.replaceObservations(snapshot.observations);
|
|
16
|
-
let writeQueue = Promise.resolve();
|
|
17
|
-
const enqueueWrite = (event) => {
|
|
18
|
-
writeQueue = writeQueue.then(() => persistMemoryEvent(database, event));
|
|
19
|
-
void writeQueue;
|
|
20
|
-
};
|
|
21
|
-
const unsubscribe = store.on(enqueueWrite);
|
|
22
15
|
// The backfill worker runs whenever the CLI is up. Observers and
|
|
23
16
|
// reflectors write rows during turns; embeddings catch up in the
|
|
24
17
|
// background within a few batches, never blocking the foreground.
|
|
@@ -41,7 +34,7 @@ export async function loadStoredMemory(memoryPath, cwd, store, options = {}) {
|
|
|
41
34
|
try {
|
|
42
35
|
await rebuildMemoryContextPack({
|
|
43
36
|
db: database,
|
|
44
|
-
|
|
37
|
+
cache: options.contextPack.cache,
|
|
45
38
|
settings: options.contextPack.settings,
|
|
46
39
|
...(options.contextPack.sessionId !== undefined
|
|
47
40
|
? { sessionId: options.contextPack.sessionId }
|
|
@@ -54,16 +47,11 @@ export async function loadStoredMemory(memoryPath, cwd, store, options = {}) {
|
|
|
54
47
|
// since callers do not need the noise mid-startup.
|
|
55
48
|
}
|
|
56
49
|
}
|
|
57
|
-
const flush = async () => {
|
|
58
|
-
await writeQueue;
|
|
59
|
-
};
|
|
60
50
|
const dispose = async () => {
|
|
61
|
-
unsubscribe();
|
|
62
51
|
await worker?.stop();
|
|
63
|
-
await flush();
|
|
64
52
|
await database.close();
|
|
65
53
|
};
|
|
66
|
-
return {
|
|
54
|
+
return { db: database, embed: options.embed, dispose };
|
|
67
55
|
}
|
|
68
56
|
function defaultEmbeddingLogPath() {
|
|
69
57
|
return join(homedir(), ".duet", "logs", "memory-backfill.log");
|
|
@@ -79,47 +67,77 @@ async function openMemoryDatabase(path) {
|
|
|
79
67
|
},
|
|
80
68
|
});
|
|
81
69
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Insert a new observation row, materializing `id`, `createdAt`, and
|
|
72
|
+
* `lastUsedAt = createdAt`. Returns the populated `Observation` so
|
|
73
|
+
* callers can refer to the freshly-assigned id (e.g. to keep
|
|
74
|
+
* observation-group range markers consistent).
|
|
75
|
+
*/
|
|
76
|
+
export async function appendObservation(db, input) {
|
|
77
|
+
const observation = {
|
|
78
|
+
...input,
|
|
79
|
+
id: createMemoryId(),
|
|
80
|
+
createdAt: Date.now(),
|
|
81
|
+
lastUsedAt: Date.now(),
|
|
93
82
|
};
|
|
83
|
+
await upsertObservation(db, observation);
|
|
84
|
+
return observation;
|
|
94
85
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
async function
|
|
86
|
+
/**
|
|
87
|
+
* Replace this session's rows with the given list. Used by reflection,
|
|
88
|
+
* which condenses the session's observation log into one reflection
|
|
89
|
+
* row. Scoping the delete to `session_id = $1` is what makes the
|
|
90
|
+
* cross-session global pool durable: another session's rows are never
|
|
91
|
+
* touched by the current session's reflection.
|
|
92
|
+
*/
|
|
93
|
+
export async function replaceSessionObservations(db, sessionId, observations) {
|
|
103
94
|
const ids = observations.map((observation) => observation.id);
|
|
104
|
-
await
|
|
95
|
+
await db.transaction(async (tx) => {
|
|
105
96
|
if (ids.length === 0) {
|
|
106
|
-
await tx.
|
|
97
|
+
await tx.query("DELETE FROM observations WHERE session_id = $1", [sessionId]);
|
|
107
98
|
}
|
|
108
99
|
else {
|
|
109
|
-
await tx.query("DELETE FROM observations WHERE NOT (id = ANY($
|
|
100
|
+
await tx.query("DELETE FROM observations WHERE session_id = $1 AND NOT (id = ANY($2::text[]))", [sessionId, ids]);
|
|
110
101
|
}
|
|
111
102
|
for (const observation of observations) {
|
|
112
103
|
await upsertObservation(tx, observation);
|
|
113
104
|
}
|
|
114
105
|
});
|
|
115
106
|
}
|
|
107
|
+
export async function readSessionObservations(db, sessionId) {
|
|
108
|
+
const result = await db.query(`SELECT id, created_at, last_used_at, session_id, kind, observed_date, referenced_date,
|
|
109
|
+
relative_date, time_of_day, priority, source_json, content, tags_json
|
|
110
|
+
FROM observations
|
|
111
|
+
WHERE session_id = $1
|
|
112
|
+
ORDER BY created_at ASC`, [sessionId]);
|
|
113
|
+
const observations = result.rows.map(rowToObservation);
|
|
114
|
+
return {
|
|
115
|
+
observations,
|
|
116
|
+
estimatedObservationTokens: estimateTokens(observations.map((observation) => observation.content).join("\n")),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Bump `last_used_at` for the given ids to `now`. Fire-and-forget
|
|
121
|
+
* usage signal: if the model's response leaned on a particular
|
|
122
|
+
* memory, that memory's ranking refreshes so it stays surfaced. No-op
|
|
123
|
+
* when `ids` is empty.
|
|
124
|
+
*/
|
|
125
|
+
export async function bumpLastUsed(db, ids, now) {
|
|
126
|
+
if (ids.length === 0)
|
|
127
|
+
return;
|
|
128
|
+
await db.query("UPDATE observations SET last_used_at = $1 WHERE id = ANY($2::text[])", [
|
|
129
|
+
now,
|
|
130
|
+
ids,
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
116
133
|
async function upsertObservation(database, observation) {
|
|
117
134
|
await database.query(`INSERT INTO observations (
|
|
118
|
-
id, created_at, session_id, kind, observed_date, referenced_date, relative_date, time_of_day,
|
|
135
|
+
id, created_at, last_used_at, session_id, kind, observed_date, referenced_date, relative_date, time_of_day,
|
|
119
136
|
priority, source_json, content, tags_json
|
|
120
|
-
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
137
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
|
121
138
|
ON CONFLICT (id) DO UPDATE SET
|
|
122
139
|
created_at = EXCLUDED.created_at,
|
|
140
|
+
last_used_at = EXCLUDED.last_used_at,
|
|
123
141
|
session_id = EXCLUDED.session_id,
|
|
124
142
|
kind = EXCLUDED.kind,
|
|
125
143
|
observed_date = EXCLUDED.observed_date,
|
|
@@ -132,6 +150,7 @@ async function upsertObservation(database, observation) {
|
|
|
132
150
|
tags_json = EXCLUDED.tags_json`, [
|
|
133
151
|
observation.id,
|
|
134
152
|
observation.createdAt,
|
|
153
|
+
observation.lastUsedAt,
|
|
135
154
|
observation.sessionId ?? null,
|
|
136
155
|
observation.kind,
|
|
137
156
|
observation.observedDate,
|
|
@@ -150,10 +169,14 @@ function resolveMemoryPath(path, cwd) {
|
|
|
150
169
|
function estimateTokens(text) {
|
|
151
170
|
return Math.ceil(text.length / 4);
|
|
152
171
|
}
|
|
172
|
+
function createMemoryId() {
|
|
173
|
+
return `mem_${nanoid(12)}`;
|
|
174
|
+
}
|
|
153
175
|
function rowToObservation(row) {
|
|
154
176
|
return {
|
|
155
177
|
id: row.id,
|
|
156
178
|
createdAt: row.created_at,
|
|
179
|
+
lastUsedAt: row.last_used_at,
|
|
157
180
|
...(row.session_id !== null ? { sessionId: row.session_id } : {}),
|
|
158
181
|
kind: row.kind,
|
|
159
182
|
observedDate: row.observed_date,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/memory/storage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/memory/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Observation } from "../types/memory.js";
|
|
2
2
|
/**
|
|
3
3
|
* Frozen view of memory rendered into the prompt prefix. Captured at
|
|
4
4
|
* compaction events (initial load, reflector completion, wire-shaping
|
|
@@ -7,10 +7,10 @@ import type { MemoryStoreEventHandler, Observation, ObservationQuery, Observatio
|
|
|
7
7
|
* cache survives unchanged until the next compaction event, at which
|
|
8
8
|
* point exactly one cache invalidation is paid — not one per turn.
|
|
9
9
|
*
|
|
10
|
-
* Observations the observer writes mid-session still flow to
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Observations the observer writes mid-session still flow to PGlite in
|
|
11
|
+
* real time; they just do not enter `contextPack` until the next
|
|
12
|
+
* refresh. The model still sees them through `recall_memory` if it
|
|
13
|
+
* asks.
|
|
14
14
|
*/
|
|
15
15
|
export interface ContextPack {
|
|
16
16
|
/** Cross-session ranked memory; rendered above the local section. */
|
|
@@ -18,28 +18,27 @@ export interface ContextPack {
|
|
|
18
18
|
/** Current session's chronological compaction summary; rendered below global. */
|
|
19
19
|
local: Observation[];
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Holds the frozen context pack rendered above the message tail.
|
|
23
|
+
*
|
|
24
|
+
* This is the only piece of memory state the runner keeps in process —
|
|
25
|
+
* everything else (observation rows, ranking, recall) lives in PGlite
|
|
26
|
+
* and is read on demand. Holding the pack here, rather than
|
|
27
|
+
* recomputing inside the transform on every dispatch, is what lets
|
|
28
|
+
* the rendered prefix stay byte-identical between compaction events
|
|
29
|
+
* so the provider's prompt cache survives.
|
|
30
|
+
*/
|
|
31
|
+
export declare class MemoryContextCache {
|
|
24
32
|
private contextPack;
|
|
25
|
-
on(handler: MemoryStoreEventHandler): () => void;
|
|
26
|
-
appendObservation(input: Omit<Observation, "id" | "createdAt">): Promise<Observation>;
|
|
27
|
-
recall(query: ObservationQuery): Promise<Observation[]>;
|
|
28
|
-
getSnapshot(): Promise<ObservationalMemorySnapshot>;
|
|
29
|
-
replaceObservations(observations: Observation[]): Promise<void>;
|
|
30
33
|
/**
|
|
31
34
|
* Replace the frozen view used by the runner's context transform.
|
|
32
|
-
* Called by `rebuildMemoryContextPack()` at compaction
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* on every dispatch) is the entire point of the cache-stability
|
|
37
|
-
* design: until the next refresh, every turn renders exactly the
|
|
38
|
-
* same memory prefix.
|
|
35
|
+
* Called by `rebuildMemoryContextPack()` at every compaction trigger
|
|
36
|
+
* (initial load, reflector completion, wire-shaping eviction
|
|
37
|
+
* horizon advance) and never anywhere else — that constraint is
|
|
38
|
+
* what makes the rendered prefix cache-stable.
|
|
39
39
|
*/
|
|
40
40
|
setContextPack(pack: ContextPack): void;
|
|
41
41
|
/** Current frozen pack. Returns empty arrays when memory is disabled or pre-compaction. */
|
|
42
42
|
getContextPack(): ContextPack;
|
|
43
|
-
private emit;
|
|
44
43
|
}
|
|
45
44
|
//# sourceMappingURL=store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/memory/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/memory/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,iFAAiF;IACjF,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,WAAW,CAA0C;IAE7D;;;;;;OAMG;IACH,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAIvC,2FAA2F;IAC3F,cAAc,IAAI,WAAW;CAG9B"}
|
package/dist/src/memory/store.js
CHANGED
|
@@ -1,75 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Holds the frozen context pack rendered above the message tail.
|
|
3
|
+
*
|
|
4
|
+
* This is the only piece of memory state the runner keeps in process —
|
|
5
|
+
* everything else (observation rows, ranking, recall) lives in PGlite
|
|
6
|
+
* and is read on demand. Holding the pack here, rather than
|
|
7
|
+
* recomputing inside the transform on every dispatch, is what lets
|
|
8
|
+
* the rendered prefix stay byte-identical between compaction events
|
|
9
|
+
* so the provider's prompt cache survives.
|
|
10
|
+
*/
|
|
11
|
+
export class MemoryContextCache {
|
|
5
12
|
contextPack = { global: [], local: [] };
|
|
6
|
-
on(handler) {
|
|
7
|
-
this.handlers.add(handler);
|
|
8
|
-
return () => {
|
|
9
|
-
this.handlers.delete(handler);
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
async appendObservation(input) {
|
|
13
|
-
const observation = {
|
|
14
|
-
...input,
|
|
15
|
-
id: createMemoryId(),
|
|
16
|
-
createdAt: Date.now(),
|
|
17
|
-
};
|
|
18
|
-
this.observations.set(observation.id, observation);
|
|
19
|
-
this.emit({ type: "observation_appended", observation });
|
|
20
|
-
return observation;
|
|
21
|
-
}
|
|
22
|
-
async recall(query) {
|
|
23
|
-
let candidates = Array.from(this.observations.values());
|
|
24
|
-
if (query.sessionId !== undefined) {
|
|
25
|
-
candidates = candidates.filter((observation) => observation.sessionId === query.sessionId);
|
|
26
|
-
}
|
|
27
|
-
if (query.kind) {
|
|
28
|
-
candidates = candidates.filter((observation) => observation.kind === query.kind);
|
|
29
|
-
}
|
|
30
|
-
if (query.tags?.length) {
|
|
31
|
-
candidates = candidates.filter((observation) => query.tags.some((tag) => observation.tags.includes(tag)));
|
|
32
|
-
}
|
|
33
|
-
if (query.minPriority) {
|
|
34
|
-
const minRank = priorityRank(query.minPriority);
|
|
35
|
-
candidates = candidates.filter((observation) => priorityRank(observation.priority) >= minRank);
|
|
36
|
-
}
|
|
37
|
-
if (query.query) {
|
|
38
|
-
const terms = tokenize(query.query);
|
|
39
|
-
candidates = candidates.sort((a, b) => textScore(b, terms) - textScore(a, terms));
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
candidates.sort((a, b) => b.createdAt - a.createdAt);
|
|
43
|
-
}
|
|
44
|
-
return candidates.slice(0, query.limit ?? 10);
|
|
45
|
-
}
|
|
46
|
-
async getSnapshot() {
|
|
47
|
-
const observations = Array.from(this.observations.values()).sort((a, b) => a.createdAt - b.createdAt);
|
|
48
|
-
const updatedAt = Date.now();
|
|
49
|
-
return {
|
|
50
|
-
observations,
|
|
51
|
-
estimatedTokens: {
|
|
52
|
-
observations: estimateTokens(observations.map((item) => item.content).join("\n")),
|
|
53
|
-
},
|
|
54
|
-
updatedAt,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
async replaceObservations(observations) {
|
|
58
|
-
this.observations.clear();
|
|
59
|
-
for (const observation of observations) {
|
|
60
|
-
this.observations.set(observation.id, observation);
|
|
61
|
-
}
|
|
62
|
-
this.emit({ type: "observations_replaced", observations });
|
|
63
|
-
}
|
|
64
13
|
/**
|
|
65
14
|
* Replace the frozen view used by the runner's context transform.
|
|
66
|
-
* Called by `rebuildMemoryContextPack()` at compaction
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* on every dispatch) is the entire point of the cache-stability
|
|
71
|
-
* design: until the next refresh, every turn renders exactly the
|
|
72
|
-
* same memory prefix.
|
|
15
|
+
* Called by `rebuildMemoryContextPack()` at every compaction trigger
|
|
16
|
+
* (initial load, reflector completion, wire-shaping eviction
|
|
17
|
+
* horizon advance) and never anywhere else — that constraint is
|
|
18
|
+
* what makes the rendered prefix cache-stable.
|
|
73
19
|
*/
|
|
74
20
|
setContextPack(pack) {
|
|
75
21
|
this.contextPack = pack;
|
|
@@ -78,35 +24,5 @@ export class MemoryStore {
|
|
|
78
24
|
getContextPack() {
|
|
79
25
|
return this.contextPack;
|
|
80
26
|
}
|
|
81
|
-
emit(event) {
|
|
82
|
-
for (const handler of this.handlers) {
|
|
83
|
-
handler(event);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
function createMemoryId() {
|
|
88
|
-
return `mem_${nanoid(12)}`;
|
|
89
|
-
}
|
|
90
|
-
function priorityRank(priority) {
|
|
91
|
-
return priority === "high" ? 3 : priority === "medium" ? 2 : 1;
|
|
92
|
-
}
|
|
93
|
-
function tokenize(text) {
|
|
94
|
-
return new Set(text
|
|
95
|
-
.toLowerCase()
|
|
96
|
-
.split(/[^a-z0-9]+/)
|
|
97
|
-
.filter(Boolean));
|
|
98
|
-
}
|
|
99
|
-
function textScore(observation, terms) {
|
|
100
|
-
const text = `${observation.content} ${observation.tags.join(" ")}`.toLowerCase();
|
|
101
|
-
let score = priorityRank(observation.priority);
|
|
102
|
-
for (const term of terms) {
|
|
103
|
-
if (text.includes(term)) {
|
|
104
|
-
score += 1;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return score;
|
|
108
|
-
}
|
|
109
|
-
function estimateTokens(text) {
|
|
110
|
-
return Math.ceil(text.length / 4);
|
|
111
27
|
}
|
|
112
28
|
//# sourceMappingURL=store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/memory/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/memory/store.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;GASG;AACH,MAAM,OAAO,kBAAkB;IACrB,WAAW,GAAgB,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAE7D;;;;;;OAMG;IACH,cAAc,CAAC,IAAiB;QAC9B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,2FAA2F;IAC3F,cAAc;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/tui/app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/tui/app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA0BlE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAMV,iBAAiB,EAElB,MAAM,sBAAsB,CAAC;AAiC9B,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1F,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EACV,iBAAiB,EACjB,iBAAiB,IAAI,sBAAsB,EAC3C,oBAAoB,EACpB,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,uBAAuB,EACvB,+BAA+B,EAC/B,kCAAkC,EAClC,2BAA2B,EAC3B,8BAA8B,EAC9B,2BAA2B,EAC3B,4BAA4B,EAC5B,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,2BAA2B,EAC3B,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAGtB,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,+FAA+F;IAC/F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACnD,6DAA6D;IAC7D,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAUD;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CA+hEvF"}
|