From 1bc3a5428d7bc38602a2b0f2c987906b40245c60 Mon Sep 17 00:00:00 2001 From: Tom Owers Date: Tue, 21 Jul 2026 18:03:03 +0100 Subject: [PATCH 1/6] feat(data-warehouse): add Intercom and HubSpot as self-driving inbox sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the two OAuth-connected support sources deferred from the initial inbox-sources batch, now that the Code app connects OAuth sources generically. - HubSpot (`tickets` → ticket): flat table, uses the shared emitter factory (id `hs_object_id`, `subject`/`content`, `createdate`). - Intercom (`conversations` → ticket): bespoke emitter — the opening message lives in the `source` JSON blob and `created_at` is a Unix epoch, so it JSON-extracts the body and wraps the partition cursor in fromUnixTimestamp (verify the stored type on first sync). Adds the two SignalSourceProduct enum values (one migration), contract variants, registry entries, and parametrized test coverage. Also corrects the adding-inbox-sources skill: OAuth sources no longer need a bespoke setup form or a per-kind integration service/router. DynamicSourceSetup renders `oauth`/`oauth-account-select` generically and the connect flow is started by the generic `integration` router, so an OAuth source is the same one-registry-entry change as a credential source. --- .agents/skills/adding-inbox-sources/SKILL.md | 50 +++++---- products/signals/backend/contracts.py | 35 +++++- .../backend/emission/hubspot_tickets.py | 44 ++++++++ .../emission/intercom_conversations.py | 103 ++++++++++++++++++ products/signals/backend/emission/registry.py | 5 + .../emission/tests/test_tier1_registry.py | 3 + products/signals/backend/enums.py | 5 + ...alter_signalsourceconfig_source_product.py | 69 ++++++++++++ .../backend/migrations/max_migration.txt | 2 +- 9 files changed, 294 insertions(+), 22 deletions(-) create mode 100644 products/signals/backend/emission/hubspot_tickets.py create mode 100644 products/signals/backend/emission/intercom_conversations.py create mode 100644 products/signals/backend/migrations/0065_alter_signalsourceconfig_source_product.py diff --git a/.agents/skills/adding-inbox-sources/SKILL.md b/.agents/skills/adding-inbox-sources/SKILL.md index e14b7956c43f..42be562c7d43 100644 --- a/.agents/skills/adding-inbox-sources/SKILL.md +++ b/.agents/skills/adding-inbox-sources/SKILL.md @@ -49,19 +49,24 @@ A new credential-based source needs **zero form code** — just route its `sourceType` and the `schemas` to sync. - Endpoint: `GET /api/environments/{projectId}/external_data_sources/wizard/?source_type=` → `Record`. Client method: `PostHogAPIClient.getExternalDataSourceConfigs`. Hook: `useSourceConfig(sourceType)`. -- `SourceConfig.fields` is a union: `input` (text/email/password/url/number/…), `select`, `switch-group`, `oauth`, `ssh-tunnel`, `file-upload`. `DynamicSourceSetup` renders input/select/switch-group and builds the `createExternalDataSource` payload from field `name`s. The backend is the single source of truth for field names/labels/required/secret, so forms never drift. +- `SourceConfig.fields` is a union: `input` (text/email/password/url/number/…), `select`, `switch-group`, `oauth`, `oauth-account-select`, `ssh-tunnel`, `file-upload`. `DynamicSourceSetup` renders input/select/switch-group **and `oauth`/`oauth-account-select`** generically, and builds the `createExternalDataSource` payload from field `name`s. The backend is the single source of truth for field names/labels/required/secret, so forms never drift. - The field `name`s become the `payload` keys — so you no longer hand-maintain them. (Jira → `subdomain`, `email`, `api_token`; all `secret:false` except the token.) -Three cases still need bespoke handling (the generic renderer flags `oauth`/`ssh-tunnel`/`file-upload` as unsupported and disables submit): - -| Case | When | Existing example | -| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | -| **Generic dynamic form** | Credential inputs only (Jira, Zendesk, Freshdesk, Front, Gorgias, Sentry, GitLab). | `DynamicSourceSetup` (route the switch case to it) | -| **OAuth + integration polling** | Source authenticates via OAuth grant (Intercom `kind=intercom`); poll `getIntegrationsForProject` for the `kind`, pass `_integration_id`. | `LinearSetup` | -| **Deep-link OAuth + resource picker** | User must pick a specific resource (repo/board) during setup. | `GitHubSetup` | - -`ZendeskSetup`/`PgAnalyzeSetup` are the _old_ hardcoded forms — leave them or -migrate them to `DynamicSourceSetup` opportunistically; don't add new ones. +**OAuth sources need NO bespoke form.** `DynamicSourceSetup` renders the `oauth` field (a +connect button that starts the flow by `kind` and polls `getIntegrationsForProject` for the new +integration, writing its id into `payload[]`) and the `oauth-account-select` field (a +server-side-searched picker over the integration's resources) generically. The connect flow is +started by the generic, `kind`-parameterized `integration` tRPC router +(`packages/host-router/src/routers/integration.router.ts` → `IntegrationService`), so any +provider in `OauthIntegration.supported_kinds` works with no per-kind service or router. So an +OAuth source (Intercom, HubSpot, Salesforce, …) is the same one-registry-entry change as a +credential source — route its `DataSourceSetup` case to `DynamicSourceSetup`. + +Only two field types still lack a generic renderer (disable submit): `ssh-tunnel` and +`file-upload`. Route those to a bespoke form. Resource pickers that must run *after* OAuth +(GitHub's repo picker) are handled by `oauth-account-select`; the old `GitHubSetup`/`ZendeskSetup` +/`PgAnalyzeSetup` hardcoded forms remain only for historical reasons — leave them or migrate to +`DynamicSourceSetup` opportunistically; don't add new ones. Supported OAuth `kind` values (posthog `OauthIntegration.supported_kinds`, `posthog/models/integration.py`): `slack, salesforce, hubspot, google-ads, @@ -86,7 +91,7 @@ Verify exact `source_type` + `payload` key names against the posthog | Freshdesk | `Freshdesk` | `tickets` | API key | `DynamicSourceSetup` | `subdomain`, `api_key` | | Front | `Front` | `conversations` | API token | `DynamicSourceSetup` | `api_token` | | Gorgias | `Gorgias` | `tickets` | API key | `DynamicSourceSetup` | `gorgias_domain`, `email`, `api_key` | -| Intercom | `Intercom` | `conversations` | OAuth (`kind=intercom`) | Linear | `intercom_integration_id` | +| Intercom | `Intercom` | `conversations` | OAuth (`kind=intercom`) | `DynamicSourceSetup` | `intercom_integration_id` | (Zendesk `tickets`, GitHub `issues`, Linear `issues`, pganalyze `issues`+`servers`, and Jira `issues` are already shipped — copy them, don't re-add. The Jira row above @@ -119,18 +124,23 @@ source-list-relevant is a place you must add the new product. The canonical list 8. `packages/core/src/inbox/signalSourceService.ts` — mirror `SOURCE_TYPE_MAP`, `DATA_WAREHOUSE_SOURCES`, `ALL_SOURCE_PRODUCTS`, `computeSourceValues` init, plus `WarehouseSourceProduct`/`SignalSourceValues`. 9. `packages/core/src/inbox/dataSourceService.ts` — `DataSourceType`, `REQUIRED_SCHEMAS`, a `createXDataSource` method. -### OAuth plumbing — **only** for OAuth sources (Intercom); API-key sources skip this +### OAuth plumbing — NOT needed per source anymore -10. `packages/core/src/integrations/.ts` — `XIntegrationService.startFlow(region, projectId)` (clone `linear.ts`). -11. `packages/core/src/integrations/identifiers.ts` — new `X_INTEGRATION_SERVICE` symbol. -12. `packages/core/src/integrations/integrations.module.ts` — bind it. -13. `packages/host-router/src/routers/-integration.router.ts` — clone `linear-integration.router.ts`. -14. `packages/host-router/src/router.ts` — import + register the router in `appRouter`. +There is now a **generic, `kind`-parameterized** integration flow: `IntegrationService` +(`packages/core/src/integrations/integration.ts`) + the `integration` tRPC router +(`packages/host-router/src/routers/integration.router.ts`). `DynamicSourceSetup` starts any +OAuth flow through it via the field's `kind`. So a new OAuth source needs **no** per-kind +service, symbol, or router — do not clone `linear.ts`/`linear-integration.router.ts` per source. +(The old per-kind linear/slack/github routers still exist for other callers; leave them.) ### Setup-form specifics -- **Credential source:** route the `DataSourceSetup` switch case to `DynamicSourceSetup` (above). Nothing else — the fields come from the wizard endpoint. -- **OAuth form:** clone `LinearSetup`. Change the `kind` matched in the poll loop and the `_integration_id` payload key; swap `trpc.linearIntegration.startFlow` for the new router. +- **Credential source:** route the `DataSourceSetup` switch case to `DynamicSourceSetup`. Nothing else — the fields come from the wizard endpoint. +- **OAuth source:** also just route to `DynamicSourceSetup`. Its connect-form schema carries the + `oauth` field (and, if the provider needs a resource picked, an `oauth-account-select` field), + which `DynamicSourceSetup` renders generically — connect button + integration polling + account + picker, all by `kind`. No bespoke form. The provider must be in + `OauthIntegration.supported_kinds`. - Issues sources (`github`/`linear`/`jira`) force `issues` to `full_refresh` in `ensureRequiredTableSyncing` (`useSignalSourceToggles.ts`) — add the new product to that condition if it syncs an `issues` table (issues get edited/closed, so incremental append would miss updates). Ticket/conversation sources only force `should_sync=true`. ### Verify diff --git a/products/signals/backend/contracts.py b/products/signals/backend/contracts.py index fe74812934dd..59c1ae7ab55a 100644 --- a/products/signals/backend/contracts.py +++ b/products/signals/backend/contracts.py @@ -843,6 +843,35 @@ class JudgemeReviewsReviewSignalInput(SignalInputBase): extra: JudgemeReviewsReviewSignalExtra +# ── OAuth-connected support sources ─────────────────────────────────────────────── + + +class IntercomTicketSignalExtra(SignalExtraBase): + state: str | None + priority: str | None + admin_assignee_id: str | None + created_at: str | None + + +class IntercomTicketSignalInput(SignalInputBase): + source_type: Literal[SignalSourceType.TICKET] + source_product: Literal[SignalSourceProduct.INTERCOM] + extra: IntercomTicketSignalExtra + + +class HubspotTicketSignalExtra(SignalExtraBase): + hs_ticket_priority: str | None + hs_pipeline_stage: str | None + hs_ticket_category: str | None + createdate: str | None + + +class HubspotTicketSignalInput(SignalInputBase): + source_type: Literal[SignalSourceType.TICKET] + source_product: Literal[SignalSourceProduct.HUBSPOT] + extra: HubspotTicketSignalExtra + + # ── Union over all signal variants ────────────────────────────────────────────── # Discrimination is by the composite (source_product, source_type) pair, resolved via # SIGNAL_VARIANT_LOOKUP below — a single-field pydantic discriminator can't express it @@ -896,7 +925,9 @@ class JudgemeReviewsReviewSignalInput(SignalInputBase): | RetentlyFeedbackSignalInput | AppfiguresReviewSignalInput | AppfollowReviewSignalInput - | JudgemeReviewsReviewSignalInput, + | JudgemeReviewsReviewSignalInput + | IntercomTicketSignalInput + | HubspotTicketSignalInput, Field(union_mode="left_to_right"), ] @@ -948,6 +979,8 @@ class JudgemeReviewsReviewSignalInput(SignalInputBase): AppfiguresReviewSignalInput, AppfollowReviewSignalInput, JudgemeReviewsReviewSignalInput, + IntercomTicketSignalInput, + HubspotTicketSignalInput, ) diff --git a/products/signals/backend/emission/hubspot_tickets.py b/products/signals/backend/emission/hubspot_tickets.py new file mode 100644 index 000000000000..ebae843bde48 --- /dev/null +++ b/products/signals/backend/emission/hubspot_tickets.py @@ -0,0 +1,44 @@ +"""Signal emitter for hubspot `tickets` (record kind: ticket). + +HubSpot is an OAuth-connected source, but its `tickets` table is flat, so the shared factory +applies. `hs_object_id` is the record id, `subject`/`content` the text, `createdate` an ISO +string. Only the ticket properties the user selected during setup are synced; the defaults +include subject/content/createdate. +""" + +from products.signals.backend.emission._common import make_flat_emitter +from products.signals.backend.emission._prompts import TICKET_ACTIONABILITY_PROMPT, TICKET_SUMMARIZATION_PROMPT +from products.signals.backend.emission.fetchers.data_warehouse import data_warehouse_record_fetcher +from products.signals.backend.emission.registry import SignalSourceTableConfig + +HUBSPOT_FIELDS = ( + "hs_object_id", + "subject", + "content", + "hs_ticket_priority", + "hs_pipeline_stage", + "hs_ticket_category", + "createdate", +) + +HUBSPOT_CONFIG = SignalSourceTableConfig( + source_product="hubspot", + source_type="ticket", + emitter=make_flat_emitter( + source_product="hubspot", + source_type="ticket", + id_field="hs_object_id", + title_field="subject", + body_field="content", + extra_fields=("hs_ticket_priority", "hs_pipeline_stage", "hs_ticket_category", "createdate"), + ), + record_fetcher=data_warehouse_record_fetcher, + partition_field="createdate", + partition_field_is_datetime_string=True, + fields=HUBSPOT_FIELDS, + max_records=200, + first_sync_lookback_days=1, + actionability_prompt=TICKET_ACTIONABILITY_PROMPT, + summarization_prompt=TICKET_SUMMARIZATION_PROMPT, + description_summarization_threshold_chars=2000, +) diff --git a/products/signals/backend/emission/intercom_conversations.py b/products/signals/backend/emission/intercom_conversations.py new file mode 100644 index 000000000000..fefb518936e0 --- /dev/null +++ b/products/signals/backend/emission/intercom_conversations.py @@ -0,0 +1,103 @@ +"""Signal emitter for intercom `conversations` (record kind: ticket). + +Intercom is OAuth-connected and its `conversations` table isn't flat, so this is a bespoke +emitter (like Jira's): +- The opening message isn't a top-level column — it lives in the `source` JSON blob + (`source.body`, HTML). `title` is often null, so we fall back to the stripped source body. + The rest of the thread is in `conversation_parts` (a separate table we don't sync). +- `created_at` is a Unix epoch (seconds), so the partition cursor wraps it in + `fromUnixTimestamp(...)`. Verify the stored type on the first real sync. +""" + +import re +from typing import Any + +from structlog import get_logger + +from products.signals.backend.emission._prompts import TICKET_ACTIONABILITY_PROMPT, TICKET_SUMMARIZATION_PROMPT +from products.signals.backend.emission.fetchers.data_warehouse import data_warehouse_record_fetcher +from products.signals.backend.emission.registry import SignalEmitterOutput, SignalSourceTableConfig + +logger = get_logger(__name__) + +INTERCOM_IGNORED_STATES = ("closed",) + +FIELDS = ( + "id", + "title", + "state", + "priority", + "admin_assignee_id", + "created_at", + "JSONExtractString(source, 'body') AS source_body", +) + +_TAG_RE = re.compile(r"<[^>]+>") + + +def _strip_html(raw: Any) -> str: + if not raw or not isinstance(raw, str): + return "" + return _TAG_RE.sub(" ", raw).replace(" ", " ").strip() + + +def intercom_conversation_emitter(team_id: int, record: dict[str, Any]) -> SignalEmitterOutput | None: + try: + conversation_id = record["id"] + except KeyError as e: + msg = f"Intercom conversation record missing required field {e}" + logger.exception(msg, record=record, team_id=team_id, signals_type="data-import-signals") + raise ValueError(msg) from e + if not conversation_id: + msg = f"Intercom conversation record has empty id: {conversation_id!r}" + logger.exception(msg, record=record, team_id=team_id, signals_type="data-import-signals") + raise ValueError(msg) + + title = record.get("title") + body = _strip_html(record.get("source_body")) + # Prefer an explicit subject; otherwise use the opening message. Skip conversations with no + # readable text (auto-created/empty ones can't produce a useful signal). + description = title or body + if not description: + logger.info( + "Ignoring Intercom conversation without text", + team_id=team_id, + signals_type="data-import-signals", + ) + return None + if title and body: + description = f"{title}\n{body}" + + return SignalEmitterOutput( + source_product="intercom", + source_type="ticket", + source_id=str(conversation_id), + description=description, + weight=1.0, + extra={ + "state": record.get("state") or None, + "priority": record.get("priority") or None, + "admin_assignee_id": ( + str(record["admin_assignee_id"]) if record.get("admin_assignee_id") is not None else None + ), + "created_at": str(record["created_at"]) if record.get("created_at") is not None else None, + }, + ) + + +INTERCOM_CONFIG = SignalSourceTableConfig( + source_product="intercom", + source_type="ticket", + emitter=intercom_conversation_emitter, + record_fetcher=data_warehouse_record_fetcher, + # created_at is a Unix epoch (seconds). Wrap so the cursor compares as a datetime; verify the + # stored column type on the first real sync (it may already be a DateTime). + partition_field="fromUnixTimestamp(toUInt32(created_at))", + fields=FIELDS, + where_clause=f"state NOT IN ({', '.join(repr(s) for s in INTERCOM_IGNORED_STATES)})", + max_records=200, + first_sync_lookback_days=1, + actionability_prompt=TICKET_ACTIONABILITY_PROMPT, + summarization_prompt=TICKET_SUMMARIZATION_PROMPT, + description_summarization_threshold_chars=2000, +) diff --git a/products/signals/backend/emission/registry.py b/products/signals/backend/emission/registry.py index 77c689646209..10c91949fe03 100644 --- a/products/signals/backend/emission/registry.py +++ b/products/signals/backend/emission/registry.py @@ -136,6 +136,8 @@ def _register_all_emitters() -> None: from products.signals.backend.emission.gitlab_issues import GITLAB_CONFIG from products.signals.backend.emission.gorgias_tickets import GORGIAS_CONFIG from products.signals.backend.emission.honeybadger_faults import HONEYBADGER_CONFIG + from products.signals.backend.emission.hubspot_tickets import HUBSPOT_CONFIG + from products.signals.backend.emission.intercom_conversations import INTERCOM_CONFIG from products.signals.backend.emission.jira_issues import JIRA_ISSUES_CONFIG from products.signals.backend.emission.judgeme_reviews_reviews import JUDGEME_REVIEWS_CONFIG from products.signals.backend.emission.kustomer_conversations import KUSTOMER_CONFIG @@ -199,6 +201,9 @@ def _register_all_emitters() -> None: register_signal_source(ExternalDataSourceType.APPFIGURES, "reviews", APPFIGURES_CONFIG) register_signal_source(ExternalDataSourceType.APPFOLLOW, "reviews", APPFOLLOW_CONFIG) register_signal_source(ExternalDataSourceType.JUDGEMEREVIEWS, "reviews", JUDGEME_REVIEWS_CONFIG) + # OAuth-connected support sources (record kind: ticket) + register_signal_source(ExternalDataSourceType.INTERCOM, "conversations", INTERCOM_CONFIG) + register_signal_source(ExternalDataSourceType.HUBSPOT, "tickets", HUBSPOT_CONFIG) _register_all_emitters() diff --git a/products/signals/backend/emission/tests/test_tier1_registry.py b/products/signals/backend/emission/tests/test_tier1_registry.py index 51a0480a7a21..5e87b7597a96 100644 --- a/products/signals/backend/emission/tests/test_tier1_registry.py +++ b/products/signals/backend/emission/tests/test_tier1_registry.py @@ -41,6 +41,9 @@ (ExternalDataSourceType.APPFIGURES, "reviews", "appfigures", "review"), (ExternalDataSourceType.APPFOLLOW, "reviews", "appfollow", "review"), (ExternalDataSourceType.JUDGEMEREVIEWS, "reviews", "judgeme_reviews", "review"), + # OAuth-connected support sources + (ExternalDataSourceType.INTERCOM, "conversations", "intercom", "ticket"), + (ExternalDataSourceType.HUBSPOT, "tickets", "hubspot", "ticket"), ] IDS = [product for _, _, product, _ in TIER1_SOURCES] diff --git a/products/signals/backend/enums.py b/products/signals/backend/enums.py index 673d61bc23de..a981b2bd5f2c 100644 --- a/products/signals/backend/enums.py +++ b/products/signals/backend/enums.py @@ -65,6 +65,9 @@ class SignalSourceProduct(StrEnum): APPFIGURES = "appfigures" APPFOLLOW = "appfollow" JUDGEME_REVIEWS = "judgeme_reviews" + # OAuth-connected support sources + INTERCOM = "intercom" + HUBSPOT = "hubspot" class SignalSourceType(StrEnum): @@ -141,6 +144,8 @@ class SignalSourceType(StrEnum): SignalSourceProduct.APPFIGURES: "Appfigures", SignalSourceProduct.APPFOLLOW: "AppFollow", SignalSourceProduct.JUDGEME_REVIEWS: "Judge.me", + SignalSourceProduct.INTERCOM: "Intercom", + SignalSourceProduct.HUBSPOT: "HubSpot", } # The Django model's `source_product` choices, frozen-equivalent to the prior nested TextChoices so diff --git a/products/signals/backend/migrations/0065_alter_signalsourceconfig_source_product.py b/products/signals/backend/migrations/0065_alter_signalsourceconfig_source_product.py new file mode 100644 index 000000000000..930356531e43 --- /dev/null +++ b/products/signals/backend/migrations/0065_alter_signalsourceconfig_source_product.py @@ -0,0 +1,69 @@ +# Generated by Django 5.2.14 on 2026-07-21 11:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("signals", "0064_alter_signalsourceconfig_source_product"), + ] + + operations = [ + migrations.AlterField( + model_name="signalsourceconfig", + name="source_product", + field=models.CharField( + choices=[ + ("session_replay", "Session replay"), + ("llm_analytics", "LLM analytics"), + ("github", "GitHub"), + ("linear", "Linear"), + ("jira", "Jira"), + ("zendesk", "Zendesk"), + ("conversations", "Conversations"), + ("error_tracking", "Error tracking"), + ("pganalyze", "pganalyze"), + ("signals_scout", "Signals scout"), + ("logs", "Logs"), + ("health_checks", "Health checks"), + ("endpoints", "Endpoints"), + ("replay_vision", "Replay Vision"), + ("analytics", "Product analytics"), + ("freshdesk", "Freshdesk"), + ("freshservice", "Freshservice"), + ("front", "Front"), + ("gorgias", "Gorgias"), + ("kustomer", "Kustomer"), + ("dixa", "Dixa"), + ("plain", "Plain"), + ("gitlab", "GitLab"), + ("gitea", "Gitea"), + ("shortcut", "Shortcut"), + ("sentry", "Sentry"), + ("rollbar", "Rollbar"), + ("bugsnag", "Bugsnag"), + ("honeybadger", "Honeybadger"), + ("raygun", "Raygun"), + ("snyk", "Snyk"), + ("sonarqube", "SonarQube"), + ("semgrep", "Semgrep"), + ("rapid7_insightvm", "Rapid7 InsightVM"), + ("featurebase", "Featurebase"), + ("frill", "Frill"), + ("aha", "Aha"), + ("uservoice", "UserVoice"), + ("productboard", "Productboard"), + ("canny", "Canny"), + ("asknicely", "AskNicely"), + ("retently", "Retently"), + ("appfigures", "Appfigures"), + ("appfollow", "AppFollow"), + ("judgeme_reviews", "Judge.me"), + ("intercom", "Intercom"), + ("hubspot", "HubSpot"), + ], + max_length=100, + ), + ), + ] diff --git a/products/signals/backend/migrations/max_migration.txt b/products/signals/backend/migrations/max_migration.txt index eab24b0d2214..b9bda9dd0ccb 100644 --- a/products/signals/backend/migrations/max_migration.txt +++ b/products/signals/backend/migrations/max_migration.txt @@ -1 +1 @@ -0064_alter_signalsourceconfig_source_product +0065_alter_signalsourceconfig_source_product From 32258dd0c42a5f31cec2718717f336601f55cbac Mon Sep 17 00:00:00 2001 From: Tom Owers Date: Tue, 21 Jul 2026 18:03:25 +0100 Subject: [PATCH 2/6] chore: update OpenAPI generated types Generated-By: PostHog Code Task-Id: 7f82f613-b489-484f-90c6-ac67e9b0d10b --- .../signals/frontend/generated/api.schemas.ts | 28 ++++++++++++++++++- .../signals/frontend/generated/api.zod.ts | 12 ++++++-- services/mcp/src/api/generated.ts | 28 +++++++++++++++++-- services/mcp/src/generated/signals/api.ts | 12 ++++++-- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/products/signals/frontend/generated/api.schemas.ts b/products/signals/frontend/generated/api.schemas.ts index b312874fe513..d975d2dae038 100644 --- a/products/signals/frontend/generated/api.schemas.ts +++ b/products/signals/frontend/generated/api.schemas.ts @@ -338,6 +338,8 @@ export interface SignalReportRefundResponseApi { * * `appfigures` - appfigures * * `appfollow` - appfollow * * `judgeme_reviews` - judgeme_reviews + * * `intercom` - intercom + * * `hubspot` - hubspot */ export type SignalSourceProductApi = (typeof SignalSourceProductApi)[keyof typeof SignalSourceProductApi] @@ -387,6 +389,8 @@ export const SignalSourceProductApi = { Appfigures: 'appfigures', Appfollow: 'appfollow', JudgemeReviews: 'judgeme_reviews', + Intercom: 'intercom', + Hubspot: 'hubspot', } as const /** @@ -937,6 +941,20 @@ export interface JudgemeReviewsReviewSignalExtraApi { created_at: string | null } +export interface IntercomTicketSignalExtraApi { + state: string | null + priority: string | null + admin_assignee_id: string | null + created_at: string | null +} + +export interface HubspotTicketSignalExtraApi { + hs_ticket_priority: string | null + hs_pipeline_stage: string | null + hs_ticket_category: string | null + createdate: string | null +} + export type SignalExtraApi = | SessionProblemSignalExtraApi | LlmEvalSignalExtraApi @@ -985,6 +1003,8 @@ export type SignalExtraApi = | AppfiguresReviewSignalExtraApi | AppfollowReviewSignalExtraApi | JudgemeReviewsReviewSignalExtraApi + | IntercomTicketSignalExtraApi + | HubspotTicketSignalExtraApi export interface SpecificityMetadataApi { /** Title of the PR the specificity gate evaluated. */ @@ -1068,7 +1088,9 @@ export interface SignalNodeApi { * * `retently` - retently * * `appfigures` - appfigures * * `appfollow` - appfollow - * * `judgeme_reviews` - judgeme_reviews */ + * * `judgeme_reviews` - judgeme_reviews + * * `intercom` - intercom + * * `hubspot` - hubspot */ source_product: SignalSourceProductApi /** Signal type within the source product. * @@ -2799,6 +2821,8 @@ export interface ForgetResponseApi { * * `appfigures` - Appfigures * * `appfollow` - AppFollow * * `judgeme_reviews` - Judge.me + * * `intercom` - Intercom + * * `hubspot` - HubSpot */ export type SignalSourceConfigSourceProductEnumApi = (typeof SignalSourceConfigSourceProductEnumApi)[keyof typeof SignalSourceConfigSourceProductEnumApi] @@ -2849,6 +2873,8 @@ export const SignalSourceConfigSourceProductEnumApi = { Appfigures: 'appfigures', Appfollow: 'appfollow', JudgemeReviews: 'judgeme_reviews', + Intercom: 'intercom', + Hubspot: 'hubspot', } as const /** diff --git a/products/signals/frontend/generated/api.zod.ts b/products/signals/frontend/generated/api.zod.ts index fd29ddc3f483..2e7c5979fd7e 100644 --- a/products/signals/frontend/generated/api.zod.ts +++ b/products/signals/frontend/generated/api.zod.ts @@ -708,9 +708,11 @@ export const SignalsSourceConfigsCreateBody = /* @__PURE__ */ zod.object({ 'appfigures', 'appfollow', 'judgeme_reviews', + 'intercom', + 'hubspot', ]) .describe( - '\* `session_replay` - Session replay\n\* `llm_analytics` - LLM analytics\n\* `github` - GitHub\n\* `linear` - Linear\n\* `jira` - Jira\n\* `zendesk` - Zendesk\n\* `conversations` - Conversations\n\* `error_tracking` - Error tracking\n\* `pganalyze` - pganalyze\n\* `signals_scout` - Signals scout\n\* `logs` - Logs\n\* `health_checks` - Health checks\n\* `endpoints` - Endpoints\n\* `replay_vision` - Replay Vision\n\* `analytics` - Product analytics\n\* `freshdesk` - Freshdesk\n\* `freshservice` - Freshservice\n\* `front` - Front\n\* `gorgias` - Gorgias\n\* `kustomer` - Kustomer\n\* `dixa` - Dixa\n\* `plain` - Plain\n\* `gitlab` - GitLab\n\* `gitea` - Gitea\n\* `shortcut` - Shortcut\n\* `sentry` - Sentry\n\* `rollbar` - Rollbar\n\* `bugsnag` - Bugsnag\n\* `honeybadger` - Honeybadger\n\* `raygun` - Raygun\n\* `snyk` - Snyk\n\* `sonarqube` - SonarQube\n\* `semgrep` - Semgrep\n\* `rapid7_insightvm` - Rapid7 InsightVM\n\* `featurebase` - Featurebase\n\* `frill` - Frill\n\* `aha` - Aha\n\* `uservoice` - UserVoice\n\* `productboard` - Productboard\n\* `canny` - Canny\n\* `asknicely` - AskNicely\n\* `retently` - Retently\n\* `appfigures` - Appfigures\n\* `appfollow` - AppFollow\n\* `judgeme_reviews` - Judge.me' + '\* `session_replay` - Session replay\n\* `llm_analytics` - LLM analytics\n\* `github` - GitHub\n\* `linear` - Linear\n\* `jira` - Jira\n\* `zendesk` - Zendesk\n\* `conversations` - Conversations\n\* `error_tracking` - Error tracking\n\* `pganalyze` - pganalyze\n\* `signals_scout` - Signals scout\n\* `logs` - Logs\n\* `health_checks` - Health checks\n\* `endpoints` - Endpoints\n\* `replay_vision` - Replay Vision\n\* `analytics` - Product analytics\n\* `freshdesk` - Freshdesk\n\* `freshservice` - Freshservice\n\* `front` - Front\n\* `gorgias` - Gorgias\n\* `kustomer` - Kustomer\n\* `dixa` - Dixa\n\* `plain` - Plain\n\* `gitlab` - GitLab\n\* `gitea` - Gitea\n\* `shortcut` - Shortcut\n\* `sentry` - Sentry\n\* `rollbar` - Rollbar\n\* `bugsnag` - Bugsnag\n\* `honeybadger` - Honeybadger\n\* `raygun` - Raygun\n\* `snyk` - Snyk\n\* `sonarqube` - SonarQube\n\* `semgrep` - Semgrep\n\* `rapid7_insightvm` - Rapid7 InsightVM\n\* `featurebase` - Featurebase\n\* `frill` - Frill\n\* `aha` - Aha\n\* `uservoice` - UserVoice\n\* `productboard` - Productboard\n\* `canny` - Canny\n\* `asknicely` - AskNicely\n\* `retently` - Retently\n\* `appfigures` - Appfigures\n\* `appfollow` - AppFollow\n\* `judgeme_reviews` - Judge.me\n\* `intercom` - Intercom\n\* `hubspot` - HubSpot' ), source_type: zod .enum([ @@ -785,9 +787,11 @@ export const SignalsSourceConfigsUpdateBody = /* @__PURE__ */ zod.object({ 'appfigures', 'appfollow', 'judgeme_reviews', + 'intercom', + 'hubspot', ]) .describe( - '\* `session_replay` - Session replay\n\* `llm_analytics` - LLM analytics\n\* `github` - GitHub\n\* `linear` - Linear\n\* `jira` - Jira\n\* `zendesk` - Zendesk\n\* `conversations` - Conversations\n\* `error_tracking` - Error tracking\n\* `pganalyze` - pganalyze\n\* `signals_scout` - Signals scout\n\* `logs` - Logs\n\* `health_checks` - Health checks\n\* `endpoints` - Endpoints\n\* `replay_vision` - Replay Vision\n\* `analytics` - Product analytics\n\* `freshdesk` - Freshdesk\n\* `freshservice` - Freshservice\n\* `front` - Front\n\* `gorgias` - Gorgias\n\* `kustomer` - Kustomer\n\* `dixa` - Dixa\n\* `plain` - Plain\n\* `gitlab` - GitLab\n\* `gitea` - Gitea\n\* `shortcut` - Shortcut\n\* `sentry` - Sentry\n\* `rollbar` - Rollbar\n\* `bugsnag` - Bugsnag\n\* `honeybadger` - Honeybadger\n\* `raygun` - Raygun\n\* `snyk` - Snyk\n\* `sonarqube` - SonarQube\n\* `semgrep` - Semgrep\n\* `rapid7_insightvm` - Rapid7 InsightVM\n\* `featurebase` - Featurebase\n\* `frill` - Frill\n\* `aha` - Aha\n\* `uservoice` - UserVoice\n\* `productboard` - Productboard\n\* `canny` - Canny\n\* `asknicely` - AskNicely\n\* `retently` - Retently\n\* `appfigures` - Appfigures\n\* `appfollow` - AppFollow\n\* `judgeme_reviews` - Judge.me' + '\* `session_replay` - Session replay\n\* `llm_analytics` - LLM analytics\n\* `github` - GitHub\n\* `linear` - Linear\n\* `jira` - Jira\n\* `zendesk` - Zendesk\n\* `conversations` - Conversations\n\* `error_tracking` - Error tracking\n\* `pganalyze` - pganalyze\n\* `signals_scout` - Signals scout\n\* `logs` - Logs\n\* `health_checks` - Health checks\n\* `endpoints` - Endpoints\n\* `replay_vision` - Replay Vision\n\* `analytics` - Product analytics\n\* `freshdesk` - Freshdesk\n\* `freshservice` - Freshservice\n\* `front` - Front\n\* `gorgias` - Gorgias\n\* `kustomer` - Kustomer\n\* `dixa` - Dixa\n\* `plain` - Plain\n\* `gitlab` - GitLab\n\* `gitea` - Gitea\n\* `shortcut` - Shortcut\n\* `sentry` - Sentry\n\* `rollbar` - Rollbar\n\* `bugsnag` - Bugsnag\n\* `honeybadger` - Honeybadger\n\* `raygun` - Raygun\n\* `snyk` - Snyk\n\* `sonarqube` - SonarQube\n\* `semgrep` - Semgrep\n\* `rapid7_insightvm` - Rapid7 InsightVM\n\* `featurebase` - Featurebase\n\* `frill` - Frill\n\* `aha` - Aha\n\* `uservoice` - UserVoice\n\* `productboard` - Productboard\n\* `canny` - Canny\n\* `asknicely` - AskNicely\n\* `retently` - Retently\n\* `appfigures` - Appfigures\n\* `appfollow` - AppFollow\n\* `judgeme_reviews` - Judge.me\n\* `intercom` - Intercom\n\* `hubspot` - HubSpot' ), source_type: zod .enum([ @@ -862,10 +866,12 @@ export const SignalsSourceConfigsPartialUpdateBody = /* @__PURE__ */ zod.object( 'appfigures', 'appfollow', 'judgeme_reviews', + 'intercom', + 'hubspot', ]) .optional() .describe( - '\* `session_replay` - Session replay\n\* `llm_analytics` - LLM analytics\n\* `github` - GitHub\n\* `linear` - Linear\n\* `jira` - Jira\n\* `zendesk` - Zendesk\n\* `conversations` - Conversations\n\* `error_tracking` - Error tracking\n\* `pganalyze` - pganalyze\n\* `signals_scout` - Signals scout\n\* `logs` - Logs\n\* `health_checks` - Health checks\n\* `endpoints` - Endpoints\n\* `replay_vision` - Replay Vision\n\* `analytics` - Product analytics\n\* `freshdesk` - Freshdesk\n\* `freshservice` - Freshservice\n\* `front` - Front\n\* `gorgias` - Gorgias\n\* `kustomer` - Kustomer\n\* `dixa` - Dixa\n\* `plain` - Plain\n\* `gitlab` - GitLab\n\* `gitea` - Gitea\n\* `shortcut` - Shortcut\n\* `sentry` - Sentry\n\* `rollbar` - Rollbar\n\* `bugsnag` - Bugsnag\n\* `honeybadger` - Honeybadger\n\* `raygun` - Raygun\n\* `snyk` - Snyk\n\* `sonarqube` - SonarQube\n\* `semgrep` - Semgrep\n\* `rapid7_insightvm` - Rapid7 InsightVM\n\* `featurebase` - Featurebase\n\* `frill` - Frill\n\* `aha` - Aha\n\* `uservoice` - UserVoice\n\* `productboard` - Productboard\n\* `canny` - Canny\n\* `asknicely` - AskNicely\n\* `retently` - Retently\n\* `appfigures` - Appfigures\n\* `appfollow` - AppFollow\n\* `judgeme_reviews` - Judge.me' + '\* `session_replay` - Session replay\n\* `llm_analytics` - LLM analytics\n\* `github` - GitHub\n\* `linear` - Linear\n\* `jira` - Jira\n\* `zendesk` - Zendesk\n\* `conversations` - Conversations\n\* `error_tracking` - Error tracking\n\* `pganalyze` - pganalyze\n\* `signals_scout` - Signals scout\n\* `logs` - Logs\n\* `health_checks` - Health checks\n\* `endpoints` - Endpoints\n\* `replay_vision` - Replay Vision\n\* `analytics` - Product analytics\n\* `freshdesk` - Freshdesk\n\* `freshservice` - Freshservice\n\* `front` - Front\n\* `gorgias` - Gorgias\n\* `kustomer` - Kustomer\n\* `dixa` - Dixa\n\* `plain` - Plain\n\* `gitlab` - GitLab\n\* `gitea` - Gitea\n\* `shortcut` - Shortcut\n\* `sentry` - Sentry\n\* `rollbar` - Rollbar\n\* `bugsnag` - Bugsnag\n\* `honeybadger` - Honeybadger\n\* `raygun` - Raygun\n\* `snyk` - Snyk\n\* `sonarqube` - SonarQube\n\* `semgrep` - Semgrep\n\* `rapid7_insightvm` - Rapid7 InsightVM\n\* `featurebase` - Featurebase\n\* `frill` - Frill\n\* `aha` - Aha\n\* `uservoice` - UserVoice\n\* `productboard` - Productboard\n\* `canny` - Canny\n\* `asknicely` - AskNicely\n\* `retently` - Retently\n\* `appfigures` - Appfigures\n\* `appfollow` - AppFollow\n\* `judgeme_reviews` - Judge.me\n\* `intercom` - Intercom\n\* `hubspot` - HubSpot' ), source_type: zod .enum([ diff --git a/services/mcp/src/api/generated.ts b/services/mcp/src/api/generated.ts index ce2a3df192d3..6f7e6362c2bc 100644 --- a/services/mcp/src/api/generated.ts +++ b/services/mcp/src/api/generated.ts @@ -33772,6 +33772,20 @@ export namespace Schemas { created_at: string | null; } + export interface IntercomTicketSignalExtra { + state: string | null; + priority: string | null; + admin_assignee_id: string | null; + created_at: string | null; + } + + export interface HubspotTicketSignalExtra { + hs_ticket_priority: string | null; + hs_pipeline_stage: string | null; + hs_ticket_category: string | null; + createdate: string | null; + } + /** * One chunk in a drill-down window over a single knowledge document. * @@ -40616,6 +40630,8 @@ export namespace Schemas { * * `appfigures` - Appfigures * * `appfollow` - AppFollow * * `judgeme_reviews` - Judge.me + * * `intercom` - Intercom + * * `hubspot` - HubSpot */ export type SignalSourceConfigSourceProductEnum = typeof SignalSourceConfigSourceProductEnum[keyof typeof SignalSourceConfigSourceProductEnum]; @@ -40666,6 +40682,8 @@ export namespace Schemas { Appfigures: 'appfigures', Appfollow: 'appfollow', JudgemeReviews: 'judgeme_reviews', + Intercom: 'intercom', + Hubspot: 'hubspot', } as const; /** @@ -55959,6 +55977,8 @@ export namespace Schemas { * * `appfigures` - appfigures * * `appfollow` - appfollow * * `judgeme_reviews` - judgeme_reviews + * * `intercom` - intercom + * * `hubspot` - hubspot */ export type SignalSourceProduct = typeof SignalSourceProduct[keyof typeof SignalSourceProduct]; @@ -56009,6 +56029,8 @@ export namespace Schemas { Appfigures: 'appfigures', Appfollow: 'appfollow', JudgemeReviews: 'judgeme_reviews', + Intercom: 'intercom', + Hubspot: 'hubspot', } as const; /** @@ -56177,7 +56199,7 @@ export namespace Schemas { createdDate: string | null; } - export type SignalExtra = SessionProblemSignalExtra | LlmEvalSignalExtra | LlmEvalReportSignalExtra | ZendeskTicketSignalExtra | GithubIssueSignalExtra | LinearIssueSignalExtra | JiraIssueSignalExtra | ConversationsTicketSignalExtra | ErrorTrackingSignalExtra | PgAnalyzeIssueSignalExtra | EndpointExecutionFailedSignalExtra | EndpointBreakdownLimitExceededSignalExtra | SignalsScoutSignalExtra | LogsAlertStateChangeSignalExtra | ReplayVisionScannerFindingSignalExtra | AnalyticsAnomalyInvestigationSignalExtra | HealthCheckSignalExtra | FreshdeskTicketSignalExtra | FreshserviceTicketSignalExtra | FrontConversationSignalExtra | GorgiasTicketSignalExtra | KustomerConversationSignalExtra | DixaConversationSignalExtra | PlainThreadSignalExtra | GitlabIssueSignalExtra | GiteaIssueSignalExtra | ShortcutStorySignalExtra | SentryIssueSignalExtra | RollbarItemSignalExtra | BugsnagErrorSignalExtra | HoneybadgerFaultSignalExtra | RaygunErrorGroupSignalExtra | SnykScannerFindingSignalExtra | SonarqubeScannerFindingSignalExtra | SemgrepScannerFindingSignalExtra | Rapid7InsightvmScannerFindingSignalExtra | FeaturebaseFeedbackSignalExtra | FrillFeedbackSignalExtra | AhaFeedbackSignalExtra | UservoiceFeedbackSignalExtra | ProductboardFeedbackSignalExtra | CannyFeedbackSignalExtra | AsknicelyFeedbackSignalExtra | RetentlyFeedbackSignalExtra | AppfiguresReviewSignalExtra | AppfollowReviewSignalExtra | JudgemeReviewsReviewSignalExtra; + export type SignalExtra = SessionProblemSignalExtra | LlmEvalSignalExtra | LlmEvalReportSignalExtra | ZendeskTicketSignalExtra | GithubIssueSignalExtra | LinearIssueSignalExtra | JiraIssueSignalExtra | ConversationsTicketSignalExtra | ErrorTrackingSignalExtra | PgAnalyzeIssueSignalExtra | EndpointExecutionFailedSignalExtra | EndpointBreakdownLimitExceededSignalExtra | SignalsScoutSignalExtra | LogsAlertStateChangeSignalExtra | ReplayVisionScannerFindingSignalExtra | AnalyticsAnomalyInvestigationSignalExtra | HealthCheckSignalExtra | FreshdeskTicketSignalExtra | FreshserviceTicketSignalExtra | FrontConversationSignalExtra | GorgiasTicketSignalExtra | KustomerConversationSignalExtra | DixaConversationSignalExtra | PlainThreadSignalExtra | GitlabIssueSignalExtra | GiteaIssueSignalExtra | ShortcutStorySignalExtra | SentryIssueSignalExtra | RollbarItemSignalExtra | BugsnagErrorSignalExtra | HoneybadgerFaultSignalExtra | RaygunErrorGroupSignalExtra | SnykScannerFindingSignalExtra | SonarqubeScannerFindingSignalExtra | SemgrepScannerFindingSignalExtra | Rapid7InsightvmScannerFindingSignalExtra | FeaturebaseFeedbackSignalExtra | FrillFeedbackSignalExtra | AhaFeedbackSignalExtra | UservoiceFeedbackSignalExtra | ProductboardFeedbackSignalExtra | CannyFeedbackSignalExtra | AsknicelyFeedbackSignalExtra | RetentlyFeedbackSignalExtra | AppfiguresReviewSignalExtra | AppfollowReviewSignalExtra | JudgemeReviewsReviewSignalExtra | IntercomTicketSignalExtra | HubspotTicketSignalExtra; export type SignalMatchMetadata = MatchedMetadata | NoMatchMetadata; @@ -56232,7 +56254,9 @@ export namespace Schemas { * * `retently` - retently * * `appfigures` - appfigures * * `appfollow` - appfollow - * * `judgeme_reviews` - judgeme_reviews */ + * * `judgeme_reviews` - judgeme_reviews + * * `intercom` - intercom + * * `hubspot` - hubspot */ source_product: SignalSourceProduct; /** Signal type within the source product. * diff --git a/services/mcp/src/generated/signals/api.ts b/services/mcp/src/generated/signals/api.ts index c2f567a7def7..d7a46a990192 100644 --- a/services/mcp/src/generated/signals/api.ts +++ b/services/mcp/src/generated/signals/api.ts @@ -1188,9 +1188,11 @@ export const SignalsSourceConfigsCreateBody = /* @__PURE__ */ zod.object({ 'appfigures', 'appfollow', 'judgeme_reviews', + 'intercom', + 'hubspot', ]) .describe( - '* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me' + '* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me\n* `intercom` - Intercom\n* `hubspot` - HubSpot' ), source_type: zod .enum([ @@ -1283,9 +1285,11 @@ export const SignalsSourceConfigsUpdateBody = /* @__PURE__ */ zod.object({ 'appfigures', 'appfollow', 'judgeme_reviews', + 'intercom', + 'hubspot', ]) .describe( - '* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me' + '* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me\n* `intercom` - Intercom\n* `hubspot` - HubSpot' ), source_type: zod .enum([ @@ -1369,10 +1373,12 @@ export const SignalsSourceConfigsPartialUpdateBody = /* @__PURE__ */ zod.object( 'appfigures', 'appfollow', 'judgeme_reviews', + 'intercom', + 'hubspot', ]) .optional() .describe( - '* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me' + '* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me\n* `intercom` - Intercom\n* `hubspot` - HubSpot' ), source_type: zod .enum([ From 9bccb0aa70fa27110522d126d3de80ed899693d1 Mon Sep 17 00:00:00 2001 From: "tests-posthog[bot]" <250237707+tests-posthog[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 17:07:50 +0000 Subject: [PATCH 3/6] test(mcp): update unit test snapshots --- .../tool-schemas/inbox-source-configs-create.json | 6 ++++-- .../tool-schemas/inbox-source-configs-partial-update.json | 6 ++++-- .../tool-schemas/inbox-source-configs-update.json | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-create.json b/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-create.json index b40295330d16..8a1695deb288 100644 --- a/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-create.json +++ b/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-create.json @@ -6,7 +6,7 @@ "type": "boolean" }, "source_product": { - "description": "* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me", + "description": "* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me\n* `intercom` - Intercom\n* `hubspot` - HubSpot", "enum": [ "session_replay", "llm_analytics", @@ -52,7 +52,9 @@ "retently", "appfigures", "appfollow", - "judgeme_reviews" + "judgeme_reviews", + "intercom", + "hubspot" ], "type": "string" }, diff --git a/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-partial-update.json b/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-partial-update.json index 405583031418..59ecfa197218 100644 --- a/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-partial-update.json +++ b/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-partial-update.json @@ -10,7 +10,7 @@ "type": "string" }, "source_product": { - "description": "* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me", + "description": "* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me\n* `intercom` - Intercom\n* `hubspot` - HubSpot", "enum": [ "session_replay", "llm_analytics", @@ -56,7 +56,9 @@ "retently", "appfigures", "appfollow", - "judgeme_reviews" + "judgeme_reviews", + "intercom", + "hubspot" ], "type": "string" }, diff --git a/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-update.json b/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-update.json index 43073ebd865d..9349d4912950 100644 --- a/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-update.json +++ b/services/mcp/tests/unit/__snapshots__/tool-schemas/inbox-source-configs-update.json @@ -10,7 +10,7 @@ "type": "string" }, "source_product": { - "description": "* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me", + "description": "* `session_replay` - Session replay\n* `llm_analytics` - LLM analytics\n* `github` - GitHub\n* `linear` - Linear\n* `jira` - Jira\n* `zendesk` - Zendesk\n* `conversations` - Conversations\n* `error_tracking` - Error tracking\n* `pganalyze` - pganalyze\n* `signals_scout` - Signals scout\n* `logs` - Logs\n* `health_checks` - Health checks\n* `endpoints` - Endpoints\n* `replay_vision` - Replay Vision\n* `analytics` - Product analytics\n* `freshdesk` - Freshdesk\n* `freshservice` - Freshservice\n* `front` - Front\n* `gorgias` - Gorgias\n* `kustomer` - Kustomer\n* `dixa` - Dixa\n* `plain` - Plain\n* `gitlab` - GitLab\n* `gitea` - Gitea\n* `shortcut` - Shortcut\n* `sentry` - Sentry\n* `rollbar` - Rollbar\n* `bugsnag` - Bugsnag\n* `honeybadger` - Honeybadger\n* `raygun` - Raygun\n* `snyk` - Snyk\n* `sonarqube` - SonarQube\n* `semgrep` - Semgrep\n* `rapid7_insightvm` - Rapid7 InsightVM\n* `featurebase` - Featurebase\n* `frill` - Frill\n* `aha` - Aha\n* `uservoice` - UserVoice\n* `productboard` - Productboard\n* `canny` - Canny\n* `asknicely` - AskNicely\n* `retently` - Retently\n* `appfigures` - Appfigures\n* `appfollow` - AppFollow\n* `judgeme_reviews` - Judge.me\n* `intercom` - Intercom\n* `hubspot` - HubSpot", "enum": [ "session_replay", "llm_analytics", @@ -56,7 +56,9 @@ "retently", "appfigures", "appfollow", - "judgeme_reviews" + "judgeme_reviews", + "intercom", + "hubspot" ], "type": "string" }, From 44e35e0863c0d25ef70d558214518888f587e191 Mon Sep 17 00:00:00 2001 From: Tom Owers Date: Tue, 21 Jul 2026 18:10:59 +0100 Subject: [PATCH 4/6] chore(signals): format migration 0065 Generated-By: PostHog Code Task-Id: 7f82f613-b489-484f-90c6-ac67e9b0d10b --- .../migrations/0065_alter_signalsourceconfig_source_product.py | 1 - 1 file changed, 1 deletion(-) diff --git a/products/signals/backend/migrations/0065_alter_signalsourceconfig_source_product.py b/products/signals/backend/migrations/0065_alter_signalsourceconfig_source_product.py index 930356531e43..0895f6f6352d 100644 --- a/products/signals/backend/migrations/0065_alter_signalsourceconfig_source_product.py +++ b/products/signals/backend/migrations/0065_alter_signalsourceconfig_source_product.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ("signals", "0064_alter_signalsourceconfig_source_product"), ] From b92a121070439bfd5219c6626528eb6e260acec8 Mon Sep 17 00:00:00 2001 From: "tests-posthog[bot]" <250237707+tests-posthog[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 17:18:34 +0000 Subject: [PATCH 5/6] chore: update OpenAPI generated types --- services/mcp/src/api/generated.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/services/mcp/src/api/generated.ts b/services/mcp/src/api/generated.ts index 6f7e6362c2bc..11c9c9011c2f 100644 --- a/services/mcp/src/api/generated.ts +++ b/services/mcp/src/api/generated.ts @@ -32883,6 +32883,13 @@ export namespace Schemas { created_at: string | null; } + export interface HubspotTicketSignalExtra { + hs_ticket_priority: string | null; + hs_pipeline_stage: string | null; + hs_ticket_category: string | null; + createdate: string | null; + } + export interface IdentityMatchingError { /** Human-readable explanation of why the request could not be served. */ detail: string; @@ -33671,6 +33678,13 @@ export namespace Schemas { Recording: 'recording', } as const; + export interface IntercomTicketSignalExtra { + state: string | null; + priority: string | null; + admin_assignee_id: string | null; + created_at: string | null; + } + export interface InterestingNote { text: string; line_refs: string; @@ -33772,20 +33786,6 @@ export namespace Schemas { created_at: string | null; } - export interface IntercomTicketSignalExtra { - state: string | null; - priority: string | null; - admin_assignee_id: string | null; - created_at: string | null; - } - - export interface HubspotTicketSignalExtra { - hs_ticket_priority: string | null; - hs_pipeline_stage: string | null; - hs_ticket_category: string | null; - createdate: string | null; - } - /** * One chunk in a drill-down window over a single knowledge document. * From 6bb7a0e8c96f112f3809748b16382f8fd7eded56 Mon Sep 17 00:00:00 2001 From: Tom Owers Date: Tue, 21 Jul 2026 18:28:21 +0100 Subject: [PATCH 6/6] chore(signals): fix markdown emphasis in adding-inbox-sources skill Generated-By: PostHog Code Task-Id: 7f82f613-b489-484f-90c6-ac67e9b0d10b --- .agents/skills/adding-inbox-sources/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.agents/skills/adding-inbox-sources/SKILL.md b/.agents/skills/adding-inbox-sources/SKILL.md index 42be562c7d43..590a28dd0c6e 100644 --- a/.agents/skills/adding-inbox-sources/SKILL.md +++ b/.agents/skills/adding-inbox-sources/SKILL.md @@ -63,7 +63,7 @@ OAuth source (Intercom, HubSpot, Salesforce, …) is the same one-registry-entry credential source — route its `DataSourceSetup` case to `DynamicSourceSetup`. Only two field types still lack a generic renderer (disable submit): `ssh-tunnel` and -`file-upload`. Route those to a bespoke form. Resource pickers that must run *after* OAuth +`file-upload`. Route those to a bespoke form. Resource pickers that must run _after_ OAuth (GitHub's repo picker) are handled by `oauth-account-select`; the old `GitHubSetup`/`ZendeskSetup` /`PgAnalyzeSetup` hardcoded forms remain only for historical reasons — leave them or migrate to `DynamicSourceSetup` opportunistically; don't add new ones.