feat(quick-pane): persist default thinking level
This commit is contained in:
@@ -12,6 +12,7 @@ const MAX_DRAFT_BYTES: usize = 128 * 1024;
|
||||
const MAX_TOTAL_DRAFT_BYTES: usize = 1024 * 1024;
|
||||
const MAX_TOTAL_STRING_BYTES: usize = 1536 * 1024;
|
||||
const MAX_FILE_BYTES: usize = 2 * 1024 * 1024;
|
||||
const DEFAULT_THINKING_LEVEL: &str = "medium";
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
@@ -47,6 +48,12 @@ pub struct QuickPaneSettings {
|
||||
pub follow_up_model: String,
|
||||
pub instructions: String,
|
||||
pub maximum_tool_calls: u32,
|
||||
#[serde(default = "default_thinking_level")]
|
||||
pub default_thinking_level: String,
|
||||
}
|
||||
|
||||
fn default_thinking_level() -> String {
|
||||
DEFAULT_THINKING_LEVEL.to_owned()
|
||||
}
|
||||
|
||||
impl Default for QuickPaneSettings {
|
||||
@@ -59,6 +66,7 @@ impl Default for QuickPaneSettings {
|
||||
follow_up_model: String::new(),
|
||||
instructions: "Answer quickly. Do not use subagents. Prefer read-only inspection. Escalate mutations, multi-step work, or complex work.".to_owned(),
|
||||
maximum_tool_calls: 6,
|
||||
default_thinking_level: default_thinking_level(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,6 +241,12 @@ pub fn validate(state: &UiStateV1) -> Result<(), String> {
|
||||
if !(1..=64).contains(&quick.maximum_tool_calls) {
|
||||
return Err("Quick-pane tool-call limit is outside the supported range".to_owned());
|
||||
}
|
||||
if !matches!(
|
||||
quick.default_thinking_level.as_str(),
|
||||
"off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"
|
||||
) {
|
||||
return Err("Quick-pane default thinking level is invalid".to_owned());
|
||||
}
|
||||
total_string_bytes = total_string_bytes
|
||||
.saturating_add(quick.workspace.len())
|
||||
.saturating_add(quick.provider.len())
|
||||
@@ -449,6 +463,12 @@ mod tests {
|
||||
state.quick_pane.follow_up_provider = "provider".into();
|
||||
state.quick_pane.follow_up_model = "follow-up-model".into();
|
||||
assert!(validate(&state).is_ok());
|
||||
state.quick_pane.default_thinking_level = "high".into();
|
||||
save_to(&root, &state).unwrap();
|
||||
assert_eq!(load_from(&root).unwrap().quick_pane.default_thinking_level, "high");
|
||||
state.quick_pane.default_thinking_level = "unsupported".into();
|
||||
assert!(validate(&state).is_err());
|
||||
state.quick_pane.default_thinking_level = default_thinking_level();
|
||||
state.quick_pane.maximum_tool_calls = 0;
|
||||
assert!(validate(&state).is_err());
|
||||
fs::remove_dir_all(root).unwrap();
|
||||
@@ -465,8 +485,11 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
let state = load_from(&root).unwrap();
|
||||
assert_eq!(state.quick_pane.default_thinking_level, DEFAULT_THINKING_LEVEL);
|
||||
save_to(&root, &state).unwrap();
|
||||
assert!(!fs::read_to_string(&path).unwrap().contains("maximumSeconds"));
|
||||
let saved = fs::read_to_string(&path).unwrap();
|
||||
assert!(!saved.contains("maximumSeconds"));
|
||||
assert!(saved.contains("\"defaultThinkingLevel\": \"medium\""));
|
||||
fs::remove_dir_all(root).unwrap();
|
||||
}
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "gpt-5",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
mocks.invoke.mockImplementation((command: string) => {
|
||||
@@ -189,6 +190,10 @@ describe("App command progress and errors", () => {
|
||||
await user.click(
|
||||
screen.getByRole("option", { name: /Claude.*anthropic\/claude\/sonnet/i }),
|
||||
);
|
||||
await user.selectOptions(
|
||||
screen.getByRole("combobox", { name: "Default thinking" }),
|
||||
"high",
|
||||
);
|
||||
await user.click(
|
||||
screen.getByRole("button", { name: "Save Quick Pane settings" }),
|
||||
);
|
||||
@@ -200,6 +205,7 @@ describe("App command progress and errors", () => {
|
||||
...loaded.quickPane,
|
||||
provider: "openrouter",
|
||||
model: "anthropic/claude/sonnet",
|
||||
defaultThinkingLevel: "high",
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -222,6 +228,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "old-model",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
});
|
||||
if (command === "create_quick_runtime") {
|
||||
@@ -283,6 +290,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "old-model",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
const first = deferred<{
|
||||
@@ -357,6 +365,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "old-model",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
const staleSnapshot = deferred<{
|
||||
@@ -450,6 +459,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "model",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
const snapshot = deferred<unknown>();
|
||||
@@ -487,6 +497,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "old-model",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
let openCount = 0;
|
||||
@@ -546,6 +557,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "old-model",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
let runtimeCount = 0;
|
||||
@@ -620,6 +632,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "old-model",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
const oldSnapshot = deferred<{
|
||||
@@ -693,6 +706,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "model",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
let runtimeCount = 0;
|
||||
@@ -764,6 +778,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "model",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
let createCount = 0;
|
||||
@@ -819,6 +834,7 @@ describe("App command progress and errors", () => {
|
||||
followUpModel: "model",
|
||||
instructions: "Stay concise.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
const nextSnapshot = deferred<unknown>();
|
||||
|
||||
+50
-2
@@ -6,13 +6,18 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import "./App.css";
|
||||
import { ConversationWorkspace } from "./components/ConversationWorkspace";
|
||||
import { ModelSelector } from "./components/ModelSelector";
|
||||
import { ThinkingSelector } from "./components/ThinkingSelector";
|
||||
import { DirectorySidebar } from "./components/DirectorySidebar";
|
||||
import { ExtensionDialog } from "./components/ExtensionDialog";
|
||||
import { CommandFormDialog } from "./components/CommandFormDialog";
|
||||
import type { RpcCommand } from "./commands/rpc";
|
||||
import { SessionPicker } from "./components/SessionPicker";
|
||||
import { SessionTabs } from "./components/SessionTabs";
|
||||
import { modelIdentity, reconcileModelIdentity } from "./model-catalog";
|
||||
import {
|
||||
modelIdentity,
|
||||
reconcileModelIdentity,
|
||||
reconcileThinkingLevel,
|
||||
} from "./model-catalog";
|
||||
import type {
|
||||
DirectorySession,
|
||||
DirectoryWorkspace,
|
||||
@@ -37,6 +42,7 @@ const defaultQuickPaneSettings: QuickPaneSettings = {
|
||||
instructions:
|
||||
"Answer quickly. Do not use subagents. Prefer read-only inspection. Escalate mutations, multi-step work, or complex work.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
};
|
||||
function validateQuickPaneSettings(settings: QuickPaneSettings) {
|
||||
if (!settings.workspace.startsWith("/"))
|
||||
@@ -53,6 +59,8 @@ function validateQuickPaneSettings(settings: QuickPaneSettings) {
|
||||
settings.maximumToolCalls > 64
|
||||
)
|
||||
return "Tool calls must be an integer from 1 to 64.";
|
||||
if (!settings.defaultThinkingLevel)
|
||||
return "Default thinking level is required.";
|
||||
}
|
||||
let submissionId = 0;
|
||||
type LocalOperation = {
|
||||
@@ -164,6 +172,15 @@ export default function App() {
|
||||
const [modelCatalogError, setModelCatalogError] = useState<string>();
|
||||
const [catalogWorkspace, setCatalogWorkspace] = useState<string>();
|
||||
const modelCatalogRequest = useRef(0);
|
||||
const quickPaneModel = useMemo(
|
||||
() =>
|
||||
modelCatalog.find(
|
||||
(model) =>
|
||||
model.provider === quickPaneSettings.provider &&
|
||||
model.id === quickPaneSettings.model,
|
||||
),
|
||||
[modelCatalog, quickPaneSettings.model, quickPaneSettings.provider],
|
||||
);
|
||||
const catalogProbeRuntimeIds = useRef(new Set<string>());
|
||||
const retainedCatalogProbeRuntimeIds = useRef(new Set<string>());
|
||||
const retainedCatalogProbeErrors = useRef(new Map<string, unknown>());
|
||||
@@ -291,18 +308,30 @@ export default function App() {
|
||||
provider: current.followUpProvider,
|
||||
modelId: current.followUpModel,
|
||||
});
|
||||
const selectedQuickModel = freshModels.find(
|
||||
(model) =>
|
||||
model.provider === quickModel?.provider &&
|
||||
model.id === quickModel?.modelId,
|
||||
);
|
||||
const next = {
|
||||
...current,
|
||||
provider: quickModel?.provider ?? "",
|
||||
model: quickModel?.modelId ?? "",
|
||||
followUpProvider: followUpModel?.provider ?? "",
|
||||
followUpModel: followUpModel?.modelId ?? "",
|
||||
defaultThinkingLevel: selectedQuickModel
|
||||
? reconcileThinkingLevel(
|
||||
selectedQuickModel,
|
||||
current.defaultThinkingLevel,
|
||||
)
|
||||
: current.defaultThinkingLevel,
|
||||
};
|
||||
if (
|
||||
next.provider === current.provider &&
|
||||
next.model === current.model &&
|
||||
next.followUpProvider === current.followUpProvider &&
|
||||
next.followUpModel === current.followUpModel
|
||||
next.followUpModel === current.followUpModel &&
|
||||
next.defaultThinkingLevel === current.defaultThinkingLevel
|
||||
)
|
||||
return current;
|
||||
setQuickPaneSettingsStatus(
|
||||
@@ -1082,12 +1111,31 @@ export default function App() {
|
||||
...current,
|
||||
provider: identity.provider,
|
||||
model: identity.modelId,
|
||||
defaultThinkingLevel: reconcileThinkingLevel(
|
||||
model,
|
||||
current.defaultThinkingLevel,
|
||||
),
|
||||
}));
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
<small>Provider comes from selected catalog model.</small>
|
||||
</div>
|
||||
<div className="quick-pane-model-setting">
|
||||
<ThinkingSelector
|
||||
label="Default thinking"
|
||||
model={quickPaneModel}
|
||||
value={quickPaneSettings.defaultThinkingLevel}
|
||||
disabled={modelCatalogStatus !== "ready" || !quickPaneModel}
|
||||
onChange={(defaultThinkingLevel) =>
|
||||
setQuickPaneSettings((current) => ({
|
||||
...current,
|
||||
defaultThinkingLevel,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<small>Used when a Quick Pane opens.</small>
|
||||
</div>
|
||||
<div className="quick-pane-model-setting">
|
||||
<ModelSelector
|
||||
label="Follow-up model"
|
||||
|
||||
@@ -4,6 +4,7 @@ import "./ThinkingSelector.css";
|
||||
|
||||
type ThinkingSelectorProps = {
|
||||
model?: Model;
|
||||
label?: string;
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
@@ -13,6 +14,7 @@ type ThinkingSelectorProps = {
|
||||
|
||||
export function ThinkingSelector({
|
||||
model,
|
||||
label = "Thinking",
|
||||
value,
|
||||
disabled = false,
|
||||
className,
|
||||
@@ -24,10 +26,10 @@ export function ThinkingSelector({
|
||||
|
||||
return (
|
||||
<label className={classes}>
|
||||
<span>Thinking</span>
|
||||
<span>{label}</span>
|
||||
<select
|
||||
className={controlClassName}
|
||||
aria-label="Thinking"
|
||||
aria-label={label}
|
||||
value={selected}
|
||||
disabled={disabled}
|
||||
onChange={(event) => onChange(event.currentTarget.value)}
|
||||
|
||||
@@ -45,6 +45,7 @@ const settings: QuickPaneSettings = {
|
||||
followUpModel: "full/model",
|
||||
instructions: "Stay read-only.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
};
|
||||
const uiState: UiState = {
|
||||
version: 1,
|
||||
@@ -112,6 +113,7 @@ function bridgeMock(
|
||||
activeSnapshot?: (
|
||||
index: number,
|
||||
) => RuntimeSnapshot | Promise<RuntimeSnapshot>,
|
||||
loadedState = uiState,
|
||||
) {
|
||||
let runtimeCount = 0;
|
||||
const submitted = new Set<string>();
|
||||
@@ -121,7 +123,7 @@ function bridgeMock(
|
||||
command: string,
|
||||
payload?: { runtimeId?: string; agentId?: string; message?: string },
|
||||
) => {
|
||||
if (command === "load_ui_state") return Promise.resolve(uiState);
|
||||
if (command === "load_ui_state") return Promise.resolve(loadedState);
|
||||
if (command === "create_quick_runtime") {
|
||||
runtimeCount += 1;
|
||||
return Promise.resolve({
|
||||
@@ -232,7 +234,7 @@ describe("QuickPane", () => {
|
||||
expect(
|
||||
Array.from(pane?.children ?? []).map((element) => element.classList[0]),
|
||||
).toEqual([
|
||||
"quick-pane-drag-strip",
|
||||
"quick-pane-drag-region",
|
||||
"quick-pane-toolbar",
|
||||
"quick-pane-composer",
|
||||
"quick-pane-exchange",
|
||||
@@ -278,15 +280,18 @@ describe("QuickPane", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("starts native dragging only from the strip above toolbar controls", async () => {
|
||||
it("starts native dragging from whole visible region, never toolbar controls", async () => {
|
||||
bridgeMock([defaultModels]);
|
||||
const { container } = render(<QuickPane />);
|
||||
await preparedTrigger();
|
||||
fireEvent.pointerDown(container.querySelector(".quick-pane-drag-strip")!);
|
||||
const dragRegion = container.querySelector(".quick-pane-drag-region")!;
|
||||
expect(dragRegion.querySelector(".quick-pane-drag-grip")).toBeTruthy();
|
||||
fireEvent.pointerDown(dragRegion);
|
||||
expect(mocks.startDragging).toHaveBeenCalledTimes(1);
|
||||
fireEvent.pointerDown(
|
||||
screen.getByRole("button", { name: "Close Quick Pane" }),
|
||||
);
|
||||
fireEvent.pointerDown(screen.getByRole("combobox", { name: "Thinking" }));
|
||||
expect(mocks.startDragging).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -422,6 +427,33 @@ describe("QuickPane", () => {
|
||||
expect(input.selectionDirection).toBe("none");
|
||||
});
|
||||
|
||||
it("applies persisted default thinking level when supported", async () => {
|
||||
const thinkingModels: Model[] = [
|
||||
{
|
||||
provider: "provider",
|
||||
id: "quick/model",
|
||||
reasoning: true,
|
||||
thinkingLevelMap: { high: "high" },
|
||||
},
|
||||
];
|
||||
bridgeMock([thinkingModels], undefined, {
|
||||
...uiState,
|
||||
quickPane: { ...settings, defaultThinkingLevel: "high" },
|
||||
});
|
||||
render(<QuickPane />);
|
||||
await preparedTrigger();
|
||||
expect(screen.getByRole("combobox", { name: "Thinking" })).toHaveValue(
|
||||
"high",
|
||||
);
|
||||
await ask("Inspect");
|
||||
await waitFor(() =>
|
||||
expect(mocks.invoke).toHaveBeenCalledWith("set_thinking_level", {
|
||||
agentId: "agent-1",
|
||||
level: "high",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("derives thinking choices from selected model metadata and falls back to Off", async () => {
|
||||
const thinkingModels: Model[] = [
|
||||
{
|
||||
@@ -448,6 +480,7 @@ describe("QuickPane", () => {
|
||||
.getAllByRole("option")
|
||||
.map((option) => option.textContent),
|
||||
).toEqual(["Off", "High"]);
|
||||
expect(thinking).toHaveValue("off");
|
||||
await userEvent.selectOptions(thinking, "high");
|
||||
await ask("Inspect");
|
||||
await waitFor(() =>
|
||||
|
||||
@@ -39,6 +39,7 @@ const defaults: QuickPaneSettings = {
|
||||
instructions:
|
||||
"Answer quickly. Do not use subagents. Prefer read-only inspection. Escalate mutations, multi-step work, or complex work.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
};
|
||||
|
||||
type Runtime = { agentId: string; runtimeId: string };
|
||||
@@ -170,6 +171,8 @@ function settingsError(settings: QuickPaneSettings) {
|
||||
settings.maximumToolCalls > 64
|
||||
)
|
||||
return "Tool calls must be an integer from 1 to 64.";
|
||||
if (!settings.defaultThinkingLevel)
|
||||
return "Default thinking level is required.";
|
||||
}
|
||||
|
||||
function currentTurnAssistantText(
|
||||
@@ -223,7 +226,9 @@ export default function QuickPane() {
|
||||
const [requestActive, setRequestActive] = useState(false);
|
||||
const [chatReplacing, setChatReplacing] = useState(false);
|
||||
const [promoting, setPromoting] = useState(false);
|
||||
const [thinkingLevel, setThinkingLevel] = useState("medium");
|
||||
const [thinkingLevel, setThinkingLevel] = useState(
|
||||
defaults.defaultThinkingLevel,
|
||||
);
|
||||
const [models, setModels] = useState<Model[]>([]);
|
||||
const [catalogStatus, setCatalogStatus] = useState<
|
||||
"loading" | "ready" | "error"
|
||||
@@ -605,6 +610,7 @@ export default function QuickPane() {
|
||||
const nextSettings = { ...defaults, ...state.quickPane };
|
||||
settingsRef.current = nextSettings;
|
||||
setSettings(nextSettings);
|
||||
setThinkingLevel(nextSettings.defaultThinkingLevel);
|
||||
setSettingsLoaded(true);
|
||||
const configuredModel = {
|
||||
provider: nextSettings.provider,
|
||||
@@ -815,6 +821,7 @@ export default function QuickPane() {
|
||||
const nextSettings = { ...defaults, ...event.payload };
|
||||
settingsRef.current = nextSettings;
|
||||
setSettings(nextSettings);
|
||||
setThinkingLevel(nextSettings.defaultThinkingLevel);
|
||||
const configuredModel = {
|
||||
provider: nextSettings.provider,
|
||||
modelId: nextSettings.model,
|
||||
@@ -1129,14 +1136,16 @@ export default function QuickPane() {
|
||||
return (
|
||||
<main className="quick-pane" ref={paneRef}>
|
||||
<div
|
||||
className="quick-pane-drag-strip"
|
||||
className="quick-pane-drag-region"
|
||||
aria-hidden="true"
|
||||
onPointerDown={() =>
|
||||
void getCurrentWindow()
|
||||
.startDragging()
|
||||
.catch(() => undefined)
|
||||
}
|
||||
/>
|
||||
>
|
||||
<span className="quick-pane-drag-grip" />
|
||||
</div>
|
||||
<header className="quick-pane-toolbar">
|
||||
<div className="quick-pane-primary-controls">
|
||||
<ModelSelector
|
||||
|
||||
@@ -47,11 +47,13 @@ body {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-pane-drag-strip {
|
||||
.quick-pane-drag-region {
|
||||
position: relative;
|
||||
height: 8px;
|
||||
flex: 0 0 8px;
|
||||
cursor: move;
|
||||
display: grid;
|
||||
height: 24px;
|
||||
flex: 0 0 24px;
|
||||
place-items: center;
|
||||
cursor: grab;
|
||||
touch-action: none;
|
||||
border-bottom: 1px solid #70522f;
|
||||
background: linear-gradient(
|
||||
@@ -62,17 +64,20 @@ body {
|
||||
);
|
||||
}
|
||||
|
||||
.quick-pane-drag-strip::after {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 50%;
|
||||
.quick-pane-drag-region:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.quick-pane-drag-grip {
|
||||
width: 44px;
|
||||
height: 3px;
|
||||
height: 5px;
|
||||
border-radius: 999px;
|
||||
background: #f0a347;
|
||||
background: repeating-linear-gradient(
|
||||
90deg,
|
||||
#f0a347 0 4px,
|
||||
transparent 4px 7px
|
||||
);
|
||||
box-shadow: 0 0 8px rgba(240, 163, 71, 0.45);
|
||||
content: "";
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.quick-pane-toolbar {
|
||||
|
||||
@@ -36,6 +36,7 @@ const settings: UiState = {
|
||||
followUpModel: "model",
|
||||
instructions: "Stay read-only.",
|
||||
maximumToolCalls: 6,
|
||||
defaultThinkingLevel: "medium",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -239,6 +239,7 @@ export type QuickPaneSettings = {
|
||||
followUpModel: string;
|
||||
instructions: string;
|
||||
maximumToolCalls: number;
|
||||
defaultThinkingLevel: string;
|
||||
};
|
||||
|
||||
export type UiState = {
|
||||
|
||||
Reference in New Issue
Block a user