Commit Graph

368 Commits

Author SHA1 Message Date
jubnl 4e3b27c712 fix(offline): cache accommodations, trip members, tags, and categories for full offline support 2026-04-14 23:50:52 +02:00
jubnl b194e8317d feat(pwa): implement real offline mode with IndexedDB sync
Add genuine offline read/write capability for trips:

- Dexie IndexedDB schema (trips, places, packing, todo, budget,
  reservations, files, mutationQueue, syncMeta, blobCache)
- Repo layer for all domains: offline reads from Dexie, writes
  optimistically to Dexie and enqueue mutations for later replay
- Mutation queue with UUID idempotency keys (X-Idempotency-Key),
  FIFO flush, temp-ID reconciliation on 2xx, fail-and-continue on 4xx
- Trip sync manager: caches all trips with end_date >= today or null,
  auto-evicts 7d after end_date, fetches bundle endpoint in one request
- Map tile prefetcher: bbox from place coords, zooms 10-16, 50MB cap,
  warms SW cache via fetch
- Sync triggers: network online → flush + syncAll; WS reconnect →
  flush only (rate-limiter safe); visibilitychange/30s → flush only
- WS remoteEventHandler writes through to Dexie on every event
- Server idempotency middleware + idempotency_keys table (migration 100,
  24h TTL nightly cleanup)
- GET /api/trips/:id/bundle endpoint for efficient single-request sync
- OfflineBanner component: amber (offline) / blue (syncing) / hidden
- OfflineTab in Settings: cached trip list, re-sync and clear actions
- usePendingMutations hook for per-item pending indicators

Closes #505 #541
2026-04-14 23:04:25 +02:00
Julien G. 1268d3e7b1 Merge pull request #632 from tiquis0290/bug/synology-thumbnail
fix: currently synology thumbnails resolve to error
2026-04-14 20:57:50 +02:00
Julien G. 80e1574c26 Merge pull request #643 from tiquis0290/fix/synology-adding-photos
fix: pagination in synology
2026-04-14 20:57:27 +02:00
Maurice b3571f391a Fix skeleton entry deletion and add hide suggestions toggle (#619)
- Revert filled skeleton entries back to skeleton on delete instead of permanently removing them
- Add per-user hide_skeletons preference on journey_contributors (migration 99)
- Add PATCH /journeys/:id/preferences endpoint for toggling skeleton visibility
- Add Eye/EyeOff toggle button with custom tooltip in journey detail header
- Filter skeleton entries from timeline when hidden
- Add i18n keys for all 14 languages
2026-04-14 19:58:13 +02:00
Marek Maslowski 65931a1777 fix pagination in synology 2026-04-14 19:03:31 +02:00
jubnl 6a23118342 fix(notifications): fix SMTP error surfacing, webhook button label, backup timestamp
- testSmtp now surfaces real nodemailer error instead of generic 'SMTP not configured' on send failure
- admin webhook test button uses correct i18n key (was showing 'Test-E-Mail senden' in all languages)
- backup created_at uses stat.mtime instead of unreliable stat.birthtime on Linux
2026-04-14 16:20:52 +02:00
jubnl 98340aa855 fix(tests): fix remaining 3 immich test failures
IMMICH-057: use two-step trek_photos/trip_photos insert (same fix
as SYNO-035) to avoid missing asset_id column error.

IMMICH-061: mock regex /\/api\/albums$/ did not match the ?shared=true
variant; updated to /\/api\/albums(\?.*)?$/ so both owned and shared
album requests resolve correctly.

IMMICH-090: /search route only fetched a single page; implement
internal pagination loop (max 20 pages) accumulating all assets
before responding, which is what the test and the feature require.
2026-04-14 13:57:38 +02:00
jubnl aa32b1f372 fix(migrations): qualify provider column in trip_photos JOIN (migration 98)
Both trip_photos (alias tp) and trek_photos (alias tkp) have a provider
column. Using the bare identifier 'provider' in the JOIN condition was
ambiguous and caused SQLite to throw SQLITE_ERROR, failing migration 98
and taking down the entire test suite setup.

Fix: introduce providerJoinExpr = 'tp.provider' when the legacy
trip_photos table already carries a provider column, used only in the
two-table JOIN. The single-table INSERT keeps the unqualified form.
2026-04-14 13:39:28 +02:00
jubnl 375ae53566 fix(atlas): shared Nominatim throttle, background region fill, fetch timeout
- Extract throttleNominatim() so reverseGeocodeCountry and
  reverseGeocodeRegion share the same lastNominatimCall state.
  Concurrent /stats + /regions no longer interleave requests
  faster than 1 req/s, closing the remaining 429 path from #576.
- getVisitedRegions now returns cached data immediately and fills
  uncached places in a fire-and-forget background loop. Eliminates
  the N×1.1s response time that caused 504s behind reverse proxies
  (likely root cause of #493). geocodingInFlight set prevents
  double-enqueuing on concurrent page loads.
- Add AbortSignal.timeout(10_000) to both Nominatim fetch calls so
  a hung upstream no longer stalls the endpoint indefinitely.
- Unify User-Agent header in reverseGeocodeRegion to match policy.
2026-04-14 13:29:14 +02:00
Marek Maslowski f686902cd3 adding default value of small when getting thumbnail 2026-04-14 11:22:20 +02:00
Maurice 24bcf6ded8 fix(journey): websocket sync across devices + 404 redirect
- broadcastJourneyEvent now excludes by socket ID instead of user ID,
  so other devices of the same user receive real-time updates (#615)
- Routes pass x-socket-id header through to broadcast functions
- loadJourney handles 404 gracefully — redirects to /journey with
  toast instead of infinite spinner (#616)
2026-04-13 23:03:58 +02:00
Maurice 240b10a192 fix(journey): thumbnails, batch add, optimistic delete, shared albums
- Gallery/timeline load thumbnails instead of originals (50-100KB vs 2-5MB)
- Batch endpoint for adding multiple provider photos in one request
- Optimistic photo deletion — no full page reload on delete
- Immich albums include shared albums
- Select-all button moved outside scroll container (always visible)
- Album tab loads actual album contents via /albums/:id/photos
2026-04-13 22:48:40 +02:00
Maurice 87de60d8de fix(photos): paginated search with infinite scroll (#613)
Replace bulk-loading all Immich photos (up to 20k) with paginated
search: 50 photos per page, automatic infinite scroll via
IntersectionObserver. Prevents server blocking on large libraries.

- Backend: searchPhotos accepts page/size params, returns hasMore
- Frontend: loads 50 at a time, appends on scroll
- AbortController cancels in-flight requests on tab switch
2026-04-13 21:46:48 +02:00
Maurice e395935f6a fix(photos): cap search to 5000 photos + abort pending requests
Large Immich libraries (7k+ photos) caused timeouts and pending
requests when using "All Photos". Cap pagination at 5 pages (5000
photos) and abort in-flight requests when switching tabs.
2026-04-13 21:31:03 +02:00
Maurice 3a52b80e3a fix(migration): handle old trip_photos schema (immich_asset_id)
Migration 98 assumed trip_photos already had asset_id + provider
columns, but older DBs still have the original immich_asset_id
column. Now detects schema variant and adapts accordingly.
2026-04-13 21:16:16 +02:00
Maurice 7e3cb29c57 fix(journey): album photos, select-all, heading/hr fixes, dark mode
- Load actual album photos instead of date-range search fallback
  (new GET /albums/:id/photos for Immich + Synology)
- Add select all / deselect all toggle in photo picker
- Normalize Markdown headings to plain text in journal stories
- Fix setext headings (---) rendering as hr instead of h2
- Add remark-breaks for proper line break rendering
- Fix pros/cons dark mode gradient backgrounds
- i18n: selectAll/deselectAll in 14 languages
2026-04-13 21:06:15 +02:00
Maurice 6c253c71c3 fix(weather): handle archive date out of range for future trips (#599)
When a trip is far in the future (e.g. May 2027), the climate fallback
looked up last year's data (May 2026). But if that date hasn't passed
yet, the Open-Meteo archive API returns 400. Now checks if the
reference date is still in the future and goes back one more year.

Fixes the flood of 400 errors that could trigger CrowdSec bans.
2026-04-13 20:33:30 +02:00
Maurice 33c63d34e7 fix(journey): prevent duplicate skeleton entries for multi-day places (#606)
When syncing trip places to journal, places assigned to multiple days
(e.g. multi-night hotels) produced one skeleton entry per day_assignment
row. The existing dedup check only looked at DB state, not at entries
added within the same sync loop. Add the place ID to the tracking set
after insertion so the same place is never inserted twice.
2026-04-13 20:27:48 +02:00
Maurice 1f68ba1ea1 fix(atlas): prevent Nominatim 429 rate limiting (#576)
- Swap resolve order: try local bbox lookup before Nominatim reverse
  geocode — eliminates most external API calls
- Add global throttling (1.1s min between requests) to
  reverseGeocodeCountry so /stats can't flood Nominatim
- Update User-Agent header to include repo URL per Nominatim policy
2026-04-13 20:16:36 +02:00
Maurice c0c59b6d80 feat: unified photo provider abstraction layer (#584)
Introduce trek_photos as central photo registry. Frontend uses
/api/photos/:id/:kind instead of provider-specific URLs. Adding
a new photo provider is now backend-only work.

- New trek_photos table (migration 98) with photo_id FK in
  trip_photos and journey_photos
- Unified /api/photos/:id/thumbnail|original|info endpoint
- photoResolverService for central resolution and streaming
- ProviderPicker: add "All Photos" tab, rename tabs, fix i18n
- Localize all hardcoded strings in JourneyDetailPage (14 langs)
- Fix date formatting to use browser locale instead of hardcoded 'en'
- Journey stats as styled tile cards
2026-04-13 20:08:31 +02:00
Maurice e179769a8f Fix ghost Gallery entries in journal timeline and public share
- deleteEntry now deletes photos with the entry instead of moving them
  to a hidden Gallery entry that becomes an undeletable ghost
- deletePhoto cleans up empty Gallery entries after last photo removed
- getJourneyFull and getPublicJourney filter out empty Gallery entries
  so existing ghosts are hidden in both internal and shared views
2026-04-13 00:18:45 +02:00
Maurice 6a19807a72 Fix Nominatim User-Agent and improve error diagnostics
- Update User-Agent from old NOMAD URL to TREK
- Include HTTP status code and response body in Nominatim error messages
2026-04-12 23:31:22 +02:00
Maurice 4680aa254d Fix map tooltips, journey creation, and contributor avatars
- Map tooltips now respect light/dark mode via CSS variables
- Journey creation inherits cover image from first selected trip
- Only day-assigned places are synced to journey (no unplanned places)
- Place count in trip picker reflects assigned places only
- Contributor avatars shown in journey detail page
- Suggestion banner button visible in dark mode (!important override)
- Dashboard list view uses correct trips array and status label
2026-04-12 23:20:13 +02:00
jubnl ad27c5f6be fix: restore broken tests after prerelease workflow refactor
- Export __clearVersionCacheForTests() from adminService; call in
  versionNotification beforeEach to reset module-scoped cache between
  tests (VNOTIF-002..006 failed because VNOTIF-001 cached
  update_available:false, short-circuiting all subsequent test fetches)
- Seed appVersion:'2.9.10' in Navbar test authStore; appVersion moved
  from local useEffect state to authStore in last commit so the test
  render no longer fetches it independently (FE-COMP-NAVBAR-016)
- Add data-testid="weekend-days" to VacaySettings weekend-days
  container; use within() in tests to scope button count to that
  section, fixing false positives from the week-start buttons which
  share the same inline styles (FE-COMP-VACAYSETTINGS-003/004)
- Pass isPrerelease={true} in GitHubPanel FE-ADMIN-GH-007; component
  filters out prerelease releases when isPrerelease=false so the badge
  was never rendered (pre-existing, unrelated to last commit)
2026-04-12 17:19:24 +02:00
jubnl 86be4d7997 fix: address prerelease workflow review bugs
- Type checkVersion() with VersionInfo interface; fixes TS errors in
  checkAndNotifyVersion() where object type blocked property access
- Don't cache fallback on !resp.ok or fetch throw; prevents a transient
  GitHub outage from poisoning the 5-min version cache
- Guard parseInt result with Number.isFinite() in compareVersions;
  malformed -pre.abc tags no longer silently compare as equal via NaN
- Pre-compute stripped versions before sort in checkVersion(); avoids
  mutating input array and redundant replace() calls in comparator
- Bump GitHub releases fetch from per_page=20 to per_page=100
- Store appVersion in authStore; populate from App.tsx getAppConfig call
  and remove redundant getAppConfig fetch in Navbar useEffect
- Type GitHubPanel error/expanded state as string|null and Record<number,boolean>
2026-04-12 17:05:17 +02:00
jubnl 62453ebefa fix: harden prerelease workflow against races, orphan tags, and edge cases
- Add concurrency groups to both workflows to prevent parallel version-bump races
- Defer git tag push to merge job so orphan tags can't exist without a live image
- Pin build/merge jobs to the SHA captured in version-bump to prevent TOCTOU
- Guard auto-finalize in docker.yml against cross-major prereleases (requires bump=major + confirm_major=MAJOR)
- Add STABLE fallback to 0.0.0 for fresh repos with no stable tag
- Fix cleanup sort to extract numeric N via awk instead of fragile sort -t. -k4 -n
- Add 5-minute in-memory cache to checkVersion to avoid GitHub API rate limits
- Type GitHubPanel releases state; remove any cast on filter
- Quote all $VERSION/$MAJOR_TAG vars in imagetools create calls
2026-04-12 16:50:54 +02:00
jubnl e198791139 fix: address prerelease workflow review issues
- Remove stale mauriceboe/nomad tags from docker-dev.yml
- Fix APP_VERSION empty string fallback (?? -> ||)
- Fix compareVersions to handle -pre.N suffixes correctly
- Use highest existing N instead of tag count to avoid collision after cleanup
- Add cleanup step to keep only last 5 prerelease tags per base version
2026-04-12 16:39:50 +02:00
jubnl 981b667fbb feat: prerelease workflow with major version support and version propagation
- Add docker-dev.yml: prerelease CI for dev branch with minor/major bump
  inputs; auto-continues in-flight major line via existing pre tags;
  publishes floating major-pre Docker tag (e.g. 2-pre)
- Rewrite docker.yml version-bump: tag-based versioning, manual bump
  inputs (auto/patch/minor/major), major guarded by confirm_major=MAJOR,
  auto-finalizes in-flight prereleases; publishes floating major tag (e.g. 2)
- Inject APP_VERSION build-arg through Dockerfile so the running container
  knows its real version instead of reading package.json
- Server reads APP_VERSION env in authService/adminService; exposes
  is_prerelease in app config and update-check response; prerelease builds
  compare against GitHub prerelease releases rather than latest stable
- Client stores isPrerelease from config; navbar shows amber version badge
  on prerelease builds (left of dark-mode toggle); GitHubPanel filters out
  prerelease releases unless the running build is itself a prerelease
2026-04-12 16:26:44 +02:00
Maurice f323952012 feat: configurable week start day in Vacay (Monday or Sunday)
- New setting in Vacay Settings to choose Mon or Sun as week start
- DB migration adds week_start column to vacay_plans (default: Monday)
- Calendar grid and weekday headers adapt to the selected start day
- Weekend column highlighting works correctly for both modes
- Translations added for all 14 languages
2026-04-12 02:18:45 +02:00
Maurice 344b769583 fix: deduplicate places on Google Maps list re-import (#543)
Skip places that already exist in the trip (same name + coordinates
within ~10m) when re-importing a Google Maps list. Only new places
are added, preventing duplicates on repeated imports.
2026-04-12 01:45:32 +02:00
jubnl bfd2553d1e feat(auth): split OIDC_ONLY into granular auth toggles
Replaces the coarse oidc_only + allow_registration settings with four
independent toggles: password_login, password_registration, oidc_login,
oidc_registration. Each can be enabled/disabled individually in
Admin > Settings without affecting the others.

- Add resolveAuthToggles() in authService.ts as the central resolver;
  falls back to legacy oidc_only/allow_registration keys when new keys
  are absent (backward compat)
- OIDC_ONLY env var still works and overrides DB toggles for password_*,
  with a visual lock in the admin UI when active
- Server enforces lockout prevention: cannot disable all login methods
- oidc_login gate added to OIDC /login and /callback routes
- Remove oidc_only toggle from OIDC settings panel; replaced by the
  granular toggles in the Settings tab
- Add 6 new resolveAuthToggles() unit tests; fix AUTH-DB-033 error
  message assertion
- Update OIDC_ONLY descriptions in README, docker-compose, Helm values,
  Unraid template, and .env.example to clarify override semantics

Closes #492
2026-04-11 20:21:36 +02:00
Julien G. 2b1889b9a9 Merge pull request #567 from mauriceboe/fix/atlas-country-region-matching
fix(atlas): scope region name matching by country and expand country lookup tables
2026-04-11 19:50:30 +02:00
Maurice 468035fc3c fix: reorder migrations — OAuth (84-88) before Journey (89-96)
Dev DB already ran OAuth migrations at indices 84-88. The merge
incorrectly placed Journey migrations before OAuth, causing
'duplicate column: parent_token_id' crash on the dev server.
2026-04-11 19:48:43 +02:00
jubnl 467d35702b fix(atlas): scope region name matching by country and expand country lookup tables
- Fix #521: `isVisitedFeature()` now scopes name-based region matching to
  the feature's parent country (via `iso_a2`), preventing same-name regions
  in different countries (e.g. Luxembourg BE vs LU) from falsely lighting up
- Fix #489: Add ~50 missing countries to COUNTRY_BOXES, NAME_TO_CODE, and
  CONTINENT_MAP so the bounding-box fallback correctly identifies Georgia
  instead of falling through to Russia/Azerbaijan's overlapping boxes
2026-04-11 19:45:26 +02:00
Maurice 956c4270df merge: resolve conflicts with dev, fix 7 Snyk security issues
- Resolve translation conflicts (keep both journey + OAuth scope keys)
- Resolve migrations.ts (dev OAuth migrations + journey migrations)
- Fix hono directory traversal, response splitting, input validation (CVE-2026-39407/08/09/10)
- Fix @hono/node-server directory traversal (CVE-2026-39406)
- Fix nodemailer CRLF injection (upgrade to 8.0.5)
2026-04-11 19:11:21 +02:00
Maurice 13956804c2 feat: Journey addon — travel journal with entries, photos, public sharing & PDF export
- 5-table schema (journeys, entries, photos, trips, contributors) with migrations 87-91
- Trip-to-Journey sync engine with skeleton entries and photo sync
- Full CRUD API for journeys, entries, photos with Immich/Synology integration
- Timeline, Gallery and Map views with entry editor (markdown, mood, weather, pros/cons)
- Journey frontpage with hero card, stats and trip suggestions
- Public share links with token-based access and photo proxy
- PDF photo book export (Polarsteps-inspired)
- Dashboard redesign: mobile greeting, live trip hero, quick actions, unified card design
- BottomNav profile sheet with settings/admin/logout
- DayPlan mobile inline place picker
- TripFormModal members management
- Vacay calendar trip date indicator dots
- Fix contributor photo access (403) for journey Immich/Synology photos
- Trip deletion cleanup for journey skeleton entries
- i18n: 231 new keys across all 14 languages (native translations, no fallbacks)
2026-04-11 19:01:34 +02:00
jubnl 0257e0d842 feat: route Synology session-cleared notification through unified send()
Replace direct createNotification() call with notificationService.send()
so the notification respects user preferences and reaches all enabled
channels (in-app, email, webhook) instead of only WebSocket.

Registers synology_session_cleared as a proper NotifEventType (inapp-only)
and adds localized text for all 14 supported languages.
2026-04-11 18:36:50 +02:00
jubnl 7871c06059 feat: enhance Synology Photos integration with OTP, SSL skip, and better UX
- Fix endpoint path: users now provide full base URL (e.g. https://nas:5001/photo)
- Add OTP/2FA field for Synology login
- Add skip SSL verification option (DB column + checkbox UI)
- Add device ID (synology_did) column for session tracking
- Trigger in-app notification when Synology session is cleared
- Show disconnection banner in MemoriesPanel
- Add URL hint in provider settings
- Map Synology API error codes to human-readable messages
- Update i18n for all locales
2026-04-11 18:25:42 +02:00
Julien G. 3b080ac116 Merge pull request #544 from mauriceboe/feat/mcp-oauth2-addon-gating
Implement OAuth 2.1 authentication for MCP, enforce addon gating
2026-04-11 14:39:50 +02:00
jubnl dd8d2ae54a chore(mcp): raise default session and rate-limit caps
Higher defaults reduce config friction for self-hosters while
staying within reasonable server limits.

- MCP_MAX_SESSION_PER_USER: 5 → 20
- MCP_RATE_LIMIT: 60 → 300 req/min
2026-04-11 02:29:11 +02:00
jubnl 535c06bb3f feat(mcp): granular OAuth scopes and per-client rate limiting
- Split `media:read` into `geo:read` and `weather:read` scopes
- Add dedicated `atlas:read/write` scopes (previously under `places`)
- Add dedicated `todos:read/write` scopes (previously under `collab`)
- Rate limiting now keyed by userId+clientId instead of userId alone
- Bind MCP sessions to the OAuth client that created them
- Log MCP tool calls to audit log with clientId
- Invalidate all MCP sessions on addon state change
- Reduce session sweep interval from 10min to 1min
- Update all translations with new scope labels
2026-04-11 02:06:32 +02:00
jubnl 4b1286d53c feat(admin): add OAuth sessions to MCP Access panel
Show active OAuth sessions (first) and static API tokens (second) in
the admin MCP Access tab. Admins can revoke any OAuth session, which
immediately terminates the live MCP transport for that client.

- Add admin-level listOAuthSessions / revokeOAuthSession in adminService
- Add GET /admin/oauth-sessions and DELETE /admin/oauth-sessions/:id routes
- Restructure AdminMcpTokensPanel into two sections; rename tab to MCP Access
- Fix stale writeAudit call in rotate-jwt-secret route (user_id → userId)
- Add admin.oauthSessions.* i18n keys across all 14 locale files
2026-04-10 06:47:35 +02:00
jubnl cc2a2ddca3 remove(oauth): drop browser-initiated DCR registration flow
OAuthRegisterPage and its server routes (GET /api/oauth/register/validate,
POST /api/oauth/register) are superseded by the RFC 7591 machine-to-machine
DCR endpoint (POST /oauth/register). Claude.ai and compliant MCP clients
register via RFC 7591, then go through the standard /oauth/authorize consent
screen for scope selection.
2026-04-10 06:23:07 +02:00
jubnl 4ad1ccf5dd fix(oauth): gate scope selection UI to DCR clients only
Settings-created clients have fixed scopes chosen at creation time and
should show a read-only scope list on the consent screen. Only DCR-registered
clients expose the interactive checkbox UI for user-controlled scope selection.
2026-04-10 06:03:52 +02:00
jubnl cb3aeda8e0 fix(oauth): add public RFC 7591 DCR endpoint at POST /oauth/register
Claude.ai's start-auth flow POSTs to the registration_endpoint advertised
in the discovery document, but no public handler existed at /oauth/register
(only /api/oauth/register with browser cookie auth). This caused a
start_error redirect immediately on every connect attempt.

- Add POST /oauth/register to oauthPublicRouter following RFC 7591
- Make oauth_clients.user_id nullable via a raw (no-transaction) migration
  so anonymous DCR clients can be created without a user context
- Update migration runner to support { raw: () => void } migrations for
  DDL that requires PRAGMA foreign_keys = OFF outside a transaction
- Update createOAuthClient to accept userId: number | null with a global
  cap (500) for anonymous DCR clients in place of the per-user limit
2026-04-10 05:42:18 +02:00
jubnl 9b1baaf7b8 feat(oauth): browser-initiated dynamic client registration (DCR)
Adds an OAuth 2.1 public client registration flow so MCP clients can
self-register via a user-facing consent page instead of requiring manual
setup in Settings.

Server:
- DB migration adds `is_public` and `created_via` columns to oauth_clients
- New GET /api/oauth/register/validate — validates DCR params, returns
  requested scopes; unauthenticated callers get loginRequired flag
- New POST /api/oauth/register — creates a public client, saves consent,
  and redirects with client_id (cookie auth required)
- `authenticateClient` / `refreshTokens` skip secret check for public
  clients (PKCE provides the security guarantee)
- `createOAuthClient` accepts options for isPublic/createdVia; public
  clients store an opaque secret hash instead of a usable secret
- `rotateOAuthClientSecret` blocked on public clients
- `isValidRedirectUri` extracted as a shared helper
- Discovery metadata now advertises registration_endpoint and auth method
  `none`; token/revoke endpoints no longer require client_secret for
  public clients

Client:
- New OAuthRegisterPage (/oauth/register) — loading → optional
  login-required gate → scope selection → done states
- New ScopeGroupPicker component — collapsible groups, indeterminate
  checkboxes, select-all per group or globally
- oauthApi.register.{validate,submit} added to api/client.ts
- apiClient exported so it can be reused outside api/client.ts
- IntegrationsTab tests fixed for new collapsible section structure
- collab_notes fallback changed from undefined to [] in MCP trip tools
2026-04-10 05:20:54 +02:00
jubnl 81a360f9a7 fix(mcp): bundle data with deprecation error and add verbatim instruction
Claude retried the tool silently and answered without mentioning the
notice. Two fixes:

1. Include actual trip data in the same isError response so no retry
   is needed and Claude has both the warning and the answer in one shot.

2. Reword the notice to instruct Claude to include the warning verbatim
   in its response before answering the user's question.
2026-04-10 03:15:18 +02:00
jubnl a74a6313dd fix(mcp): instruct Claude to retry tool call after deprecation notice
Claude stopped after surfacing the error rather than retrying.
Append an explicit instruction to retry the tool call so the user
gets both the deprecation warning and their actual answer.
2026-04-10 03:10:02 +02:00
jubnl 89a109560e fix(mcp): return deprecation notice as isError tool result
isError: true is the one MCP mechanism Claude.ai cannot ignore —
it is obligated to surface tool errors to the user.

On the first tool call of a static-token session, return only the
deprecation notice with isError: true (no data). The per-session
_noticeEmitted flag is set before returning, so the immediate retry
(or any subsequent call) goes through normally and returns real data.
2026-04-10 03:04:05 +02:00