startGoalTracker was only ever called from tests, so DrinkLogged /
TaskCompleted / MealLogged events never incremented currentValue and
GoalReached never fired — the progress bars were cosmetic. Wire it into
the (app)/+layout idle boot next to startStreakTracker, with matching
teardown in onDestroy.
Also drop <AiProposalInbox module="goals"/> into the module ListView so
create_goal / pause_goal / resume_goal / complete_goal proposals are
reviewable inline (previously only visible in the mission-detail view).
Refresh the tool-coverage tables while we're at it: apps/mana/CLAUDE.md
now reflects the real catalog state (59 tools, 19 modules — was 37/12),
and services/mana-ai/CLAUDE.md shows the correct server-side propose
subset (31 tools, 16 modules). Also fixes a stale 'location_log' →
'get_current_location' typo in the places row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The header still showed the original three-tool surface after the
update/delete/stats additions landed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Completes the Quiz CRUD surface for the AI agent. Five new tools:
- update_quiz (propose) — rename/archive/pin + description/category
- update_quiz_question (propose) — text, type+options, explanation;
rejects a type swap without a matching optionsJson
- delete_quiz_question (propose) — symmetric to add_quiz_question
- get_quiz_questions (auto) — lets the planner see existing questions
before appending more (avoids duplicates)
- get_quiz_stats (auto) — attemptCount / avgScore / bestScore /
lastAttemptAt; enables adaptive missions like "analyze my weak spots
and generate harder questions"
delete_quiz deliberately left out — too destructive to leave in the
AI's hands when the user can delete manually in two clicks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pgSchema was enforced in practice across every service (auth, credits,
usr, events, mail, research, subscriptions, analytics) but nowhere
documented as a rule. New services had to reverse-engineer the pattern
from existing code, and the example in the guideline itself still used
raw pgTable() — actively steering readers in the wrong direction.
- New "Schema Isolation" section: the rule, the why, the naming table
(service → schema), the mana_sync exception, a grep-based verification
command.
- Updated the "Table Definition Pattern" example to use pgSchema so
readers copy the right thing.
The root CLAUDE.md already links here from the "Database (PostgreSQL)"
section — no change needed there.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TRUSTED_ORIGINS was defined inside better-auth.config.ts, which pulls
in the whole Better Auth stack just to read a list of hostnames. Anyone
who wants to consume the list (infra tooling, compose-env generators,
monitoring) had to either duplicate it or pay the import cost.
- New `sso-origins.ts` — zero-dep module exposing
`PRODUCTION_TRUSTED_ORIGINS` + `LOCAL_TRUSTED_ORIGINS` + the combined
`TRUSTED_ORIGINS` list. This is now the canonical place to add a new
top-level SSO origin.
- `better-auth.config.ts` imports + re-exports so existing consumers
keep working without a touch.
- `sso-config.spec.ts` imports directly from `./sso-origins` (cleaner
coupling) and now HARD-FAILS when mana-auth CORS_ORIGINS contains a
production origin that isn't in trustedOrigins. Previously this was
a `console.warn` only, meaning dead-drift could silently accumulate
and then surface as a confusing runtime auth rejection.
- Root CLAUDE.md "Adding an app to SSO" updated to point at the SSOT
and mention the new hard-fail direction.
No current drift — the mana-auth CORS_ORIGINS already match. The
hardened assertion is defensive for future changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Quiz is now an AI-accessible module. The agent can mint empty quizzes
and append questions across all four types (single / multi / truefalse
/ text) via a single add_quiz_question tool whose optionsJson payload
shape is documented in the catalog description. list_quizzes (auto)
returns decrypted metadata so the planner can reference existing
quizzes when extending them. Enables missions like "baue ein Quiz aus
meinen Notizen zu Thema X" — planner reads via list_notes, proposes
create_quiz, then N × add_quiz_question.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI previously ran `pnpm run test || true` — test failures were silently
swallowed with no artifact, so we had no visibility into what was actually
passing across 1,296 test files.
- New `test:coverage` turbo pipeline task + root script; packages that opt
in by declaring their own `test:coverage` get picked up automatically.
- Wired up three high-value Vitest targets: apps/mana/apps/web (main
frontend, ~590 tests), shared-ui (Svelte component library), and
shared-storage (S3 client). Each emits lcov.info + coverage-summary.json
+ browsable HTML.
- apps/mana/apps/web `"test"` was running in watch mode (just `vitest`),
which hangs under turbo orchestration — changed to `vitest run` and
added `test:watch` for the interactive case.
- CI uploads coverage artifacts (14-day retention) regardless of whether
tests passed. `continue-on-error: true` replaces `|| true` so a failed
suite shows up as a warning annotation on the PR rather than being
invisible. Flip to a hard gate once main is green for a full week.
- Testing guideline documents the pattern + the template vitest config
+ the planned 80% threshold.
- ESLint flat-config `vitest.config.ts` ignore only matched at the root;
widened to `**/vitest.config.{ts,js,mjs}` so nested configs don't trip
the project-service parser.
Coverage baseline produced locally:
shared-storage: 91.37% lines (6 files, 123 tests)
shared-ui: 2.87% lines (mostly Svelte components, untested)
apps/mana/web: 9/59 test files fail — pre-existing, not regression
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The flow was only documented in code comments scattered across the
catalog, executor, and runner. This guide collects the three-file
contract (catalog / executor / init.ts), the auto-vs-propose policy
matrix, and the drift-guard semantics into one place so future
sessions adding a new module's tools have a single entry point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The JWT already carried a `tier` claim but nothing on the server read it
— AuthGate enforcement was client-only, so a valid JWT could hit paid
LLM/research endpoints regardless of the user's access tier.
- shared-hono authMiddleware now extracts `tier` into `c.userTier`,
defaulting unknown/missing claims to `public` (never silently grants
higher access).
- New `requireTier(minTier)` middleware + `hasTier`/`getTierLevel`
helpers. Tier hierarchy (guest < public < beta < alpha < founder) is
mirrored locally to avoid pulling the Svelte-facing shared-branding
package into Bun services.
- Applied `requireTier('beta')` as defense-in-depth on resource-heavy
apps/api modules (chat, context, food, guides, news-research, picture,
plants, research, traces, who) and the MCP endpoint. Pure CRUD modules
stay auth-only — access there is gated by ownership, not tier.
- DEV_BYPASS_AUTH now injects `userTier` (defaults to founder, override
via DEV_USER_TIER).
- Authentication guideline documents the pattern + test suite covers
hierarchy, passes-at-minimum, and rejection paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Quiz module code was complete but not wired into the app-registry, so
it never appeared in AppPagePicker. Adds an AppDescriptor with the
Phosphor Exam icon, collection/paramKey/createItem for future DnD &
linking, plus a "Neues Quiz" context-menu action. Categorised under
'creative' next to cards, skilltree and library. Edit/Play stay on
route-based navigation (same pattern as library).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sync and MyData panels now follow the GeneralSection/SecuritySection
pattern — scoped CSS with theme tokens, Phosphor icons, .rows/.row
layout, action-snippet headers. MyData splits into seven focused
SettingsPanels (Profil, Auth, Credits, Projektdaten, Aufbewahrung,
Backup, Gefahrenzone). Projektdaten renders as an edge-to-edge compact
table that pulls app icons from the workbench app-registry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Complete walkthrough per provider — signup URL, free-tier details,
pay-per-use pricing, env-var name, key format — plus sections on where
to paste keys (.env.secrets), BYO-keys vs server-keys, verification
curl commands and troubleshooting (including the cross-service
MANA_SERVICE_KEY mismatch encountered during live testing).
Linked from services/mana-research/CLAUDE.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Populated help text for every real module that was showing no content
when the user clicked the PageShell ? icon:
activity, admin, ai-health, ai-insights, ai-policy, api-keys,
companion, complexity, credits, feedback, help, news, profile,
rituals, settings, spiral, themes
Each entry follows the established pattern: one-line description,
3–7 concrete features, optional tips. Internal / admin-only tools
(admin, complexity, ai-health) still get help so admins see the
same ?-icon behaviour as users.
The new-* quick-action "apps" (new-task, new-note, new-dream, …) and
log-day / open-feed were intentionally skipped — they don't have a
ListView and fire a CustomEvent instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- `app-registry/types.ts` now includes `tips` in the inline help shape,
matching `ModuleHelp` and what `AppPage.svelte` actually renders.
Drops 3 recurring type errors.
- `event-scout` template's `{ kind: 'daily' }` cadence now carries the
required `atHour` / `atMinute` fields (daily 08:00). Drops the 4th
type error — svelte-check is clean.
- `apps/mana/CLAUDE.md` gains a "Scene Scope" section documenting the
pattern: wire `filterBySceneScopeBatch` in the query AND render
`<ScopeEmptyState>` from the empty branch, so users always see why
the list is empty.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- The PageShell's ? icon already renders in-view help from the central
MODULE_HELP map, so the inline subtitle was duplicative. Added a
rich help entry (description + 7 features + 4 tips) matching the
calendar/contacts pattern.
- ListView header now just carries the mode-toggle (Suche/Extrakt/
Agent). Clean single-row layout.
- Moved "🔑 Eigene API-Keys verwalten" to a footer section at the
bottom of the page, separated by a border. Less busy header, and
BYO-key management is a rare action — belongs at the end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The workbench PageShell renders the app name in the page header, so the
in-view h2 "Research Lab" was duplicated. Dropped the heading and
realigned the subtitle alongside the API-Keys button and mode-toggle on
the same row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Funnel badge on a scoped scene pill is now a real button:
- Click clears the scene's scopeTagIds in one interaction instead of
sending the user through the TagSelector in SceneHeader.
- Tooltip shows the active scope tag names ("Bereich: Deep Work,
Urlaub — klicken zum Aufheben") so users see which filter is on
without opening the scene header.
- Keyboard-accessible via Enter/Space; stopPropagation prevents the
surrounding scene-pill button from also firing scene-select.
Tag names are resolved via the existing useAllTags liveQuery — no
extra Dexie round-trip per render.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Google deprecated `gemini-2.0-flash` for new API users — existing
accounts still work, but a freshly-billed key returns 404
"models/gemini-2.0-flash is no longer available to new users". The
working replacement is `gemini-2.5-flash` (same price tier, better
quality, groundingMetadata shape unchanged).
Verified live: the fix produced a real answer with 6 grounding
citations in 2.6s.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 3 follow-up from docs/plans/scene-scope-empty-state.md — a small
Funnel icon now sits next to the scene count whenever the scene has an
explicit `scopeTagIds` set. Users can see at a glance that a scope
filter is active even when the module lists aren't empty, instead of
only noticing the filter when a list turns up zero results.
Only marks explicit scene-level scope; the agent-derived scope is
already signalled by the bound-agent avatar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 1 of the scene-scope empty state plan (docs/plans/scene-scope-
empty-state.md). When the active scene's scope tags filter a module
down to zero results, the ListView now shows a dedicated empty state
with a one-click "Bereich zurücksetzen" button instead of the generic
"Keine Aufgaben"/"Keine Treffer" message. Previously the user couldn't
tell whether the list was empty because of missing data or because of
the scope filter.
- New `ScopeEmptyState.svelte` shared component.
- New `hasActiveSceneScope()` reactive helper on the scene-scope store.
- Wired into todo, notes, calendar, contacts ListViews — the four
modules that currently use `filterBySceneScopeBatch`.
- 4 unit tests for the scope primitives.
Phase 2 (per-module hidden count) and Phase 3 (persistent scope badge)
remain optional follow-ups.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eventbrite shut down their public Event Search API (/v3/events/search)
in 2023. The provider now uses the website extractor pipeline
(mana-research + LLM) to scrape Eventbrite's public search pages.
No API key needed — same pipeline as any website source.
Also adds mana-events to generate-env.mjs for automatic .env generation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- claude-web-search.ts: only send `temperature` when caller explicitly
sets one. Opus 4.7 deprecated the param and returns
400 invalid_request_error "`temperature` is deprecated for this
model." Sonnet/Haiku still accept it, so keep the opt-in path.
- execute-research.ts: log provider errors via console.warn so future
integration failures are visible in stdout. Previously the executor
swallowed the underlying error and only returned a generic errorCode,
which made diagnosing vendor-specific API changes impossible.
Discovered via smoke-testing with a real Anthropic key — the direct
curl worked, but our provider 400'd because Opus 4.7 tightened the
accepted param set.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the UX gap — a scoped scene that filters out everything shows
the generic "Keine Treffer" with no hint that the scope is the reason
or how to clear it. Plan lays out a minimal Phase 1 (shared
ScopeEmptyState component + clearSceneScope helper, ~10 LOC per
ListView) with optional Phase 2/3 extensions. No code yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 10 unit tests for the two helpers we hardened this session:
- toScene round-trips the core presentation fields and the two
previously-dropped extras (viewingAsAgentId, scopeTagIds). Guards
against the silent field-loss regression fixed in a1baf1053.
- pickActiveId covers empty lists, surviving current, MRU fallback,
skipping deleted MRU entries, corrupted-JSON MRU payload, and
non-string entries. Locks down the fallback ladder introduced in
4e5c3179f so scenes[0] stays a last resort.
Both helpers are now exported from the .svelte.ts store. The test
file mocks `$app/environment.browser=true` and polyfills localStorage
so it runs without jsdom (the web app doesn't bundle jsdom as a test
dep).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hoisted the rootMargin and threshold literals into PREMOUNT_MARGIN and
INTERSECTION_THRESHOLD constants next to MAX_MOUNTED. Same behavior —
the intent of the three tuning knobs is now visible at a glance and
easy to adjust from one spot if the lazy-mount envelope needs tweaking.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- pickActiveId now consults a per-device MRU list (top 5 recent
scenes, stored in localStorage) when the current scene disappears
(delete, sync pull, tier filter). Previously the fallback was
always scenes[0], which could strand the user on whatever sorted
first after a delete rather than the scene they were just on.
- reorderScenes runs all per-scene order patches inside one Dexie
rw-transaction. A partial failure previously left the scene list
with gapped or duplicated `order` values visible to subscribers;
the transaction makes the reorder all-or-nothing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
handleRequestRename focused the scene-header h1 after a hard-coded
120 ms setTimeout. On slower hardware the query fired before the
SceneHeader had re-rendered with the new active scene, focusing the
previous scene's h1. Replacing the timeout with `await tick()` flushes
Svelte's pending DOM updates before the query and removes the magic
number.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add EventProvider interface (base.ts) with fetchEvents(url, name, ctx, config)
- Refactor iCal parser and website extractor as provider adapters
- Add Eventbrite provider: API v3 search by location, category mapping,
price info extraction. Requires EVENTBRITE_API_KEY env var.
- Add Meetup provider: GraphQL API search by location, topic→category
mapping, HTML stripping. Requires MEETUP_API_KEY env var.
- Provider registry (getProvider, PROVIDER_TYPES) replaces hardcoded
switch in crawl-scheduler
- Crawl scheduler now joins sources with regions for ProviderContext
(lat/lon/radius/label) — platform providers need this for geo-search
- Source creation accepts 'eventbrite' and 'meetup' types (url optional)
- Both providers gracefully return empty when API keys unconfigured
116 tests (all passing), no regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously the intersection-observer cache grew monotonically: once a
card mounted its ListView + Dexie liveQuery, it stayed mounted for the
lifetime of the workbench page. A user who scrolled through 20 apps
kept 20 parallel liveQueries alive.
Now the cache is capped at MAX_MOUNTED=8 with insertion-order LRU
semantics: re-intersecting a mounted card bumps it to MRU, and the
oldest gets evicted when a new mount pushes the set over cap. Set
insertion-order is used for the LRU list so the template's has()
check stays O(1).
The cap is well above typical working-set sizes (3–6 apps) so regular
workbench use never hits the eviction path. Users with large scenes
pay at most one extra liveQuery + chunk re-request when scrolling back
to an evicted card.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add discover_events (auto) and suggest_event (propose) to shared-ai
tool catalog. discover_events reads the discovery feed, suggest_event
creates a proposal to save a discovered event to the user's calendar.
- Add Event-Scout agent template with daily "Events der Woche" mission.
Policy: discover_events=auto, suggest_event=propose, all else denied.
- Add frontend tool implementations in events/tools.ts — discover_events
calls the feed API, suggest_event delegates to discoveryStore.saveEvent.
- Add feedback.ts — computes implicit user profile from save/dismiss
history (category affinity + source quality as 0–2x weight multipliers).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Workbench CRUD handlers now emit a localized toast on failure instead
of only logging to the console. Quota, structured-clone or Dexie
transaction failures are now user-visible, so an add/remove/resize
that silently rejected can no longer leave the user guessing at a
frozen UI.
- Added a dev-only onMount that checks for stale Service Workers on
the homepage. vite-plugin-pwa is disabled in dev (see vite.config.ts
`devEnabled: false`), but a surviving SW from a previous `pnpm build
&& pnpm preview` session keeps serving cached HTML — e.g. showing
`/email-verified` at `/`. We detect, warn via toast, and unregister
automatically. Prod builds are unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two backlog items landed in one commit because an earlier amend in a
parallel terminal dropped the initial Phase 3b commit and the BYO-keys
work was blocked on the same wiring.
openai-deep-research (async):
- New research.async_jobs table persists the OpenAI response.id, query,
reservation, and cached result/error.
- POST /v1/research/async reserves credits, submits to the Responses API
with background=true, returns a taskId. Submit failure refunds.
- GET /v1/research/async/:taskId polls upstream, commits the reservation
on completion, refunds on failure, short-circuits for terminal states.
- GET /v1/research/async lists the user's async tasks.
BYO-keys:
- research.provider_configs CRUD at /v1/provider-configs. Keys are masked
(••••last4) on read so the raw secret never re-transits to the browser.
Currently stored plaintext with a TODO for AES-GCM-256 via the shared
KEK — single call site in storage/configs.ts.decryptKey().
- New frontend route /research-lab/keys lets the user paste a key per
provider, toggle enabled, and set daily/monthly credit budgets.
- ListView grew a 🔑 link in the header.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Merge the onMount + $effect deep-link handlers into one $effect gated
on `workbenchScenesStore.initialized`. On cold load the effect fires
early (store not ready), bounces, and re-fires once init completes.
Removes the duplicated logic and eliminates the race between the two
paths. onMount now only kicks off initialize().
- `dispose()` now resets `initializedState` and `subscribeRetryCount`
so a navigate-away → back cycle re-runs `initialize()` with a fresh
subscription and a clean retry budget. scenesState is left intact
to avoid an empty-workbench flash while the new liveQuery re-emits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The existing RSS-based path stays the default (shallow, free). `depth=deep`
fans out to two research agents in parallel (Perplexity Sonar first, then
Gemini Grounding if available) via the new mana-research /v1/research/compare
endpoint, merges their answers + citations into a single markdown context
block that the AI can cite from, and attaches the runId so the user can
revisit the comparison in Research Lab later.
AI missions keep calling the tool with no depth arg — they still get
free RSS results. Only explicit depth=deep consumes credits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Branding: research-lab registered in @mana/shared-branding with requiredTier: 'beta' + a custom flask-on-purple icon, so guest/public users are filtered out of the workbench picker.
- Backend: compare routes now return resultId alongside each CompareEntry so the frontend can wire ratings to the eval_results rows in research.*.
- Frontend: click-to-rate stars in CompareColumn (persists via POST /v1/runs/:runId/results/:resultId/rate), recent-run list rows are now buttons that navigate to /research-lab/runs/[id], and the detail route reconstructs CompareEntry shapes from eval_results + reuses CompareColumn for a full read-only view of any past run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PageCarousel now creates the IntersectionObserver exactly once when
the track mounts and diffs observed wrappers on pages changes,
instead of tearing down and rebuilding the entire IO on every
add/remove. Already-mounted pages no longer re-fire the intersection
callback on each reactive tick.
- SceneAppBar receives stable callback identities. The bar-props effect
previously recreated fresh inline arrows on every reactive pass
(carouselPages / appTitles / DEFAULT_WIDTH change), forcing the bar
to see new props even when only data changed. Hoisted handlers make
the bar's prop diff a pure data comparison.
- `createScene` failures from the bar now surface in the console
instead of silently rejecting.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses a "frozen workbench until reload" bug where adding a new page
sometimes stopped updating the UI and no further changes rendered until
the user reloaded.
- Wrap the workbench-scenes liveQuery `next` handler in try/catch so a
single malformed row can't kill the reactive chain. Re-subscribe on
terminal errors (up to 3× with backoff) so transient Dexie failures
(e.g. DatabaseClosed during a schema upgrade in another tab) recover
automatically instead of requiring reload.
- Rewrite `patchActiveScene` as a Dexie rw-transaction that reads the
row fresh and skips writes that produce the same array reference, so
two rapid writes (add A, then add B before the liveQuery echoes the
first change) can no longer clobber each other with a stale snapshot.
- Restore `viewingAsAgentId` and `scopeTagIds` in `toScene` — they were
silently dropped, breaking the agent-avatar pill in SceneAppBar and
the auto-inferred scope in SceneHeader.
- Surface Dexie write failures from the workbench CRUD handlers. Previous
fire-and-forget calls swallowed quota / structured-clone rejections,
leaving the picker closed but no new page visible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Voice-based interview for the profile module — users choose between text,
voice (question read aloud + mic for answer), or conversation mode (fully
automatic flow with auto-save).
Interview audio:
- 92 pre-rendered MP3 files (23 questions × 4 voices) via Edge TTS
- Voices: Seraphina (DE-f), Florian (DE-m), Leni (CH-f), Jan (CH-m)
- User picks voice via dropdown, persisted in localStorage
- Web Speech API fallback for missing audio files
Profile UI:
- Interview hero block on overview with 3 start modes (text/voice/conversation)
- Voice/conversation toggle + voice picker in interview view
- Mic button on text/textarea/tags inputs for per-question voice input
- Conversation mode: auto-save + auto-advance after STT transcription
- Recording/transcribing/speaking state indicators
mana-tts service:
- New Orpheus TTS backend (German finetune, SNAC codec)
- New Zonos TTS backend (Zyphra, 200k hours, emotion control)
- Endpoints: POST /synthesize/orpheus, POST /synthesize/zonos
- espeak-ng installed on GPU server for Zonos phonemizer
- Compare script for side-by-side voice quality testing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New SvelteKit module that consumes mana-research directly on port 3068
(JWT auth + CORS are already wired). Three modes — search, extract, agent
— with a shared ephemeral session store (sessionStorage, no Dexie) so
tab-refreshes survive but nothing leaks into the local-first data layer.
Components:
- ProviderPicker — chip multi-select per category. Shows free/ready/
needs-key status + per-call pricing from the providers catalog.
- CompareColumn — one provider's result: search hits (ordered list +
snippets + scores), extract (title + excerpt + body preview + stats),
or agent answer (text + citations list + token usage).
- ListView — mode toggle, query/URL input, providers row with cost
estimate, results grid, recent-runs history list.
Data flow: store calls api.ts → fetch(`${getManaResearchUrl()}/...`) with
Bearer JWT. Cost, latency, cache-hit, and billing-mode come back in each
result's meta and are rendered inline per column.
The module registers itself as "Research Lab" (Flask icon, purple brand
color). No collection, no IndexedDB table, no sync — this is purely a
live query interface over the comparison backend.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When clicking "KI-Einstellungen öffnen" from the companion chat while
settings is already open on a different tab, the settings panel now
correctly switches to the right tab and scrolls to the anchor.
The workbench deep-link $effect dispatches a custom
workbench:navigate-anchor event after opening/focusing the target panel.
The settings ListView listens for both this event and native hashchange,
then calls navigateToHash() to switch activeCategory and scroll.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move getUserMessage() to the base LlmError class so every error type
gets a German explanation with a clickable settings deep-link:
- TierTooLowError: "Kein KI-Modell aktiviert. Mindestens X benötigt."
- ProviderBlockedError: "… hat die Anfrage blockiert (Inhaltsfilter)."
- BackendUnreachableError: "… ist nicht erreichbar."
- EdgeLoadFailedError: "Browser-Modell konnte nicht geladen werden."
- Generic fallback: also includes the settings link now
The companion engine now catches LlmError (base class) instead of
only NoTierAvailableError, covering all failure modes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The onMount handler only fires once, so clicking a /?app=settings link
from the companion chat (or any in-app link) while already on the
workbench page did nothing. Add a reactive $effect that watches
$page.url.searchParams for 'app' changes and opens/scrolls to the
target panel on every navigation, not just on initial mount.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On mobile, long-press was selecting the row text instead of firing
`contextmenu`. Adds `user-select: none` + `-webkit-touch-callout: none`
on collapsed rows across 12 ListViews (notes, todo, dreams, journal,
firsts, contacts, places, mail, moodlit, chat, calendar) and re-enables
`user-select: text` on the inline-editor variants so the textarea
stays selectable while editing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Perplexity Sonar, Claude web_search, OpenAI Responses, and Gemini
Grounding as ResearchAgents behind the same comparison interface as the
search and extract providers.
New endpoints:
POST /v1/research — single-agent (or auto-routed to the first
provider with a configured key)
POST /v1/research/compare — fan-out across N agents, persist all
answers + citations in research.eval_*
Each agent normalizes its native response into a common AgentAnswer shape
(answer text + citations[] + tokenUsage), storing the provider's raw
response alongside for later inspection. Implementations use direct HTTP
against each vendor's public API — no SDK deps added.
Auto-routing preference: perplexity-sonar → gemini-grounding →
openai-responses → claude-web-search → (openai-deep-research stubbed for
Phase 3b). Credits orchestration reuses the search/extract executor
pattern (reserve → call → commit/refund).
Deferred to Phase 3b: openai-deep-research (async job queue), migration
of mana-ai + mana-api news-research to call this service directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Error messages now include a clickable Markdown link
"KI-Einstellungen öffnen" that navigates to /?app=settings#ai-options,
which opens the settings panel in the workbench, switches to the AI
tab, and scrolls to the LLM options section.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Lightning was inherited from the ai-rituals era (AI-feature vibe) and
didn't match what rituals actually express — recurring, cyclical
practice. ArrowClockwise reads as "loop / recurrence / do this
regularly", which fits both utility and ceremony flavours.
Lightning is kept for `automations` (still the right match there).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Track skip reasons per tier in the orchestrator (no-consent,
no-backend, not-available, not-ready, runtime-error) and expose
them via NoTierAvailableError.getUserMessage() with actionable
German text pointing the user to the right settings page.
Before: "No tier could run task 'companion.chat' (attempted: cloud)"
After: "Cloud (Gemini): Cloud-Einwilligung fehlt. Aktiviere sie
unter Einstellungen → KI."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>