diff --git a/packages/agent/src/adapters/codex-app-server/local-tools-mcp-server.ts b/packages/agent/src/adapters/codex-app-server/local-tools-mcp-server.ts index 7c65cfe560..92faafd74b 100644 --- a/packages/agent/src/adapters/codex-app-server/local-tools-mcp-server.ts +++ b/packages/agent/src/adapters/codex-app-server/local-tools-mcp-server.ts @@ -34,6 +34,7 @@ if (!ctxEnv) { let parsed: { cwd: string; taskId?: string; + taskRunId?: string; token?: string; baseBranch?: string; }; @@ -51,6 +52,7 @@ const ctx: LocalToolCtx = { cwd: parsed.cwd, token: parsed.token ?? readGithubTokenFromEnv(), taskId: parsed.taskId, + taskRunId: parsed.taskRunId, baseBranch: parsed.baseBranch, }; diff --git a/packages/ui/src/features/loops/components/LoopsListView.test.tsx b/packages/ui/src/features/loops/components/LoopsListView.test.tsx index f0157a4f04..62af735344 100644 --- a/packages/ui/src/features/loops/components/LoopsListView.test.tsx +++ b/packages/ui/src/features/loops/components/LoopsListView.test.tsx @@ -18,11 +18,13 @@ vi.mock("./LoopRow", () => ({ function loop( id: string, visibility: LoopSchemas.LoopVisibilityEnum, + createdById = 1, ): LoopSchemas.Loop { return { id, name: `${visibility} loop`, visibility, + created_by_id: createdById, } as LoopSchemas.Loop; } @@ -34,22 +36,45 @@ function controlledPanel(tab: HTMLElement): HTMLElement { } describe("LoopsListViewPresentation", () => { - it("shows only the selected ownership tab", async () => { + it("does not render ownership groups while identity is loading", () => { render( , ); - const personalTab = screen.getByRole("tab", { name: "My loops (1)" }); + expect(screen.queryByRole("tab")).not.toBeInTheDocument(); + }); + + it("groups loops by ownership rather than visibility", async () => { + render( + + + , + ); + + const personalTab = screen.getByRole("tab", { name: "My loops (2)" }); expect( within(controlledPanel(personalTab)).getByText("personal loop"), ).toBeVisible(); - expect(screen.queryByText("team loop")).not.toBeInTheDocument(); + expect( + within(controlledPanel(personalTab)).getByText("team loop"), + ).toBeVisible(); const teamTab = screen.getByRole("tab", { name: "Team loops (1)" }); await userEvent.click(teamTab); diff --git a/packages/ui/src/features/loops/components/LoopsListView.tsx b/packages/ui/src/features/loops/components/LoopsListView.tsx index 3dbbe2bfd5..8798e4a908 100644 --- a/packages/ui/src/features/loops/components/LoopsListView.tsx +++ b/packages/ui/src/features/loops/components/LoopsListView.tsx @@ -8,6 +8,8 @@ import type { LoopSchemas } from "@posthog/api-client/loops"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@posthog/quill"; import { ANALYTICS_EVENTS } from "@posthog/shared/analytics-events"; import type { UserBasic } from "@posthog/shared/domain-types"; +import { useOptionalAuthenticatedClient } from "@posthog/ui/features/auth/authClient"; +import { useCurrentUser } from "@posthog/ui/features/auth/useCurrentUser"; import { useOrgMembers } from "@posthog/ui/features/canvas/hooks/useOrgMembers"; import { StopCloudRunDialog } from "@posthog/ui/features/sessions/components/StopCloudRunDialog"; import { useSetHeaderContent } from "@posthog/ui/hooks/useSetHeaderContent"; @@ -67,9 +69,22 @@ function startLoopFromTemplate(template: LoopTemplate): void { export function LoopsListView() { const { data: loops, isLoading, isError, error } = useLoops(); + const authenticatedClient = useOptionalAuthenticatedClient(); + const { + data: currentUser, + isLoading: currentUserLoading, + isError: currentUserError, + error: currentUserQueryError, + } = useCurrentUser({ client: authenticatedClient }); const limits = useLoopLimits(); const limitReason = limits?.atLimit === true ? loopLimitReason(limits.max) : null; + let listError: unknown = null; + if (isError) { + listError = error; + } else if (currentUserError) { + listError = currentUserQueryError; + } const headerContent = useMemo( () => ( @@ -134,8 +149,9 @@ export function LoopsListView() { return ( loop.visibility === "personal"); - const teamLoops = loops.filter((loop) => loop.visibility === "team"); + const personalLoops = loops.filter( + (loop) => + loop.visibility === "personal" || + (currentUserId !== null && loop.created_by_id === currentUserId), + ); + const teamLoops = loops.filter( + (loop) => + loop.visibility === "team" && + (currentUserId === null || loop.created_by_id !== currentUserId), + ); return ( diff --git a/packages/ui/src/shell/HedgehogMode.test.tsx b/packages/ui/src/shell/HedgehogMode.test.tsx index 1bcfada5f6..6efe6079da 100644 --- a/packages/ui/src/shell/HedgehogMode.test.tsx +++ b/packages/ui/src/shell/HedgehogMode.test.tsx @@ -100,6 +100,8 @@ describe("HedgehogMode", () => { expect(mocks.mount).toHaveBeenCalledTimes(1); expect(overlay.querySelector("canvas")).not.toBeNull(); expect(overlay.style.visibility).toBe("visible"); + expect(overlay).toHaveClass("absolute"); + expect(overlay).not.toHaveClass("fixed"); }); it("destroys the game and reports when the context loss callback fires", async () => { diff --git a/packages/ui/src/shell/HedgehogMode.tsx b/packages/ui/src/shell/HedgehogMode.tsx index 179825964c..e4c9d44b48 100644 --- a/packages/ui/src/shell/HedgehogMode.tsx +++ b/packages/ui/src/shell/HedgehogMode.tsx @@ -134,7 +134,7 @@ export function HedgehogMode() { zIndex: 999998, visibility: hedgehogMode && !gameDead ? "visible" : "hidden", }} - className="pointer-events-none fixed inset-0" + className="pointer-events-none absolute inset-0" /> ); }