diff --git a/src/bundled/agents/AgentBrowse.tsx b/src/bundled/agents/AgentBrowse.tsx
index d1805031ff..401acc0311 100644
--- a/src/bundled/agents/AgentBrowse.tsx
+++ b/src/bundled/agents/AgentBrowse.tsx
@@ -13,7 +13,7 @@ import { AGENT_CATALOG_KIND } from "../../features/agents/catalog-envelope";
import { ModelIcon } from "../../features/agents/ModelPicker";
import type {
AgentsClient,
- RegisteredAgentType,
+ AgentTypeView,
} from "../../features/agents/service";
import {
type SharedAgent,
@@ -51,7 +51,7 @@ const pageSize = 200;
type Importing = Readonly<{
agents: AgentsClient;
/** The types an import can be, which run on this computer. */
- types: readonly RegisteredAgentType[];
+ types: readonly AgentTypeView[];
onImported(): void;
}>;
diff --git a/src/bundled/agents/AgentCreateDialog.tsx b/src/bundled/agents/AgentCreateDialog.tsx
index 4d18e60747..4a39b41f19 100644
--- a/src/bundled/agents/AgentCreateDialog.tsx
+++ b/src/bundled/agents/AgentCreateDialog.tsx
@@ -4,7 +4,7 @@ import { randomAgentAvatar } from "../../features/agents/avatar-packs";
import type {
Agent,
AgentsClient,
- RegisteredAgentType,
+ AgentTypeView,
NewAgent,
AgentCreationRequest,
AgentUpdateRequest,
@@ -37,12 +37,12 @@ const reveal = (element: HTMLElement | null) => {
};
/** Use each registered provider's icon in creation and library cards. */
-export function TypeIcon({ type }: { type: RegisteredAgentType | undefined }) {
+export function TypeIcon({ type }: { type: AgentTypeView | undefined }) {
const Icon = type?.icon ?? RobotIcon;
return ;
}
-const startingConfig = (type: RegisteredAgentType | undefined) => {
+const startingConfig = (type: AgentTypeView | undefined) => {
try {
return type?.defaults().config;
} catch {
@@ -61,7 +61,7 @@ export function AgentCreateDialog({
update,
}: {
agents: AgentsClient;
- types: readonly RegisteredAgentType[];
+ types: readonly AgentTypeView[];
community?: string | undefined;
onClose(): void;
onCreated(agent: Agent): void;
@@ -91,7 +91,7 @@ export function AgentCreateDialog({
"",
);
const type = types.find((item) => item.key === typeKey) ?? types[0];
- const migration = (item: RegisteredAgentType | undefined) => {
+ const migration = (item: AgentTypeView | undefined) => {
if (!edited || !item || item.key === edited.type) return undefined;
try {
return agents.previewRetype(edited.key, item.key);
@@ -100,7 +100,7 @@ export function AgentCreateDialog({
}
};
/** The agent's settings in `item`, with the requested instructions. */
- const seed = (item: RegisteredAgentType | undefined) => {
+ const seed = (item: AgentTypeView | undefined) => {
if (!update) return startingConfig(item);
const { agent, request } = update;
const config =
@@ -126,7 +126,7 @@ export function AgentCreateDialog({
const Create = type?.Create;
const Model = type?.CreateModel;
const close = () => setOpen(false);
- const chooseModel = (item: RegisteredAgentType, next: unknown) => {
+ const chooseModel = (item: AgentTypeView, next: unknown) => {
setTypeKey(item.key);
setConfig(next);
setDirty(true);
diff --git a/src/bundled/agents/AgentDefaults.tsx b/src/bundled/agents/AgentDefaults.tsx
index 83cd29fe9d..91fa438e4c 100644
--- a/src/bundled/agents/AgentDefaults.tsx
+++ b/src/bundled/agents/AgentDefaults.tsx
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState, useSyncExternalStore } from "react";
import type {
AgentsClient,
- RegisteredAgentType,
+ AgentTypeView,
} from "../../features/agents/service";
import {
ContributionBoundary,
@@ -68,8 +68,8 @@ function ProviderDefaults({
type,
provider,
}: {
- type: RegisteredAgentType;
- provider: NonNullable;
+ type: AgentTypeView;
+ provider: NonNullable;
}) {
const state = useSyncExternalStore(
provider.subscribe,
diff --git a/src/bundled/agents/AgentDetails.tsx b/src/bundled/agents/AgentDetails.tsx
index 5ab46692d8..6115b19ff2 100644
--- a/src/bundled/agents/AgentDetails.tsx
+++ b/src/bundled/agents/AgentDetails.tsx
@@ -19,7 +19,7 @@ import type {
Agent,
AgentsClient,
AgentConversation,
- RegisteredAgentType,
+ AgentTypeView,
} from "../../features/agents/service";
import {
AgentArchiveRetry,
@@ -71,8 +71,8 @@ export function AgentDetails({
}: {
agents: AgentsClient;
agent: Agent;
- type: RegisteredAgentType | undefined;
- types: readonly RegisteredAgentType[];
+ type: AgentTypeView | undefined;
+ types: readonly AgentTypeView[];
channels: readonly ChannelChoice[];
memories: AgentMemories | undefined;
onRemoved(): void;
diff --git a/src/bundled/agents/AgentSettingsHost.tsx b/src/bundled/agents/AgentSettingsHost.tsx
index c61f873552..660db0fd4b 100644
--- a/src/bundled/agents/AgentSettingsHost.tsx
+++ b/src/bundled/agents/AgentSettingsHost.tsx
@@ -5,7 +5,7 @@ import type {
AgentSettingsHost,
AgentsClient,
Carry,
- RegisteredAgentType,
+ AgentTypeView,
} from "../../features/agents/service";
import { ArrowRightIcon } from "../../shared/design-system/icons";
import { Button } from "../../shared/design-system/ui/Button";
@@ -28,21 +28,21 @@ const message = (error: unknown) =>
export function useAgentSettingsHost(options: {
agents: AgentsClient;
agent: Agent;
- type: RegisteredAgentType | undefined;
- types: readonly RegisteredAgentType[];
+ type: AgentTypeView | undefined;
+ types: readonly AgentTypeView[];
}): {
agent: Agent;
- type: RegisteredAgentType | undefined;
+ type: AgentTypeView | undefined;
settingsHost: AgentSettingsHost;
};
export function useAgentSettingsHost(options: {
agents: AgentsClient | undefined;
agent: Agent | undefined;
- type: RegisteredAgentType | undefined;
- types: readonly RegisteredAgentType[];
+ type: AgentTypeView | undefined;
+ types: readonly AgentTypeView[];
}): {
agent: Agent | undefined;
- type: RegisteredAgentType | undefined;
+ type: AgentTypeView | undefined;
settingsHost: AgentSettingsHost | undefined;
};
export function useAgentSettingsHost({
@@ -53,11 +53,11 @@ export function useAgentSettingsHost({
}: {
agents: AgentsClient | undefined;
agent: Agent | undefined;
- type: RegisteredAgentType | undefined;
- types: readonly RegisteredAgentType[];
+ type: AgentTypeView | undefined;
+ types: readonly AgentTypeView[];
}): {
agent: Agent | undefined;
- type: RegisteredAgentType | undefined;
+ type: AgentTypeView | undefined;
settingsHost: AgentSettingsHost | undefined;
} {
const [pendingType, setPendingType] = useState<{
@@ -126,8 +126,8 @@ function TypeField({
}: {
agents: AgentsClient;
agent: Agent;
- type: RegisteredAgentType | undefined;
- types: readonly RegisteredAgentType[];
+ type: AgentTypeView | undefined;
+ types: readonly AgentTypeView[];
dirty: boolean;
selected: string;
onSelect(type: { key: string; config: unknown } | undefined): void;
@@ -142,7 +142,7 @@ function TypeField({
const [checking, setChecking] = useState(false);
const [configuring, setConfiguring] = useState(false);
const [migration, setMigration] = useState<{
- target: RegisteredAgentType;
+ target: AgentTypeView;
preview: ReturnType;
}>();
const choose = (next: string) => {
@@ -310,8 +310,8 @@ export function MigrationPreview({
preview,
discardDraft,
}: {
- source: RegisteredAgentType | undefined;
- target: RegisteredAgentType;
+ source: AgentTypeView | undefined;
+ target: AgentTypeView;
preview: ReturnType;
discardDraft: boolean;
}) {
diff --git a/src/bundled/agents/AgentTabs.tsx b/src/bundled/agents/AgentTabs.tsx
index 7aa30617bf..517fb7d9c6 100644
--- a/src/bundled/agents/AgentTabs.tsx
+++ b/src/bundled/agents/AgentTabs.tsx
@@ -5,7 +5,7 @@ import type {
AgentConversation,
AgentSettingsHost,
AgentsClient,
- RegisteredAgentType,
+ AgentTypeView,
} from "../../features/agents/service";
import type { AgentMemories } from "../../features/agents/MemoryPanel";
import { agentSections } from "../../features/agents/tabs";
@@ -15,7 +15,7 @@ import { Tabs } from "../../shared/design-system/ui/Tabs";
export function AgentTabs(props: {
agents: AgentsClient;
agent: Agent;
- type: RegisteredAgentType | undefined;
+ type: AgentTypeView | undefined;
channels: readonly ChannelChoice[];
settingsHost: AgentSettingsHost;
memories: AgentMemories | undefined;
diff --git a/src/bundled/agents/AgentsPage.tsx b/src/bundled/agents/AgentsPage.tsx
index 2858b1813d..4e4a1d573d 100644
--- a/src/bundled/agents/AgentsPage.tsx
+++ b/src/bundled/agents/AgentsPage.tsx
@@ -41,7 +41,7 @@ import type {
Agent,
AgentsClient,
FoundAgents,
- RegisteredAgentType,
+ AgentTypeView,
Team,
TeamPort,
} from "../../features/agents/service";
@@ -99,7 +99,7 @@ const message = (error: unknown) =>
error instanceof Error ? error.message : String(error);
/** The type's one-line summary of an agent; its title when it has none. */
-function summaryOf(agent: Agent, type: RegisteredAgentType | undefined) {
+function summaryOf(agent: Agent, type: AgentTypeView | undefined) {
if (agent.place === "elsewhere") return "On another device";
if (agent.unfinished) return "Setup unfinished";
if (!type) return "Type unavailable";
@@ -614,7 +614,7 @@ type InventoryProps = {
service: AgentsClient;
agents: readonly Agent[];
selected: string | undefined;
- typeOf(agent: Agent): RegisteredAgentType | undefined;
+ typeOf(agent: Agent): AgentTypeView | undefined;
onSelect(pubkey?: string): void;
onBuild(pubkey: string): void;
newAgent: ReactNode;
@@ -862,7 +862,7 @@ function InventoryCard({
}: Omit, "agent"> & {
agent: Agent;
service: AgentsClient;
- type: RegisteredAgentType | undefined;
+ type: AgentTypeView | undefined;
onBuild(): void;
onRemoved(): void;
/** Whether it is the community's main agent and can be made it. */
@@ -1026,7 +1026,7 @@ function AgentPeek({
}: {
agents: AgentsClient;
agent: Agent;
- type: RegisteredAgentType | undefined;
+ type: AgentTypeView | undefined;
channels: readonly ChannelChoice[];
onClose(): void;
}) {
@@ -1165,7 +1165,7 @@ function ImportAgents({
onPendingChange,
}: {
agents: AgentsClient;
- types: readonly RegisteredAgentType[];
+ types: readonly AgentTypeView[];
/** The section its entry point imports. */
only: "agents" | "teams";
onImported(): void;
diff --git a/src/bundled/agents/ProviderDialog.tsx b/src/bundled/agents/ProviderDialog.tsx
index f480357e24..fe0b054f7f 100644
--- a/src/bundled/agents/ProviderDialog.tsx
+++ b/src/bundled/agents/ProviderDialog.tsx
@@ -1,6 +1,6 @@
import { ProviderDialogFooter } from "../../features/agents/ProviderDialogActions";
import { useState, useSyncExternalStore, type RefObject } from "react";
-import type { RegisteredAgentType } from "../../features/agents/service";
+import type { AgentTypeView } from "../../features/agents/service";
import {
ContributionBoundary,
contributionKey,
@@ -12,7 +12,7 @@ export function ProviderDialog({
finalFocus,
onClose,
}: {
- type: RegisteredAgentType | undefined;
+ type: AgentTypeView | undefined;
finalFocus: RefObject;
onClose(): void;
}) {
@@ -51,8 +51,8 @@ function ManagedProviderDialog({
finalFocus,
onClose,
}: {
- type: RegisteredAgentType;
- provider: NonNullable;
+ type: AgentTypeView;
+ provider: NonNullable;
finalFocus: RefObject;
onClose(): void;
}) {
diff --git a/src/features/agents/service.ts b/src/features/agents/service.ts
index 3464462147..f7380fcaaf 100644
--- a/src/features/agents/service.ts
+++ b/src/features/agents/service.ts
@@ -1193,8 +1193,37 @@ export type Agents = {
/** What the app's screens use of the agents. It leaves out what only running
* agents and plugin entries use (registration, tools, wakes, relay access,
* storage), so agents can later run in another page with screens as clients. */
+/** An agent type as screens see it: what it shows, its config functions, and
+ * the reads and calls screens make. Its runtime (`run`, `runOnce`, hosted
+ * provisioning, session listing and `expose`) stays with the service. */
+export type AgentTypeView = Contribution<
+ Pick<
+ AgentType,
+ | "id"
+ | "title"
+ | "description"
+ | "icon"
+ | "Peek"
+ | "Create"
+ | "CreateModel"
+ | "tabs"
+ | "provider"
+ | "models"
+ | "usage"
+ | keyof AgentTypeConfig
+ > &
+ Readonly<{
+ /** Present when the type is hosted. */
+ remote?: object;
+ sessionControls?: Pick<
+ NonNullable["sessionControls"]>,
+ "scope" | "subscribe"
+ >;
+ }>
+>;
export type AgentsClient = Omit<
Agents,
+ | "types"
| "addAttentionSource"
| "attentionProviders"
| "register"
@@ -1207,7 +1236,8 @@ export type AgentsClient = Omit<
| "core"
| "storage"
| "note"
->;
+> &
+ Readonly<{ types(): readonly AgentTypeView[] }>;
declare module "@deepseek-ai/cordis" {
interface Context {
agents: Agents;
diff --git a/src/features/agents/tabs.tsx b/src/features/agents/tabs.tsx
index 1d44e8d814..3b7ee099ec 100644
--- a/src/features/agents/tabs.tsx
+++ b/src/features/agents/tabs.tsx
@@ -11,7 +11,7 @@ import type {
AgentConversation,
AgentSettingsHost,
AgentsClient,
- RegisteredAgentType,
+ AgentTypeView,
} from "./service";
import { AgentSaveButton } from "./AgentSaveButton";
@@ -36,7 +36,7 @@ export function agentSections({
}: {
agents: AgentsClient;
agent: Agent;
- type: RegisteredAgentType | undefined;
+ type: AgentTypeView | undefined;
channels: readonly ChannelChoice[];
conversation?: AgentConversation | undefined;
settingsHost?: AgentSettingsHost;