mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-04 15:34:39 +00:00
feat(plugin): provider plugins declare option controls and receive option changes
A provider plugin reports `controls` and the `providerOptions` it runs in `session.config`, and receives running-session changes as `session.configure` `changes.providerOptions` (null returns a key to its default), under the same apply-or-throw contract as built-in providers. A control may declare a `defaultValue`; a value a plugin reports at that default for a key nobody set stays unset instead of being pinned to the agent. A plugin that still reports `settings` keeps working: the daemon reads each setting as a control plus an option and sends changes in both shapes (COMPAT(plugin-provider-settings)). The app resolves a control's icon as a Lucide name when it is not one of the built-in kebab-case names, so a plugin's icon shows live and in drafts. Public docs and the provider-direct example use only `providerOptions` and controls; the migration doc gains the rows.
This commit is contained in:
@@ -116,7 +116,9 @@ export interface ProviderSessionConfig {
|
||||
model?: string;
|
||||
mode?: string;
|
||||
thinkingOption?: string;
|
||||
/** @deprecated The same values as `providerOptions`. */
|
||||
settings: Readonly<Record<string, JsonValue>>;
|
||||
/** The agent's options, the provider defaults from `config.json` included. */
|
||||
providerOptions?: Readonly<Record<string, unknown>>;
|
||||
title?: string;
|
||||
persist: boolean;
|
||||
@@ -126,6 +128,13 @@ export interface ProviderConfigChanges {
|
||||
model?: string | null;
|
||||
mode?: string | null;
|
||||
thinkingOption?: string | null;
|
||||
/**
|
||||
* The option keys to change on the running session. A key it cannot apply live is not in
|
||||
* `controls`, and Paseo does not send it. `null` returns the key to the provider's own default.
|
||||
* Apply the change or fail the request; the committed values come back in `session.config`.
|
||||
*/
|
||||
providerOptions?: Readonly<Record<string, JsonValue>>;
|
||||
/** @deprecated Sent next to `providerOptions` to a provider that reports `settings`. */
|
||||
settings?: Readonly<Record<string, JsonValue>>;
|
||||
}
|
||||
|
||||
@@ -311,6 +320,34 @@ export interface ProviderMode {
|
||||
isUnattended?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws one top-level option key in the composer. The control holds no value: Paseo reads it
|
||||
* from the agent's options.
|
||||
*/
|
||||
export type ProviderOptionControl =
|
||||
| {
|
||||
type: "toggle";
|
||||
key: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
/** A Lucide icon name. */
|
||||
icon?: string;
|
||||
/** Shown while the key has no value. */
|
||||
defaultValue?: boolean;
|
||||
}
|
||||
| {
|
||||
type: "select";
|
||||
key: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
/** A Lucide icon name. */
|
||||
icon?: string;
|
||||
/** The choice shown while the key has no value. */
|
||||
defaultValue?: string;
|
||||
choices: ReadonlyArray<{ value: string; label: string; description?: string }>;
|
||||
};
|
||||
|
||||
/** @deprecated Report `controls` and `providerOptions` instead. */
|
||||
export type ProviderSetting =
|
||||
| {
|
||||
type: "toggle";
|
||||
@@ -335,7 +372,12 @@ export interface ProviderConfigState {
|
||||
models: readonly ProviderModel[];
|
||||
modes: readonly ProviderMode[];
|
||||
thinkingOptions: readonly ProviderThinkingOption[];
|
||||
settings: readonly ProviderSetting[];
|
||||
/** The option keys that get a composer control. May change at runtime, for example per model. */
|
||||
controls?: readonly ProviderOptionControl[];
|
||||
/** What the session runs with now, for the keys it reports. Publish the committed values. */
|
||||
providerOptions?: Readonly<Record<string, JsonValue>>;
|
||||
/** @deprecated Report `controls` and `providerOptions`. Each setting id is an option key. */
|
||||
settings?: readonly ProviderSetting[];
|
||||
}
|
||||
|
||||
export interface ProviderCatalog {
|
||||
@@ -888,6 +930,7 @@ const configChangesSchema = z
|
||||
model: z.string().nullable().optional(),
|
||||
mode: z.string().nullable().optional(),
|
||||
thinkingOption: z.string().nullable().optional(),
|
||||
providerOptions: jsonObjectSchema.optional(),
|
||||
settings: jsonObjectSchema.optional(),
|
||||
})
|
||||
.strict();
|
||||
@@ -1024,6 +1067,33 @@ const settingSchema = z.discriminatedUnion("type", [
|
||||
})
|
||||
.strip(),
|
||||
]);
|
||||
const controlSchema = z.discriminatedUnion("type", [
|
||||
z
|
||||
.object({
|
||||
type: z.literal("toggle"),
|
||||
key: idSchema,
|
||||
label: z.string(),
|
||||
description: z.string().optional(),
|
||||
icon: z.string().optional(),
|
||||
defaultValue: z.boolean().optional(),
|
||||
})
|
||||
.strip(),
|
||||
z
|
||||
.object({
|
||||
type: z.literal("select"),
|
||||
key: idSchema,
|
||||
label: z.string(),
|
||||
description: z.string().optional(),
|
||||
icon: z.string().optional(),
|
||||
defaultValue: z.string().optional(),
|
||||
choices: z.array(
|
||||
z
|
||||
.object({ value: z.string(), label: z.string(), description: z.string().optional() })
|
||||
.strip(),
|
||||
),
|
||||
})
|
||||
.strip(),
|
||||
]);
|
||||
const configStateSchema = z
|
||||
.object({
|
||||
model: z.string().optional(),
|
||||
@@ -1032,7 +1102,9 @@ const configStateSchema = z
|
||||
models: z.array(modelSchema),
|
||||
modes: z.array(modeSchema),
|
||||
thinkingOptions: z.array(selectOptionSchema),
|
||||
settings: z.array(settingSchema),
|
||||
controls: z.array(controlSchema).optional(),
|
||||
providerOptions: jsonObjectSchema.optional(),
|
||||
settings: z.array(settingSchema).optional(),
|
||||
})
|
||||
.strip();
|
||||
const catalogSchema = z
|
||||
|
||||
Reference in New Issue
Block a user