Commit Graph

680 Commits

Author SHA1 Message Date
bvandeusen dda62265c9 fix(journal): drop dangling JournalSetupWizard import
The setup-wizard component was deleted in the RSS hard-cut (it was
briefing-config-shaped), but JournalView still imported it — breaking
the Vite build.

Removed the import + showWizard / wizardChecked / checkSetup /
onWizardDone plumbing. JournalView now mounts straight into the day view.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 12:53:06 -04:00
bvandeusen 297f2252a3 fix(test): repoint lookup tests to article_fetcher.fetch_article_text
The two test_lookup_tool.py cases were patching the now-deleted
fabledassistant.services.rss._fetch_full_article. The trafilatura URL→text
helper moved to services/article_fetcher in the RSS hard-cut.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 12:44:09 -04:00
bvandeusen dbd9f00061 refactor: hard-cut RSS infrastructure (scope C)
Removes the entire RSS feature surface — feeds, items, embeddings, reactions,
discussion-note flow, briefing news context, settings, env-vars, and DB
tables. Keeps the URL-generic article-reader (the read_article LLM tool)
under a clean module so the LLM can still fetch arbitrary article content
from URLs the user provides.

Backend:
- New services/article_fetcher.py — single source of trafilatura URL→text
- New services/tools/article.py — read_article tool (was nested under tools/rss)
- Delete services/rss.py, rss_classifier.py, rss_filtering.py, article_context.py
- Delete services/tools/rss.py
- Delete models/rss_feed.py (RssFeed, RssItem), models/rss_item_embedding.py
- services/embeddings.py: drop upsert/semantic_search/backfill RSS helpers
- services/llm.py: remove _build_briefing_article_context, briefing-conv branch,
  ARTICLE_DISCUSS_SEED skip-RAG branch; drop get_rss_items / add_rss_feed from
  the actions list
- services/generation_task.py: drop _maybe_save_article_discussion_note + caller
- routes/chat.py: drop /api/chat/from-article/<id> endpoint
- routes/journal.py: re-import via web.py refactor (article_fetcher path)
- services/tools/__init__.py: register `article`, drop `rss`
- services/tools/_registry.py: drop the requires=='rss' check
- app.py: drop backfill_rss_item_embeddings + backfill_rss_article_content tasks
- config.py: prose-only edit (no env var change — RSS env vars were never first-class)

Frontend:
- stores/settings.ts: drop rssEnabled
- SettingsView.vue: drop the RSS-classification mention
- api/client.ts: drop openArticleInChat (the from-article endpoint is gone)

Tests:
- Delete tests/test_rss_service.py, test_news_api.py, test_article_reading.py

Migration:
- 0042_drop_rss: DROP TABLE rss_item_embeddings, rss_item_reactions, rss_items,
  rss_feeds; DELETE settings rows for rss_enabled / briefing_*_topics

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 12:33:30 -04:00
bvandeusen cacfcac86a fix(journal): restore weather + events panels; hide daily-prep system msg in chat
The journal UI was over-stripped earlier — weather panel, current-conditions
poll, and the upcoming-events sidebar were all dropped. Restored those (calls
the new /api/journal/weather, /api/journal/weather/current,
/api/journal/weather/refresh, /api/journal/weather/geocode endpoints).

Also: the daily-prep system message was rendering as flat text inside
ChatPanel because there's no .role-system bubble styling. Added a hideMessage
guard in ChatMessage so daily-prep system messages don't render in the chat
stream — they're already shown above as a structured prep card.

News / RSS reactions / article-discuss are intentionally NOT in the journal
(scoped out per user direction). The broader RSS infrastructure cleanup is
a separate, larger task.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 12:22:51 -04:00
bvandeusen 873e70c7ea feat(journal): add JournalView UI with /journal route + nav link
Restores a working browser surface for the journal feature, which was
left UI-less when Stage F of the original plan was deferred. JournalView
is a fresh write (not a rename of BriefingView) — drops the briefing-
specific weather panel, news cards, RSS reactions, article-discuss
button, and setup wizard, since none of those have journal-backend
equivalents.

What it does:
- Fetches /api/journal/today on mount; shows today's daily-prep card
  (rendered from msg_metadata.kind === 'daily_prep' sections)
- Day picker via /api/journal/days lets you switch to past days
- Refresh-prep button hits /api/journal/trigger-prep
- Center is the existing ChatPanel pinned to today's journal conversation
  (so the chat input + SSE + tool-call cards inherit unchanged)
- Right sidebar keeps the upcoming-events list (uses the generic
  /api/events endpoint, not a briefing-specific one)

Also:
- Replace the dead Briefing API client functions with Journal equivalents
  (getJournalConfig, saveJournalConfig, getJournalToday, getJournalDay,
  getJournalDays, triggerJournalPrep, list/update/deleteJournalMoment).
- Remove NewsView.vue — it was orphaned (no route, no nav) and depended
  on the deleted /api/briefing/* endpoints. If a standalone news surface
  is wanted later it'll need to be rebuilt against new endpoints.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 11:54:06 -04:00
bvandeusen 0c91ab026d docs: add FabledSword design system + Scribe iteration decisions
Captures the FabledSword baseline (verbatim from initial draft) plus the
Scribe-specific decisions made during the first iteration pass:

- Hybrid accent rule (accent reserved for brand moments + nav/cursor +
  tags/wikilinks/in-progress/focus rings; Moss/Bronze/Oxblood/Pewter
  for action buttons by default)
- Light mode warm parchment (specific hex values pinned)
- Status + priority palette extension table (status-paused added)
- Typography: Inter for body, doc scale verbatim, two weights
- Chat-bubble "Illuminated Transcript" pattern codified
- Voice and tone: adopt principles, defer formal audit
- Border philosophy: structural not decorative
- Iconography: Lucide as source, strict 16/24 scale, 1.5/1 stroke
- Warm gold accent dropped (dates → text-secondary, paused → Warning)

Doc lives at docs/design-system.md. Polish pass (applying the system to
existing UI) is a separate, deferred phase.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 10:51:38 -04:00
bvandeusen ce76a003f7 feat(journal): /api/journal/* routes blueprint + cosine helper unit tests
Endpoints:
- GET/PUT /api/journal/config — per-user journal config
- GET /api/journal/today — today's journal conversation, generates daily prep on demand
- GET /api/journal/day/<iso_date> — past day's journal
- GET /api/journal/days — list of dates with journal content
- POST /api/journal/trigger-prep — manual regeneration of prep
- GET /api/journal/moments — list/search moments with filters
- PATCH /api/journal/moments/<id> — edit content/tags/pinned + junctions
- DELETE /api/journal/moments/<id>

Blueprint registered in app.py.
tests/test_journal_search.py — cosine helper coverage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:40:56 -04:00
bvandeusen ac188c40a5 feat(journal): LLM tools (record_moment, search_journal) + system prompt wiring
- services/tools/journal.py — record_moment + search_journal tool handlers
- services/tools/_registry.py: add `journal` flag on ToolDef + tool() decorator
- get_tools_for_user(user_id, conversation_type='chat'|'journal') —
  exclude journal-only tools from chat sessions; exclude set_rag_scope
  from journal sessions
- services/tools/__init__.py: register the new journal module; drop the
  unused get_briefing_tools export
- services/llm.py build_context: short-circuit for journal conversations,
  using journal_pipeline.build_journal_system_prompt and skipping all
  notes-RAG injection (preserves the journal/notes isolation invariant)
- services/generation_task.py: pass conversation_type into get_tools_for_user

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:39:42 -04:00
bvandeusen d9ab538ef4 feat(journal): backend services (moments, search, prep, scheduler, pipeline)
- services/moments.py — CRUD with embedding sync + entity-link helpers
- services/journal_search.py — three-mode search (temporal / entity / semantic)
  with notes-RAG isolation; cosine-similarity helper unit-tested
- services/journal_prep.py — gathers tasks/events/weather/news/projects/
  recent-moments/open-threads into a structured prep block
- services/journal_scheduler.py — per-user APScheduler cron for daily prep,
  follows the BackgroundScheduler + threadsafe-async pattern from event_scheduler
- services/journal_pipeline.py — system prompt (persona + calibration rules)
  with last-48h ambient moments injection
- app.py: wire journal scheduler start/stop hooks
- routes/settings.py: re-add live-reschedule on timezone change (now via
  journal_scheduler.update_user_schedule)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:37:39 -04:00
bvandeusen 7602bf2293 feat(briefing): hard-cut tear-down
Backend:
- Delete briefing services (pipeline, scheduler, conversations, profile, tools)
- Delete routes/briefing.py + remove blueprint registration
- Move _get_temp_unit into services/weather.get_temp_unit (reads top-level temp_unit setting)
- Rename briefing_preferences.py → rss_filtering.py (functions are RSS-specific)
- Strip briefing scheduler hooks from app.py
- Strip briefing scheduler call from routes/settings.py
- Update test imports (test_rss_service, test_tz_helpers)

Frontend:
- Delete BriefingView, BriefingSetupWizard, BriefingToolStatusRow
- Strip /briefing route + nav links (AppHeader, KnowledgeView)
- Strip Settings → Briefing tab + state + functions + imports
- Strip briefing-intermediate handling from ChatMessage
- Hide /news route + nav links (NewsView depended on briefing endpoints; orphaned in tree)
- Drop unused useSettingsStore from AppHeader

The Android BriefingScreen lives in a separate repo and is not touched here.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:33:37 -04:00
bvandeusen d352e9264b feat(models): Moment + MomentEmbedding + junctions; rename Conversation.briefing_date → day_date
Also rename services/tz.user_briefing_date → user_day_date with a backwards
compat alias (briefing modules using the old name will be deleted in the
upcoming briefing tear-down stage). Update services/chat.py to_dict to use
day_date.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 20:40:02 -04:00
bvandeusen 6752765e2b feat(db): migration 0041 — add moments + four junction tables (FK notes) + embeddings
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 20:38:37 -04:00
bvandeusen 0fe7141949 feat(db): migration 0040 — rename briefing_date to day_date, hard cut briefing data
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 20:35:37 -04:00
bvandeusen b20d6dec66 chore(scripts): make pre_commit_fable_mcp.sh location-independent
Derive REPO_ROOT from the script's own location instead of hardcoding
the absolute project path, so the hook keeps working if the project
directory is renamed or moved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 17:56:02 -04:00
bvandeusen e652dece9b refactor: rename Fabled Assistant to Fabled Scribe
Updates user-visible branding across frontend (PWA manifest, page title,
Settings, push fallback), backend (email templates and subjects, LLM
system prompt, CalDAV displayname, SMTP from-name default), README,
quickstart compose, and MCP server description.

Also updates the CI image path and quickstart image reference to
git.fabledsword.com/bvandeusen/fabledscribe in preparation for the
Forgejo repo rename. Internal Python package, env vars, and database
schema unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 16:28:25 -04:00
bvandeusen 96a44886ef Merge pull request 'feat(workspace): redesign project workspace with floating chat widget' (#42) from dev into main v26.04.22.1 2026-04-22 19:11:11 +00:00
bvandeusen f422d4158c style(workspace-notes): larger, more spacious note title
Title input bumps to 1.4rem with Fraunces serif (matches the
design language's heading treatment) and gains 0.9/1.1rem padding
on the title row for breathing room.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 14:52:58 -04:00
bvandeusen 70ec060b7a style(workspace-notes): move active-note indicator to right edge
Anchors it to the editor side rather than the tasks-panel side.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 14:43:59 -04:00
bvandeusen 3b41a45757 style(workspace-notes): drop border between toolbar and body
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 14:35:45 -04:00
bvandeusen 19e1ca505d style(workspace-notes): unify editor chrome, distinguish rail
- Drop border-bottom lines between header / title / tags / toolbar
  so they read as one continuous input surface.
- Add a single border-top above the editor body so the body's
  existing boundary stays clear.
- Notes rail gets --color-bg-card so it no longer visually merges
  with the tasks panel (both previously shared --color-surface).
  Dropped the rail's border-right since the tint now carries the
  division from the editor pane.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 13:30:17 -04:00
bvandeusen f75c40b417 refactor(workspace): match KnowledgeView mini-chat dock style
- Widget is now a bottom-anchored dock spanning the content width
  (max-width 1200px, rounded top corners, bg-secondary, upward shadow)
  instead of a floating corner box.
- Drop the closed state and ✕ button — widget is always present,
  matching KnowledgeView's pattern. Collapsed = input-only dock,
  expanded = full ChatPanel above the input.
- Grid ratio 1fr:2fr → 0.85fr:2.15fr so tasks get a touch less room
  and notes a touch more.
- Workspace note-rail (the notes picker column inside the editor)
  widens from 155px to 200px so note titles breathe.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 12:53:02 -04:00
bvandeusen 649c0f124b feat(workspace): history dropdown on project chat widget
Clicking the ▾ button reveals conversations where
rag_project_id === projectId, most-recent first. Selecting one
repoints the widget (and the workspace_conv_{pid} storage key) to
that conversation without deleting the previously active one.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 11:10:54 -04:00
bvandeusen 6026c24450 refactor(workspace): two-column grid; widget drives tool-call refresh
WorkspaceView now renders tasks (1fr) on the left and notes (2fr) on
the right with the floating WorkspaceChatWidget layered on top. The
widget owns the SSE streamingToolCalls watcher and emits note-changed
and task-changed events; the view listens and reloads the affected
panel refs.

Dropped from the view:
- ChatPanel mount, empty-state quick chips, and Chat header toggle
- The conversation lifecycle + SSE watcher (moved into the widget)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 11:09:50 -04:00
bvandeusen ae5c61a179 feat(workspace): floating project chat widget
Modeled on KnowledgeView's mini-chat. Three states (closed handle,
collapsed input-only, expanded full panel). Takes over the project
conversation lifecycle from WorkspaceView (resume-or-create on mount,
delete-if-empty-and-new on unmount) and adds a restart action. Reuses
ChatPanel for expanded view and ChatInputBar for collapsed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 11:08:25 -04:00
bvandeusen 7657f48f1e Merge pull request 'feat: unified lookup tool, Wikipedia integration, weather & briefing UX' (#41) from dev into main v26.04.18.1 2026-04-18 03:33:57 +00:00
bvandeusen 61e62a6904 fix: serialize article fetches in lookup to avoid lxml double-free
trafilatura.extract dispatched via run_in_executor isn't safe to run
concurrently — two parallel calls can crash the process with a
libxml2-level double free. The top-level Wikipedia+SearXNG gather is
fine; only the inner per-article extraction needs to stay sequential,
matching the pre-parallelization behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 23:04:05 -04:00
bvandeusen 9a252c8dde feat: parallelize lookup (Wiki+SearXNG) and include Wikipedia thumbnails
Runs the Wikipedia summary and SearXNG search concurrently and returns
both when available, so current-event questions aren't masked by a
generic role article from Wikipedia. When the Wikipedia summary includes
a thumbnail, it is cached through the existing image pipeline and
surfaced as an embeddable markdown snippet alongside the extract.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 22:44:42 -04:00
bvandeusen 8db6b4d230 feat: add Wikipedia as research pipeline source alongside SearXNG
Runs wiki_search in parallel with SearXNG queries; Wikipedia results
(which already carry content via their extract field) are merged into
the source pool before outline generation, skipping a separate fetch
step. Also fixes a pre-existing F811 ruff violation in the test file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 21:59:35 -04:00
bvandeusen d5e6a8f6da refactor: update all search_web references to lookup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 21:57:46 -04:00
bvandeusen 06cd3493fd feat: replace search_web with unified lookup tool (Wikipedia + SearXNG fallback)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 21:56:31 -04:00
bvandeusen 6a8f0e9143 feat: add wikipedia service with summary lookup and search
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 21:54:44 -04:00
bvandeusen 6f8c2201ed docs: add unified lookup & Wikipedia implementation plan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 21:20:47 -04:00
bvandeusen ec49e24c8d docs: add unified lookup tool & Wikipedia integration spec
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 21:17:48 -04:00
bvandeusen cac62c6caf feat(briefing): add 2-week upcoming events list below weather card
Fetch and display the next 14 days of events in the briefing right column,
grouped by day with color dots, time, and location. Fills the empty space
below the weather card.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 17:14:20 -04:00
bvandeusen 619f069358 feat(weather): add refresh button and return card data from refresh endpoint
Add a refresh button (↻) to the weather section header in BriefingView so
users can manually re-fetch weather data (needed after deploying the hourly
precip changes to populate the cache). Update the existing POST
/api/briefing/weather/refresh endpoint to return full card data instead of
just location keys.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 16:40:57 -04:00
bvandeusen ddab0db781 feat(weather): add hourly precipitation summaries and peak timing to weather card
Fetch hourly precipitation probabilities from Open-Meteo alongside daily
forecasts. Generate human-readable precip summaries ("Rain likely 2–5 PM",
"Rain likely all day") for today and each forecast day. Display today's
summary as a styled callout and show peak precipitation hour in forecast rows.
Also fix briefing pipeline to parse all weather location rows (not just first).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 16:05:26 -04:00
bvandeusen 443b11f287 fix(tests): patch get_setting at source module, not lazy import site
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 15:15:01 -04:00
bvandeusen 3c9e473823 fix(tests): mock get_setting in calendar tools availability test
The rss_enabled check in _check_requires now calls get_setting, which
needs a database connection. Mock it in the test that exercises
get_tools_for_user.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 14:49:33 -04:00
bvandeusen 3ac32dc3bc feat: add rss_enabled user setting to toggle RSS functionality
RSS is now off by default. When disabled:
- Scheduler skips RSS feed sync during compilation slot
- Briefing pipeline skips RSS item gathering
- RSS LLM tools (get_rss_items, add_rss_feed) are hidden
- API routes return empty results for feeds/news
- Frontend hides News nav link, RSS Feeds and News Preferences in settings
- Briefing view hides news sidebar section

Toggle in Settings > Briefing > RSS / News.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 13:58:50 -04:00
bvandeusen 29ef17f4f3 fix(llm): frame RAG notes as reference material, remove RSS from chat context
Add framing preamble to auto-injected notes so the model treats them as
reference material rather than user input. Remove RSS semantic search
injection from all chat conversations — the discuss tool handles that need.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 08:17:10 -04:00
bvandeusen fc82f7a0cb Merge pull request 'Fix CSP for Google Fonts' (#40) from dev into main v26.04.17.1 2026-04-17 11:16:33 +00:00
bvandeusen b743754ec2 fix(csp): allow Google Fonts for Fraunces typeface
Add fonts.googleapis.com to style-src and fonts.gstatic.com to
font-src in Content-Security-Policy header.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 20:45:22 -04:00
bvandeusen c4ffe418b5 Merge pull request 'Fix CSP blocking Silero VAD WASM compilation' (#39) from dev into main v26.04.16.4 2026-04-17 00:23:59 +00:00
bvandeusen 6cf880506d fix(voice): allow WASM compilation in CSP for Silero VAD
Add 'wasm-unsafe-eval' to script-src and blob: to worker-src in
Content-Security-Policy header. Required by onnxruntime-web to compile
the Silero VAD ONNX model. Also surface VAD init errors as a toast
instead of silent console log.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 20:10:29 -04:00
bvandeusen aca34e2dfb Merge pull request 'Release v26.04.16.3 — Silero VAD + Briefing layout' (#38) from dev into main v26.04.16.3 2026-04-16 23:37:04 +00:00
bvandeusen c07a0f0f7e fix(chat): remove input bar border across all chat views
Remove border-top from .input-wrapper in ChatPanel directly instead
of overriding per-view. Applies to chat, workspace, and briefing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 19:25:48 -04:00
bvandeusen 776e5edb68 fix(briefing): responsive sidebar, remove dividers, scale forecast icons
Switch sidebar from fixed px to proportional 35% so it resizes with
the viewport. Remove border between chat and input bar and between
weather and news. Use container query units for weather/forecast icons
so they scale with column width.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 19:10:49 -04:00
bvandeusen 8b0dd732f7 feat(briefing): tabbed weather locations, sticky weather, wider sidebar
Add location tabs when multiple weather sources configured. Weather
section stays pinned at top of sidebar while news scrolls independently.
Bump sidebar width from 420px to 620px max for better readability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 18:30:16 -04:00
bvandeusen f12d29563a fix(briefing): 2-column layout with table-based weather forecast
Collapse briefing from 3-column to 2-column grid (chat + sidebar).
Weather moves to top of news column, forecast uses an HTML table so
rows align cleanly. Drop verbose condition text from forecast days
(emoji already conveys it). Switch CI node_modules cache to npm
download cache to avoid tar size error from onnxruntime-web.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 18:10:46 -04:00
bvandeusen a4995606e5 chore(voice): remove amplitude-based silence detector (replaced by VAD)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 17:47:45 -04:00