services/events.py::list_events treated any event with end_dt IS NULL
as perpetually matching, so a point-event created last week would be
returned as "happening today" whenever the briefing or list_events
tool queried today's window. That manifested as briefings claiming
past events were scheduled for the current day.
Split the null-end branch: point events now only match when start_dt
falls within [date_from, date_to]. Events with an end_dt continue to
use standard overlap logic.
This bug affected both the legacy briefing synthesis and the agentic
list_events tool path, since both call the same events service.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
First cut of the agentic briefing redesign. Morning compilation can now
route through a tool-call loop that grounds every factual claim in an
actual tool result, eliminating the hallucinated meetings, tasks, and
news items the legacy one-shot path was producing. Behind a per-user
`briefing_mode` setting (default "legacy"); falls back to the legacy
path automatically if the new path returns empty (e.g. model too weak
to drive tool calls reliably).
New: services/briefing_tools.py — explicit read-only allowlist of 10
tools (tasks, events, weather, rss, projects, notes). New tools added
to tools.py must be opted in by name. Excludes all mutating tools and
external search tools (search_images, search_web, research_topic) which
are neither useful nor safe for a scheduled background job.
New: briefing_pipeline.run_agentic_briefing — wraps the existing
stream_chat_with_tools loop with slot-specific system prompts that tell
the model to only assert facts from tool results and to be honest when
tools return nothing. Max 8 rounds, per-round exception handling,
returns the full message list so tool-call receipts can be persisted
alongside the prose in a later PR.
Sentence-count floors bumped: compilation 6–10 (was 4–8), check-ins
3–5 (was 2–3). Weekly review 5–8.
Design doc: docs/2026-04-10-agentic-briefing-design.md
Out of scope for this PR (future PRs): slot-injection migration,
persisting tool-call receipts into the conversation so chat follow-ups
see them, UI polish for tool-call status, sidecar storage for
briefings. See the design doc's migration path for details.
Enable on an account with:
UPDATE settings SET value='agentic'
WHERE user_id=<id> AND key='briefing_mode';
or insert the row if missing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the hardcoded "2h" keep_alive everywhere with a helper that
returns OLLAMA_KEEP_ALIVE_MAIN (default 30m) for the interactive model
and OLLAMA_KEEP_ALIVE_BACKGROUND (default 10m) for the background
model. Lets the main model release VRAM during long idle periods
while keeping it warm enough for bursty chat use, and stops the
sporadic background model from camping VRAM it rarely needs.
Seven call sites updated to route through llm.keep_alive_for(model):
the streaming helpers, generate_completion, the two startup warmers,
the settings KV-cache primer, and the chat warmer endpoint.
Override via env vars: OLLAMA_KEEP_ALIVE_MAIN, OLLAMA_KEEP_ALIVE_BACKGROUND.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the hand-edit-JSON instructions in Settings → API Keys with a
tabbed UI (Claude Code / Claude Desktop / Other). The Claude Code tab
leads with the `claude mcp add` command, pre-filled with FABLE_URL and
the most recently generated API key, plus copy-to-clipboard buttons on
every snippet. Recommend `uv tool install` or `pipx install` over bare
`pip install` so fable-mcp reliably lands on PATH under PEP 668.
Also fix incorrect priority values in fable-mcp tool docstrings — the
enum is `none|low|medium|high`, not `low|normal|high`.
DRY pass: extract shared `copyToClipboard()` helper used by both
`copyApiKey` and the new snippet buttons; reuse existing
`btn btn-secondary btn-sm` for the copy buttons instead of a bespoke
class.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The standalone current-conditions div showed just a bare temperature with no
forecast context, making the weather panel look incomplete. Now the live
temperature from /weather/current is patched directly into the WeatherCard's
current_temp so it stays fresh without a second display block.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ProjectListView: add an overall task completion bar above the per-milestone
bars showing the percentage of all project tasks that are done.
ProjectView: milestones where every task is complete now start collapsed
by default, keeping the view clean for projects with many finished stages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace indigo (#6366f1) with deep violet (#7c3aed) gradient header,
violet-tinted card border and background, refined border radius and
spacing, and violet-accented footer to match the app's design language.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes birthday, organization, address (person) and website, category (place)
from entity_meta in the knowledge API response; updates KnowledgeView cards
to display organization and category as visible meta chips/text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When note type is 'person', replace the main TipTap editor with a contact card form
(Relationship, Birthday, Email, Phone, Organization, Address). The TipTap editor moves
to a collapsible 'Notes' section below the form, auto-expanded when existing body content
is present. Person fields are removed from the sidebar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>