Commit graph

660 commits

Author SHA1 Message Date
Till JS
5b7564b3a4 test(ai): promote MockLlmClient to a shared @mana/shared-ai export
The runPlannerLoop test file and the webapp's mission-runner test each
had their own inline scripted LLM mock — same interface, diverged
slightly. Consolidates into packages/shared-ai/src/planner/mock-llm.ts
and re-exports from the package root so any consumer can drive the
loop deterministically.

Both existing test files now use the shared client. 5 + 3 tests pass,
44 total in shared-ai still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 18:05:46 +02:00
Till JS
9f7d2f24b3 feat(companion): chat on runPlannerLoop with native function calling
The companion chat had its own ad-hoc 3-round tool-calling pipeline:
build a system prompt with tool descriptions, ask the LLM to emit
```tool JSON blocks, regex-extract, execute, feed back the result as
a synthetic user message. Same fragility class as the old text-JSON
planner — and now unnecessary since mana-llm speaks native function
calling.

Migrates companion/engine.ts to the shared runPlannerLoop, same as
the mission runner (commit 5a) and the server tick (commit 6). Tools
go to the LLM as proper function-schemas; tool_calls come back
structured; the executor runs them directly under USER_ACTOR.

Extends shared-ai/planner/loop.ts with an optional priorMessages[]
input field so the chat can preserve multi-turn history between
turns (missions don't need this and leave it empty).

Deletes the old llm-tasks/companion-chat.ts LlmTask wrapper. Nothing
else imported it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 16:45:33 +02:00
Till JS
166d6c6ffb feat(spaces): validate space metadata on Better Auth organization hooks
Moves the canonical SpaceType + SPACE_MODULE_ALLOWLIST to @mana/shared-types
(framework-free) so the Bun services can consume them without pulling in
Svelte. shared-branding keeps only the UI-facing labels and descriptions
and re-exports the canonical types for frontend convenience.

Wires two Better Auth organization hooks in mana-auth:
- beforeCreateOrganization asserts metadata.type is a valid SpaceType,
  rejecting the create with a BAD_REQUEST otherwise.
- beforeDeleteOrganization rejects deletion of the personal space.

Covered by bun tests (11 assertions) for the helper module.

No migration and no schema change — type lives in the existing
organization.metadata jsonb column.

Plan: docs/plans/spaces-foundation.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 16:05:38 +02:00
Till JS
b249345174 feat(spaces): add space types + module allowlist as multi-tenancy foundation
Introduces SpaceType ('personal' | 'brand' | 'club' | 'family' | 'team' |
'practice') and SPACE_MODULE_ALLOWLIST as the shared-branding primitives
for the Spaces refactor that replaces the user-vs-org polymorphy with a
single tenancy primitive (Notion/Linear pattern).

Pure additive — no runtime behaviour change yet. Better Auth config,
Dexie migration, scope wrapper and rolling module migration follow in
separate commits.

Plan: docs/plans/spaces-foundation.md
Social-relay plan now defers brand storage to the Spaces primitive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:57:57 +02:00
Till JS
0077752456 fix(type-check): clear the last five failures — monorepo type-check is now 76/76 green
After the mobile-app deletion unblocked \`@context/mobile\`, five more
pre-existing failures surfaced across shared packages and two services.
All were silent-masked by the postinstall \`|| true\` for months.

- **shared-ai**: \`planner/loop.ts\` imported \`ToolSchema\` from
  \`../tools/function-schema\`, which only imports (not re-exports) the
  type. Fixed to import from the source (\`../tools/schemas\`).
- **shared-logger**: \`typeof window !== 'undefined'\` blows up under
  tsconfigs that don't include the DOM lib (e.g. uload-server's
  \`bun-types\`-only config), because shared-logger is consumed via
  source import. Replaced with a \`globalThis\`-indirected check that
  compiles under any lib configuration.
- **shared-hono**: \`credits.ts\` returned \`res.json()\` directly as
  \`Promise<T | null>\`. Modern \`@types/node\` / undici types return
  \`unknown\` strictly — cast to \`T\` at the boundary so the generic
  contract is explicit.
- **uload-server**: \`routes/analytics.ts\` + \`routes/email.ts\` still
  imported \`AuthUser\` from a \`middleware/jwt-auth\` module that was
  deleted during the migration to \`@mana/shared-hono\`. Replaced with
  \`AuthVariables\` from shared-hono, which matches the actual context
  shape set by \`authMiddleware()\`.
- **manavoxel/web**: \`guestSeed\` collection entries were wrapped in
  arrow functions, but \`local-store\` expects \`T[]\` directly and
  iterates \`seed.length\` — which on a function is 0. The "guest
  seed" was silently dead; eager-evaluating \`generateGuestWorld()\`
  once and sharing the result fixes both the type and the runtime.

Verified: \`pnpm run type-check\` from the repo root now exits 0 —
76/76 tasks successful, no failures. First fully green state since
well before the postinstall \`|| true\` was introduced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:53:07 +02:00
Till JS
4daca8970b feat(shared-ai): runPlannerLoop + compact system prompt for function calling
Introduces the new planner pipeline both the webapp runner and the
mana-ai tick will swap onto in the next commits. Additive for now —
the legacy buildPlannerPrompt + parsePlannerResponse stay exported so
callers can migrate one at a time; they get removed once the last
consumer is gone.

- planner/loop.ts — runPlannerLoop orchestrates a multi-turn chat
  against a caller-supplied LlmClient. Tool-calls from the LLM are
  handed to an onToolCall callback and their results fed back as
  tool-messages. Parallel tool-calls in one turn execute sequentially
  to keep the message log linear for debugging. Stops on assistant
  stop, empty tool_calls, or a hard max-rounds ceiling (default 5).
- planner/system-prompt.ts — new buildSystemPrompt. ~40-line German
  system frame, no tool listing (the SDK-level tools field carries
  the schemas now), no JSON format example, no "please return JSON"
  plea. User frame renders mission + linked inputs + last 3
  iteration summaries, same as before.
- Five test cases covering the loop: immediate stop, single tool
  call with result feedback, parallel calls execute in order, tool
  failures propagate as tool-messages the LLM can react to, and
  maxRounds ceiling fires with the right stopReason.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:31:01 +02:00
Till JS
2cf89ce26a feat(invoices): M1 skeleton — module registration + empty ListView
New outbound-finance module that issues invoices to clients. M1 scope:
- types, constants, collections with demo seed (not auto-loaded)
- module.config registered in module-registry
- Dexie v27 with invoices / invoiceClients / invoiceSettings tables
- encryption registry entries for all three tables (type-safe via entry<T>)
- app entry (requiredTier: alpha) + gradient icon (emerald→teal, QR corner)
- route /invoices mounts ListView with empty state

Money stored as integers in minor units (Rappen/cents) to avoid float
drift. Totals kept plaintext for liveQuery aggregation; lines encrypted
as a whole array so titles ride alongside. Settings is a singleton with
stable sentinel id so sync dedupes on it.

Plan: docs/plans/invoices-module.md. Next: M2 CRUD + number generator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:28:09 +02:00
Till JS
4523ab24e3 feat(shared-ai): toolToFunctionSchema — catalog → OpenAI function-spec
Single bridge between the AI_TOOL_CATALOG shape and the wire format every
provider (Gemini, OpenAI-compat, Ollama ≥ 0.3) speaks for native tool
calling. Keeps the catalog as the source of truth — the runner never
reads catalog entries directly; it asks this converter for function-spec
shapes to hand the LLM.

- No _rationale or wrapper-tool injection: the runner doesn't need it
  and the added schema noise would hurt planner quality.
- Throws on unknown parameter types so catalog typos (e.g. "array"
  instead of "string") fail loudly instead of coercing silently.
- Preserves enum constraints; drops the enum key entirely when absent
  so Gemini doesn't reject empty-enum function-declarations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:24:36 +02:00
Till JS
c612a22371 fix(type-check): unblock two more pre-existing failures
After yesterday's type-check cascade repair (c34175afa), the root
\`pnpm run type-check\` progressed through 5 more packages but still
stopped on two pre-existing failures:

- \`services/mana-media\` delivery route: \`c.body(transformedBuffer)\`
  passed a Node \`Buffer<ArrayBufferLike>\`, but Hono 4.7 types the
  body argument as \`Uint8Array<ArrayBuffer>\` (strict — no
  ArrayBufferLike). \`Uint8Array.from(buf)\` gives a clean copy with a
  fresh \`ArrayBuffer\` backing that the strict type accepts. Runtime
  cost for a handful of KB per image transform is negligible next to
  the Sharp pipeline that produced the buffer.
- \`packages/shared-llm\`: same rune issue as local-stt + local-llm —
  \`store.svelte.ts\` uses \`$state\` and transitively pulls in
  \`local-llm/src/svelte.svelte.ts\`. Plain tsc can't resolve Svelte 5
  runes. Same treatment: \`type-check\` script explicitly skips with a
  message pointing at svelte-check.

Root \`pnpm run type-check\` now reaches \`@context/mobile\`, which has
real code-level type errors (adapter shape mismatches, an RN event-
handler typing drift, and a deleted Supabase module still imported by
\`utils/supabaseTest.ts\`). Those need domain changes, not config
tweaks — out of scope for this repair pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:20:08 +02:00
Till JS
c34175afab fix(type-check): repair silently broken per-package type-check scripts
Yesterday's postinstall fix (\`d1d37749f\`) removed the \`|| true\`
guards, which in turn exposed that \`pnpm run type-check\` at the
root had been red for a long time but nobody noticed. Several per-
package scripts were genuinely broken:

- \`@mana/test-config\`: \`vitest.config.base.ts\` and \`.svelte.ts\`
  pass \`all: true\` to the coverage block. Vitest 4 removed that flag
  (including uncovered files is now the default), so tsc reports
  \`'all' does not exist in type 'CoverageOptions'\`. Removed both.
- \`@mana/credits\`: \`tsconfig.json\` include glob had
  \`"src/**/*.svelte"\`, which makes tsc try to parse .svelte files
  as TS source. It can't. Removed .svelte from include; added
  \`"exclude": ["src/web/**"]\` — the web consumer layer is checked by
  svelte-check in the apps that import it, not here.
- \`@mana/local-stt\` + \`@mana/local-llm\`: ship \`svelte.svelte.ts\`
  files that use Svelte 5 runes (\`$state\` etc.). Plain tsc has no
  rune support — \`$state\` is not a name it knows about. Both
  packages' \`type-check\` scripts now explicitly skip with a message
  pointing at svelte-check as the right tool. The rune code is still
  type-checked by svelte-check when a consumer app runs \`pnpm check\`.
- \`@manavoxel/shared\`: was missing its \`tsconfig.json\` entirely,
  so the \`type-check\` script ran tsc with no config, which dumped
  the CLI help and exited non-zero. Added a minimal bundler-mode
  tsconfig matching the pattern used by sibling packages.

\`pnpm run type-check\` now goes further than it has in months —
next failure is a real pre-existing Hono type mismatch in
\`services/mana-media/apps/api/src/routes/delivery.ts\` (Buffer vs
c.body signature), which is out of scope here and needs a proper
code fix, not a config fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 15:13:54 +02:00
Till JS
d1d37749f7 fix(install): remove silent || true from postinstall + narrow filter
The root postinstall was `node scripts/generate-env.mjs || true &&
pnpm run build:packages || true`. Two failures were being swallowed:

1. shared-auth's build has been broken for a while. shared-types
   re-exports its submodules with explicit `.ts` extensions
   (`export * from './theme.ts'`), which only works for downstream
   consumers that set `allowImportingTsExtensions: true`. shared-auth
   didn't — tsc emitted TS5097 on every re-export, the build failed,
   `|| true` hid it, every `pnpm install` appeared clean.

2. The filter `@mana/*` matches everything in the workspace, including
   `@mana/web` — the full 27-module SvelteKit build. On postinstall
   this kicked off vite, which OOM-aborted during SW generation.
   That's the original reason `|| true` was added, judging by shape.

Fixes:
- Dropped the `.ts` suffix from shared-types/src/index.ts re-exports.
  shared-types is consumed in bundler-mode tsconfigs everywhere, so no
  extension is the portable form. shared-types' own `tsc --noEmit`
  still passes.
- Narrowed the filter from `@mana/*` (name-glob, matches apps) to
  `./packages/*` (path-glob, only workspace packages). Scope drops
  from 133 → 39 projects; build:packages now runs cleanly in ~15s.
- Removed both `|| true` guards. A broken postinstall now fails
  loudly instead of producing a half-built state nobody notices.

Verified: `pnpm install` completes exit 0 in 13s; all 39 packages
build green.

Closes audit item #37 (postinstall swallows errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 14:58:59 +02:00
Till JS
7fb31e41b5 feat(ai): expand Quiz tools — edit/delete questions, edit meta, stats
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>
2026-04-19 19:50:24 +02:00
Till JS
2bcc3954ea feat(ai): add Quiz tools (create_quiz, add_quiz_question, list_quizzes)
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>
2026-04-19 19:29:35 +02:00
Till JS
4b8defcc4a chore(ci): add v8 test coverage tracking (non-blocking baseline)
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>
2026-04-19 19:21:14 +02:00
Till JS
76d11a84ee feat(auth): server-side tier gating via requireTier middleware
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>
2026-04-19 17:38:06 +02:00
Till JS
cd594509b2 chore: clear svelte-check errors + document scene-scope pattern
- `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>
2026-04-18 17:24:52 +02:00
Till JS
2c0d866287 feat(events): Phase 3 — AI tools, Event-Scout template, feedback loop
- 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>
2026-04-18 15:37:28 +02:00
Till JS
8f0a74b2e7 feat(research-lab): tier gate (beta+), 1–5 star ratings, run detail route
- 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>
2026-04-17 15:28:02 +02:00
Till JS
1cfd05939e fix(llm): user-friendly messages + settings link for all LLM errors
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>
2026-04-17 15:13:48 +02:00
Till JS
928f036033 fix(llm): add deep-link to AI settings in tier error messages
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>
2026-04-17 14:58:32 +02:00
Till JS
2b96953ad1 fix(llm): user-friendly error messages when no LLM tier available
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>
2026-04-17 14:46:39 +02:00
Till JS
2bdb48bdd1 feat(research): add mana-research service — Phase 1 + 2
New Bun/Hono service on port 3068 that bundles many web-research providers
behind a unified interface for side-by-side comparison. All eval runs
persist in research.* (mana_platform) so quality can be reviewed later.

Providers (Phase 1+2):
  search:  searxng, duckduckgo, brave, tavily, exa, serper
  extract: readability (via mana-search), jina-reader, firecrawl

Endpoints:
  POST /v1/search, /v1/search/compare       — single + fan-out
  POST /v1/extract, /v1/extract/compare     — single + fan-out
  GET  /v1/runs, /v1/runs/:id               — history
  POST /v1/runs/:run/results/:id/rate       — manual eval
  GET  /v1/providers, /v1/providers/health  — catalog + readiness

Auto-routing: when `provider` is omitted, queries are classified via regex
(fast path, 0ms) with optional mana-llm fallback, then routed to the first
available provider for that query type (news → tavily, academic → exa,
semantic → exa, etc.).

Credits: server-key calls go through mana-credits reserve → commit/refund
so failed provider calls don't charge the user. BYO-keys supported via
research.provider_configs (UI arrives in Phase 4).

Cache: Redis with graceful degradation (1h TTL for search, 24h for
extract). Pay-per-use APIs only — no subscription-gated providers.

Docs: docs/plans/mana-research-service.md + docs/reports/web-research-capabilities.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 14:42:25 +02:00
Till JS
5bdacaa5ea feat(wishes): add Wünsche module — wishlists with price tracking
New module for managing wishes/gift ideas with lists, price targets,
product URLs, price history, and AI tools. Includes ListView with
filter tabs, inline list management, search, and DetailView with
notes and price history. Encrypted at rest (title, description, URLs,
notes). Registered in database v24, module-registry, crypto registry,
seed registry, tool init, and DnD type system.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:02:37 +02:00
Till JS
8c6502d0ff feat(library): add Bibliothek module — books/movies/series/comics log
M1 skeleton for a new media-consumption module. Single-table design with
a `kind: 'book' | 'movie' | 'series' | 'comic'` discriminator and a
discriminated `details` union for kind-specific fields (pages / runtime /
episode tracker / issue count). Shared kern: status, rating, review,
favourites, times counter, completedAt — which enables cross-media
queries like a year-in-review.

Dexie migration v26 was already registered in module-registry.ts /
database.ts via the preceding wetter commit (62aac6dfd); this commit
adds the actual module code, encryption registry entry, app-icon,
MANA_APPS entry, Kreativität & Medien category row, and the module
plan at docs/plans/library-module.md.

Encrypted fields (via ENCRYPTION_REGISTRY):
  title, originalTitle, creators, review, tags
Plaintext (intentional):
  kind, status, year, rating, genres, completedAt, isFavorite, times,
  externalIds, details — all needed for the tab filter, status chips,
  Jahresrückblick range-scan, and progress UIs.

Product decisions (frozen in the plan):
  - audiobooks = kind='book' with details.format='audio'
  - manga     = kind='comic' (no sub-discriminator)
  - metadata lookup (M7) lands as an endpoint in apps/api, not a
    standalone service

Guest seed ships one example per kind (Dune, Arrival, Severance, Saga)
so first-run users immediately see what the module does.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 03:49:01 +02:00
Till JS
62aac6dfdb feat(wetter): add weather module with Open-Meteo, DWD alerts, and rain nowcast
New module providing weather data for the DACH region via three sources:
- Open-Meteo (DWD ICON-D2 model) for current conditions and 7-day forecast
- DWD warnings endpoint for severe weather alerts
- Rainbow.ai / Open-Meteo fallback for minute-level rain nowcast

Includes API proxy with in-memory caching, Svelte 5 UI with location
picker, hourly/daily forecast, alert cards, and precipitation bar chart.
Two AI tools (get_weather, get_rain_forecast) enable the companion to
answer weather questions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 03:46:15 +02:00
Till JS
efc7641a60 chore(ai): P2 batch — prompt sync, perf, dedup, scope unification
Six P2 items from the AI Workbench audit:

#7 Prompt ↔ loop budget sync:
  System prompt now says "1 bis 5 Schritte pro Planungsrunde, bis zu 5
  Planungsrunden" — matches MAX_REASONING_LOOP_ITERATIONS. Cross-ref
  comment added to runner.ts.

#9 SceneHeader: useAgents() → useAgent(id):
  Only loads the single bound agent instead of the full agent list.
  Eliminates unnecessary Dexie churn on every scene header render.

#10 Unified scope filter:
  New scope-filter.ts with filterByScopeTagMap() (batch, sync) and
  filterByScopeAsync() (per-record). Both scope-context.ts (AI) and
  scene-scope.svelte.ts (UI) now import from the shared module —
  zero duplicated filter logic.

#11 Research dedup:
  Research input ID changed from `news-research-${Date.now()}` to
  `news-research-${mission.id}` — re-runs overwrite instead of
  appending duplicates.

#12 Kontext injection policy clarified:
  loadAgentKontextAsResolvedInput no longer falls back to the global
  singleton. Comment + code aligned: kontext injection is explicit
  (via input picker), not auto. Dead loadKontextAsResolvedInput
  kept for potential future opt-in auto-inject feature.

Audit doc updated with all items marked DONE.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 16:33:52 +02:00
Till JS
a480393bfd fix(ai): P1 batch — N+1 queries, vault-locked, debug hardening, timeout
Four P1 fixes from the AI Workbench audit:

#3 N+1 junction queries → batch lookups:
  - TagLinkOps gains getTagIdsForMany(entityIds) — single
    where(field).anyOf(ids).toArray() instead of N calls.
  - filterBySceneScopeBatch() uses a pre-fetched Map<id, tagId[]>.
  - All 4 module queries (notes, todo, contacts, calendar) migrated.
  - 500 notes now = 2 Dexie queries (records + junctions) instead of 501.

#4 Vault-locked detection in readLocalNote:
  - Catches VaultLockedError from decryptRecords.
  - Throws descriptive "Vault ist gesperrt" instead of returning null.
  - Tools surface it as a clear error to the planner ("bitte Vault
    entsperren") instead of "Notiz nicht gefunden".

#5 Debug log hardening:
  - Resolved-input content truncated to 500 chars before storage.
  - Time-based purge: entries older than 7 days auto-deleted.
  - Reduces privacy exposure if device is stolen/profile synced.

#6 Timeout 90s → 180s:
  - 5 LLM calls on slow models (Ollama/GPU) regularly hit 90s.
  - 180s gives comfortable headroom for the reasoning loop.

Audit doc updated with status markers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 16:25:17 +02:00
Till JS
fad7f4bea3 feat(ai): guardrail layer — pre/post-plan + pre-execute checks
Add a guardrail system that runs alongside the Mission Runner pipeline
to catch obvious issues before they waste tokens or corrupt data.

Architecture (packages/shared-ai/src/guardrails/):
- types.ts: Guardrail, GuardrailResult, 4 phase interfaces
- builtin.ts: 4 built-in guardrails (always active):
  - input-size-limit: blocks >100K chars of resolved input
  - plan-step-limit: blocks plans with >25 steps (runaway planner)
  - duplicate-destructive-tool: warns if undo_drink called 2x
  - empty-required-params: blocks create_task without title
- runner.ts: runPrePlanGuardrails/runPostPlanGuardrails/runPreExecuteGuardrails

Wired into runner.ts at 3 checkpoints:
- Before deps.plan() — pre-plan check
- After plan received — post-plan check
- Before each stage() call — pre-execute check

Guardrails are synchronous, never hit the network, and produce
clear error messages when they block.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 15:11:34 +02:00
Till JS
ed01d24f2d feat(ai): add AI tools for myday, goals, mood, finance, and times
Expand agent tool coverage from 28 to 47 tools across 16 modules:

- myday: get_myday_summary (full daily context in one call)
- goals: list_goals, get_goal_progress, create_goal, pause/resume/complete_goal
- mood: log_mood, get_mood_today, get_mood_insights (trends + correlations)
- finance: extend add_transaction, add get_month_summary + list_transactions
- times: extend start/stop_timer, add get_timer_status, get_time_stats, list_projects

All tools registered in both AI_TOOL_CATALOG (shared-ai) and webapp init.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 15:01:12 +02:00
Till JS
acd7e0d6b0 docs: update architecture comparison — 5/10 roadmap items done
Update report to reflect all completed work:
- Matrix: streaming , tool registration updated to 29 tools + MCP
- §5.2 Streaming: marked done
- §5.3 Tool System: marked done
- §6 Table: items 1-3 + 5 struck through with commit refs
- §8 Fazit: updated gaps and recommendations

5 of 10 roadmap items complete in one session:
1. SSE Streaming, 2. Dynamic Tool Registry, 3. Budget Enforcement,
5. MCP Server Export (27/29 tools with DB ops), plus Tool Drift Fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 15:00:09 +02:00
Till JS
e2d540a958 refactor(settings): rewrite GeneralSection inline + delete @mana/subscriptions
GeneralSection: replace the GlobalSettingsSection wrapper (which
rendered its own SettingsSection pill + SettingsCard, requiring
title="" to suppress the inner header) with inline settings rows.
Each setting is a label+control row with scoped CSS — no double-card,
no wrapper hack.

Delete packages/subscriptions/ — the package is dead after merging
its SubscriptionPage into the Credits & Abo workbench app.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 14:33:35 +02:00
Till JS
f203e100c1 chore: final cleanup — dead link, security status, showTitle, drop subscriptions
1. CompleteStep: /apps → / ("Workbench" instead of dead "Alle Apps")
2. pnpm lockfile synced after @mana/subscriptions removal
3. Security header panel: status dots for Passkey/2FA/Sessions count
4. GeneralSection: inline settings rows replace GlobalSettingsSection
   wrapper — no more title="" hack or double-card nesting
5. shared-auth-ui: showTitle prop on PasskeyManager, SessionManager,
   TwoFactorSetup, AuditLog; SecuritySection passes showTitle={false}
6. Drop @mana/subscriptions from sources.css + package.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 14:27:46 +02:00
Till JS
10acabfed6 feat(ai): tag-based agent scoping — agents see only their tagged records
Connects the existing global tag system (@mana/shared-tags, 15+ module
junctions, TagSelector UI) to the AI agent model so different agents
can operate on different slices of the user's data.

Core additions:

1. Agent.scopeTagIds — optional array of global tag IDs. When set,
   the agent sees only records tagged with at least one of those tags
   (plus untagged records, which stay globally visible). Empty/undefined
   = General-Agent, sees everything. Agent-editor grows a <TagSelector>
   under "Bereiche (Tag-Scope)".

2. Per-agent kontext documents — new Dexie table `agentKontextDocs`
   (v22, encrypted, synced). Each agent can have its own markdown
   context doc, replacing the global singleton auto-inject. Runner
   tries agent kontext first, falls back to global singleton when
   the agent has no dedicated doc.

3. Ambient scope context — `withAgentScope(tagIds, fn)` sets a
   module-level scope during the reasoning loop. Auto-tools read it
   via `getAgentScopeTagIds()` and filter their result sets.
   `filterByScope(records, getTagIds)` is the reusable filter
   primitive (keeps untagged records, drops mismatched tagged ones).

4. Notes tag junction — `noteTags` table (v22) + `noteTagOps` via
   `createTagLinkOps`. Notes was the only major module without
   structured tag support. `list_notes` now calls `filterByScope`
   so a scoped agent only sees notes tagged with its scope.

Flow: mission starts → runner resolves owning agent → reads
agent.scopeTagIds → wraps entire reasoning loop in withAgentScope →
list_notes (and future list_tasks etc.) auto-filter → planner sees
only scope-relevant records → proposes scoped edits.

Runner tests: 8/8. shared-ai type-check: clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:43:33 +02:00
Till JS
3f60f68573 fix(shared-tailwind): export CSS under 'style' condition for Vite build
@tailwindcss/vite's enhanced-resolve looks for the 'style' export
condition when resolving CSS imports. The previous exports field used
plain string values which worked in dev but not in the Docker
production build (Vite build mode). Adding explicit 'style' +
'default' conditions fixes the "'sources.css' is not exported under
the condition 'style'" error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:39:01 +02:00
Till JS
56171ff13b fix(ai): resolve tool name + parameter drift between catalog and webapp
Some checks are pending
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
CI / Build mana-media (push) Blocked by required conditions
CI / Build mana-credits (push) Blocked by required conditions
CI / Build mana-web (push) Blocked by required conditions
CI / Build chat-backend (push) Blocked by required conditions
CI / Build chat-web (push) Blocked by required conditions
CI / Build todo-backend (push) Blocked by required conditions
CI / Build todo-web (push) Blocked by required conditions
CI / Build calendar-backend (push) Blocked by required conditions
CI / Build calendar-web (push) Blocked by required conditions
CI / Build clock-web (push) Blocked by required conditions
CI / Build contacts-backend (push) Blocked by required conditions
CI / Build contacts-web (push) Blocked by required conditions
CI / Build presi-web (push) Blocked by required conditions
CI / Build storage-backend (push) Blocked by required conditions
CI / Build storage-web (push) Blocked by required conditions
CI / Build telegram-stats-bot (push) Blocked by required conditions
CI / Build food-backend (push) Blocked by required conditions
CI / Build food-web (push) Blocked by required conditions
CI / Build skilltree-web (push) Blocked by required conditions
Docker Validate / Validate Dockerfiles (push) Waiting to run
Docker Validate / Build calendar-web (push) Blocked by required conditions
Docker Validate / Build quotes-web (push) Blocked by required conditions
Docker Validate / Build todo-backend (push) Blocked by required conditions
Docker Validate / Build todo-web (push) Blocked by required conditions
Docker Validate / Build mana-auth (push) Blocked by required conditions
Docker Validate / Build mana-sync (push) Blocked by required conditions
Docker Validate / Build mana-media (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
8 mismatches fixed between AI_TOOL_CATALOG and webapp module tools:

Tool name renames (webapp → catalog name):
- record_visit → visit_place (places)
- undo_last_drink → undo_drink (drink)
- location_log → get_current_location (places, catalog side)

Catalog parameter fixes (aligned to webapp execute functions):
- create_event: startIso/endIso → startTime/endTime + isAllDay/location/description
- create_note: title required→optional, content optional→required
- complete_tasks_by_title: titleSubstring → titleMatch
- create_place: add latitude/longitude (required) + category enum + address
- create_journal_entry: English mood enum → German mood enum

Webapp parameter additions:
- create_contact: add company + notes params (store already accepts them)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:18:51 +02:00
Till JS
299cf9cf72 chore: cleanup dead props, deps, dirs, and stale comments
- Remove empty dirs: modules/mana/, modules/subscription/, and 7
  empty route dirs (spiral, profile, themes, help, apps, mana, credits)
- Remove @mana/subscriptions from web app dependencies (never imported)
- PillNavigation: drop settingsHref, manaHref, allAppsHref/allAppsLabel
  props + their link-builder blocks (none are passed anymore; all system
  pages route via /?app=<id> deep-links now)
- Update layout comment to reflect current architecture

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:17:59 +02:00
Till JS
d40a61119e refactor(ai): dynamic tool registry — single-source catalog in shared-ai
Introduce AI_TOOL_CATALOG in @mana/shared-ai as the single source of truth
for all 29 tool schemas (17 propose + 12 auto). Both the webapp policy and
the server-side mana-ai planner now derive their tool lists from the catalog
instead of maintaining independent hardcoded copies.

- New: packages/shared-ai/src/tools/schemas.ts — catalog with ToolSchema type
- Rewrite: proposable-tools.ts — derived from catalog instead of hardcoded array
- Rewrite: services/mana-ai/src/planner/tools.ts — 277→30 lines (imports from catalog)
- Simplify: webapp policy.ts — derives AUTO/PROPOSE from catalog defaultPolicy

Adding a new tool now requires 2 files instead of 3-5:
1. Add schema to AI_TOOL_CATALOG (shared-ai)
2. Add execute function in the module's tools.ts (webapp)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:06:07 +02:00
Till JS
3be4612f04 fix(mana-llm): google-genai v1.73 keyword-only Part.from_text()
google-genai >=1.70 changed Part.from_text() from positional to
keyword-only argument. The production container installed v1.73.1
and crashed on startup with "Part.from_text() takes 1 positional
argument but 2 were given".

Fix: Part.from_text(msg.content) → Part.from_text(text=msg.content)

Tested live: curl https://llm.mana.how/v1/chat/completions with
model=google/gemini-2.5-flash returns correct response.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:47:23 +02:00
Till JS
c6c4d630fe refactor(subscriptions): compact row-based card layout
SubscriptionCard and PackageCard were large centered multi-cell cards
designed for a standalone pricing page. In the workbench context (narrow
card inside a carousel), they wasted too much vertical space for users
to compare plans at a glance.

Redesigned both as horizontal rows:
- Icon | name+mana | price | action — all in one line
- Badges (current/popular) inline next to the plan name
- No more 3-column internal grid with 70px min-height cells
- Clickable row replaces separate SubscriptionButton

SubscriptionPage:
- Drop the big centered header (icon + title + subtitle)
- Move Usage + Costs into a collapsed <details> section
- Section titles as small-caps labels
- Billing toggle at top, plans immediately visible

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:38:30 +02:00
Till JS
2ead0f36df fix(subscriptions): single-column layout for workbench context
SubscriptionPage had responsive multi-column grids (up to 4 columns
on xl). In the workbench card context this created awkward layouts.
Switch to single-column throughout and narrow max-width to 40rem.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:34:36 +02:00
Till JS
be81d11dc3 feat(ai): SSE streaming for foreground Mission Runner
Enable real-time token streaming during the planner "calling-llm" phase
so the user sees live progress ("empfange Plan… 128 tokens") instead of
a static spinner. The parser still receives the full text once complete —
no partial-JSON risk.

Changes:
- Extract shared SSE parser from playground into @mana/shared-llm/sse-parser
- remote.ts: use stream:true when onToken callback is provided
- AiPlanInput: add optional onToken field (shared-ai)
- ai-plan task: pass onToken through to backend.generate()
- runner.ts: throttled (500ms) phaseDetail updates during streaming
- Playground: refactored to use shared SSE parser

Also includes: AI agent architecture comparison report (docs/reports/)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:32:43 +02:00
Till JS
8a0bf93699 chore(cloud-tier): upgrade default model gemini-2.0-flash → gemini-2.5-flash
gemini-2.0-flash is deprecated June 1 2026. gemini-2.5-flash has been
stable since Q1 2026 with similar pricing ($0.15/$0.60 per 1M tokens
vs $0.10/$0.40 — pricing table already had the entry).

Three files touched:
- packages/shared-llm/src/backends/cloud.ts — client default
- services/mana-llm/src/config.py — server default
- services/mana-llm/src/providers/google.py — Ollama→Gemini fallback
  map + constructor default + deduplicated model list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:32:03 +02:00
Till JS
23b8cc13fb feat(ai-tools): server-side web-research + contacts for agents
Two major tool expansions — the Recherche-Agent and Today-Agent can
now research the web autonomously (no browser needed), and a future
Meeting-Prep agent can read + create contacts.

=== research_news (server-side execution) ===

The biggest addition: mana-ai can now call mana-api's news-research
endpoints (POST /discover + /search) directly, without a browser.

Infrastructure:
- services/mana-ai/src/planner/news-research-client.ts — full HTTP
  client with discover→search pipeline. 15s/30s timeouts. Graceful
  null on any failure (network, mana-api down, bad response) so the
  tick never crashes from research errors.
- config.manaApiUrl added (default http://localhost:3060); wired in
  docker-compose.macmini.yml as http://mana-api:3060 + depends_on
  mana-api with service_healthy condition.

Pre-planning research step (cron/tick.ts):
- Before the planner prompt is built, the tick checks if the
  mission's objective or conceptMarkdown matches research keywords
  (same RESEARCH_TRIGGER regex the webapp uses). When it matches:
  * NewsResearchClient.research(objective) runs discovery + search
  * Results are injected as a synthetic ResolvedInput with id
    '__web-research__' and a formatted markdown context block
  * The Planner then sees real article URLs/titles/excerpts and can
    reference them in create_note / save_news_article steps
  * Log line: "pre-research: N feeds, M articles"

Tool registration:
- research_news added to AI_PROPOSABLE_TOOL_NAMES + mana-ai tools.ts
  with params (query, language?, limit?). This lets the planner also
  explicitly propose a research step as a PlanStep (in addition to
  the pre-planning auto-injection).

=== create_contact ===

- Added to AI_PROPOSABLE_TOOL_NAMES + mana-ai tools.ts with params
  (firstName required, lastName/email/phone/company/notes optional).
- Contacts are encrypted at rest; server planner can plan the step
  but execution stays on the webapp (same as all propose tools).
  Full server-side contact resolution via Key-Grant is a future
  enhancement.
- get_contacts added to webapp AUTO_TOOLS so agents can inspect
  existing contacts without nagging (read-only, auto-policy).

Module coverage now:
   todo (5)   calendar (2)   notes (5)   places (4)
   drink (3)  food (2)       news (1)    journal (1)
   habits (3)  news-research (1)  contacts (1)

  11 modules, 28 tools total (17 propose, 11 auto).

Tests: mana-ai 41/41 (drift-guard passes), shared-ai type-check
clean, webapp svelte-check 0 errors, 0 warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:25:45 +02:00
Till JS
1266b583e4 feat(ai-tools): unlock create_note + create_journal_entry + habit tools for agents
Closes the three biggest tool-coverage gaps so the shipped agent
templates can actually do their job end-to-end. Before this, the
Recherche-Agent couldn't create notes (only edit), the Today-Agent
couldn't create journal entries, and no habit-related tool was
server-proposable at all.

shared-ai (proposable-tools.ts):
- create_note (notes) — key unlock: Recherche-Agent now creates
  per-source notes and the summary report.
- create_journal_entry (journal) — key unlock: Today-Agent proposes
  a poem as a journal entry with optional mood.
- create_habit (habits) — agent can suggest new habits.
- log_habit (habits) — agent can log a habit completion for today.

Organized the list with per-module section comments for readability
now that we're at 15 proposable tools.

mana-ai (planner/tools.ts):
- 5 new tool definitions with full parameter schemas:
  * create_note (title, content?)
  * create_journal_entry (content, title?, mood? enum)
  * create_habit (title, icon, color)
  * log_habit (habitId, note?)
- Drift-guard contract test passes (41/41) — confirms the mana-ai
  tool list is in sync with the shared-ai canonical set.

Webapp (policy.ts):
- get_habits added to AUTO_TOOLS (read-only; agent can inspect
  which habits exist without nagging the user for approval).
- list_notes added to AUTO_TOOLS (was already used in the reasoning
  loop but missing from the explicit auto-list; the planner default
  fell through to 'propose' which was wasteful for a read op).

Module coverage after this change:
   todo (5 tools)   calendar (2)   notes (5 incl. create)
   places (4)       drink (3)      food (2)
   news (1)         journal (1)    habits (3)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:00:17 +02:00
Till JS
9161c0b3ab feat(templates): two more non-AI templates + split gallery into two sections
Closes out T1 with three templates per category as discussed. The
gallery now renders agent-templates and workbench-templates as two
distinct labeled sections — the earlier implicit "everything's a
template for an agent" framing is gone.

Seed handlers (new):
- apps/mana/apps/web/src/lib/modules/habits/seed.ts — title-based
  idempotency (there's no description column on LocalHabit). If a
  non-deleted habit with the same title exists, the seed is skipped.
- apps/mana/apps/web/src/lib/companion/goals/seed.ts — title-based
  idempotency on companionGoals where status !== 'abandoned'.
- Both pulled in via side-effect imports in missions/setup.ts so the
  handler registry is populated before any apply.

New templates:
- 🏋️ Fitness (wellness) — scene body/habits/stretch/sleep + 3 habit
  seeds (Täglich 30min Bewegung, 3× Woche Training, 2L Wasser) + 1
  goal seed (3 Workouts pro Woche). No agent.
- 💻 Deep Work (work) — scene todo/calendar/notes/times + 2 habit
  seeds (1 wichtigste Aufgabe pro Tag, 4h Deep Work pro Tag) + 1
  goal seed (20h Deep Work pro Woche). No agent.

Gallery two-section layout:
- Title "Templates" (not "Agent-Templates") — broader framing.
- Section 1: "🤖 Agent-Templates" — filters ALL_TEMPLATES where
  category ∈ {'ai','delight'}: Recherche-Agent, Kontext-Agent,
  Today-Agent.
- Section 2: "🎨 Workbench-Templates" — filters to the rest:
  Calmness, Fitness, Deep Work.
- Each section gets a short intro paragraph so users understand the
  distinction before scanning the cards.
- Cards themselves unchanged; rendering extracted into a
  {#snippet templateCard(t)} shared between both sections.
- Per-category arrays computed once at module-load time (const in
  <script>); no per-render filter cost.

Result: each section has 3 templates, categorised by "does this
create an AI agent" rather than by use-case. Keeps the separation
honest — Agent-Templates set up autonomous work; Workbench-Templates
set up the user's own workspace.

Tests: shared-ai 26/26, webapp svelte-check 0 errors, 0 warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 11:45:40 +02:00
Till JS
a08e45ca16 feat(templates): generalise to WorkbenchTemplate + ship Calmness pilot (T1)
First pass of the workbench-templates plan (docs/plans/workbench-
templates.md) — templates are no longer agent-centric but a general
"starter kit" bundle: optional agent + optional scene + optional
missions + optional per-module seeds. Pilot non-AI template "Calmness"
ships alongside.

Shape generalisation (packages/shared-ai/src/agents/templates/types.ts):
- AgentTemplate renamed to WorkbenchTemplate; all fields now optional
  (agent, scene, missions, seeds). Back-compat AgentTemplate alias
  kept so research/context/today keep compiling.
- Added `category: 'ai'|'wellness'|'work'|'lifeEvent'|'delight'` +
  `icon` (for non-agent templates that have no avatar) + `version`
  field (for future update-detection).
- New WorkbenchTemplateSeedItem shape: `{stableId?, data: unknown}`.
  Module-specific seed payloads are typed at the handler side.
- Existing three AI templates nachgezogen: category='ai' (or
  'delight' for today-agent), icon, version='1'.

Seed infrastructure:
- apps/mana/apps/web/src/lib/data/ai/agents/seed-registry.ts — in-
  memory handler map keyed by module name; module-local seed.ts files
  register themselves at import time.
- apps/mana/apps/web/src/lib/modules/meditate/seed.ts — first handler:
  createPreset-based, idempotent via stableId embedded as HTML
  comment in the preset description (T1 pragmatism; T2 adds a proper
  column on the preset schema).
- data/ai/missions/setup.ts pulls `import '$lib/modules/meditate/seed'`
  so the handler is registered before any template is applied.

Applicator upgrades (data/ai/agents/apply-template.ts):
- Agent step now optional — skipped cleanly when template has no
  agent part.
- New step 4: seeds. Walks template.seeds, looks up the handler for
  each module, aggregates per-item outcomes (created/skipped-exists/
  failed) into result.seedOutcomes. Missing handler = warning, not
  fatal. Crypto/encryption unchanged — seeds go through the same
  module stores that module code already uses.
- Result shape gains `seedOutcomes: Record<string, SeedOutcome[]>`
  so the gallery can show "3 new, 1 already there".

Calmness pilot (packages/shared-ai/src/agents/templates/calmness.ts):
- category='wellness', NO agent, scene with meditate/mood/journal/
  sleep apps, two meditate preset seeds:
  * 4-7-8 Atmung (breathing preset)
  * Body-Scan 10min (bodyscan preset with 9 scan steps)
- Each seed has a stableId so re-apply is idempotent.

Gallery updates (routes/(app)/agents/templates/+page.svelte):
- Card avatar falls back to t.icon when no agent. "Agent" chip shows
  only for agent-templates; "N Seeds" chip shows for templates with
  seeds.
- Detail header shows "Workbench-Setup ohne AI-Agent" when no agent.
- New "Seeds" preview section: lists per-module counts + item names.
- Options section gains a "Seed-Daten in Module einpflegen" checkbox.
- Success panel shows seed summary: "3 Seeds neu, 1 bereits
  vorhanden".

Tests: shared-ai 26/26, webapp svelte-check 0 errors, 0 warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 01:07:41 +02:00
Till JS
a524997a2f refactor(tailwind): centralize @source list in @mana/shared-tailwind/sources.css
Each consuming app was duplicating eight @source directives with hand-
counted relative paths (../../../../../packages/…). The mana web app's
were off-by-one for months before anyone noticed, silently disabling the
scan for every shared-ui file.

Tailwind v4 resolves @source paths relative to the CSS file that declares
them, so we can drop the list once into packages/shared-tailwind/src/
sources.css. Consumer apps now just add one more @import next to themes.css:

  @import "tailwindcss";
  @import "@mana/shared-tailwind/themes.css";
  @import "@mana/shared-tailwind/sources.css";

New package.json export: "./sources.css". Drop the local paths from the
mana web app's app.css.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 00:59:33 +02:00
Till JS
8a5d200c84 fix(ai): bump planner maxTokens 1024→4096 + teach prompt about the loop
Debug log from a "tag 4 notes" mission showed the planner's second-round
response truncated mid-step: it was proposing one add_tag_to_note per
listed note but ran out of tokens halfway through note #2. Parser
rejected the malformed JSON → loop exited with 0 staged, user saw
nothing to approve.

Raising maxTokens to 4096 fits ~15-20 step objects, which covers the
batch-tagging / batch-save pattern the reasoning loop is designed for.

Also updating the system prompt so the planner actually knows about
the loop it's running inside: read-only tools are announced as
auto-executing with outputs visible next turn, and a new rule makes
explicit that batch jobs must emit all write-steps in one plan (because
staging a propose-tool ends the turn). Step count raised 1-5 → 1-10.

Prompt snapshot tests still pass (they check structure, not text).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 00:55:18 +02:00
Till JS
7822340ea0 feat(ai-agents): Template gallery — 3 ready-to-use agent bundles
First pass of the Multi-Agent discoverability UX. A new /agents/
templates route showcases pre-configured agents; clicking one creates
agent + scene + starter mission(s) as a single bundle. Addresses the
"blank form anxiety" + "user doesn't know what agents are for"
observations from the UX brainstorm.

Three templates for v1 (shared-ai/src/agents/templates/):
- 🔍 Recherche-Agent — reads sources one by one, writes a note per
  source, summarizes into a report. Manual-cadence mission; all
  writes propose so user curates.
- 🧭 Kontext-Agent — learns about the user via a weekly check-in.
  Reads kontext/notes/goals, asks 2-3 questions, proposes a diff-
  style context update. Weekly Sunday cadence.
- 🌅 Today-Agent — researches "on this day" history each morning,
  writes a 4-8 line German poem, proposes a journal note. Daily 7am
  cadence. A "delight" agent, not a productive one.

Each template packs (agent config, scene layout, starter mission):
- AgentTemplate type lives in @mana/shared-ai — pure data, no runtime
  imports. Adding a new template = drop a file in templates/ and
  extend ALL_TEMPLATES.
- Template-specific policies derive from the proposable-tool list so
  drift-guard catches divergence from the canonical set.
- Starter missions default to startPaused=true — user sees the
  mission ready-to-go and hits Play when ready. Prevents surprise
  autonomous work on first apply.

Applicator (data/ai/agents/apply-template.ts):
- Creates agent → scene (if template defines one) → missions in
  order. Agent failure = abort; scene/mission failures surface as
  warnings in the result without blocking.
- Duplicate-name handling: falls through to findByName, returns
  existing agent with wasExisting=true; scene is skipped in that
  case to avoid clone-proliferation.

Gallery page /(app)/agents/templates/+page.svelte:
- Three large cards side-by-side (stacks on mobile) with avatar /
  label / tagline / meta chips (Scene, N Missionen).
- Click opens detail panel with full description, scene preview
  (app-ids + widths), mission preview (title / objective / cadence),
  and override checkboxes (create scene, create missions, start
  active vs paused).
- Success panel shows what landed with warnings inline; CTA back to
  workbench.

Discoverability in /ai-agents module:
- Bar now has two buttons: "Aus Template" (primary, goto templates
  route) + "Eigener Agent" (secondary, opens the existing blank-form
  create mode).
- When only the default "Mana" agent exists, render a dashed promo
  banner at the top linking to the template gallery. Disappears as
  soon as the user has a second agent.

Tests: webapp svelte-check 0 errors, 0 warnings. shared-ai 26/26.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 00:36:39 +02:00
Till JS
4d9b16a683 feat(notes): list + update + append + add_tag tools for the AI
Makes the "read all notes and tag them #Natur/#Technologie/…" use case
fully functional. Four new ModuleTool entries in notes/tools.ts:

- list_notes(limit?, query?, includeArchived?) — auto, read-only. Returns
  id + title + excerpt so the planner can reference concrete notes
  without dumping full bodies.
- update_note(noteId, title?, content?) — proposable. Destructive full
  overwrite. Docstring nudges toward append_to_note when applicable.
- append_to_note(noteId, content) — proposable, non-destructive. Handles
  the trailing-newline separator so markdown stays clean.
- add_tag_to_note(noteId, tag) — proposable, idempotent, case-insensitive.
  Strips leading #, replaces spaces with _, skips if already present.
  Exactly the categorization primitive the user asked for.

All three writes are added to AI_PROPOSABLE_TOOL_NAMES so both the
webapp policy and mana-ai's boot-time drift guard agree (now 11 tools).
Mirrored in services/mana-ai/src/planner/tools.ts.

AiProposalInbox mounted on /notes so approvals land inline in the
notes module too (already appears in the mission-detail cross-module
inbox via the earlier commit).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 00:24:48 +02:00