diff --git a/apps/web/src/components/chat/MarkdownThreadLink.browser.test.tsx b/apps/web/src/components/chat/MarkdownThreadLink.browser.test.tsx index a379a6afc580..6febe0ff8b34 100644 --- a/apps/web/src/components/chat/MarkdownThreadLink.browser.test.tsx +++ b/apps/web/src/components/chat/MarkdownThreadLink.browser.test.tsx @@ -21,6 +21,12 @@ vi.mock(import("../../state/entities"), () => ({ useProject: (ref: { projectId: string } | null) => ref?.projectId === "essay-project" ? ({ title: "Essays" } as never) : null, })); +vi.mock(import("../../state/environments"), () => ({ + useEnvironmentIdentities: () => [ + { environmentId: EnvironmentId.make("environment"), label: "Here" }, + { environmentId: EnvironmentId.make("remote"), label: "Remote" }, + ], +})); vi.mock(import("../ProjectFavicon"), () => ({ ProjectFavicon: () => ( @@ -132,3 +138,21 @@ it.each([ } }, ); + +it("names a thread on a machine this client has no connection to, without waiting on it", async () => { + const { registry } = setup(); + using _registry = { [Symbol.dispose]: () => registry.dispose() }; + await using screen = await render( + + + , + ); + await expect + .element(screen.getByText("Thread on another machine", { exact: true })) + .toBeVisible(); + await expect.element(screen.getByRole("link")).not.toBeInTheDocument(); +}); diff --git a/apps/web/src/components/chat/MarkdownThreadLink.tsx b/apps/web/src/components/chat/MarkdownThreadLink.tsx index a88535167db7..9e897f822ef3 100644 --- a/apps/web/src/components/chat/MarkdownThreadLink.tsx +++ b/apps/web/src/components/chat/MarkdownThreadLink.tsx @@ -9,6 +9,7 @@ import { import { configuredHostedAppUrl } from "../../hostedPairing"; import { useThreadRefs, useThreadShell } from "../../state/entities"; +import { useEnvironmentIdentities } from "../../state/environments"; import { orchestrationEnvironment } from "../../state/orchestration"; import { useEnvironmentQuery } from "../../state/query"; import { UnresolvedChip } from "../contextChipParts"; @@ -23,8 +24,12 @@ export function MarkdownThreadLink(props: { const candidates = threadLinkCandidates(props.link, props.environmentId); const writtenRef = candidates[0]!; const written = useThreadShell(writtenRef); + // A linked machine's threads are reachable only through its server, not from this client. + const reachable = useEnvironmentIdentities().some( + (environment) => environment.environmentId === writtenRef.environmentId, + ); const lookup = useEnvironmentQuery( - written === null + written === null && reachable ? orchestrationEnvironment.threadLinkMetadata({ environmentId: writtenRef.environmentId, input: { threadIds: candidates.map((ref) => ref.threadId) }, @@ -39,7 +44,7 @@ export function MarkdownThreadLink(props: { const metadata = lookup.data?.[ref.threadId]; return metadata ? [{ ref, metadata }] : []; })[0] ?? null); - const missing = target === null && settled; + const missing = target === null && (settled || !reachable); const guess = missing && props.link.environmentId === null; const refs = useThreadRefs(guess); const remoteRef = guess