diff --git a/apps/mana/apps/web/src/lib/modules/eventstream/ListView.svelte b/apps/mana/apps/web/src/lib/modules/activity/ListView.svelte similarity index 100% rename from apps/mana/apps/web/src/lib/modules/eventstream/ListView.svelte rename to apps/mana/apps/web/src/lib/modules/activity/ListView.svelte diff --git a/apps/mana/apps/web/src/lib/modules/cycles/ListView.svelte b/apps/mana/apps/web/src/lib/modules/period/ListView.svelte similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/ListView.svelte rename to apps/mana/apps/web/src/lib/modules/period/ListView.svelte diff --git a/apps/mana/apps/web/src/lib/modules/cycles/ROADMAP.md b/apps/mana/apps/web/src/lib/modules/period/ROADMAP.md similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/ROADMAP.md rename to apps/mana/apps/web/src/lib/modules/period/ROADMAP.md diff --git a/apps/mana/apps/web/src/lib/modules/cycles/collections.ts b/apps/mana/apps/web/src/lib/modules/period/collections.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/collections.ts rename to apps/mana/apps/web/src/lib/modules/period/collections.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/components/CycleCalendar.svelte b/apps/mana/apps/web/src/lib/modules/period/components/CycleCalendar.svelte similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/components/CycleCalendar.svelte rename to apps/mana/apps/web/src/lib/modules/period/components/CycleCalendar.svelte diff --git a/apps/mana/apps/web/src/lib/modules/cycles/components/SymptomManager.svelte b/apps/mana/apps/web/src/lib/modules/period/components/SymptomManager.svelte similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/components/SymptomManager.svelte rename to apps/mana/apps/web/src/lib/modules/period/components/SymptomManager.svelte diff --git a/apps/mana/apps/web/src/lib/modules/cycles/index.ts b/apps/mana/apps/web/src/lib/modules/period/index.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/index.ts rename to apps/mana/apps/web/src/lib/modules/period/index.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/module.config.ts b/apps/mana/apps/web/src/lib/modules/period/module.config.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/module.config.ts rename to apps/mana/apps/web/src/lib/modules/period/module.config.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/queries.ts b/apps/mana/apps/web/src/lib/modules/period/queries.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/queries.ts rename to apps/mana/apps/web/src/lib/modules/period/queries.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/stores/cycles.integration.test.ts b/apps/mana/apps/web/src/lib/modules/period/stores/cycles.integration.test.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/stores/cycles.integration.test.ts rename to apps/mana/apps/web/src/lib/modules/period/stores/cycles.integration.test.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/stores/cycles.svelte.ts b/apps/mana/apps/web/src/lib/modules/period/stores/cycles.svelte.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/stores/cycles.svelte.ts rename to apps/mana/apps/web/src/lib/modules/period/stores/cycles.svelte.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/stores/dayLogs.svelte.ts b/apps/mana/apps/web/src/lib/modules/period/stores/dayLogs.svelte.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/stores/dayLogs.svelte.ts rename to apps/mana/apps/web/src/lib/modules/period/stores/dayLogs.svelte.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/stores/symptoms.svelte.ts b/apps/mana/apps/web/src/lib/modules/period/stores/symptoms.svelte.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/stores/symptoms.svelte.ts rename to apps/mana/apps/web/src/lib/modules/period/stores/symptoms.svelte.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/tools.ts b/apps/mana/apps/web/src/lib/modules/period/tools.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/tools.ts rename to apps/mana/apps/web/src/lib/modules/period/tools.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/types.ts b/apps/mana/apps/web/src/lib/modules/period/types.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/types.ts rename to apps/mana/apps/web/src/lib/modules/period/types.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/utils/auto-detect.test.ts b/apps/mana/apps/web/src/lib/modules/period/utils/auto-detect.test.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/utils/auto-detect.test.ts rename to apps/mana/apps/web/src/lib/modules/period/utils/auto-detect.test.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/utils/auto-detect.ts b/apps/mana/apps/web/src/lib/modules/period/utils/auto-detect.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/utils/auto-detect.ts rename to apps/mana/apps/web/src/lib/modules/period/utils/auto-detect.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/utils/phase.test.ts b/apps/mana/apps/web/src/lib/modules/period/utils/phase.test.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/utils/phase.test.ts rename to apps/mana/apps/web/src/lib/modules/period/utils/phase.test.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/utils/phase.ts b/apps/mana/apps/web/src/lib/modules/period/utils/phase.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/utils/phase.ts rename to apps/mana/apps/web/src/lib/modules/period/utils/phase.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/utils/prediction.test.ts b/apps/mana/apps/web/src/lib/modules/period/utils/prediction.test.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/utils/prediction.test.ts rename to apps/mana/apps/web/src/lib/modules/period/utils/prediction.test.ts diff --git a/apps/mana/apps/web/src/lib/modules/cycles/utils/prediction.ts b/apps/mana/apps/web/src/lib/modules/period/utils/prediction.ts similarity index 100% rename from apps/mana/apps/web/src/lib/modules/cycles/utils/prediction.ts rename to apps/mana/apps/web/src/lib/modules/period/utils/prediction.ts diff --git a/apps/mana/apps/web/src/routes/(app)/cycles/+page.svelte b/apps/mana/apps/web/src/routes/(app)/period/+page.svelte similarity index 100% rename from apps/mana/apps/web/src/routes/(app)/cycles/+page.svelte rename to apps/mana/apps/web/src/routes/(app)/period/+page.svelte diff --git a/packages/shared-llm/src/orchestrator.ts b/packages/shared-llm/src/orchestrator.ts index 7176f6a08..505c1b139 100644 --- a/packages/shared-llm/src/orchestrator.ts +++ b/packages/shared-llm/src/orchestrator.ts @@ -101,7 +101,8 @@ export class LlmOrchestrator { if (task.minTier === 'none') return true; if (task.runRules) return true; - const candidates = this.candidateTiers(task); + const override = this.settings.taskOverrides[task.name]; + const candidates = this.candidateTiers(task, override); return candidates.some((t) => { const backend = this.backendsByTier.get(t); return backend?.isAvailable() ?? false; @@ -117,9 +118,17 @@ export class LlmOrchestrator { const start = performance.now(); const attempted: LlmTier[] = []; - // Rule 1: tier-too-low check - const userMaxTier = this.userMaxTier(); - if (TIER_RANK[task.minTier] > TIER_RANK[userMaxTier]) { + // Rule 1: tier-too-low check. + // An explicit per-task override counts as opting-in to that tier + // even if it isn't in the user's global allowedTiers — that's the + // whole point of overrides (e.g. "use BYOK just for the Companion"). + const override = this.settings.taskOverrides[task.name]; + const effectiveMaxTier = override + ? TIER_RANK[override] > TIER_RANK[this.userMaxTier()] + ? override + : this.userMaxTier() + : this.userMaxTier(); + if (TIER_RANK[task.minTier] > TIER_RANK[effectiveMaxTier]) { if (task.runRules) { const value = await task.runRules(input); return { @@ -129,12 +138,11 @@ export class LlmOrchestrator { attempted: ['none'], }; } - throw new TierTooLowError(task.name, task.minTier, userMaxTier); + throw new TierTooLowError(task.name, task.minTier, effectiveMaxTier); } // Rules-2-3: candidate tier list and per-task override - const candidates = this.candidateTiers(task); - const override = this.settings.taskOverrides[task.name]; + const candidates = this.candidateTiers(task, override); const orderedTiers = override ? [override].filter((t) => candidates.includes(t)) : candidates; // Rule 4-5: try the first runnable tier @@ -236,17 +244,23 @@ export class LlmOrchestrator { /** Candidate tier list after applying rules 1 + 2. * - Rule 1: only tiers >= task.minTier - * - Rule 2: sensitive content excludes mana-server + cloud + * - Rule 2: sensitive content excludes mana-server + cloud + byok + * - If a per-task override is given, it's allowed even if not in + * settings.allowedTiers (explicit per-task opt-in beats global) * Also always includes 'none' at the end if the task has runRules. */ - private candidateTiers(task: LlmTask): LlmTier[] { - // Sort by privacy gradient (most private first) so the browser tier - // always wins over mana-server when both are enabled, regardless of - // the order the user toggled them in settings. - let tiers = this.settings.allowedTiers + private candidateTiers(task: LlmTask, override?: LlmTier): LlmTier[] { + // Start with the user's allowed tiers, plus the override if set + // (the override is an explicit per-task opt-in even if the user + // hasn't enabled that tier globally). + const baseTiers = override + ? Array.from(new Set([...this.settings.allowedTiers, override])) + : this.settings.allowedTiers; + + let tiers = baseTiers .filter((t) => TIER_RANK[t] >= TIER_RANK[task.minTier]) .sort((a, b) => TIER_RANK[a] - TIER_RANK[b]); - // Rule 2: sensitive content backstop + // Rule 2: sensitive content backstop — only browser-local stays if (task.contentClass === 'sensitive') { tiers = tiers.filter((t) => t === 'browser'); }