diff --git a/packages/views/settings/components/lark-tab.test.tsx b/packages/views/settings/components/lark-tab.test.tsx
index 5d01a8d96..4c7840cac 100644
--- a/packages/views/settings/components/lark-tab.test.tsx
+++ b/packages/views/settings/components/lark-tab.test.tsx
@@ -187,28 +187,24 @@ function resetFixtures() {
describe("LarkAgentBindButton (CTA gate)", () => {
beforeEach(resetFixtures);
- it("renders both Feishu and Lark bind CTAs when the viewer is a workspace owner and install is supported", () => {
- // The CTA was split into two explicit entry points — one per cloud
- // — so the begin POST hits the right accounts host up front (no
- // tenant-brand mid-poll auto-switch from a Feishu-first start) and
- // the QR / dialog copy reflects the cloud the user picked. Both
- // buttons must mount side by side for owners/admins; either one
- // alone would re-introduce the "Lark user has to scan a Feishu QR"
- // confusion this split is meant to remove (MUL-3083 follow-up).
+ it("shows the Feishu bind CTA but hides the Lark CTA for an owner (MUL-3083)", () => {
+ // Mainland Feishu binding stays available; the Lark (international)
+ // entry is temporarily hidden via LARK_INTL_CONNECT_ENABLED while its
+ // install→inbound pipeline is stabilized (MUL-3083).
render(, {
wrapper: I18nWrapper,
});
expect(screen.getByRole("button", { name: /Bind to Feishu/i })).toBeTruthy();
- expect(screen.getByRole("button", { name: /Bind to Lark/i })).toBeTruthy();
+ expect(screen.queryByRole("button", { name: /Bind to Lark/i })).toBeNull();
});
- it("renders both bind CTAs when the viewer is a workspace admin", () => {
+ it("shows the Feishu bind CTA but hides the Lark CTA for an admin (MUL-3083)", () => {
membersRef.current = [{ user_id: "user-1", role: "admin" }];
render(, {
wrapper: I18nWrapper,
});
expect(screen.getByRole("button", { name: /Bind to Feishu/i })).toBeTruthy();
- expect(screen.getByRole("button", { name: /Bind to Lark/i })).toBeTruthy();
+ expect(screen.queryByRole("button", { name: /Bind to Lark/i })).toBeNull();
});
it("hides both bind CTAs for a non-admin agent owner (matches backend admin gate)", () => {
@@ -258,28 +254,12 @@ describe("LarkAgentBindButton (CTA gate)", () => {
);
});
- it("clicking Bind to Lark begins an install with region='lark'", async () => {
- const user = userEvent.setup();
- mockBeginInstall.mockResolvedValue({
- session_id: "sess-lark",
- qr_code_url: "https://accounts.larksuite.com/oauth/v1/device?u=lark",
- expires_in_seconds: 300,
- poll_interval_seconds: 2,
- });
- mockGetStatus.mockResolvedValue({ status: "pending" });
- render(, {
- wrapper: I18nWrapper,
- });
- await user.click(screen.getByRole("button", { name: /Bind to Lark/i }));
- await waitFor(() => {
- expect(mockBeginInstall).toHaveBeenCalledTimes(1);
- });
- expect(mockBeginInstall).toHaveBeenCalledWith(
- "workspace-1",
- "agent-1",
- "lark",
- );
- });
+ // NOTE (MUL-3083): the "clicking Bind to Lark begins an install with
+ // region='lark'" test was removed alongside the temporarily-hidden Lark
+ // (international) CTA — there is no Lark button to click while
+ // LARK_INTL_CONNECT_ENABLED is false. The Feishu region routing is still
+ // pinned by the "clicking Bind to Feishu …" test above; restore the Lark
+ // case when the entry is re-enabled.
it("swaps the bind CTAs for a 'Connected + Manage in Lark' badge when this agent already has an active installation", () => {
// Anti-zombie guard: re-scanning the same agent upserts the row
@@ -352,7 +332,7 @@ describe("LarkAgentBindButton (CTA gate)", () => {
expect(link.href).toBe("https://open.larksuite.com/app/cli_lark_app");
});
- it("still shows both bind CTAs when an installation exists for a DIFFERENT agent (per-agent scoping)", () => {
+ it("shows the Feishu CTA (Lark hidden) for an agent without its own installation, per-agent scoping (MUL-3083)", () => {
installationsRef.current.installations = [
{
id: "inst-other",
@@ -371,7 +351,7 @@ describe("LarkAgentBindButton (CTA gate)", () => {
wrapper: I18nWrapper,
});
expect(screen.getByRole("button", { name: /Bind to Feishu/i })).toBeTruthy();
- expect(screen.getByRole("button", { name: /Bind to Lark/i })).toBeTruthy();
+ expect(screen.queryByRole("button", { name: /Bind to Lark/i })).toBeNull();
});
it("keeps the Connected + Manage badge for an already-installed agent even when new installs are unavailable (install_supported=false)", () => {
@@ -413,7 +393,7 @@ describe("LarkAgentBindButton (CTA gate)", () => {
).toBeTruthy();
});
- it("still shows both bind CTAs when this agent's only installation is revoked (treat as not-installed for re-bind)", () => {
+ it("shows the Feishu CTA (Lark hidden) when this agent's only installation is revoked (MUL-3083)", () => {
installationsRef.current.installations = [
{
id: "inst-revoked",
@@ -432,7 +412,7 @@ describe("LarkAgentBindButton (CTA gate)", () => {
wrapper: I18nWrapper,
});
expect(screen.getByRole("button", { name: /Bind to Feishu/i })).toBeTruthy();
- expect(screen.getByRole("button", { name: /Bind to Lark/i })).toBeTruthy();
+ expect(screen.queryByRole("button", { name: /Bind to Lark/i })).toBeNull();
});
});
@@ -599,7 +579,9 @@ describe("LarkInstallDialog (polling terminal errors)", () => {
render(, {
wrapper: I18nWrapper,
});
- await user.click(screen.getByRole("button", { name: /Bind to Lark/i }));
+ // The Lark CTA is hidden (MUL-3083); open the dialog via the Feishu CTA
+ // — the polling-error behavior under test is region-agnostic.
+ await user.click(screen.getByRole("button", { name: /Bind to Feishu/i }));
// Let the begin-session promise resolve and the QR render.
await waitFor(() => {
expect(screen.getByTestId("qr-code")).toBeTruthy();
@@ -673,7 +655,9 @@ describe("LarkInstallDialog (polling terminal errors)", () => {
render(, {
wrapper: StrictModeWrapper,
});
- await user.click(screen.getByRole("button", { name: /Bind to Lark/i }));
+ // The Lark CTA is hidden (MUL-3083); the StrictMode regression is about
+ // the dialog mount cycle, so open it via the Feishu CTA.
+ await user.click(screen.getByRole("button", { name: /Bind to Feishu/i }));
// The QR must appear even though the dialog mounted, unmounted, and
// mounted again under StrictMode. The previous bug left the body
diff --git a/packages/views/settings/components/lark-tab.tsx b/packages/views/settings/components/lark-tab.tsx
index ed0bf3bb5..f38edb245 100644
--- a/packages/views/settings/components/lark-tab.tsx
+++ b/packages/views/settings/components/lark-tab.tsx
@@ -43,6 +43,15 @@ import type { LarkInstallation, LarkInstallStatusResponse } from "@multica/core/
import { ActorAvatar } from "../../common/actor-avatar";
import { useT } from "../../i18n";
+// MUL-3083: the Lark (international, open.larksuite.com) "connect a Bot"
+// entry is temporarily hidden while its install → inbound pipeline is
+// stabilized — some Lark installs complete on Lark's side but never land a
+// `lark_installation` row, so the Bot silently can't receive messages.
+// Mainland Feishu is unaffected and keeps its bind entry. Existing
+// installations (either cloud) stay fully manageable. Flip this back to
+// `true` to restore the "Bind to Lark" CTA; nothing else needs to change.
+const LARK_INTL_CONNECT_ENABLED: boolean = false;
+
// LarkTab is the workspace settings panel for Lark Bot installations.
// Listing is member-visible; the disconnect action is admin-only (the
// backend enforces it; the UI hides the button for non-admins to match).
@@ -389,21 +398,26 @@ export function LarkAgentBindButton({
{t(($) => $.lark.bind_button_feishu)}
-
+ {/* MUL-3083: Lark (international) bind entry is temporarily hidden —
+ see LARK_INTL_CONNECT_ENABLED. Mainland Feishu (above) is
+ unaffected. */}
+ {LARK_INTL_CONNECT_ENABLED && (
+
+ )}
{dialogRegion && (