@vue-skuilder/db 0.2.7 → 0.2.8

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.
@@ -738,6 +738,7 @@ __export(PipelineDebugger_exports, {
738
738
  buildRunReport: () => buildRunReport,
739
739
  captureRun: () => captureRun,
740
740
  clearRunHistory: () => clearRunHistory,
741
+ getActivePipeline: () => getActivePipeline,
741
742
  mountPipelineDebugger: () => mountPipelineDebugger,
742
743
  pipelineDebugAPI: () => pipelineDebugAPI,
743
744
  registerPipelineForDebug: () => registerPipelineForDebug
@@ -745,6 +746,9 @@ __export(PipelineDebugger_exports, {
745
746
  function registerPipelineForDebug(pipeline) {
746
747
  _activePipeline = pipeline;
747
748
  }
749
+ function getActivePipeline() {
750
+ return _activePipeline;
751
+ }
748
752
  function clearRunHistory() {
749
753
  runHistory.length = 0;
750
754
  }
@@ -4531,6 +4535,68 @@ var init_Pipeline = __esm({
4531
4535
  // ---------------------------------------------------------------------------
4532
4536
  // Card-space diagnostic
4533
4537
  // ---------------------------------------------------------------------------
4538
+ /**
4539
+ * Commit-free forecast: score the user's full card space through the filter
4540
+ * chain and return the cards that are currently *reachable* (score >=
4541
+ * threshold), optionally nudged by caller-supplied hints and/or restricted
4542
+ * to cards the user hasn't seen yet.
4543
+ *
4544
+ * This is a GENERIC primitive — it returns scored, tag-hydrated cards and
4545
+ * stops there. It has no knowledge of any particular tag convention; callers
4546
+ * decide what the surviving cards mean (e.g. filter to their own "intro"
4547
+ * tag family). Nothing is written and no session is started.
4548
+ *
4549
+ * The optional `hints` are the "out-of-band kick": they run through the same
4550
+ * {@link applyHints} path a live replan uses, so the two semantics carry over —
4551
+ * - `boostTags`/`boostCards` reweight *within* gating (a gated score-0 card
4552
+ * stays out), and
4553
+ * - `requireTags`/`requireCards` inject from the full pre-filter pool,
4554
+ * *bypassing* gating (use when you want a card regardless of reachability).
4555
+ * Note `unseenOnly` is applied LAST, so it can drop a `require`d card that the
4556
+ * user has already seen — pass `unseenOnly: false` if that matters.
4557
+ *
4558
+ * Cost note: like {@link diagnoseCardSpace}, this scans every card through the
4559
+ * filters, so it's heavier than a normal replan. Intended for one-shot
4560
+ * out-of-band use (e.g. a session-end "what's next" snapshot), not the hot path.
4561
+ *
4562
+ * @param opts.hints Optional ephemeral hints to apply after the filter chain.
4563
+ * @param opts.unseenOnly Only return cards the user hasn't encountered (default true).
4564
+ * @param opts.threshold Min score to count as reachable (default 0.10).
4565
+ * @param opts.limit Optional cap on results (already sorted desc).
4566
+ */
4567
+ async forecast(opts) {
4568
+ const threshold = opts?.threshold ?? 0.1;
4569
+ const unseenOnly = opts?.unseenOnly ?? true;
4570
+ const courseId = this.course.getCourseID();
4571
+ const allCardIds = await this.course.getAllCardIds();
4572
+ let cards = allCardIds.map((cardId) => ({
4573
+ cardId,
4574
+ courseId,
4575
+ score: 1,
4576
+ provenance: []
4577
+ }));
4578
+ cards = await this.hydrateTags(cards);
4579
+ const fullPool = cards.slice();
4580
+ const context = await this.buildContext();
4581
+ for (const filter of this.filters) {
4582
+ cards = await filter.transform(cards, context);
4583
+ }
4584
+ if (opts?.hints) {
4585
+ cards = this.applyHints(cards, opts.hints, fullPool);
4586
+ }
4587
+ cards = cards.filter((c) => c.score >= threshold);
4588
+ if (unseenOnly) {
4589
+ let encountered;
4590
+ try {
4591
+ encountered = new Set(await this.user.getSeenCards(courseId));
4592
+ } catch {
4593
+ encountered = /* @__PURE__ */ new Set();
4594
+ }
4595
+ cards = cards.filter((c) => !encountered.has(c.cardId));
4596
+ }
4597
+ cards.sort((a, b) => b.score - a.score);
4598
+ return opts?.limit ? cards.slice(0, opts.limit) : cards;
4599
+ }
4534
4600
  /**
4535
4601
  * Scan every card in the course through the filter chain and report
4536
4602
  * how many are "well indicated" (score >= threshold) for the current user.
@@ -4828,6 +4894,7 @@ __export(navigators_exports, {
4828
4894
  NavigatorRoles: () => NavigatorRoles,
4829
4895
  Navigators: () => Navigators,
4830
4896
  diversityRerank: () => diversityRerank,
4897
+ getActivePipeline: () => getActivePipeline,
4831
4898
  getCardOrigin: () => getCardOrigin,
4832
4899
  getRegisteredNavigator: () => getRegisteredNavigator,
4833
4900
  getRegisteredNavigatorNames: () => getRegisteredNavigatorNames,