TaskStatus enum was missing 'cancelled' — the LLM tried to use it and
hit TaskStatus("cancelled") raising ValueError → 500. Added the value,
a migration to extend the task_status Postgres enum, and proper 400
validation guards on both create and update task routes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously metadata only stored rss_item_ids (integers); the full item data
was discarded after LLM synthesis. Now rss_items (id, title, url, source,
snippet, published_at) is also stored so clients can render per-story cards
without additional API calls.
Web BriefingView: replaces bare reaction-row buttons with news cards showing
source, headline (linked), 2-line snippet, and 👍/👎 per card.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The MCP fable_update_task tool calls PATCH /api/tasks/{id} but the route
only declared PUT. Adding PATCH to the same handler fixes the 405.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add parse_pagination() to routes/utils.py; replace 6 duplicate limit/offset extractions in notes, tasks, chat, projects, milestones routes
- Extract _enrich_shares() in sharing.py; eliminates identical 12-line loop in list_project_shares and list_note_shares
- Extract _deduplicate_by_permission() in sharing.py; eliminates identical deduplication blocks in list_shared_with_me for projects and notes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract milestoneColor to utils/palette.ts; remove duplicate in HomeView + ProjectListView
- Create useBackgroundRefresh composable; wire into HomeView + BriefingView (removes manual setInterval/clearInterval boilerplate)
- Extract _loadTabContent() in SettingsView so watch and onMounted share one tab→loader mapping
- Move raw fetch() api-key calls to typed helpers in api/client.ts (listApiKeys, createApiKey, revokeApiKey)
- Drop unused onUnmounted import from BriefingView
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The watch(activeTab) handler loads tab data on navigation, but not on
initial mount when localStorage restores a tab. Three more gaps:
- briefing: was inside the isAdmin guard — non-admin users who last
visited the Briefing tab would see an empty form
- users: no onMounted equivalent — admin user list never loaded
- logs: no onMounted equivalent — admin log viewer never loaded
Moves briefing outside the admin guard and adds users/logs inside it.
fetchApiKeys() and loadMcpInfo() were only wired to the activeTab watcher,
which fires on changes but not on initial mount. If localStorage had
'apikeys' as the last tab, both calls were skipped entirely — causing
an empty key list and no whl download button.
HomeView: setInterval every 90s refreshes events, orphan tasks/notes,
and hero next-up task. Never touches loading ref, so the page content
stays stable — only the data silently swaps when the fetch completes.
Skips when document.hidden or initial load is still in progress.
BriefingView: setInterval every 60s refetches today's messages, but
only when viewing today's conversation and not currently streaming.
Compares message count and last message content before updating to
avoid unnecessary re-renders.
Both timers are cleared on unmount.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
npm install -g npm@latest corrupts npm's own module tree inside Alpine,
breaking subsequent installs. Use npm ci instead (faster, deterministic).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a comprehensive instructions block to the FastMCP server covering
the data model hierarchy, valid enum values, tag format, integer-or-none
conventions, when to use fable_send_message vs direct CRUD tools, and
admin key requirements.
All tool docstrings expanded with full argument descriptions, valid
values, and return shape notes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The briefing was formatting event start_dt directly in UTC instead of
converting to the user's local timezone. Also, the day_start/day_end
query window was naive (UTC), so events at the edges of the user's day
could be missed or included incorrectly.
Now reads user_timezone setting, uses it for today's date boundary and
for converting each event's start_dt before formatting.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
marked encodes " to " in text nodes, causing linkifyWikilinks to
double-escape it (& → &) so the visible link text showed "
instead of the actual character.
Decode marked's entities on the matched title/label before running
escapeHtmlAttr so the output is correct in both the href attribute
and the visible link text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Browser timezone is now synced to user_settings["user_timezone"] on
every login/page load (App.vue). The briefing scheduler and LLM context
both read from this single source, falling back to the legacy
briefing_config.timezone for existing users during migration.
- App.vue: PUT /api/settings with browser IANA timezone on startAppServices
- routes/chat.py: fall back to stored user_timezone when not sent in request
- briefing_scheduler: read user_timezone setting; briefing_config.timezone
kept as fallback only
- routes/briefing.py: pass tz_override from user_timezone to live-patched scheduler
- Remove timezone field from BriefingConfig interface and all briefing UI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Frontend sends user_timezone (IANA, from Intl.DateTimeFormat) with
every message POST; threaded through route → generation_task → build_context
- System prompt now tells the LLM the user's timezone so it creates
events with the correct UTC offset (e.g. 15:00+01:00 not 15:00Z)
- Calendar tool guidance updated to require UTC offset in all event
datetimes
- EventSlideOver: dateFromIso/timeFromIso now use JS Date to convert
stored UTC times to local time for display; toIso includes local
timezone offset when saving so the correct UTC time is stored
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ToolCallCard: event list items replaced with rich clickable cards (color dot,
title, time, location); clicking opens EventSlideOver for edit/delete; single
create/update events in header are also clickable; updated all event types to
use start_dt/end_dt fields from internal store
- HomeView: new upcoming events widget shows today + next 7 days as a card grid
above the hero project; clicking any card opens EventSlideOver inline
- briefing_pipeline: _gather_internal now queries the internal events store for
today's events; CalDAV events are still appended (deduped) if configured
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The local `from datetime import datetime, timezone as _tz` inside
execute_tool() shadowed the module-level datetime import for the entire
function scope, causing UnboundLocalError in all calendar tool handlers
(list_events, create_event, update_event). Fixed by importing only
timezone as _tz — datetime is already available at module level.
Also removes the now-unnecessary noqa: F823 suppression.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ruff 0.15.7 incorrectly flags datetime.fromisoformat() as a
"local variable referenced before assignment" inside a try block
within execute_tool(). datetime is imported at module level and
is not a local variable. Added noqa comment at the specific line.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AI calendar tools now always available (moved from _CALDAV_TOOLS to _CORE_TOOLS);
create/list/search/update/delete events go through the internal DB store first,
with fire-and-forget CalDAV push sync when the user has CalDAV configured
- Add EventEntry interface and typed API helpers (listEvents, createEvent,
getEvent, updateEvent, deleteEvent) to client.ts
- Install @fullcalendar/vue3, daygrid, timegrid, interaction, core packages
- Add EventSlideOver.vue: create/edit/delete slide-over with title, start/end,
all-day toggle, location, description, color picker, and project selector
- Add CalendarView.vue: month/week/day FullCalendar with drag-drop and resize
wired to PATCH /api/events/:id; click empty date opens create slide-over
- Wire /calendar route, Calendar nav link in AppHeader, g+l keyboard shortcut
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds docker-compose.quickstart.yml that pulls the pre-built image from
the registry so users can get started without a local build. Updates
README Quick Start to use the new file as the default path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Task change detection via snapshot diff
- RSS scoring/filtering via briefing_preferences
- Weather card via parse_weather_card_data (staleness-gated)
- News card markdown format with ordering constraint
- Metadata stored on Message record via post_message()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
msg_metadata maps to the 'metadata' DB column ('metadata' is reserved
by SQLAlchemy Declarative API). to_dict() exposes the key as 'metadata'
for frontend compatibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>