zam-core 0.9.2 → 0.9.3

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.
@@ -1799,6 +1799,18 @@ async function getTokenBySlug(db, slug) {
1799
1799
  parseTokenFallback(token);
1800
1800
  return token;
1801
1801
  }
1802
+ async function getTokensBySlugs(db, slugs) {
1803
+ const tokens = /* @__PURE__ */ new Map();
1804
+ const unique = [...new Set(slugs)];
1805
+ if (unique.length === 0) return tokens;
1806
+ const placeholders = unique.map(() => "?").join(",");
1807
+ const rows = await db.prepare(`SELECT * FROM tokens WHERE slug IN (${placeholders})`).all(...unique);
1808
+ for (const token of rows) {
1809
+ parseTokenFallback(token);
1810
+ tokens.set(token.slug, token);
1811
+ }
1812
+ return tokens;
1813
+ }
1802
1814
  async function getTokenById(db, id) {
1803
1815
  const token = await db.prepare("SELECT * FROM tokens WHERE id = ?").get(id);
1804
1816
  parseTokenFallback(token);
@@ -3942,25 +3954,29 @@ async function cascadeBlock(db, userId, tokenSlug) {
3942
3954
  };
3943
3955
  }
3944
3956
  async function unblockReady(db, userId) {
3945
- const blockedCards = await db.prepare(
3946
- `SELECT c.token_id, t.slug, t.concept
3947
- FROM cards c
3948
- JOIN tokens t ON t.id = c.token_id
3949
- WHERE c.user_id = ? AND c.blocked = 1`
3950
- ).all(userId);
3951
3957
  const unblocked = [];
3952
- for (const card of blockedCards) {
3953
- const totalPrereqs = await db.prepare("SELECT COUNT(*) as n FROM prerequisites WHERE token_id = ?").get(card.token_id);
3954
- const metPrereqs = await db.prepare(
3955
- `SELECT COUNT(*) as n FROM cards c
3956
- JOIN prerequisites p ON p.requires_id = c.token_id
3957
- WHERE p.token_id = ? AND c.user_id = ? AND c.reps >= 1 AND c.blocked = 0`
3958
- ).get(card.token_id, userId);
3959
- if (totalPrereqs.n === 0 || metPrereqs.n === totalPrereqs.n) {
3960
- const now = (/* @__PURE__ */ new Date()).toISOString();
3961
- await db.prepare(
3962
- "UPDATE cards SET blocked = 0, due_at = ? WHERE token_id = ? AND user_id = ?"
3963
- ).run(now, card.token_id, userId);
3958
+ for (; ; ) {
3959
+ const readyCards = await db.prepare(
3960
+ `SELECT c.token_id, t.slug, t.concept
3961
+ FROM cards c
3962
+ JOIN tokens t ON t.id = c.token_id
3963
+ WHERE c.user_id = ? AND c.blocked = 1
3964
+ AND (SELECT COUNT(*) FROM prerequisites p
3965
+ WHERE p.token_id = c.token_id) =
3966
+ (SELECT COUNT(*) FROM prerequisites p
3967
+ JOIN cards pc ON pc.token_id = p.requires_id
3968
+ AND pc.user_id = c.user_id
3969
+ WHERE p.token_id = c.token_id
3970
+ AND pc.reps >= 1 AND pc.blocked = 0)`
3971
+ ).all(userId);
3972
+ if (readyCards.length === 0) break;
3973
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3974
+ const placeholders = readyCards.map(() => "?").join(",");
3975
+ await db.prepare(
3976
+ `UPDATE cards SET blocked = 0, due_at = ?
3977
+ WHERE user_id = ? AND token_id IN (${placeholders})`
3978
+ ).run(now, userId, ...readyCards.map((card) => card.token_id));
3979
+ for (const card of readyCards) {
3964
3980
  unblocked.push({ slug: card.slug, concept: card.concept });
3965
3981
  }
3966
3982
  }
@@ -4137,10 +4153,14 @@ async function prepareSessionSynthesis(db, input) {
4137
4153
  await buildSkillPatterns(db),
4138
4154
  input.explicitPatterns ?? []
4139
4155
  );
4156
+ const patternTokens = await getTokensBySlugs(
4157
+ db,
4158
+ patterns.map((pattern) => pattern.slug)
4159
+ );
4140
4160
  const validPatterns = [];
4141
4161
  const tokens = /* @__PURE__ */ new Map();
4142
4162
  for (const pattern of patterns) {
4143
- const token = await getTokenBySlug(db, pattern.slug);
4163
+ const token = patternTokens.get(pattern.slug);
4144
4164
  if (!token || token.deprecated_at) continue;
4145
4165
  validPatterns.push(pattern);
4146
4166
  tokens.set(pattern.slug, token);
@@ -4153,13 +4173,13 @@ async function prepareSessionSynthesis(db, input) {
4153
4173
  const minConfidence = input.minConfidence ?? "medium";
4154
4174
  if (session.execution_context === "ui") {
4155
4175
  const reports = readUiObservationLog(input.sessionId);
4156
- for (const report of reports) {
4157
- for (const candidate of report.candidateTokens) {
4158
- if (tokens.has(candidate.slug)) continue;
4159
- const token = await getTokenBySlug(db, candidate.slug);
4160
- if (token && !token.deprecated_at) {
4161
- tokens.set(candidate.slug, token);
4162
- }
4176
+ const candidateTokens = await getTokensBySlugs(
4177
+ db,
4178
+ reports.flatMap((report) => report.candidateTokens).map((candidate) => candidate.slug).filter((slug) => !tokens.has(slug))
4179
+ );
4180
+ for (const [slug, token] of candidateTokens) {
4181
+ if (!token.deprecated_at) {
4182
+ tokens.set(slug, token);
4163
4183
  }
4164
4184
  }
4165
4185
  const { candidates: candidates2, skippedLowConfidence: skippedLowConfidence2 } = buildUiSynthesisCandidates(