* fix(slack): use named import for @slack/bolt App class
The default import `import bolt from '@slack/bolt'` followed by
`const { App } = bolt` doesn't work correctly in Bun due to ESM/CJS
interop issues. The default export comes through as a function rather
than the module object.
Switching to a named import `import { App } from '@slack/bolt'`
resolves the issue and allows the Slack provider to start successfully.
* fix(slack): align Bolt mock with named App export
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(auth): prioritize round-robin over lastGood for multi-account rotation
When multiple OAuth accounts are configured, the round-robin rotation was
not working because lastGood was always prioritized, defeating the sort by
lastUsed.
Changes:
- Remove lastGood prioritization in resolveAuthProfileOrder
- Always apply orderProfilesByMode (sorts by lastUsed, oldest first)
- Only respect configuredOrder when explicitly set in config
- preferredProfile still takes priority for explicit user choice
Tested with 2 Google Antigravity accounts - verified alternating usage.
Follow-up to PR #269.
* style: fix formatting
- Add TelegramLocation, TelegramVenue, and TelegramMessageWithLocation types
- Add formatLocationMessage() to convert location/venue shares to text
- Add extractLocationData() for structured location access in ctxPayload
- Handle both raw location pins and venue shares (places with names)
- Include location in reply-to context for quoted messages
Location messages now appear as:
- [Location: lat, lon ±accuracy] for raw pins
- [Venue: Name - Address (lat, lon)] for places
ctxPayload includes LocationLat, LocationLon, LocationAccuracy,
VenueName, and VenueAddress fields for programmatic access.
Antigravity rate limits cause requests to hang indefinitely rather than
returning 429 errors. This change detects timeouts and treats them as
potential rate limits:
- Added timedOut flag to track timeout-triggered aborts
- Timeout now triggers profile cooldown + rotation
- Logs: "Profile X timed out (possible rate limit). Trying next account..."
This ensures automatic failover when Antigravity hangs due to rate limiting.
Adds usage tracking to auth profiles for automatic rotation:
- ProfileUsageStats type with lastUsed, cooldownUntil, errorCount
- markAuthProfileUsed(): tracks successful usage, resets errors
- markAuthProfileCooldown(): applies exponential backoff (1/5/25/60min)
- isProfileInCooldown(): checks if profile should be skipped
- orderProfilesByMode(): now sorts by lastUsed (oldest first)
On auth/rate-limit failures, profiles are marked for cooldown before
rotation. On success, usage is recorded for round-robin ordering.
This enables automatic load distribution across multiple accounts
(e.g., Antigravity 5-hour rate limit windows).
Changes writeOAuthCredentials and applyAuthProfileConfig calls to use
the email from OAuth response as part of the profile ID instead of
hardcoded ":default".
This enables multiple accounts per provider - each login creates a
separate profile (e.g., google-antigravity:user@gmail.com) instead
of overwriting the same :default profile.
Affected files:
- src/commands/onboard-auth.ts (generic writeOAuthCredentials)
- src/commands/configure.ts (Antigravity flow)
- src/wizard/onboarding.ts (Antigravity flow)
* fix: Gemini stops working after one message in a session
* fix: small issue in test file
* test: cover google role-merge behavior
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
When replying to a message in a Slack thread, the response now stays
in the thread instead of going to the channel root.
Only threads replies when the incoming message was already in a thread
(has thread_ts). Top-level messages get top-level replies.
Fixes#250