om-memory-system 4.31.2 → 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.
@@ -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, store, index, now) {
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
- proposals: skill.path ? await listProposals(store, { skillPath: skill.path }) : [],
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
- return ok("ok", { proposals: await listProposals(store, filter) });
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)