improvement(slack): merge slack_v2 auth into one credential picker for accounts and custom bots#5800
Conversation
…r accounts and custom bots
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview The shared CredentialSelector gains merged-mode combobox groups ( slack_oauth is custom-bot-only: hidden Supporting updates: subblock Reviewed by Cursor Bugbot for commit 19769ef. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryThis PR combines Slack v2 authentication into one credential flow. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (4): Last reviewed commit: "improvement(slack): move provider-specif..." | Re-trigger Greptile |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 3669a0a. Configure here.
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 85f40a4. Configure here.
…atch out of the credential selector
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 19769ef. Configure here.
…al connect hook The workflow-editor credential selector (from #5800's merged picker) resolved its service-account setup surface inline and mounted the modal with NO preview gate — so a `credentialKind: 'service-account'` picker would offer a custom-bot setup even when slack_v2 is preview-gated off, the leak the integrations page and chat already guard against. Route it through the shared useServiceAccountConnectTarget hook (the same resolver chat and the integrations page use): suppress the setup action when `hidden`, and use the hook's vendor-accurate label ("Add private app token", "Set up a custom bot") as the default connect-row copy. Existing service accounts stay selectable; the per-block `credentialLabels.serviceAccountConnect` override still wins. One resolver now backs all three SA connect surfaces.
The add-block skill had no mention of credentialKind — the mechanism (#5800) that controls whether an oauth-input offers OAuth, service-account, or a merged picker — and its example was a plain oauth-input mislabeled "Service Account". Documents the three credentialKind modes, that a default oauth-input already lets users select an existing service account (they fold in), and the credentialLabels / allowServiceAccounts companions. Regenerates the .claude and .cursor projections.
* feat(copilot): add service_account_get_setup_link handler
Resolves a loosely-specified integration name to the catalog slug whose
detail page mounts ConnectServiceAccountModal, and returns
`/integrations/{slug}?connect=service-account`. The agent surfaces it via
the existing <credential type="link"> tag, so the user gets a Connect
button and supplies the key material in Sim's own form — the agent never
handles the secret.
Exact matches beat fuzzy ones so a caller naming a specific service lands
on it (gmail stays Gmail rather than collapsing to Drive), and family
names resolve through an explicit canonical map rather than to whichever
member sorts first.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Phx1MLjf8Ui3M3VpwisZds
* fix(copilot): reject service account ids in oauth_get_auth_link
The fuzzy provider match falls back to substring containment, so
`slack-custom-bot` contains `slack` and resolved to the Slack OAuth
service. The tool then returned a personal-OAuth authorize URL and
reported success — a user who asked for a shared custom bot got a
Connect button that linked their own account instead. Every service
account id degraded this way (notion-, salesforce-, zoom-, linear-),
always silently.
Guard runs before the fuzzy pass and points at
service_account_get_setup_link. Keys off the id being a service-account
id, not off the integration offering one, so `slack` and `notion` still
resolve for OAuth.
Moves the narrowing predicate out of the integration catalog module so
callers that need only the predicate skip the integrations.json load and
the OAUTH_PROVIDERS walk.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Phx1MLjf8Ui3M3VpwisZds
* feat(copilot): open the service account form in-chat instead of linking out
The tool handed back a /integrations/{slug}?connect=service-account URL,
so accepting the agent's offer navigated away from the conversation that
asked for the credential. Adds a `service_account` credential tag that
mounts ConnectServiceAccountModal over the chat; setup_url stays as the
headless/MCP fallback.
The tag carries a provider and no value — the secret is typed into Sim's
own form and never enters the transcript — so the validator gets a branch
alongside secret_input/sim_key rather than falling through to the
value-required check.
Extracts useServiceAccountConnectTarget so the chat and the integrations
page share one source of truth for the connect label and the preview
gate. Custom Slack bots ride the slack_v2 flag; without the shared gate
the chat would have surfaced a setup form the integrations page hides.
Modal is lazy-loaded off the deep path (not the barrel) to keep three
provider-specific setup forms out of the chat's initial chunk.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Phx1MLjf8Ui3M3VpwisZds
* fix(copilot): gate service account tool on the same preview flag as the UI
The in-chat connect button hides itself when the provider's gating block
is preview-hidden (a custom Slack bot needs slack_v2). The tool didn't
check this, so it returned success for slack-custom-bot even when slack_v2
was preview-gated off — the agent said "here's the setup form" and the
button silently rendered nothing, leaving the user with no form at all.
Adds getServiceAccountGatingBlockType as the single source for the
provider→gating-block mapping, consumed by both the tool (server-side, via
getBlockVisibilityForCopilot) and the connect hook (client overlay). When
the gating block is hidden the tool now fails with a fall-back-to-OAuth
message instead of promising an invisible form.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Phx1MLjf8Ui3M3VpwisZds
* feat(copilot): make the tool own service-account discovery
Removes the VFS auth-metadata exposure and returns connectNoun from the
service_account_get_setup_link result instead. The VFS aggregate was a
second, viewer-independent source of truth that couldn't agree with the
per-viewer preview gate (it always hid slack-custom-bot, even for viewers
with slack_v2 revealed, while the tool accepts it for them). The tool now
resolves the provider, applies the per-viewer gate, and returns either the
in-chat button + connectNoun or a fall-back-to-oauth error — one source of
truth. connectNoun stays DRY via getServiceAccountConnectNoun, shared with
the connect-button label.
* feat(copilot): make service-account setup a direct tag, no tool
The agent now emits the service_account credential tag directly from
intent — like secret_input — instead of round-tripping through a tool.
Removes service_account_get_setup_link (handler, registration, display
title, Go tool def) and restores auth.serviceAccount as the VFS discovery
field so the agent knows which providers support a service account.
The link-vs-tag distinction was the wrong axis: only oauth needs a tool,
because its button carries a minted URL that can't be reconstructed. The
service_account tag carries just a provider name the agent already knows,
so it needs no tool — discovery lives in the VFS (auth.serviceAccount,
GA-only, so slack's preview-gated custom bot is never proactively
offered), and the per-viewer gate lives in the renderer, which renders
nothing when a provider isn't available for the viewer (no OAuth
fallback — a shared credential and a personal one are different intents).
oauth_get_auth_link's service-account-id guard now points at the tag.
* feat(copilot): support service-account reconnect from chat
Reconnect had no service-account path — it required oauth_get_auth_link
and a link tag for every repair, so rotating a workspace service account
either errored or pushed the user through OAuth.
The service_account tag now takes an optional credentialId; when present
the renderer opens the modal in reconnect mode (rotates the secret on that
credential in place, id preserved) and labels the button "Reconnect X".
credentials.json now carries each credential's type (oauth vs
service_account) so the agent can branch: service accounts reconnect via
the tag + credentialId, oauth via oauth_get_auth_link as before.
* fix(copilot): coherent service-account rejection in oauth_get_auth_link
Review round on #5786:
- The service-account-id guard threw into the generic catch, which
overwrote its recovery hint with a "connect manually" message and a
workspace oauth_url — contradictory signals. It now returns a coherent
failure directly, before the try, with no oauth_url.
- Normalize spaces/underscores before the check so a readable form
("slack custom bot", "google service account") is caught too, not
passed to the fuzzy OAuth resolver.
- Remove listServiceAccountIntegrationNames — dead after the tool was
removed (its only caller was the deleted handler's error copy).
* fix(copilot): service-account discovery must un-gate after the block GAs
Review round on #5786: describeServiceAccountForOAuthProvider used
`getBlock(...)?.preview ?? true`, which treats a GA'd gating block — one
that dropped its `preview` flag, exactly slack_v2's documented migration —
as still gated, so the custom bot would stay omitted from VFS discovery
forever after GA even though the UI shows it. Reuse the canonical
isHiddenUnder(null, block) predicate instead, so a non-preview block is
visible. Adds service-account-gate.test.ts covering preview → omit, GA →
include, and missing → fail-closed with a mocked getBlock (the block
registry is globally stubbed, so the real slack_v2 preview flag isn't
observable through serializeIntegrationSchema).
* fix(copilot): align SA resolver normalization and reject blank credentialId
Review round on #5786:
- resolveServiceAccountIntegration only lowercased/trimmed, but the
oauth_get_auth_link guard normalizes spaces/underscores to hyphens
before rejecting a service-account id and steering the agent to a
service_account tag. The chat renderer then couldn't resolve those same
readable forms ("slack custom bot", "notion_service_account") and
rendered nothing. Apply the same normalization to the id lookups (raw
query still used for display-name matches).
- service_account tag validation rejected a blank provider but allowed a
whitespace-only credentialId, which is truthy — the renderer took the
reconnect path and tried to rotate a non-existent credential. Reject a
blank/whitespace credentialId.
* refactor(credentials): route the editor SA picker through the canonical connect hook
The workflow-editor credential selector (from #5800's merged picker) resolved
its service-account setup surface inline and mounted the modal with NO preview
gate — so a `credentialKind: 'service-account'` picker would offer a custom-bot
setup even when slack_v2 is preview-gated off, the leak the integrations page
and chat already guard against.
Route it through the shared useServiceAccountConnectTarget hook (the same
resolver chat and the integrations page use): suppress the setup action when
`hidden`, and use the hook's vendor-accurate label ("Add private app token",
"Set up a custom bot") as the default connect-row copy. Existing service
accounts stay selectable; the per-block `credentialLabels.serviceAccountConnect`
override still wins. One resolver now backs all three SA connect surfaces.
* docs(add-block): document credentialKind and the service-account picker
The add-block skill had no mention of credentialKind — the mechanism (#5800)
that controls whether an oauth-input offers OAuth, service-account, or a merged
picker — and its example was a plain oauth-input mislabeled "Service Account".
Documents the three credentialKind modes, that a default oauth-input already
lets users select an existing service account (they fold in), and the
credentialLabels / allowServiceAccounts companions. Regenerates the .claude and
.cursor projections.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
authMethoddropdown is gone — one credential picker (credentialKind: 'any') lists Slack OAuth accounts and custom bots together in a grouped dropdown, with a connect action per group; the credential's kind is resolved server-side fromcredential.typeappTypefield and the sim-mode OAuth picker, event dropdown is the full static catalog, deploy routing always usesroutingKey = botCredentialId(native Sim-app path removed for now — ingest route stays for a future ship)dependsOn: { all: ['authMethod'], ... }gates to['credential']and dropped the dead sim-event constantscustomBotCredential/manualBotCredentialtoCREDENTIAL_SUBBLOCK_IDSso credential deletion cleans trigger referencesType of Change
Testing
Tested manually on a live canvas (merged picker groups + both connect actions, trigger shows no App Type, stale
authMethodignored on load).lint,check:api-validation:strict, typecheck, and slack/deploy/serializer test suites pass; added deploy tests for the bot-credential canonical collapse (branch previously untested).Checklist
🤖 Generated with Claude Code