v0.6.33: polling improvements, jsm forms tools, credentials reactquery invalidation, HITL docs#4080
v0.6.33: polling improvements, jsm forms tools, credentials reactquery invalidation, HITL docs#4080waleedlatif1 merged 8 commits intomainfrom
Conversation
waleedlatif1
commented
Apr 9, 2026
- improvement(release): address comments (improvement(release): code cleanup #4069)
- fix(trigger): add @react-email/components to additionalPackages (fix(trigger): add @react-email/components to additionalPackages #4068)
- improvement(deploy): improve auto-generated version descriptions (improvement(deploy): improve auto-generated version descriptions #4075)
- improvement(polling): fix correctness and efficiency across all polling handlers (improvement(polling): fix correctness and efficiency across all polling handlers #4067)
- fix(credentials): add cross-cache invalidation for oauth credential queries (fix(credentials): add cross-cache invalidation for oauth credential queries #4076)
- feat(jsm): add ProForma/JSM Forms discovery tools (feat(jsm): add ProForma/JSM Forms discovery tools #4078)
- feat(trigger): add ServiceNow webhook triggers (feat(trigger): add ServiceNow webhook triggers #4077)
- docs(openapi): add Human in the Loop API endpoints (docs(openapi): add Human in the Loop API endpoints #4079)
* improvement(deploy): improve auto-generated version descriptions * fix(deploy): address PR review - log dropdown errors, populate first-deploy details * lint
…ng handlers (#4067) * improvement(polling): fix correctness and efficiency across all polling handlers - Gmail: paginate history API, add historyTypes filter, differentiate 403/429, fetch fresh historyId on fallback to break 404 retry loop - Outlook: follow @odata.nextLink pagination, use fetchWithRetry for all Graph calls, fix $top alignment, skip folder filter on partial resolution failure, remove Content-Type from GET requests - RSS: add conditional GET (ETag/If-None-Match), raise GUID cap to 500, fix 304 ETag capture per RFC 9111, align GUID tracking with idempotency fallback key - IMAP: single connection reuse, UIDVALIDITY tracking per mailbox, advance UID only on successful fetch, fix messageFlagsAdd range type, remove cross-mailbox legacy UID fallback - Dispatch polling via trigger.dev task with per-provider concurrency key; fall back to synchronous Redis-locked polling for self-hosted * fix(rss): align idempotency key GUID fallback with tracking/filter guard * removed comments * fix(imap): clear stale UID when UIDVALIDITY changes during state merge * fix(rss): skip items with no identifiable GUID to avoid idempotency key collisions * fix(schedules): convert dynamic import of getWorkflowById to static import * fix(imap): preserve fresh UID after UIDVALIDITY reset in state merge * improvement(polling): remove trigger.dev dispatch, use synchronous Redis-locked polling * fix(polling): decouple outlook page size from total email cap so pagination works
* feat(jsm): add ProForma/JSM Forms discovery tools
Add three new tools for discovering and inspecting JSM Forms (ProForma) templates
and their structure, enabling dynamic form-based workflows:
- jsm_get_form_templates: List form templates in a project with request type bindings
- jsm_get_form_structure: Get full form design (questions, layout, conditions, sections)
- jsm_get_issue_forms: List forms attached to an issue with submission status
All endpoints validated against the official Atlassian Forms REST API OpenAPI spec.
Uses the Forms Cloud API base URL (jira/forms/cloud/{cloudId}) with X-ExperimentalApi header.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(jsm): add input validation and extract shared error parser
- Add validateJiraIssueKey for projectIdOrKey in templates and structure routes
- Add validateJiraCloudId for formId (UUID) in structure route
- Extract parseJsmErrorMessage to shared utils.ts (was duplicated across 3 routes)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* chore(jsm): remove unused FORM_QUESTION_PROPERTIES constant
Dead code — the get_form_structure tool passes the raw design object
through as JSON, so this output constant had no consumers.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
* feat(trigger): add ServiceNow webhook triggers * fix(trigger): add webhook secret field and remove non-TSDoc comment Add webhookSecret field to ServiceNow triggers (matching Salesforce pattern) so users are prompted to protect the webhook endpoint. Update setup instructions to include Authorization header in the Business Rule example. Remove non-TSDoc inline comment in the block config. Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat(trigger): add ServiceNow provider handler with event matching Add dedicated ServiceNow webhook provider handler with: - verifyAuth: validates webhookSecret via Bearer token or X-Sim-Webhook-Secret - matchEvent: filters events by trigger type and table name using isServiceNowEventMatch utility (matching Salesforce/GitHub pattern) The event matcher handles incident created/updated and change request created/updated triggers with table name enforcement and event type normalization. The generic webhook trigger passes through all events but still respects the optional table name filter. Co-Authored-By: Claude Opus 4.6 <[email protected]> * lint --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
* docs(openapi): add Human in the Loop API endpoints Add HITL pause/resume endpoints to the OpenAPI spec covering the full workflow pause lifecycle: listing paused executions, inspecting pause details, and resuming with input. Co-Authored-By: Claude Opus 4.6 <[email protected]> * docs(openapi): add 403 and 500 responses to HITL endpoints Address PR review feedback: add missing 403 Forbidden response to all HITL endpoints (from validateWorkflowAccess), and 500 responses to resume endpoints that have explicit error paths. Co-Authored-By: Claude Opus 4.6 <[email protected]> * lint --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryMedium Risk Overview Jira Service Management Forms support added: Introduces three new JSM tools/endpoints ( Webhook/polling reliability improvements: Refines Gmail history polling (pagination, error handling, refresh historyId), overhauls IMAP polling to reuse a single connection and persist Misc fixes and UX improvements: ServiceNow block now exposes triggers and registers a new Reviewed by Cursor Bugbot for commit c393791. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c393791. Configure here.
| } catch { | ||
| continue | ||
| } | ||
| const lastUidForMailbox = latestUidByMailbox[mailboxPath] |
There was a problem hiding this comment.
IMAP drops legacy UID fallback causing email reprocessing
High Severity
The fetchNewEmails function no longer falls back to config.lastProcessedUid when determining the last processed email UID. This affects older IMAP webhooks that rely solely on lastProcessedUid for tracking, causing them to lose their position and re-process previously handled emails.
Reviewed by Cursor Bugbot for commit c393791. Configure here.
Greptile SummaryThis release bundles several independent improvements: pagination fixes for Gmail (history API), IMAP (UIDVALIDITY tracking, shared client), and Outlook (hard-max pagination loop) polling handlers; cross-cache The polling refactors are generally well-structured — the Confidence Score: 5/5Safe to merge — all findings are P2 style/design suggestions with no blocking correctness issues. The two issues raised are both P2: the cross-domain fetchWithRetry import is a code-organisation smell with no runtime impact, and the ServiceNow event-type set overlap is an edge-case documentation/UX concern rather than a data-correctness bug. All core logic changes (lock release, UIDVALIDITY tracking, Gmail pagination, credential cache invalidation) are sound. apps/sim/lib/webhooks/polling/outlook.ts (cross-domain import), apps/sim/triggers/servicenow/utils.ts (ambiguous event sets)
|
| Filename | Overview |
|---|---|
| apps/sim/app/api/webhooks/poll/[provider]/route.ts | Fixes a lock-release gap by moving the acquire/release into a nested try-finally, ensuring the distributed lock is always freed even when pollProvider throws. |
| apps/sim/lib/webhooks/polling/gmail.ts | Adds paginated history-API fetching, restricts to messageAdded events, and refreshes historyId from the profile endpoint after an invalid-history error to prevent polling stalls. |
| apps/sim/lib/webhooks/polling/imap.ts | Shares one ImapFlow client between fetch and mark-as-read phases, adds UIDVALIDITY tracking to discard stale UIDs after folder resets, and fixes the messageFlagsAdd call to pass the UID directly. |
| apps/sim/lib/webhooks/polling/outlook.ts | Adds paginated Graph API fetching with a hard 200-email cap; replaces bare fetch with fetchWithRetry — but imports the utility from @/lib/knowledge/documents/utils, creating an unintended cross-domain dependency. |
| apps/sim/hooks/queries/credentials.ts | Adds OAUTH_CREDENTIALS_KEY cross-cache invalidation to create, update, and delete credential mutations so the OAuth picker updates immediately after credential changes. |
| apps/sim/hooks/queries/credential-sets.ts | Converts all mutation cache-invalidation callbacks from onSuccess to onSettled across 7 mutations, ensuring cache is reconciled even when a mutation fails. |
| apps/sim/triggers/servicenow/utils.ts | Shared event-matching logic for ServiceNow triggers; INCIDENT_CREATED and CHANGE_REQUEST_CREATED share generic event tokens (insert, created) that can match both trigger types when no table name is present in the payload. |
| apps/sim/lib/webhooks/providers/servicenow.ts | New ServiceNow webhook provider handler with token-based auth (accepts both Authorization Bearer and X-Sim-Webhook-Secret headers) and dynamic import of event-matching logic. |
| apps/sim/tools/jsm/get_issue_forms.ts | New JSM tool for listing ProForma forms attached to an issue; delegates to /api/tools/jsm/forms/issue with proper OAuth params. |
| apps/sim/app/api/tools/jsm/forms/issue/route.ts | API route for JSM issue forms with internal auth check, cloudId and issueKey validation, and well-structured error forwarding. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Cron: GET /api/webhooks/poll/provider] --> B{Auth OK?}
B -- No --> Z[401 Unauthorized]
B -- Yes --> C{Provider known?}
C -- No --> Z2[404 Not Found]
C -- Yes --> D[acquireLock provider-polling-lock]
D -- Already locked --> E[202 Skipped]
D -- Acquired --> F[pollProvider]
F --> G{Provider}
G -- gmail --> H[Gmail: paginated historyId loop - messageAdded filter only]
G -- imap --> I[IMAP: shared ImapFlow client - UIDVALIDITY tracking]
G -- outlook --> J[Outlook: paginated Graph API - hard cap 200 emails]
G -- rss --> K[RSS: ETag / Last-Modified - MAX_GUIDS 500]
H & I & J & K --> L[processEmails / trigger workflows]
L --> M[releaseLock - always in finally]
M --> N[200 Completed]
subgraph ServiceNow Webhook
SN1[POST /api/webhooks/provider/servicenow] --> SN2{verifyAuth Bearer or X-Sim-Webhook-Secret}
SN2 -- fail --> SN3[401]
SN2 -- pass --> SN4[matchEvent isServiceNowEventMatch]
SN4 -- no match --> SN5[skip]
SN4 -- match --> SN6[execute workflow]
end
Reviews (1): Last reviewed commit: "docs(openapi): add Human in the Loop API..." | Re-trigger Greptile
| @@ -1,5 +1,6 @@ | |||
| import { htmlToText } from 'html-to-text' | |||
| import { pollingIdempotency } from '@/lib/core/idempotency/service' | |||
| import { fetchWithRetry } from '@/lib/knowledge/documents/utils' | |||
There was a problem hiding this comment.
Cross-domain import of
fetchWithRetry
fetchWithRetry is only exported from @/lib/knowledge/documents/utils, a module scoped to the knowledge-base feature. Importing it here couples the webhook polling domain to an unrelated feature — any refactor or removal of the knowledge module would silently break Outlook polling.
The function should be lifted to a shared location (e.g. @/lib/core/utils/fetch) and imported from there in both the knowledge code and here.
| import { fetchWithRetry } from '@/lib/knowledge/documents/utils' | |
| import { fetchWithRetry } from '@/lib/core/utils/fetch' |
| const INCIDENT_CREATED = new Set([ | ||
| 'incident_created', | ||
| 'insert', | ||
| 'created', | ||
| 'create', | ||
| 'after_insert', | ||
| 'afterinsert', | ||
| ]) | ||
|
|
||
| const INCIDENT_UPDATED = new Set([ | ||
| 'incident_updated', | ||
| 'update', | ||
| 'updated', | ||
| 'after_update', | ||
| 'afterupdate', | ||
| ]) | ||
|
|
||
| const CHANGE_REQUEST_CREATED = new Set([ | ||
| 'change_request_created', | ||
| 'insert', | ||
| 'created', | ||
| 'create', | ||
| 'after_insert', | ||
| 'afterinsert', | ||
| ]) |
There was a problem hiding this comment.
Ambiguous event-type sets across incident and change-request triggers
INCIDENT_CREATED and CHANGE_REQUEST_CREATED share the same generic tokens ('insert', 'created', 'create', 'after_insert', 'afterinsert'). When a payload arrives with no tableName field and no configuredTableName is set on the trigger, isServiceNowEventMatch will return true for both servicenow_incident_created and servicenow_change_request_created. Any workflow that users configure with the wrong default would fire on every insert from either table.
Consider requiring tableName in the payload or in trigger configuration before accepting generic event tokens, or document that table-name configuration is mandatory for users who have both trigger types active.

