* fix(kb): improve error logging when connector token resolution fails
The generic "Failed to obtain access token" error hid the actual root cause.
Now logs credentialId, userId, authMode, and provider to help diagnose
token refresh failures in trigger.dev.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(kb): disable connectors after 10 consecutive sync failures
Connectors that fail 10 times in a row are set to 'disabled' status,
stopping the cron from scheduling further syncs. The UI shows an alert
triangle with a reconnect banner. Users can re-enable via the play
button or by reconnecting their account, which resets failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(kb): disable sync button for disabled connectors, use amber badge variant
Sync button should be disabled when connector is in disabled state to
guide users toward reconnecting first. Badge variant changed from red
to amber to match the warning banner styling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(kb): address PR review comments for disabled connector feature
- Use `=== undefined` instead of falsy check for nextSyncAt to preserve
explicit null (manual sync only) when syncIntervalMinutes is 0
- Gate Reconnect button on serviceId/providerId so it only renders for
OAuth connectors; show appropriate copy for API key connectors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(kb): move resolveAccessToken inside try/catch for circuit-breaker coverage
Token resolution failures (e.g. revoked OAuth tokens) were thrown before
the try/catch block, bypassing consecutiveFailures tracking entirely.
Also removes dead `if (refreshed)` guards at mid-sync refresh sites since
resolveAccessToken now always returns a string or throws.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(kb): remove dead interval branch when re-enabling connector
When `updates.nextSyncAt === undefined`, syncIntervalMinutes was not in
the request, so `parsed.data.syncIntervalMinutes` is always undefined.
Simplify to just schedule an immediate sync — the sync engine sets the
proper nextSyncAt based on the connector's DB interval after completion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* improvement(kb): deferred content fetching and metadata-based hashes for connectors
* fix(kb): remove message count from outlook contentHash to prevent list/get divergence
* fix(kb): increase outlook getDocument message limit from 50 to 250
* fix(kb): skip outlook messages without conversationId to prevent broken stubs
* fix(kb): scope outlook getDocument to same folder as listDocuments to prevent hash divergence
* fix(kb): add missing connector sync cron job to Helm values
The connector sync endpoint existed but had no cron job configured to trigger it,
meaning scheduled syncs would never fire.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review comments on staging release
- Add try/catch around clipboard.writeText() in CopyCodeButton
- Add missing folder and past_chat cases in resolveResourceFromContext
- Return 400 for ZodError instead of 500 in all 8 Athena API routes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(api): return 400 for Zod validation errors across 27 API routes
Routes using z.parse() were returning 500 for ZodError (client input
validation failures). Added instanceof z.ZodError check to return 400
before the generic 500 handler, matching the established pattern used
by 115+ other routes.
Affected services: CloudWatch (7), CloudFormation (7), DynamoDB (6),
Slack (3), Outlook (2), OneDrive (1), Google Drive (1).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(api): add success:false to ZodError responses for consistency
7 routes used { success: false, error: ... } in their generic error
handler but our ZodError handler only returned { error: ... }. Aligned
the ZodError response shape to match.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(polling): consolidate polling services into provider handler pattern
Eliminate self-POST anti-pattern and extract shared boilerplate from 4 polling
services into a clean handler registry mirroring lib/webhooks/providers/.
- Add processPolledWebhookEvent() to processor.ts for direct in-process webhook
execution, removing HTTP round-trips that caused Lambda 403/timeout errors
- Extract shared utilities (markWebhookFailed/Success, fetchActiveWebhooks,
runWithConcurrency, resolveOAuthCredential, updateWebhookProviderConfig)
- Create PollingProviderHandler interface with per-provider implementations
- Consolidate 4 identical route files into single dynamic [provider] route
- Standardize concurrency to 10 across all providers
- No infra changes needed — Helm cron paths resolve via dynamic route
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* polish(polling): extract lock TTL constant and remove unnecessary type casts
- Widen processPolledWebhookEvent body param to accept object, eliminating
`as unknown as Record<string, unknown>` double casts in all 4 handlers
- Extract LOCK_TTL_SECONDS constant in route, tying maxDuration and lock TTL
to a single value
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(polling): address PR review feedback
- Add archivedAt filters to fetchActiveWebhooks query, matching
findWebhookAndWorkflow in processor.ts to prevent polling archived
webhooks/workflows
- Move provider validation after auth check to prevent provider
enumeration by unauthenticated callers
- Fix inconsistent pollingIdempotency import path in outlook.ts to
match other handlers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(polling): use literal for maxDuration segment config
Next.js requires segment config exports to be statically analyzable
literals. Using a variable reference caused build failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(admin): delete workspaces on ban
* Fix lint
* Wait until workspace deletion to return ban success
---------
Co-authored-by: Theodore Li <theo@sim.ai>
* feat(athena): add AWS Athena integration
* fix(athena): address PR review comments
- Fix variable shadowing: rename inner `data` to `rowData` in row mapper
- Fix first-page maxResults off-by-one: request maxResults+1 to compensate for header row
- Add missing runtime guard for queryString in create_named_query
- Move athena registry entries to correct alphabetical position
* fix(athena): alphabetize registry keys and add type re-exports
- Reorder athena_* registry keys to strict alphabetical order
- Add type re-exports from index.ts barrel
* fix(athena): cap maxResults at 999 to prevent overflow with header row adjustment
The +1 adjustment for the header row on first-page requests could
produce MaxResults=1001 when user requests 1000, exceeding the AWS
API hard cap of 1000.
* feat(chat): drag workflows and folders from sidebar into chat input
* fix(chat): fix effectAllowed, stale atInsertPosRef, and drag-enter overlay for resource drags
* feat(chat): add task dragging and visible drag ghost for sidebar items
* feat(sidebar): add drag ghost with icons and task icon to context chips
* refactor(types): narrow ChatMessageContext.kind to ChatContextKind union and add workflowBorderColor utility
* feat(user-input): support Tab to select resource in mention dropdown
* fix(user-input): narrow ChatContext discriminated union before accessing workflowId
* fix(colors): overload workflowBorderColor to accept string | undefined
* fix(colors): simplify workflowBorderColor to single string | undefined signature
* fix(chat): remove resource panel tab when context mention is deleted from input
* fix(chat): use resource ID for context removal identity check
* fix(chat): add folder/task cases to resource resolver, task key to existingResourceKeys, and use workflowBorderColor in drag ghost
* revert(chat): remove folder/task from resolveResourceFromContext — no panel UI for these types
* fix(chat): add chatId to stored context types and workflow.color to drag callback deps
* fix(chat): guard chatId before adding task key to existingResourceKeys
* improvement(secrets): parallelize save mutations and add admin visibility for workspace secrets
* fix(secrets): sequence workspace upsert/delete to avoid read-modify-write race
* fix(secrets): use Promise.allSettled to ensure credential invalidation after all mutations settle
* feat(slack): add subtype field and signature verification to Slack trigger
* fix(slack): guard against NaN timestamp and align null/empty-string convention
* fix(docs): resolve missing tool outputs for spread-inherited V2 tools
* fix(docs): add word boundary to baseToolRegex to prevent false matches
* fix(docs): remove unnecessary case-insensitive flag from baseToolRegex
* feat(auth): add DISABLE_GOOGLE_AUTH and DISABLE_GITHUB_AUTH env vars
* fix(auth): also disable server-side OAuth provider registration when flags are set
* lint
* fix(modals): consistent text colors, copy, and workspace delete confirmation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(modal): replace useEffect with render-time state reset
Replace useEffect anti-pattern for resetting confirmation text with
React's recommended "adjusting state during render" pattern. This
ensures stale text is never painted and avoids an extra render cycle.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(knowledge): prevent navigation on context menu actions and widen tags modal
* fix(knowledge): guard onCopyId against navigation and use setTimeout for robustness
* refactor(knowledge): extract withActionGuard helper to deduplicate context menu guard
* fix(knowledge): wrap withActionGuard callback in try/finally to prevent stuck ref
* improvement(landing, blog): SEO and GEO optimization
* improvement(docs): ui/ux cleanup
* chore(blog): remove unused buildBlogJsonLd export and wordCount schema field
* fix(blog): stack related posts vertically on mobile and fill all suggestion slots
- Add flex-col sm:flex-row and matching border classes to related posts
nav for consistent mobile stacking with the main blog page
- Remove score > 0 filter in getRelatedPosts so it falls back to recent
posts when there aren't enough tag matches
- Align description text color with main page cards