Files
paseo/packages/app/src/hooks/draft-provider-options.ts
T
Mohamed Boudra 74c8f2bd42 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-04 10:05:16 +01:00

30 lines
1.3 KiB
TypeScript

import type { AgentOptionControl, ProviderOptions } from "@getpaseo/protocol/agent-types";
/**
* What a draft agent shows and runs with, from its layers:
* - `seed`: options the draft started from (a profile, a forked agent). The daemon resolves them
* into `resolved`, so a legacy Fast profile is drawn as the model's Fast tier.
* - `persisted`: last-used control values, applied to keys the seed leaves alone and that the
* provider still draws a control for.
* - `edits`: toggles made in this draft.
*/
export function composeDraftOptions(input: {
seed: ProviderOptions;
persisted: ProviderOptions;
edits: ProviderOptions;
controls: readonly AgentOptionControl[] | undefined;
resolved: ProviderOptions | undefined;
}): { shown: ProviderOptions; own: ProviderOptions | undefined } {
const { seed, persisted, edits, controls, resolved } = input;
const controlKeys = new Set((controls ?? []).map((control) => control.key));
const remembered = Object.fromEntries(
Object.entries(persisted).filter(([key]) => controlKeys.has(key) && !(key in seed)),
);
const own = { ...seed, ...remembered, ...edits };
return {
shown: { ...resolved, ...remembered, ...edits },
// The daemon resolves defaults again on create, so only what differs from them travels.
own: Object.keys(own).length > 0 ? own : undefined,
};
}