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
7 changes: 4 additions & 3 deletions apps/web/src/bench/tools-tab.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Badge, Button } from "@corbits/react-ui";
import { Button } from "@corbits/react-ui";

import { QueryView } from "@/lib/api-query";
import { Plugs } from "@/lib/icons";
import { Link } from "../navigation";
import { useMcpServers } from "../tools/mcp-servers-query";
import "../tools/tools-page.css";

/** Tools the workspace catalog makes available here; "Connected" once an
* agent in this workbench binds the server. */
Expand Down Expand Up @@ -36,9 +37,9 @@ export function ToolsTab({ workbenchTenantId }: { readonly workbenchTenantId: st
<span className="workbench-info-cell-primary">{server.name}</span>
<span className="workbench-info-cell-context">{server.url}</span>
</span>
<Badge tone={server.agentNames.length > 0 ? "success" : "neutral"}>
<span className={server.agentNames.length > 0 ? "tool-live" : "tool-kind"}>
{server.agentNames.length > 0 ? "Connected" : "Available"}
</Badge>
</span>
<Link to={manage} className="bench-tab-link">
Manage
</Link>
Expand Down
11 changes: 2 additions & 9 deletions apps/web/src/pages/worker-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type { GrantEffect } from "@intx/types";

import { WorkbenchAvatar } from "@/chat/avatar";
import { isMyraAgent, listChatAgents, type ChatAgent } from "@/chat/threads-api";
import { listWorkbenches } from "@/chat/workbench-tenants";
import { ChatCircle } from "@/lib/icons";
import { QueryView, describeApiError } from "@/lib/api-query";
import { listTopLevelRuns } from "../agents-api";
Expand All @@ -29,7 +28,7 @@ import { useTenantQuery } from "../routines-api";
import { StageTopBar } from "../shell/stage-top-bar";
import { WORKERS_PATH_PREFIX } from "../path-ids";
import { workbenchPath } from "../workbench-path";
import { StatusPill, workerStatus } from "./workers-page";
import { StatusPill, useWorkbenchList, workerStatus } from "./workers-page";

function sourceKey(tenantId: string, agentId: string) {
return [...tenantKeys.agents(tenantId), "source", agentId] as const;
Expand Down Expand Up @@ -308,13 +307,7 @@ export function WorkerRoute({ agentId }: { readonly agentId: string }) {
selectedTenantId !== null,
() => listChatAgents(selectedTenantId as string),
);
// The workspace is not a bench; the worker's workbench is one of its child
// tenants, the same rows the sidebar lists.
const benches = useQuery({
queryKey: tenantKeys.workbenches(selectedTenantId ?? "none"),
enabled: selectedTenantId !== null,
queryFn: () => listWorkbenches(selectedTenantId as string),
});
const benches = useWorkbenchList(selectedTenantId);
const bench = benches.data?.[0];

return (
Expand Down
30 changes: 15 additions & 15 deletions apps/web/src/pages/workers-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import { useState } from "react";
import { Button, RichEmptyState, toast } from "@corbits/react-ui";
import { Plus, Robot } from "@/lib/icons";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { reportError } from "@corbits/error-sink";

import { QueryView } from "@/lib/api-query";
import { tenantKeys } from "../query-client";
import { isAgentNotRunning, listChatAgents, type ChatAgent } from "@/chat/threads-api";
import { WorkbenchAvatar } from "@/chat/avatar";
import { listWorkbenches } from "@/chat/workbench-tenants";
import { describeRestartFailure, redeployWorkbenchAgent } from "../workbench-create";
import { useBench } from "../bench-context";
import { Link } from "../navigation";
Expand All @@ -35,6 +36,16 @@ export function workerStatus(agent: Pick<ChatAgent, "liveAddress" | "latestStatu
: { tone: "working", text: "Starting up" };
}

/** The workspace is not a bench; a worker's workbench is one of its child
* tenants, the same rows the sidebar lists. */
export function useWorkbenchList(tenantId: string | null) {
return useQuery({
queryKey: tenantKeys.workbenches(tenantId ?? "none"),
enabled: tenantId !== null,
queryFn: () => listWorkbenches(tenantId as string),
});
}

export function workerPath(agentId: string): string {
return `${WORKERS_PATH_PREFIX}/${encodeURIComponent(agentId)}`;
}
Expand Down Expand Up @@ -69,13 +80,12 @@ const ROW_GRID =

export function WorkersRosterList({
tenantId,
benchName,
agents,
}: {
readonly tenantId: string;
readonly benchName: string;
readonly agents: readonly ChatAgent[];
}) {
const benchName = useWorkbenchList(tenantId).data?.[0]?.title ?? "";
const [filter, setFilter] = useState<WorkerTone | "all">("all");
const [query, setQuery] = useState("");
const queryClient = useQueryClient();
Expand Down Expand Up @@ -191,7 +201,7 @@ export function WorkersRosterList({
}

export function WorkersRoute() {
const { selectedTenantId, benchMemberships } = useBench();
const { selectedTenantId } = useBench();
const agentsQuery = useTenantQuery(
tenantKeys.agents(selectedTenantId ?? "none"),
selectedTenantId !== null,
Expand All @@ -200,10 +210,6 @@ export function WorkersRoute() {
// seen without a reload.
(agents) => ((agents?.some((agent) => agent.liveAddress === null) ?? false) ? 3000 : false),
);
const benchName =
benchMemberships.find((member) => member.tenantId === selectedTenantId)?.tenantName ??
"This workbench";

const queryClient = useQueryClient();
const [createOpen, setCreateOpen] = useState(false);

Expand Down Expand Up @@ -231,13 +237,7 @@ export function WorkersRoute() {
) : (
<>
<QueryView query={agentsQuery} label="your workers" skeleton="rows">
{(agents) => (
<WorkersRosterList
tenantId={selectedTenantId}
benchName={benchName}
agents={agents}
/>
)}
{(agents) => <WorkersRosterList tenantId={selectedTenantId} agents={agents} />}
</QueryView>
<CreateAgentPanel
open={createOpen}
Expand Down
77 changes: 22 additions & 55 deletions apps/web/src/settings/models-section.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
// No write path here; changing an offering's priority belongs to the
// catalog-management routes this section deliberately doesn't touch.

import {
Badge,
EmptyState,
SettingsPanel,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@corbits/react-ui";
import { Badge, EmptyState } from "@corbits/react-ui";
import { useQuery } from "@tanstack/react-query";

import { QueryView, toAPIQuery } from "@/lib/api-query";
import { listProviders, type Provider } from "./credentials-api";
import { getResolvedCatalog, type ModelInfo } from "./inference";
import { SettingsGroup, SettingsRow } from "./rows";
import { SETTINGS_STRINGS } from "./strings";

type ModelsData = {
Expand Down Expand Up @@ -52,15 +43,15 @@ export function ModelsSection({ tenantId }: { readonly tenantId: string | null }
return (
<QueryView query={query} label={SETTINGS_STRINGS.modelsLoadError}>
{({ providers, models }) => (
<SettingsPanel
<SettingsGroup
title={SETTINGS_STRINGS.modelsSectionTitle}
description={SETTINGS_STRINGS.modelsSectionDescription}
>
<h3 className="settings-subhead">{SETTINGS_STRINGS.modelsProvidersHeading}</h3>
<ProvidersTable providers={providers} />
<h3 className="settings-subhead">{SETTINGS_STRINGS.modelsCatalogHeading}</h3>
<ModelsTable models={models} />
</SettingsPanel>
</SettingsGroup>
)}
</QueryView>
);
Expand All @@ -76,24 +67,11 @@ function ProvidersTable({ providers }: { readonly providers: readonly Provider[]
);
}
return (
<div className="settings-table-scroll">
<Table>
<TableHeader>
<TableRow>
<TableHead>{SETTINGS_STRINGS.modelsProviderColumn}</TableHead>
<TableHead>Plugin</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{providers.map((provider) => (
<TableRow key={provider.id}>
<TableCell>{provider.name}</TableCell>
<TableCell>{provider.plugin}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
<>
{providers.map((provider) => (
<SettingsRow key={provider.id} title={provider.name} meta={provider.plugin} />
))}
</>
);
}

Expand All @@ -107,30 +85,19 @@ function ModelsTable({ models }: { readonly models: readonly ModelInfo[] }) {
);
}
return (
<div className="settings-table-scroll">
<Table>
<TableHeader>
<TableRow>
<TableHead>{SETTINGS_STRINGS.modelsModelColumn}</TableHead>
<TableHead>{SETTINGS_STRINGS.modelsOfferingsColumn}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{models.map((model) => (
<TableRow key={model.id}>
<TableCell>{model.displayName ?? model.canonicalName}</TableCell>
<TableCell>
{model.offerings.map((offering, index) => (
<Badge key={offering.offeringId} tone={index === 0 ? "success" : "neutral"}>
{offering.providerName}
{index === 0 ? ` · ${SETTINGS_STRINGS.modelsDefaultBadge}` : ""}
</Badge>
))}
</TableCell>
</TableRow>
<>
{models.map((model) => (
<SettingsRow
key={model.id}
title={model.displayName ?? model.canonicalName}
meta={model.offerings.map((offering, index) => (
<Badge key={offering.offeringId} tone={index === 0 ? "success" : "neutral"}>
{offering.providerName}
{index === 0 ? ` · ${SETTINGS_STRINGS.modelsDefaultBadge}` : ""}
</Badge>
))}
</TableBody>
</Table>
</div>
/>
))}
</>
);
}
7 changes: 6 additions & 1 deletion apps/web/src/settings/people-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
} from "./tenancy-api";
import { ConfirmButton } from "../components/confirm-button";
import { SettingsGroup, SettingsRow } from "./rows";
import "../tools/tools-page.css";

const STATUS_TONE: Record<Principal["status"], "success" | "info" | "neutral"> = {
active: "success",
Expand Down Expand Up @@ -363,7 +364,11 @@ export function PeopleTable({
<>
{person.email !== undefined ? <span>{person.email}</span> : null}
<span>{PRINCIPAL_KIND_LABEL[person.kind]}</span>
<Badge tone={STATUS_TONE[person.status]}>{person.status}</Badge>
{person.status === "active" ? (
<span className="tool-live">{person.status}</span>
) : (
<Badge tone={STATUS_TONE[person.status]}>{person.status}</Badge>
)}
{selectableRoles.length === 2
? null
: person.roles.length === 0
Expand Down
4 changes: 4 additions & 0 deletions apps/web/src/shell/sidebar-footer.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* Canvas surface is near-white; the Tools button sits on the sunk token. */
[data-theme="canvas"] .sb-tools {
background: var(--surface-sunk);
}
1 change: 1 addition & 0 deletions apps/web/src/shell/sidebar-footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { Moon, Palette, Plugs, SignOut, SlidersHorizontal, Sun, type Icon } from "@/lib/icons";
import { useEffect, useRef, useState, type KeyboardEvent } from "react";

import "./sidebar-footer.css";
import { IdentityAvatar } from "../chat/avatar";
import { useSessionUser, useSignOut } from "../navigation";
import { matchesRoute, SETTINGS_PATH } from "../routes";
Expand Down
Loading