Files
paseo/packages/app/src/agent-profiles/internal/profile-summary.ts
T
Mohamed Boudra 0d82eed6b5 feat(app): the client and app work on options and controls
The agent snapshot's `options` are the state and `controls` only draw them.
Running and closed agents draw from the snapshot; drafts and profile forms ask
`provider.controls.list.request` with their `providerOptions`, so aliases such
as a legacy Fast profile's `service_tier: "fast"` are resolved once, by the
daemon, and the draft keeps Fast highlighted. A draft adds only the user's
edits and each provider's last-used control values; applying a profile seeds
its non-control options (e.g. `sandbox_mode`) into the draft, and a profile
form saves them back instead of pruning to the controls it can draw.

A daemon without `server_info.features.providerOptionControls` still sends
`features` and `featureValues`. `LegacyProviderOptions` in the client package
is the one place that translates snapshots, draft listings, create and apply
requests, and daemon-config profiles, so the app never branches on it.
Persisted preferences and tab layouts rename `featureValues` through tagged
COMPAT preprocessors.

The features/controls bridge moves from the server into the protocol package
so the client can reuse it.
2026-11-05 15:30:11 +01:00

57 lines
1.9 KiB
TypeScript

import type { ProviderSnapshotEntry } from "@getpaseo/protocol/agent-types";
import type { AgentProfile } from "@getpaseo/protocol/messages";
import { formatAgentModeLabel, formatThinkingOptionLabel } from "@/agent-controls/labels";
export interface AgentProfileTag {
id: string;
label: string;
}
function findEntry(
entries: readonly ProviderSnapshotEntry[],
provider: string,
): ProviderSnapshotEntry | null {
return entries.find((entry) => entry.provider === provider) ?? null;
}
/**
* The one-line résumé of a profile in the settings list. Every label falls back
* to the stored id: a row must stay readable on a host whose provider catalog
* is offline, or that no longer ships the model the profile names.
*/
export function buildAgentProfileTags(input: {
profile: AgentProfile;
entries: readonly ProviderSnapshotEntry[] | undefined;
formatFeatureCount: (count: number) => string;
}): AgentProfileTag[] {
const entries = input.entries ?? [];
const entry = findEntry(entries, input.profile.provider);
const tags: AgentProfileTag[] = [
{ id: "provider", label: entry?.label ?? input.profile.provider },
];
const modelId = input.profile.model?.trim();
if (modelId) {
const model = entry?.models?.find((candidate) => candidate.id === modelId);
tags.push({ id: "model", label: model?.label ?? modelId });
}
const modeId = input.profile.modeId?.trim();
if (modeId) {
const mode = entry?.modes?.find((candidate) => candidate.id === modeId);
tags.push({ id: "mode", label: mode ? formatAgentModeLabel(mode) : modeId });
}
const thinkingOptionId = input.profile.thinkingOptionId?.trim();
if (thinkingOptionId) {
tags.push({ id: "thinking", label: formatThinkingOptionLabel({ id: thinkingOptionId }) });
}
const featureCount = Object.keys(input.profile.options ?? {}).length;
if (featureCount > 0) {
tags.push({ id: "features", label: input.formatFeatureCount(featureCount) });
}
return tags;
}