Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions apps/web/src/pages/agents-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import type { ReactNode } from "react";

import type { AgentDefinition, AgentInstance } from "../agents-api";
import type { AgentDirectoryData } from "../agents-api";
import { PrincipalsSchema, useAPIQuery } from "../api";
import type { APIQuery } from "../api";
import { useAgentDirectory } from "../agents-api";
import { useBench } from "../bench-context";
import { countProp } from "../optional-props";
import { QueryView } from "../query-view";
import { CreateAgentDialog } from "./create-agent-dialog";
Expand Down Expand Up @@ -275,6 +275,8 @@ export function AgentsPage({
const [createOpen, setCreateOpen] = useState(false);

const isReady = directory.kind === "ready";
const canCreate =
directory.kind === "ready" && directory.data.tenantId !== "";

return (
<>
Expand All @@ -299,7 +301,7 @@ export function AgentsPage({
<Button
type="button"
onClick={() => setCreateOpen(true)}
disabled={!isReady}
disabled={!canCreate}
>
<Plus /> Create agent
</Button>
Expand Down Expand Up @@ -434,7 +436,7 @@ export function AgentsPage({
}}
</QueryView>
</PageShellBody>
{isReady && (
{canCreate && (
<CreateAgentDialog
open={createOpen}
onOpenChange={setCreateOpen}
Expand All @@ -459,16 +461,16 @@ function PageShellBody({ children }: { readonly children: ReactNode }) {
}

export function AgentsRoute() {
const principals = useAPIQuery("/api/me/principals", PrincipalsSchema);
// BenchProvider is the only source of the active tenant — never re-fetch
// /api/me/principals and take memberships[0], which ignores the switcher.
const { memberships, selectedTenantId } = useBench();
const [reloadKey, setReloadKey] = useState(0);
const membership =
principals.kind === "ready" ? principals.data.data[0] : undefined;
const directory = useAgentDirectory(membership?.tenantId, reloadKey);
const directory = useAgentDirectory(selectedTenantId ?? undefined, reloadKey);

const resolvedDirectory: APIQuery<AgentDirectoryData> =
principals.kind !== "ready"
? principals
: membership === undefined
memberships.kind !== "ready"
? memberships
: selectedTenantId === null
? {
kind: "ready",
data: {
Expand Down
19 changes: 10 additions & 9 deletions apps/web/src/pages/routines-page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// The Routines screen: named automations over workflow runs. Follows
// runs-page.tsx / library-page.tsx's shape (pure `*Page` components fed
// `APIQuery` props, a `*Route` container that resolves data) plus
// chat-page.tsx's tenant-resolution convention — the account's first
// bench membership, since this app has no bench switcher yet.
// `APIQuery` props, a `*Route` container that resolves data). The active
// bench comes from `useBench()` — the shell's one source of truth — never
// a page-local `/api/me/principals` fetch that ignores the switcher.
//
// The create flow's trigger picker is workbench-specific composition
// (`RoutineTrigger`'s exact shape, including the raw-cron escape hatch)
Expand Down Expand Up @@ -41,9 +41,11 @@ import type { BadgeTone } from "@corbits/react-ui";
import { Clock, Plus } from "lucide-react";
import { useState } from "react";

import { PrincipalsSchema, RunsSchema, useAPIQuery } from "../api";
import { RunsSchema, useAPIQuery } from "../api";
import type { APIQuery, WorkflowRun } from "../api";
import { useBench } from "../bench-context";
import { countProp } from "../optional-props";

import { QueryView } from "../query-view";
import { approximateNextRun, cadenceLabel } from "../routine-trigger";
import {
Expand Down Expand Up @@ -645,12 +647,11 @@ export function RoutinesRoute({
readonly path: string;
readonly navigate: (to: string) => void;
}) {
const principals = useAPIQuery("/api/me/principals", PrincipalsSchema);
// BenchProvider owns the active tenant. Never re-fetch principals and take
// memberships[0] — that ignores the shell's bench switcher.
const { selectedTenantId } = useBench();
const allRuns = useAPIQuery("/api/me/workflows/runs", RunsSchema);
const tenantId =
principals.kind === "ready"
? (principals.data.data[0]?.tenantId ?? null)
: null;
const tenantId = selectedTenantId;

// Bumped after a mutation (create/toggle) so the affected queries
// re-run without a full page reload — `useTenantQuery`'s effect keys
Expand Down
19 changes: 19 additions & 0 deletions apps/web/test/pages.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,25 @@ describe("live data", () => {
expect(markup).toContain("No agents yet");
});

test("agents disables Create when no bench is selected", () => {
const markup = renderToStaticMarkup(
<AgentsPage
directory={ready({
tenantId: "",
definitions: [],
instances: [],
models: [],
})}
onAgentCreated={() => undefined}
/>,
);
expect(markup).toMatch(
/disabled[^>]*>[\s\S]*Create agent|Create agent[\s\S]*disabled/,
);
// Dialog must not mount without a real tenant — no create form markup.
expect(markup).not.toContain("Define a new agent");
});

test("home counts what the hub reports", () => {
const markup = renderToStaticMarkup(
<HomePage
Expand Down
Loading