om-memory-system 4.31.1 → 4.32.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/importer/import-turn-outputs.js +3 -0
- package/dist/services/ai/providers/base-provider.d.ts +15 -0
- package/dist/services/ai/providers/base-provider.js +26 -0
- package/dist/services/ai/providers/openai-chat-completion.js +3 -6
- package/dist/services/skill-learning/activity-range.d.ts +46 -0
- package/dist/services/skill-learning/activity-range.js +83 -0
- package/dist/services/skill-learning/activity.d.ts +61 -0
- package/dist/services/skill-learning/activity.js +88 -0
- package/dist/services/skill-learning/apply.d.ts +4 -1
- package/dist/services/skill-learning/apply.js +21 -8
- package/dist/services/skill-learning/drafting.js +2 -1
- package/dist/services/skill-learning/history.d.ts +48 -0
- package/dist/services/skill-learning/history.js +95 -0
- package/dist/services/skill-learning/query-params.d.ts +5 -0
- package/dist/services/skill-learning/query-params.js +13 -0
- package/dist/services/skill-learning/ranking.d.ts +1 -0
- package/dist/services/skill-learning/ranking.js +8 -3
- package/dist/services/skill-learning/turn-summaries.d.ts +9 -3
- package/dist/services/skill-learning/turn-summaries.js +40 -28
- package/dist/services/skill-routes.js +46 -9
- package/dist/web/assets/index-CzTfLJbi.js +167 -0
- package/dist/web/assets/index-DOfXzWqw.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-9hu4Z7SV.css +0 -2
- package/dist/web/assets/index-BMt9gPNK.js +0 -129
|
@@ -13,12 +13,18 @@ export interface SkillTurnSummary {
|
|
|
13
13
|
failedToolCalls: number;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Turns that used one skill, newest first, `limit` of them after skipping
|
|
17
|
+
* `offset`. `path` is null for a skill that no watched folder holds. A missing
|
|
18
|
+
* `skills.db` gives no turns and stays missing.
|
|
18
19
|
*/
|
|
19
20
|
export declare function recentSkillTurns(store: SkillStore, skill: {
|
|
20
21
|
name: string;
|
|
21
22
|
path: string | null;
|
|
22
|
-
}, index: SkillIndex, limit?: number): Promise<SkillTurnSummary[]>;
|
|
23
|
+
}, index: SkillIndex, limit?: number, offset?: number): Promise<SkillTurnSummary[]>;
|
|
24
|
+
/** How many recorded turns used this skill. */
|
|
25
|
+
export declare function countSkillTurns(store: SkillStore, skill: {
|
|
26
|
+
name: string;
|
|
27
|
+
path: string | null;
|
|
28
|
+
}, index: SkillIndex): Promise<number>;
|
|
23
29
|
/** The turns with these ids, newest first; ids of deleted turns are left out. */
|
|
24
30
|
export declare function skillTurnsByIds(store: SkillStore, ids: readonly number[]): Promise<SkillTurnSummary[]>;
|
|
@@ -17,42 +17,54 @@ function toSummary(row) {
|
|
|
17
17
|
// The same use-to-skill rule as the ranking: a recorded path counts while the
|
|
18
18
|
// index lists it; a use with no path belongs to the one watched skill with its name.
|
|
19
19
|
const BY_PATH = `
|
|
20
|
-
SELECT
|
|
21
|
-
|
|
22
|
-
WHERE su.skill_path = ? OR (su.skill_path IS NULL AND su.skill_name = ? AND ? = 1)
|
|
23
|
-
) ORDER BY t.created_at DESC, t.id DESC LIMIT ?`;
|
|
20
|
+
SELECT su.turn_id FROM skill_uses su
|
|
21
|
+
WHERE su.skill_path = ? OR (su.skill_path IS NULL AND su.skill_name = ? AND ? = 1)`;
|
|
24
22
|
const BY_NAME = `
|
|
25
|
-
SELECT
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
SELECT su.turn_id FROM skill_uses su
|
|
24
|
+
WHERE su.skill_name = ? AND (
|
|
25
|
+
(su.skill_path IS NOT NULL AND su.skill_path NOT IN (SELECT value FROM json_each(?)))
|
|
26
|
+
OR (su.skill_path IS NULL AND ? = 0)
|
|
27
|
+
)`;
|
|
28
|
+
/** The SQL that selects a skill's turn ids, and its parameters. */
|
|
29
|
+
function turnIdsFor(skill, index) {
|
|
30
|
+
const named = index.entries.filter((e) => e.name === skill.name);
|
|
31
|
+
return skill.path
|
|
32
|
+
? {
|
|
33
|
+
sql: BY_PATH,
|
|
34
|
+
args: [skill.path, skill.name, named.length === 1 && named[0].path === skill.path ? 1 : 0],
|
|
35
|
+
}
|
|
36
|
+
: {
|
|
37
|
+
sql: BY_NAME,
|
|
38
|
+
args: [
|
|
39
|
+
skill.name,
|
|
40
|
+
JSON.stringify(index.entries.map((e) => e.path)),
|
|
41
|
+
named.length === 1 ? 1 : 0,
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
32
45
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
46
|
+
* Turns that used one skill, newest first, `limit` of them after skipping
|
|
47
|
+
* `offset`. `path` is null for a skill that no watched folder holds. A missing
|
|
48
|
+
* `skills.db` gives no turns and stays missing.
|
|
35
49
|
*/
|
|
36
|
-
export async function recentSkillTurns(store, skill, index, limit = RECENT_TURN_LIMIT) {
|
|
50
|
+
export async function recentSkillTurns(store, skill, index, limit = RECENT_TURN_LIMIT, offset = 0) {
|
|
37
51
|
const db = await store.existingDb();
|
|
38
52
|
if (!db)
|
|
39
53
|
return [];
|
|
40
|
-
const
|
|
41
|
-
const rows =
|
|
42
|
-
|
|
43
|
-
skill.path,
|
|
44
|
-
skill.name,
|
|
45
|
-
named.length === 1 && named[0].path === skill.path ? 1 : 0,
|
|
46
|
-
limit,
|
|
47
|
-
])
|
|
48
|
-
: await db.all(BY_NAME, [
|
|
49
|
-
skill.name,
|
|
50
|
-
JSON.stringify(index.entries.map((e) => e.path)),
|
|
51
|
-
named.length === 1 ? 1 : 0,
|
|
52
|
-
limit,
|
|
53
|
-
]);
|
|
54
|
+
const ids = turnIdsFor(skill, index);
|
|
55
|
+
const rows = await db.all(`SELECT ${COLUMNS} FROM turns t WHERE t.id IN (${ids.sql})
|
|
56
|
+
ORDER BY t.created_at DESC, t.id DESC LIMIT ? OFFSET ?`, [...ids.args, limit, offset]);
|
|
54
57
|
return rows.map(toSummary);
|
|
55
58
|
}
|
|
59
|
+
/** How many recorded turns used this skill. */
|
|
60
|
+
export async function countSkillTurns(store, skill, index) {
|
|
61
|
+
const db = await store.existingDb();
|
|
62
|
+
if (!db)
|
|
63
|
+
return 0;
|
|
64
|
+
const ids = turnIdsFor(skill, index);
|
|
65
|
+
const row = await db.get(`SELECT COUNT(*) AS n FROM turns t WHERE t.id IN (${ids.sql})`, ids.args);
|
|
66
|
+
return Number(row?.n ?? 0);
|
|
67
|
+
}
|
|
56
68
|
/** The turns with these ids, newest first; ids of deleted turns are left out. */
|
|
57
69
|
export async function skillTurnsByIds(store, ids) {
|
|
58
70
|
const db = await store.existingDb();
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { RECORDING_OFF_TEXT, runSkillOperation, } from "../core/skill-operations.js";
|
|
3
3
|
import { log } from "./logger.js";
|
|
4
|
+
import { skillActivity, skillDaily } from "./skill-learning/activity.js";
|
|
5
|
+
import { parseActivityQuery, topParam } from "./skill-learning/activity-range.js";
|
|
4
6
|
import { undoProposal } from "./skill-learning/apply.js";
|
|
7
|
+
import { skillHistory, withCanUndo } from "./skill-learning/history.js";
|
|
5
8
|
import { skillOperationsDeps } from "./skill-learning/operations-deps.js";
|
|
6
9
|
import { getProposal, listProposals, setSkillMode, } from "./skill-learning/proposal-store.js";
|
|
10
|
+
import { intParam } from "./skill-learning/query-params.js";
|
|
7
11
|
import { SKILL_ORDERS, rankSkills } from "./skill-learning/ranking.js";
|
|
8
12
|
import { skillTurnSettingsFor } from "./skill-learning/settings-for.js";
|
|
9
13
|
import { getSkillIndex } from "./skill-learning/skill-index.js";
|
|
10
14
|
import { SkillStore, SkillStoreError, skillsDbPath } from "./skill-learning/store.js";
|
|
11
|
-
import { recentSkillTurns, skillTurnsByIds } from "./skill-learning/turn-summaries.js";
|
|
15
|
+
import { RECENT_TURN_LIMIT, countSkillTurns, recentSkillTurns, skillTurnsByIds, } from "./skill-learning/turn-summaries.js";
|
|
16
|
+
const MAX_PAGE = 200;
|
|
17
|
+
const DEFAULT_PAGE = 20;
|
|
12
18
|
const PROPOSAL_STATES = [
|
|
13
19
|
"pending",
|
|
14
20
|
"applied",
|
|
@@ -123,10 +129,19 @@ async function dispatch(path, method, url, readBody, ctx) {
|
|
|
123
129
|
redact: settings.redact,
|
|
124
130
|
});
|
|
125
131
|
}
|
|
126
|
-
if (path === "/api/skills/skill")
|
|
127
|
-
return showSkill(url, store, index(), now);
|
|
132
|
+
if (path === "/api/skills/skill") {
|
|
133
|
+
return showSkill(url, { store, folders: settings.folders }, index(), now);
|
|
134
|
+
}
|
|
135
|
+
if (path === "/api/skills/activity") {
|
|
136
|
+
const query = parseActivityQuery(url, now);
|
|
137
|
+
if ("error" in query)
|
|
138
|
+
return fail("invalid-args", query.error);
|
|
139
|
+
return ok("ok", await skillActivity(store, index(), now, query, topParam(url)));
|
|
140
|
+
}
|
|
141
|
+
if (path === "/api/skills/history")
|
|
142
|
+
return history(url, store, settings.folders);
|
|
128
143
|
if (path === "/api/skills/proposals")
|
|
129
|
-
return proposals(url, store);
|
|
144
|
+
return proposals(url, store, settings.folders);
|
|
130
145
|
const one = /^\/api\/skills\/proposals\/([^/]+)$/.exec(path);
|
|
131
146
|
if (one) {
|
|
132
147
|
const proposal = await getProposal(store, decodeURIComponent(one[1]));
|
|
@@ -168,7 +183,8 @@ async function dispatch(path, method, url, readBody, ctx) {
|
|
|
168
183
|
}
|
|
169
184
|
return undefined;
|
|
170
185
|
}
|
|
171
|
-
async function showSkill(url,
|
|
186
|
+
async function showSkill(url, deps, index, now) {
|
|
187
|
+
const { store } = deps;
|
|
172
188
|
const path = text(url.searchParams.get("path"));
|
|
173
189
|
const name = text(url.searchParams.get("name"));
|
|
174
190
|
if (!path && !name)
|
|
@@ -176,13 +192,19 @@ async function showSkill(url, store, index, now) {
|
|
|
176
192
|
const skill = (await rankSkills(store, index, now)).find((s) => path ? s.path === path : s.path === null && s.name === name);
|
|
177
193
|
if (!skill)
|
|
178
194
|
return fail("not-found", "No such skill");
|
|
195
|
+
const limit = intParam(url, "turnLimit", 1, MAX_PAGE, RECENT_TURN_LIMIT);
|
|
196
|
+
const offset = intParam(url, "turnOffset", 0, Number.MAX_SAFE_INTEGER, 0);
|
|
179
197
|
return ok("ok", {
|
|
180
198
|
skill,
|
|
181
|
-
recentTurns: await recentSkillTurns(store, skill, index),
|
|
182
|
-
|
|
199
|
+
recentTurns: await recentSkillTurns(store, skill, index, limit, offset),
|
|
200
|
+
turnTotal: await countSkillTurns(store, skill, index),
|
|
201
|
+
daily: await skillDaily(store, index, skill, now),
|
|
202
|
+
proposals: skill.path
|
|
203
|
+
? await withCanUndo(deps, await listProposals(store, { skillPath: skill.path }))
|
|
204
|
+
: [],
|
|
183
205
|
});
|
|
184
206
|
}
|
|
185
|
-
async function proposals(url, store) {
|
|
207
|
+
async function proposals(url, store, folders) {
|
|
186
208
|
const state = text(url.searchParams.get("state"));
|
|
187
209
|
const skill = text(url.searchParams.get("skill"));
|
|
188
210
|
if (state && !PROPOSAL_STATES.includes(state)) {
|
|
@@ -196,7 +218,22 @@ async function proposals(url, store) {
|
|
|
196
218
|
filter.skillPath = skill;
|
|
197
219
|
else if (skill)
|
|
198
220
|
filter.skillName = skill;
|
|
199
|
-
|
|
221
|
+
const all = await listProposals(store, filter);
|
|
222
|
+
// Without limit or offset the whole list comes back, as before.
|
|
223
|
+
const offset = intParam(url, "offset", 0, Number.MAX_SAFE_INTEGER, 0);
|
|
224
|
+
const limit = intParam(url, "limit", 1, MAX_PAGE, all.length || 1);
|
|
225
|
+
const paged = url.searchParams.has("limit") || url.searchParams.has("offset");
|
|
226
|
+
const shown = paged ? all.slice(offset, offset + limit) : all;
|
|
227
|
+
return ok("ok", { proposals: await withCanUndo({ store, folders }, shown), total: all.length });
|
|
228
|
+
}
|
|
229
|
+
async function history(url, store, folders) {
|
|
230
|
+
const path = text(url.searchParams.get("path"));
|
|
231
|
+
const name = text(url.searchParams.get("name"));
|
|
232
|
+
if (!path && !name)
|
|
233
|
+
return fail("invalid-args", "Give path or name");
|
|
234
|
+
const limit = intParam(url, "limit", 1, MAX_PAGE, DEFAULT_PAGE);
|
|
235
|
+
const offset = intParam(url, "offset", 0, Number.MAX_SAFE_INTEGER, 0);
|
|
236
|
+
return ok("ok", await skillHistory({ store, folders }, { path, name }, limit, offset));
|
|
200
237
|
}
|
|
201
238
|
async function undo(id, store, folders) {
|
|
202
239
|
if (!id)
|