mirror of
https://github.com/rait-09/obsidian-agent-client.git
synced 2026-07-22 06:43:37 +00:00
feat(acp): migrate to @agentclientprotocol/sdk 0.28.1
Bump the ACP SDK from 0.14.1 to 0.28.1 (PROTOCOL_VERSION unchanged, so a
direct jump). Adapt the existing client to the SDK's breaking changes while
keeping existing features working; new 0.28 features (elicitation, NES,
providers, document lifecycle) are out of scope.
- Renames: unstable_listSessions->listSessions, unstable_resumeSession->
resumeSession (both stabilized); KillTerminalCommand{Request,Response}->
KillTerminal{Request,Response} (type-only).
- SessionConfigOption became a select|boolean union. The domain type now
carries boolean as data (UI renders/sets only select for now); the
converter and all consumers narrow on type === "select".
- The model API (unstable_setSessionModel, SessionModelState) was removed
from the SDK; model is now a config option. Remove the legacy model path
(setSessionModel, the model dropdown, SessionModel(State) domain types)
but KEEP lastUsedModels + the config-option model restore/save that back
model persistence. Mode keeps its legacy API (still in the SDK).
- zod is now a runtime (peer) dependency of the SDK for schema validation;
add it to dependencies so it bundles into main.js.
- Restore @eslint/json devDep (legacy-peer-deps had pruned this optional
peer that the lint config loads).
ClientSideConnection is deprecated in 0.28 (new: client() builder, scoped
connectWith) but kept for now with documented eslint-disables; migrating
the connection lifecycle is a separate follow-up.
This commit is contained in:
parent
edb1cfc9f0
commit
f799ad1f2e
15 changed files with 166 additions and 374 deletions
23
package-lock.json
generated
23
package-lock.json
generated
|
|
@ -9,16 +9,18 @@
|
|||
"version": "0.11.0-preview.2",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.14.1",
|
||||
"@agentclientprotocol/sdk": "^0.28.1",
|
||||
"@codemirror/state": "6.5.0",
|
||||
"@codemirror/view": "^6.35.0",
|
||||
"@tanstack/react-virtual": "^3.13.23",
|
||||
"diff": "^8.0.2",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"semver": "^7.7.3"
|
||||
"semver": "^7.7.3",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/json": "^0.14.0",
|
||||
"@types/node": "^16.11.6",
|
||||
"@types/react": "18.3.29",
|
||||
"@types/react-dom": "18.3.7",
|
||||
|
|
@ -38,9 +40,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@agentclientprotocol/sdk": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.14.1.tgz",
|
||||
"integrity": "sha512-b6r3PS3Nly+Wyw9U+0nOr47bV8tfS476EgyEMhoKvJCZLbgqoDFN7DJwkxL88RR0aiOqOYV1ZnESHqb+RmdH8w==",
|
||||
"version": "0.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.28.1.tgz",
|
||||
"integrity": "sha512-Z2Frs6YtPhnZZ+XwFXyQkRDXY0fn8FjCalEs0W4yUhQnY4TztmNq0/RnfzWdFN3vqT3h0jTz5klzYbZHGxCDyQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
|
|
@ -1026,7 +1028,6 @@
|
|||
"integrity": "sha512-rvR/EZtvUG3p9uqrSmcDJPYSH7atmWr0RnFWN6m917MAPx82+zQgPUmDu0whPFG6XTyM0vB/hR6c1Q63OaYtCQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.17.0",
|
||||
"@eslint/plugin-kit": "^0.4.1",
|
||||
|
|
@ -1119,7 +1120,6 @@
|
|||
"integrity": "sha512-KWiFQpSAqEIyrTXko3hFNLeQvSK8zXlJQzhhxsyVn58WFRYXST99b3Nqnu+ttOtjds2Pl2grUHGpe2NzhPynuQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
|
|
@ -7010,14 +7010,6 @@
|
|||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/search-insights": {
|
||||
"version": "2.17.3",
|
||||
"resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz",
|
||||
"integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.8.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
|
||||
|
|
@ -8817,7 +8809,6 @@
|
|||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
||||
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@eslint/json": "^0.14.0",
|
||||
"@types/node": "^16.11.6",
|
||||
"@types/react": "18.3.29",
|
||||
"@types/react-dom": "18.3.7",
|
||||
|
|
@ -39,13 +40,14 @@
|
|||
"vitest": "^4.1.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.14.1",
|
||||
"@agentclientprotocol/sdk": "^0.28.1",
|
||||
"@codemirror/state": "6.5.0",
|
||||
"@codemirror/view": "^6.35.0",
|
||||
"@tanstack/react-virtual": "^3.13.23",
|
||||
"diff": "^8.0.2",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"semver": "^7.7.3"
|
||||
"semver": "^7.7.3",
|
||||
"zod": "^4.4.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,11 @@ export interface TerminalOutputResult {
|
|||
*/
|
||||
export class AcpClient {
|
||||
// Connection & process
|
||||
// NOTE: ClientSideConnection is deprecated in ACP 0.28 in favor of the
|
||||
// client() builder, but that uses a scoped connectWith(...) callback rather
|
||||
// than a persistent connection object. Migrating AcpClient's lifecycle to it
|
||||
// is a separate architectural change (tracked as a follow-up).
|
||||
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
||||
private connection: acp.ClientSideConnection | null = null;
|
||||
private agentProcess: ChildProcess | null = null;
|
||||
private currentConfig: AgentConfig | null = null;
|
||||
|
|
@ -379,6 +384,7 @@ export class AcpClient {
|
|||
);
|
||||
|
||||
const stream = acp.ndJsonStream(input, output);
|
||||
// eslint-disable-next-line @typescript-eslint/no-deprecated -- see note on `connection` field (ClientSideConnection migration is a follow-up)
|
||||
this.connection = new acp.ClientSideConnection(
|
||||
() => this.handler,
|
||||
stream,
|
||||
|
|
@ -686,31 +692,6 @@ export class AcpClient {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use setSessionConfigOption instead.
|
||||
*/
|
||||
async setSessionModel(sessionId: string, modelId: string): Promise<void> {
|
||||
const connection = this.requireConnection();
|
||||
|
||||
this.logger.log(
|
||||
`[AcpClient] Setting session model to: ${modelId} for session: ${sessionId}`,
|
||||
);
|
||||
|
||||
try {
|
||||
await connection.unstable_setSessionModel({
|
||||
sessionId,
|
||||
modelId,
|
||||
});
|
||||
this.logger.log(`[AcpClient] Session model set to: ${modelId}`);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
"[AcpClient] Failed to set session model:",
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a session configuration option.
|
||||
*
|
||||
|
|
@ -789,6 +770,7 @@ export class AcpClient {
|
|||
* Assert that the ACP connection is initialized and return it.
|
||||
* @throws Error if connection is not available
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-deprecated -- see note on `connection` field (ClientSideConnection migration is a follow-up)
|
||||
private requireConnection(): acp.ClientSideConnection {
|
||||
if (!this.connection) {
|
||||
throw new Error(
|
||||
|
|
@ -848,7 +830,7 @@ export class AcpClient {
|
|||
|
||||
const filterCwd = cwd ? this.toSessionCwd(cwd) : undefined;
|
||||
|
||||
const response = await connection.unstable_listSessions({
|
||||
const response = await connection.listSessions({
|
||||
cwd: filterCwd ?? null,
|
||||
cursor: cursor ?? null,
|
||||
});
|
||||
|
|
@ -931,7 +913,7 @@ export class AcpClient {
|
|||
try {
|
||||
this.logger.log(`[AcpClient] Resuming session: ${sessionId}...`);
|
||||
|
||||
const response = await connection.unstable_resumeSession({
|
||||
const response = await connection.resumeSession({
|
||||
sessionId,
|
||||
cwd: this.toSessionCwd(cwd),
|
||||
mcpServers: [],
|
||||
|
|
|
|||
|
|
@ -232,8 +232,8 @@ export class AcpHandler {
|
|||
}
|
||||
|
||||
killTerminal(
|
||||
params: acp.KillTerminalCommandRequest,
|
||||
): Promise<acp.KillTerminalCommandResponse> {
|
||||
params: acp.KillTerminalRequest,
|
||||
): Promise<acp.KillTerminalResponse> {
|
||||
const success = this.terminalManager.killTerminal(params.terminalId);
|
||||
if (!success) {
|
||||
throw new Error(`Terminal ${params.terminalId} not found`);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import type {
|
|||
interface AcpSessionResponse {
|
||||
sessionId?: string;
|
||||
modes?: acp.SessionModeState | null;
|
||||
models?: acp.SessionModelState | null;
|
||||
configOptions?: acp.SessionConfigOption[] | null;
|
||||
}
|
||||
|
||||
|
|
@ -100,15 +99,29 @@ export class AcpTypeConverter {
|
|||
static toSessionConfigOptions(
|
||||
acpOptions: acp.SessionConfigOption[],
|
||||
): SessionConfigOption[] {
|
||||
return acpOptions.map((opt) => ({
|
||||
id: opt.id,
|
||||
name: opt.name,
|
||||
description: opt.description ?? undefined,
|
||||
category: opt.category ?? undefined,
|
||||
type: opt.type,
|
||||
currentValue: opt.currentValue,
|
||||
options: this.toSessionConfigSelectOptions(opt.options),
|
||||
}));
|
||||
return acpOptions.map((opt): SessionConfigOption => {
|
||||
const base = {
|
||||
id: opt.id,
|
||||
name: opt.name,
|
||||
description: opt.description ?? undefined,
|
||||
category: opt.category ?? undefined,
|
||||
};
|
||||
// boolean options (ACP 0.28+) are carried as data; the UI renders
|
||||
// and sets only select options for now.
|
||||
if (opt.type === "boolean") {
|
||||
return {
|
||||
...base,
|
||||
type: "boolean",
|
||||
currentValue: opt.currentValue,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...base,
|
||||
type: "select",
|
||||
currentValue: opt.currentValue,
|
||||
options: this.toSessionConfigSelectOptions(opt.options),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private static toSessionConfigSelectOptions(
|
||||
|
|
@ -166,18 +179,6 @@ export class AcpTypeConverter {
|
|||
};
|
||||
}
|
||||
|
||||
let models: SessionResult["models"];
|
||||
if (response.models) {
|
||||
models = {
|
||||
availableModels: response.models.availableModels.map((m) => ({
|
||||
modelId: m.modelId,
|
||||
name: m.name,
|
||||
description: m.description ?? undefined,
|
||||
})),
|
||||
currentModelId: response.models.currentModelId,
|
||||
};
|
||||
}
|
||||
|
||||
const configOptions = response.configOptions
|
||||
? this.toSessionConfigOptions(response.configOptions)
|
||||
: undefined;
|
||||
|
|
@ -185,7 +186,6 @@ export class AcpTypeConverter {
|
|||
return {
|
||||
sessionId,
|
||||
modes,
|
||||
models,
|
||||
configOptions,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import type { ChatMessage, ActivePermission } from "../types/chat";
|
|||
import type {
|
||||
ChatSession,
|
||||
SessionModeState,
|
||||
SessionModelState,
|
||||
SessionConfigOption,
|
||||
} from "../types/session";
|
||||
import type { AgentDisplayInfo } from "../services/session-helpers";
|
||||
|
|
@ -66,13 +65,11 @@ export interface UseAgentReturn {
|
|||
updateSessionFromLoad: (
|
||||
sessionId: string,
|
||||
modes?: SessionModeState,
|
||||
models?: SessionModelState,
|
||||
configOptions?: SessionConfigOption[],
|
||||
) => Promise<void>;
|
||||
|
||||
// Config
|
||||
setMode: (modeId: string) => Promise<void>;
|
||||
setModel: (modelId: string) => Promise<void>;
|
||||
setConfigOption: (configId: string, value: string) => Promise<void>;
|
||||
|
||||
// Message operations
|
||||
|
|
@ -199,7 +196,6 @@ export function useAgent(
|
|||
|
||||
// Config
|
||||
setMode: agentSession.setMode,
|
||||
setModel: agentSession.setModel,
|
||||
setConfigOption: agentSession.setConfigOption,
|
||||
|
||||
// Message operations
|
||||
|
|
@ -232,7 +228,6 @@ export function useAgent(
|
|||
agentSession.getAvailableAgents,
|
||||
agentSession.updateSessionFromLoad,
|
||||
agentSession.setMode,
|
||||
agentSession.setModel,
|
||||
agentSession.setConfigOption,
|
||||
agentMessages.sendMessage,
|
||||
agentMessages.clearMessages,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ const { useState, useCallback, useRef } = React;
|
|||
import type {
|
||||
ChatSession,
|
||||
SessionModeState,
|
||||
SessionModelState,
|
||||
SessionUpdate,
|
||||
SessionConfigOption,
|
||||
} from "../types/session";
|
||||
|
|
@ -60,13 +59,11 @@ export interface UseAgentSessionReturn {
|
|||
updateSessionFromLoad: (
|
||||
sessionId: string,
|
||||
modes?: SessionModeState,
|
||||
models?: SessionModelState,
|
||||
configOptions?: SessionConfigOption[],
|
||||
) => Promise<void>;
|
||||
|
||||
// Config
|
||||
setMode: (modeId: string) => Promise<void>;
|
||||
setModel: (modelId: string) => Promise<void>;
|
||||
setConfigOption: (configId: string, value: string) => Promise<void>;
|
||||
|
||||
/** Handle session-level updates (commands, mode, config, usage, error) */
|
||||
|
|
@ -184,7 +181,6 @@ export function useAgentSession(
|
|||
authMethods: [],
|
||||
availableCommands: undefined,
|
||||
modes: undefined,
|
||||
models: undefined,
|
||||
configOptions: undefined,
|
||||
usage: undefined,
|
||||
promptCapabilities: prev.promptCapabilities,
|
||||
|
|
@ -225,14 +221,13 @@ export function useAgentSession(
|
|||
const sessionResult =
|
||||
await agentClient.newSession(effectiveCwd);
|
||||
|
||||
// Pre-compute restored modes/models/configOptions BEFORE
|
||||
// Pre-compute restored modes/configOptions BEFORE
|
||||
// marking state as "ready" to avoid a UI race: without this,
|
||||
// the dropdowns briefly show the agent's default values and
|
||||
// a message sent during the window hits the agent in the
|
||||
// wrong mode. With this, the first render after session
|
||||
// creation already shows the user's saved selection.
|
||||
let finalModes = sessionResult.modes;
|
||||
let finalModels = sessionResult.models;
|
||||
let finalConfigOptions = sessionResult.configOptions;
|
||||
|
||||
if (sessionResult.configOptions && sessionResult.sessionId) {
|
||||
|
|
@ -262,11 +257,9 @@ export function useAgentSession(
|
|||
const restored = await restoreLegacyConfig(
|
||||
agentClient,
|
||||
sessionResult,
|
||||
settings.lastUsedModels[agentId],
|
||||
settings.lastUsedModes[agentId],
|
||||
);
|
||||
finalModes = restored.modes;
|
||||
finalModels = restored.models;
|
||||
}
|
||||
|
||||
setSession((prev) => ({
|
||||
|
|
@ -275,7 +268,6 @@ export function useAgentSession(
|
|||
state: "ready",
|
||||
authMethods: initResult?.authMethods ?? [],
|
||||
modes: finalModes,
|
||||
models: finalModels,
|
||||
configOptions: finalConfigOptions,
|
||||
promptCapabilities: initResult
|
||||
? initResult.promptCapabilities
|
||||
|
|
@ -356,7 +348,6 @@ export function useAgentSession(
|
|||
async (
|
||||
sessionId: string,
|
||||
modes?: SessionModeState,
|
||||
models?: SessionModelState,
|
||||
configOptions?: SessionConfigOption[],
|
||||
) => {
|
||||
// Pre-compute restored config BEFORE marking ready to avoid a UI
|
||||
|
|
@ -368,7 +359,6 @@ export function useAgentSession(
|
|||
const agentId = s.agentId;
|
||||
|
||||
let finalModes = modes;
|
||||
let finalModels = models;
|
||||
let finalConfigOptions = configOptions;
|
||||
|
||||
if (configOptions && sessionId) {
|
||||
|
|
@ -397,12 +387,10 @@ export function useAgentSession(
|
|||
} else if (sessionId && modes) {
|
||||
const restored = await restoreLegacyConfig(
|
||||
agentClient,
|
||||
{ sessionId, modes, models, configOptions: undefined },
|
||||
settings.lastUsedModels[agentId],
|
||||
{ sessionId, modes, configOptions: undefined },
|
||||
settings.lastUsedModes[agentId],
|
||||
);
|
||||
finalModes = restored.modes;
|
||||
finalModels = restored.models;
|
||||
}
|
||||
|
||||
setSession((prev) => ({
|
||||
|
|
@ -410,7 +398,6 @@ export function useAgentSession(
|
|||
sessionId,
|
||||
state: "ready",
|
||||
modes: finalModes ?? prev.modes,
|
||||
models: finalModels ?? prev.models,
|
||||
configOptions: finalConfigOptions ?? prev.configOptions,
|
||||
lastActivityAt: new Date(),
|
||||
}));
|
||||
|
|
@ -423,44 +410,33 @@ export function useAgentSession(
|
|||
// ============================================================
|
||||
|
||||
const setLegacyConfigValue = useCallback(
|
||||
async (kind: "mode" | "model", value: string) => {
|
||||
async (value: string) => {
|
||||
const s = sessionRef.current;
|
||||
if (!s.sessionId) {
|
||||
getLogger().debug(`Cannot set ${kind}: no active session`);
|
||||
getLogger().debug("Cannot set mode: no active session");
|
||||
return;
|
||||
}
|
||||
|
||||
const previousValue =
|
||||
kind === "mode"
|
||||
? s.modes?.currentModeId
|
||||
: s.models?.currentModelId;
|
||||
const previousValue = s.modes?.currentModeId;
|
||||
|
||||
setSession((prev) => applyLegacyValue(prev, kind, value));
|
||||
setSession((prev) => applyLegacyValue(prev, value));
|
||||
|
||||
try {
|
||||
if (kind === "mode") {
|
||||
await agentClient.setSessionMode(s.sessionId, value);
|
||||
} else {
|
||||
await agentClient.setSessionModel(s.sessionId, value);
|
||||
}
|
||||
await agentClient.setSessionMode(s.sessionId, value);
|
||||
|
||||
if (s.agentId) {
|
||||
const persistKey =
|
||||
kind === "mode" ? "lastUsedModes" : "lastUsedModels";
|
||||
const currentSettings = settingsAccess.getSnapshot();
|
||||
void settingsAccess.updateSettings({
|
||||
[persistKey]: {
|
||||
...currentSettings[persistKey],
|
||||
lastUsedModes: {
|
||||
...currentSettings.lastUsedModes,
|
||||
[s.agentId]: value,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
getLogger().error(`Failed to set ${kind}:`, error);
|
||||
getLogger().error("Failed to set mode:", error);
|
||||
if (previousValue) {
|
||||
setSession((prev) =>
|
||||
applyLegacyValue(prev, kind, previousValue),
|
||||
);
|
||||
setSession((prev) => applyLegacyValue(prev, previousValue));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -468,12 +444,7 @@ export function useAgentSession(
|
|||
);
|
||||
|
||||
const setMode = useCallback(
|
||||
(modeId: string) => setLegacyConfigValue("mode", modeId),
|
||||
[setLegacyConfigValue],
|
||||
);
|
||||
|
||||
const setModel = useCallback(
|
||||
(modelId: string) => setLegacyConfigValue("model", modelId),
|
||||
(modeId: string) => setLegacyConfigValue(modeId),
|
||||
[setLegacyConfigValue],
|
||||
);
|
||||
|
||||
|
|
@ -494,7 +465,7 @@ export function useAgentSession(
|
|||
return {
|
||||
...prev,
|
||||
configOptions: prev.configOptions.map((opt) =>
|
||||
opt.id === configId
|
||||
opt.id === configId && opt.type === "select"
|
||||
? { ...opt, currentValue: value }
|
||||
: opt,
|
||||
),
|
||||
|
|
@ -577,7 +548,6 @@ export function useAgentSession(
|
|||
getAvailableAgents,
|
||||
updateSessionFromLoad,
|
||||
setMode,
|
||||
setModel,
|
||||
setConfigOption,
|
||||
handleSessionUpdate,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ export interface UseChatActionsReturn {
|
|||
|
||||
// Config actions
|
||||
handleSetMode: (modeId: string) => Promise<void>;
|
||||
handleSetModel: (modelId: string) => Promise<void>;
|
||||
handleSetConfigOption: (configId: string, value: string) => Promise<void>;
|
||||
|
||||
// UI state actions
|
||||
|
|
@ -343,13 +342,6 @@ export function useChatActions(
|
|||
[agent.setMode],
|
||||
);
|
||||
|
||||
const handleSetModel = useCallback(
|
||||
async (modelId: string) => {
|
||||
await agent.setModel(modelId);
|
||||
},
|
||||
[agent.setModel],
|
||||
);
|
||||
|
||||
const handleSetConfigOption = useCallback(
|
||||
async (configId: string, value: string) => {
|
||||
await agent.setConfigOption(configId, value);
|
||||
|
|
@ -385,7 +377,6 @@ export function useChatActions(
|
|||
handleSwitchAgent,
|
||||
handleRestartAgent,
|
||||
handleSetMode,
|
||||
handleSetModel,
|
||||
handleSetConfigOption,
|
||||
handleClearError,
|
||||
handleClearAgentUpdate,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import type {
|
|||
SavedSessionInfo,
|
||||
ChatSession,
|
||||
SessionModeState,
|
||||
SessionModelState,
|
||||
SessionConfigOption,
|
||||
AgentCapabilities,
|
||||
} from "../types/session";
|
||||
|
|
@ -57,13 +56,11 @@ export interface SessionLoadCallback {
|
|||
/**
|
||||
* @param sessionId - ID of the session (new session ID for fork)
|
||||
* @param modes - Available modes from the session
|
||||
* @param models - Available models from the session
|
||||
* @param configOptions - Config options from the session
|
||||
*/
|
||||
(
|
||||
sessionId: string,
|
||||
modes?: SessionModeState,
|
||||
models?: SessionModelState,
|
||||
configOptions?: SessionConfigOption[],
|
||||
): void;
|
||||
}
|
||||
|
|
@ -327,9 +324,7 @@ export function useSessionHistory(
|
|||
merged.every((s, i) => s.title === prev[i].title);
|
||||
return unchanged ? prev : merged;
|
||||
});
|
||||
setLocalSessionIds(
|
||||
new Set(localSessions.map((s) => s.sessionId)),
|
||||
);
|
||||
setLocalSessionIds(new Set(localSessions.map((s) => s.sessionId)));
|
||||
});
|
||||
}, [settingsAccess, session.agentId]);
|
||||
|
||||
|
|
@ -542,7 +537,7 @@ export function useSessionHistory(
|
|||
try {
|
||||
// IMPORTANT: Update session.sessionId BEFORE calling restore
|
||||
// so that session/update notifications are not ignored
|
||||
onSessionLoad(sessionId, undefined, undefined, undefined);
|
||||
onSessionLoad(sessionId, undefined, undefined);
|
||||
|
||||
if (capabilities.canLoad) {
|
||||
// Check local messages first to decide whether to use them or agent replay
|
||||
|
|
@ -561,7 +556,6 @@ export function useSessionHistory(
|
|||
onSessionLoad(
|
||||
result.sessionId,
|
||||
result.modes,
|
||||
result.models,
|
||||
result.configOptions,
|
||||
);
|
||||
onMessagesRestore(localMessages);
|
||||
|
|
@ -577,7 +571,6 @@ export function useSessionHistory(
|
|||
onSessionLoad(
|
||||
result.sessionId,
|
||||
result.modes,
|
||||
result.models,
|
||||
result.configOptions,
|
||||
);
|
||||
}
|
||||
|
|
@ -590,7 +583,6 @@ export function useSessionHistory(
|
|||
onSessionLoad(
|
||||
result.sessionId,
|
||||
result.modes,
|
||||
result.models,
|
||||
result.configOptions,
|
||||
);
|
||||
|
||||
|
|
@ -641,7 +633,6 @@ export function useSessionHistory(
|
|||
onSessionLoad(
|
||||
result.sessionId,
|
||||
result.modes,
|
||||
result.models,
|
||||
result.configOptions,
|
||||
);
|
||||
|
||||
|
|
@ -751,11 +742,10 @@ export function useSessionHistory(
|
|||
);
|
||||
|
||||
try {
|
||||
await settingsAccess.updateSessionTitle(
|
||||
sessionId,
|
||||
newTitle,
|
||||
{ agentId: session.agentId, cwd: sessionCwd },
|
||||
);
|
||||
await settingsAccess.updateSessionTitle(sessionId, newTitle, {
|
||||
agentId: session.agentId,
|
||||
cwd: sessionCwd,
|
||||
});
|
||||
invalidateCache();
|
||||
} catch (err) {
|
||||
// Rollback
|
||||
|
|
|
|||
|
|
@ -184,7 +184,6 @@ export function createInitialSession(
|
|||
authMethods: [],
|
||||
availableCommands: undefined,
|
||||
modes: undefined,
|
||||
models: undefined,
|
||||
createdAt: new Date(),
|
||||
lastActivityAt: new Date(),
|
||||
workingDirectory,
|
||||
|
|
|
|||
|
|
@ -24,15 +24,10 @@ import type { AcpClient } from "../acp/acp-client";
|
|||
*/
|
||||
export function applyLegacyValue(
|
||||
prev: ChatSession,
|
||||
kind: "mode" | "model",
|
||||
value: string,
|
||||
): ChatSession {
|
||||
if (kind === "mode") {
|
||||
if (!prev.modes) return prev;
|
||||
return { ...prev, modes: { ...prev.modes, currentModeId: value } };
|
||||
}
|
||||
if (!prev.models) return prev;
|
||||
return { ...prev, models: { ...prev.models, currentModelId: value } };
|
||||
if (!prev.modes) return prev;
|
||||
return { ...prev, modes: { ...prev.modes, currentModeId: value } };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
@ -53,7 +48,7 @@ export async function tryRestoreConfigOption(
|
|||
if (!savedValue) return configOptions;
|
||||
|
||||
const option = configOptions.find((o) => o.category === category);
|
||||
if (!option) return configOptions;
|
||||
if (!option || option.type !== "select") return configOptions;
|
||||
if (savedValue === option.currentValue) return configOptions;
|
||||
if (
|
||||
!flattenConfigSelectOptions(option.options).some(
|
||||
|
|
@ -91,7 +86,7 @@ export async function restoreSavedConfigOptions(
|
|||
let result = configOptions;
|
||||
for (const [optionId, savedValue] of Object.entries(savedById)) {
|
||||
const option = result.find((o) => o.id === optionId);
|
||||
if (!option) continue;
|
||||
if (!option || option.type !== "select") continue;
|
||||
if (savedValue === option.currentValue) continue;
|
||||
if (
|
||||
!flattenConfigSelectOptions(option.options).some(
|
||||
|
|
@ -127,34 +122,13 @@ export async function restoreSavedConfigOptions(
|
|||
export async function restoreLegacyConfig(
|
||||
agentClient: AcpClient,
|
||||
sessionResult: SessionResult,
|
||||
savedModelId: string | undefined,
|
||||
savedModeId: string | undefined,
|
||||
): Promise<{
|
||||
modes: SessionResult["modes"];
|
||||
models: SessionResult["models"];
|
||||
}> {
|
||||
let modes = sessionResult.modes;
|
||||
let models = sessionResult.models;
|
||||
|
||||
if (!sessionResult.sessionId) return { modes, models };
|
||||
|
||||
// Legacy model restore
|
||||
if (models && savedModelId) {
|
||||
if (
|
||||
savedModelId !== models.currentModelId &&
|
||||
models.availableModels.some((m) => m.modelId === savedModelId)
|
||||
) {
|
||||
try {
|
||||
await agentClient.setSessionModel(
|
||||
sessionResult.sessionId,
|
||||
savedModelId,
|
||||
);
|
||||
models = { ...models, currentModelId: savedModelId };
|
||||
} catch {
|
||||
// Agent default is fine as fallback
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sessionResult.sessionId) return { modes };
|
||||
|
||||
// Legacy mode restore
|
||||
if (modes && savedModeId) {
|
||||
|
|
@ -174,5 +148,5 @@ export async function restoreLegacyConfig(
|
|||
}
|
||||
}
|
||||
|
||||
return { modes, models };
|
||||
return { modes };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,44 +122,6 @@ export interface SessionModeState {
|
|||
currentModeId: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Model (Experimental)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Represents an AI model available in a session.
|
||||
*
|
||||
* Models determine which AI model is used for responses.
|
||||
* This is an experimental feature and may change.
|
||||
*/
|
||||
/** DEPRECATED: Use SessionConfigOption instead. Kept for backward compatibility. */
|
||||
export interface SessionModel {
|
||||
/** Unique identifier for this model (e.g., "claude-sonnet-4") */
|
||||
modelId: string;
|
||||
|
||||
/** Human-readable name for display */
|
||||
name: string;
|
||||
|
||||
/** Optional description of this model */
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* State of available models in a session.
|
||||
*
|
||||
* Contains both the list of available models and the currently active model.
|
||||
* Updated via NewSessionResponse initially.
|
||||
* Note: Unlike modes, there is no dedicated notification for model changes.
|
||||
*/
|
||||
/** DEPRECATED: Use SessionConfigOption instead. Kept for backward compatibility. */
|
||||
export interface SessionModelState {
|
||||
/** List of models available in this session */
|
||||
availableModels: SessionModel[];
|
||||
|
||||
/** ID of the currently active model */
|
||||
currentModelId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Context window usage and cost information for a session.
|
||||
* Reported by the agent via `usage_update` session notifications.
|
||||
|
|
@ -218,12 +180,6 @@ export interface ChatSession {
|
|||
*/
|
||||
modes?: SessionModeState;
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use configOptions instead. Kept for backward compatibility
|
||||
* with agents that don't support configOptions.
|
||||
*/
|
||||
models?: SessionModelState;
|
||||
|
||||
/**
|
||||
* Session configuration options (mode, model, thought_level, etc.).
|
||||
* Supersedes legacy modes/models fields.
|
||||
|
|
@ -454,20 +410,31 @@ export interface ProcessErrorUpdate extends SessionUpdateBase {
|
|||
// Config Option Types
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* A session configuration option (e.g. mode, model, thought_level).
|
||||
* Part of the ACP configOptions API that supersedes legacy modes/models.
|
||||
*/
|
||||
export interface SessionConfigOption {
|
||||
interface SessionConfigOptionBase {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
category?: string | null;
|
||||
type: "select";
|
||||
currentValue: string;
|
||||
options: SessionConfigSelectOption[] | SessionConfigSelectGroup[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A session configuration option (e.g. mode, model, thought_level).
|
||||
* Part of the ACP configOptions API that supersedes legacy modes/models.
|
||||
*
|
||||
* `select` carries a string value + choices. `boolean` (ACP 0.28+) is held as
|
||||
* data for future support — it is not yet rendered or settable in the UI.
|
||||
*/
|
||||
export type SessionConfigOption =
|
||||
| (SessionConfigOptionBase & {
|
||||
type: "select";
|
||||
currentValue: string;
|
||||
options: SessionConfigSelectOption[] | SessionConfigSelectGroup[];
|
||||
})
|
||||
| (SessionConfigOptionBase & {
|
||||
type: "boolean";
|
||||
currentValue: boolean;
|
||||
});
|
||||
|
||||
export interface SessionConfigSelectOption {
|
||||
value: string;
|
||||
name: string;
|
||||
|
|
@ -571,9 +538,6 @@ export interface SessionResult {
|
|||
/** DEPRECATED: Use configOptions instead. Kept for backward compatibility. */
|
||||
modes?: SessionModeState;
|
||||
|
||||
/** DEPRECATED: Use configOptions instead. Kept for backward compatibility. */
|
||||
models?: SessionModelState;
|
||||
|
||||
/** Session config options (supersedes modes/models) */
|
||||
configOptions?: SessionConfigOption[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ import {
|
|||
flattenConfigSelectOptions,
|
||||
type SlashCommand,
|
||||
type SessionModeState,
|
||||
type SessionModelState,
|
||||
type SessionConfigOption,
|
||||
} from "../types/session";
|
||||
import { checkAgentUpdate } from "../services/update-checker";
|
||||
|
|
@ -213,23 +212,16 @@ export function ChatPanel({
|
|||
(
|
||||
sessionId: string,
|
||||
modes?: SessionModeState,
|
||||
models?: SessionModelState,
|
||||
configOptions?: SessionConfigOption[],
|
||||
) => {
|
||||
logger.log(
|
||||
`[ChatPanel] Session loaded/resumed/forked: ${sessionId}`,
|
||||
{
|
||||
modes,
|
||||
models,
|
||||
configOptions,
|
||||
},
|
||||
);
|
||||
void agent.updateSessionFromLoad(
|
||||
sessionId,
|
||||
modes,
|
||||
models,
|
||||
configOptions,
|
||||
);
|
||||
void agent.updateSessionFromLoad(sessionId, modes, configOptions);
|
||||
},
|
||||
[logger, agent.updateSessionFromLoad],
|
||||
);
|
||||
|
|
@ -312,7 +304,6 @@ export function ChatPanel({
|
|||
handleSwitchAgent,
|
||||
handleRestartAgent,
|
||||
handleSetMode,
|
||||
handleSetModel,
|
||||
handleSetConfigOption,
|
||||
handleClearError,
|
||||
handleClearAgentUpdate,
|
||||
|
|
@ -639,14 +630,14 @@ export function ChatPanel({
|
|||
// Floating: create a shim with listener tracking
|
||||
return {
|
||||
app: plugin.app,
|
||||
registerDomEvent: ((
|
||||
registerDomEvent: (
|
||||
target: Window | Document | HTMLElement,
|
||||
type: string,
|
||||
callback: EventListenerOrEventListenerObject,
|
||||
) => {
|
||||
target.addEventListener(type, callback);
|
||||
registeredListenersRef.current.push({ target, type, callback });
|
||||
}),
|
||||
},
|
||||
};
|
||||
}, [viewHostProp, plugin.app]);
|
||||
|
||||
|
|
@ -673,16 +664,20 @@ export function ChatPanel({
|
|||
void agent.createSession(config?.agent || initialAgentId);
|
||||
}, [agent.createSession, config?.agent, initialAgentId]);
|
||||
|
||||
// Apply configured model when session is ready
|
||||
// Apply configured model (a select config option with category "model")
|
||||
// when session is ready.
|
||||
useEffect(() => {
|
||||
if (!config?.model || !isSessionReady) return;
|
||||
|
||||
// Prefer configOptions if available
|
||||
if (session.configOptions) {
|
||||
const modelOption = session.configOptions.find(
|
||||
(o) => o.category === "model",
|
||||
);
|
||||
if (modelOption && modelOption.currentValue !== config.model) {
|
||||
if (
|
||||
modelOption &&
|
||||
modelOption.type === "select" &&
|
||||
modelOption.currentValue !== config.model
|
||||
) {
|
||||
const valueExists = flattenConfigSelectOptions(
|
||||
modelOption.options,
|
||||
).some((o) => o.value === config.model);
|
||||
|
|
@ -694,29 +689,12 @@ export function ChatPanel({
|
|||
void agent.setConfigOption(modelOption.id, config.model);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to legacy models
|
||||
if (session.models) {
|
||||
const modelExists = session.models.availableModels.some(
|
||||
(m) => m.modelId === config.model,
|
||||
);
|
||||
if (modelExists && session.models.currentModelId !== config.model) {
|
||||
logger.log(
|
||||
"[ChatPanel] Applying configured model:",
|
||||
config.model,
|
||||
);
|
||||
void agent.setModel(config.model);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
config?.model,
|
||||
isSessionReady,
|
||||
session.configOptions,
|
||||
session.models,
|
||||
agent.setConfigOption,
|
||||
agent.setModel,
|
||||
logger,
|
||||
]);
|
||||
|
||||
|
|
@ -829,7 +807,10 @@ export function ChatPanel({
|
|||
);
|
||||
|
||||
// System notification on response completion
|
||||
if (settings.enableSystemNotifications && !activeDocument.hasFocus()) {
|
||||
if (
|
||||
settings.enableSystemNotifications &&
|
||||
!activeDocument.hasFocus()
|
||||
) {
|
||||
new Notification("Agent Client", {
|
||||
body: `${activeAgentLabel} has completed the response.`,
|
||||
});
|
||||
|
|
@ -1117,7 +1098,8 @@ export function ChatPanel({
|
|||
if (state === "error") return "error";
|
||||
if (state === "disconnected") return "disconnected";
|
||||
if (hasActivePermissionRef.current) return "permission";
|
||||
if (isSendingRef.current || sessionHistoryLoadingRef.current) return "busy";
|
||||
if (isSendingRef.current || sessionHistoryLoadingRef.current)
|
||||
return "busy";
|
||||
if (state === "ready") return "ready";
|
||||
return "busy";
|
||||
},
|
||||
|
|
@ -1262,8 +1244,6 @@ export function ChatPanel({
|
|||
onRestoredMessageConsumed={handleRestoredMessageConsumed}
|
||||
modes={session.modes}
|
||||
onModeChange={(modeId) => void handleSetMode(modeId)}
|
||||
models={session.models}
|
||||
onModelChange={(modelId) => void handleSetModel(modelId)}
|
||||
configOptions={session.configOptions}
|
||||
onConfigOptionChange={(configId, value) =>
|
||||
void handleSetConfigOption(configId, value)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import type { NoteMetadata } from "../services/vault-service";
|
|||
import type {
|
||||
SlashCommand,
|
||||
SessionModeState,
|
||||
SessionModelState,
|
||||
SessionUsage,
|
||||
SessionConfigOption,
|
||||
} from "../types/session";
|
||||
|
|
@ -212,10 +211,6 @@ export interface InputAreaProps {
|
|||
modes?: SessionModeState;
|
||||
/** Callback when mode is changed */
|
||||
onModeChange?: (modeId: string) => void;
|
||||
/** Session model state (available models and current model) - experimental */
|
||||
models?: SessionModelState;
|
||||
/** Callback when model is changed */
|
||||
onModelChange?: (modelId: string) => void;
|
||||
/** Session config options (supersedes modes/models when present) */
|
||||
configOptions?: SessionConfigOption[];
|
||||
/** Callback when a config option is changed */
|
||||
|
|
@ -279,8 +274,6 @@ export function InputArea({
|
|||
onRestoredMessageConsumed,
|
||||
modes,
|
||||
onModeChange,
|
||||
models,
|
||||
onModelChange,
|
||||
configOptions,
|
||||
onConfigOptionChange,
|
||||
usage,
|
||||
|
|
@ -310,7 +303,8 @@ export function InputArea({
|
|||
|
||||
// Unofficial Obsidian API (see src/types/obsidian-internals.d.ts)
|
||||
const obsidianSpellcheck =
|
||||
(plugin.app.vault.getConfig("spellcheck") as boolean | undefined) ?? true;
|
||||
(plugin.app.vault.getConfig("spellcheck") as boolean | undefined) ??
|
||||
true;
|
||||
|
||||
// Local state (hint and command are still local - not needed for broadcast)
|
||||
const [hintText, setHintText] = useState<string | null>(null);
|
||||
|
|
@ -1095,8 +1089,6 @@ export function InputArea({
|
|||
onSendOrStop={() => void handleSendOrStop()}
|
||||
modes={modes}
|
||||
onModeChange={onModeChange}
|
||||
models={models}
|
||||
onModelChange={onModelChange}
|
||||
configOptions={configOptions}
|
||||
onConfigOptionChange={onConfigOptionChange}
|
||||
usage={usage}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { setIcon, Menu } from "obsidian";
|
|||
import {
|
||||
flattenConfigSelectOptions,
|
||||
type SessionModeState,
|
||||
type SessionModelState,
|
||||
type SessionUsage,
|
||||
type SessionConfigOption,
|
||||
type SessionConfigSelectGroup,
|
||||
|
|
@ -152,8 +151,6 @@ export interface InputToolbarProps {
|
|||
onSendOrStop: () => void;
|
||||
modes?: SessionModeState;
|
||||
onModeChange?: (modeId: string) => void;
|
||||
models?: SessionModelState;
|
||||
onModelChange?: (modelId: string) => void;
|
||||
configOptions?: SessionConfigOption[];
|
||||
onConfigOptionChange?: (configId: string, value: string) => void;
|
||||
usage?: SessionUsage;
|
||||
|
|
@ -167,8 +164,6 @@ export function InputToolbar({
|
|||
onSendOrStop,
|
||||
modes,
|
||||
onModeChange,
|
||||
models,
|
||||
onModelChange,
|
||||
configOptions,
|
||||
onConfigOptionChange,
|
||||
usage,
|
||||
|
|
@ -227,29 +222,11 @@ export function InputToolbar({
|
|||
}));
|
||||
}, [modes]);
|
||||
|
||||
const modelItems = useMemo<ToolbarDropdownItem[]>(() => {
|
||||
if (!models?.availableModels) return [];
|
||||
return models.availableModels.map((m) => ({
|
||||
value: m.modelId,
|
||||
label: m.name,
|
||||
}));
|
||||
}, [models]);
|
||||
|
||||
const currentModeLabel = useMemo(() => {
|
||||
const id = modes?.currentModeId;
|
||||
return (
|
||||
modes?.availableModes?.find((m) => m.id === id)?.name ?? "Mode"
|
||||
);
|
||||
return modes?.availableModes?.find((m) => m.id === id)?.name ?? "Mode";
|
||||
}, [modes]);
|
||||
|
||||
const currentModelLabel = useMemo(() => {
|
||||
const id = models?.currentModelId;
|
||||
return (
|
||||
models?.availableModels?.find((m) => m.modelId === id)?.name ??
|
||||
"Model"
|
||||
);
|
||||
}, [models]);
|
||||
|
||||
// ----- Render -----
|
||||
|
||||
return (
|
||||
|
|
@ -269,63 +246,68 @@ export function InputToolbar({
|
|||
)}
|
||||
|
||||
{/* Config Options (supersedes legacy mode/model selectors) */}
|
||||
{configOptions && configOptions.length > 0
|
||||
? configOptions.map((option) => {
|
||||
const flatOptions = flattenConfigSelectOptions(
|
||||
option.options,
|
||||
);
|
||||
if (flatOptions.length <= 1) return null;
|
||||
{configOptions && configOptions.length > 0 ? (
|
||||
configOptions.map((option) => {
|
||||
// boolean options (ACP 0.28+) are carried as data but
|
||||
// not yet rendered; only select options get a dropdown.
|
||||
if (option.type !== "select") return null;
|
||||
const flatOptions = flattenConfigSelectOptions(
|
||||
option.options,
|
||||
);
|
||||
if (flatOptions.length <= 1) return null;
|
||||
|
||||
const isGrouped =
|
||||
option.options.length > 0 &&
|
||||
"group" in option.options[0];
|
||||
const isGrouped =
|
||||
option.options.length > 0 &&
|
||||
"group" in option.options[0];
|
||||
|
||||
let items: ToolbarDropdownItem[];
|
||||
if (isGrouped) {
|
||||
items = [];
|
||||
for (const group of option.options as SessionConfigSelectGroup[]) {
|
||||
for (const opt of group.options) {
|
||||
items.push({
|
||||
value: opt.value,
|
||||
label: `${group.name} / ${opt.name}`,
|
||||
groupName: group.name,
|
||||
});
|
||||
}
|
||||
let items: ToolbarDropdownItem[];
|
||||
if (isGrouped) {
|
||||
items = [];
|
||||
for (const group of option.options as SessionConfigSelectGroup[]) {
|
||||
for (const opt of group.options) {
|
||||
items.push({
|
||||
value: opt.value,
|
||||
label: `${group.name} / ${opt.name}`,
|
||||
groupName: group.name,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
items = flatOptions.map((opt) => ({
|
||||
value: opt.value,
|
||||
label: opt.name,
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
items = flatOptions.map((opt) => ({
|
||||
value: opt.value,
|
||||
label: opt.name,
|
||||
}));
|
||||
}
|
||||
|
||||
const currentItem = items.find(
|
||||
(it) => it.value === option.currentValue,
|
||||
);
|
||||
const label = currentItem?.label ?? option.name;
|
||||
const title = option.description ?? option.name;
|
||||
const currentItem = items.find(
|
||||
(it) => it.value === option.currentValue,
|
||||
);
|
||||
const label = currentItem?.label ?? option.name;
|
||||
const title = option.description ?? option.name;
|
||||
|
||||
return (
|
||||
<ToolbarDropdown
|
||||
key={option.id}
|
||||
label={label}
|
||||
title={title}
|
||||
items={items}
|
||||
currentValue={option.currentValue}
|
||||
onChange={(value) => {
|
||||
onConfigOptionChange?.(option.id, value);
|
||||
}}
|
||||
className={
|
||||
option.category
|
||||
? `agent-client-config-selector-${option.category}`
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
})
|
||||
: (
|
||||
<>
|
||||
{modes && modes.availableModes.length > 1 && onModeChange && (
|
||||
return (
|
||||
<ToolbarDropdown
|
||||
key={option.id}
|
||||
label={label}
|
||||
title={title}
|
||||
items={items}
|
||||
currentValue={option.currentValue}
|
||||
onChange={(value) => {
|
||||
onConfigOptionChange?.(option.id, value);
|
||||
}}
|
||||
className={
|
||||
option.category
|
||||
? `agent-client-config-selector-${option.category}`
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<>
|
||||
{modes &&
|
||||
modes.availableModes.length > 1 &&
|
||||
onModeChange && (
|
||||
<ToolbarDropdown
|
||||
label={currentModeLabel}
|
||||
title={
|
||||
|
|
@ -338,28 +320,8 @@ export function InputToolbar({
|
|||
onChange={onModeChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{models &&
|
||||
models.availableModels.length > 1 &&
|
||||
onModelChange && (
|
||||
<ToolbarDropdown
|
||||
label={currentModelLabel}
|
||||
title={
|
||||
models.availableModels.find(
|
||||
(m) =>
|
||||
m.modelId ===
|
||||
models.currentModelId,
|
||||
)?.description ?? "Select model"
|
||||
}
|
||||
items={modelItems}
|
||||
currentValue={
|
||||
models.currentModelId ?? undefined
|
||||
}
|
||||
onChange={onModelChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Send/Stop Button */}
|
||||
<button
|
||||
|
|
|
|||
Loading…
Reference in a new issue