diff --git a/CHANGELOG.md b/CHANGELOG.md index 453e9377..f8e157f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.9.20] - 2026-08-01 + +### Added + +- 💬 **Chat settings have their own Admin page.** Admins can now turn chat title generation on or off, choose the models used for titles, summaries, and approval reviews, and adjust when long chats are compacted. +- 🗂️ **Git settings have their own Admin page.** Admins can now choose which model drafts commit messages. +- 🧠 **Memory and skills reviews can use a chosen model.** Background reviews can now use a specific model, or keep following the current chat model when left unset. + +### Changed + +- 🧭 **Model choices are clearer for helper features.** Model pickers now show when a feature is using the current or default model instead of forcing a saved choice. +- ⚙️ **Admin settings are easier to scan.** Chat and Git options moved out of Models into dedicated pages, leaving model setup focused on model setup. + +### Fixed + +- 🤖 **Auto mode asks at smarter moments.** Computer can now approve safe, expected tool use in Auto mode and still asks when an action looks risky or unclear. + ## [0.9.19] - 2026-07-31 ### Changed diff --git a/cptr/frontend/src/lib/apis/memory.ts b/cptr/frontend/src/lib/apis/memory.ts index fb9bbcd8..a660aac6 100644 --- a/cptr/frontend/src/lib/apis/memory.ts +++ b/cptr/frontend/src/lib/apis/memory.ts @@ -18,6 +18,7 @@ export type MemorySettings = { enabled: boolean; tool_enabled: boolean; background_review_enabled: boolean; + 'background_review.model'?: string | null; review_interval_turns: number; user_char_limit: number; workspace_char_limit: number; diff --git a/cptr/frontend/src/lib/components/Admin/Chat.svelte b/cptr/frontend/src/lib/components/Admin/Chat.svelte new file mode 100644 index 00000000..2f8f1369 --- /dev/null +++ b/cptr/frontend/src/lib/components/Admin/Chat.svelte @@ -0,0 +1,190 @@ + + +
+ {#if loading} +
+ {:else} +
+

{$t('admin.chat')}

+ +

+ {$t('admin.chatTitles')} +

+
+ +

+ {$t('admin.chatTitleGenerationHint')} +

+ + {#if titleGenerationEnabled} +
+
+ + {$t('admin.chatTitleModel')} + +
+ +
+
+

+ {$t('admin.chatTitleModelHint')} +

+
+ {/if} +
+ +

+ {$t('admin.contextCompaction')} +

+
+
+
+ + {$t('admin.contextSummaryModel')} + +
+ +
+
+

+ {$t('admin.contextSummaryModelHint')} +

+
+
+ +
+ + + {$t('admin.compactTokenThresholdUnit')} + +
+

+ {$t('admin.compactTokenThresholdHint')} +

+
+
+ +

+ {$t('admin.toolApproval')} +

+
+
+ + {$t('admin.toolApprovalReviewModel')} + +
+ +
+
+

+ {$t('admin.toolApprovalReviewModelHint')} +

+
+
+ +
+ +
+ {/if} +
diff --git a/cptr/frontend/src/lib/components/Admin/Git.svelte b/cptr/frontend/src/lib/components/Admin/Git.svelte new file mode 100644 index 00000000..3f3b954e --- /dev/null +++ b/cptr/frontend/src/lib/components/Admin/Git.svelte @@ -0,0 +1,77 @@ + + +
+ {#if loading} +
+ {:else} +
+

{$t('admin.git')}

+ +
+
+ + {$t('admin.gitCommitMessageModel')} + +
+ +
+
+

+ {$t('admin.gitCommitMessageModelHint')} +

+
+
+ +
+ +
+ {/if} +
diff --git a/cptr/frontend/src/lib/components/Admin/Models.svelte b/cptr/frontend/src/lib/components/Admin/Models.svelte index 45cba5b1..0b1a6c9d 100644 --- a/cptr/frontend/src/lib/components/Admin/Models.svelte +++ b/cptr/frontend/src/lib/components/Admin/Models.svelte @@ -48,10 +48,6 @@ // Default model let defaultModelId = $state(''); - // Context compaction - let compactTokenThreshold = $state(80000); - let compactDirty = $state(false); - const TEMPLATE_VARIABLES = [ { name: 'CPTR_CONTEXT', desc: 'Runtime, machine, workspace, and tool context' }, { name: 'RUNTIME_ENV', desc: 'Runtime environment (host or container)' }, @@ -117,7 +113,7 @@ Workspace: {{WORKSPACE_NAME}} Files: {{FILE_TREE}}`; - let hasDirty = $derived(globalDirty || compactDirty || models.some((m) => m.dirty)); + let hasDirty = $derived(globalDirty || models.some((m) => m.dirty)); function parseRows(config: ModelConfigEntry | undefined): ParamRow[] { const rp = config?.params?.request_params; @@ -256,10 +252,9 @@ Files: onMount(async () => { await loadModelConfig(); - // Load admin config (default model, context compaction) + // Load default model try { const adminCfg = await getAdminConfig(); - compactTokenThreshold = Number(adminCfg['chat.compact_token_threshold']) || 80000; defaultModelId = typeof adminCfg['chat.default_model'] === 'string' ? adminCfg['chat.default_model'] : ''; } catch {} @@ -349,12 +344,7 @@ Files: globalDirty = false; models.forEach((m) => (m.dirty = false)); - // Save default model - await updateConfig({ - 'chat.compact_token_threshold': compactTokenThreshold, - 'chat.default_model': defaultModelId - }); - compactDirty = false; + await updateConfig({ 'chat.default_model': defaultModelId }); toast.success($t('settings.saved')); } catch { @@ -552,44 +542,18 @@ Files: - -

- {$t('models.defaultModel')} -

- -
-

- {$t('models.defaultModelHint')} -

- - -

{$t('admin.contextCompaction')}

- -
-
- -
- (compactDirty = true)} - min="10000" - max="1000000" - step="10000" - class="w-24 h-7 px-2 rounded-lg text-xs bg-gray-100 dark:bg-white/6 text-gray-700 dark:text-gray-300 border border-gray-200 dark:border-white/8 outline-none focus:border-blue-400 dark:focus:border-blue-500 transition-colors" - /> - {$t('admin.compactTokenThresholdUnit')} +
+

+ {$t('models.defaultModel')} +

+
+
-

- {$t('admin.compactTokenThresholdHint')} -

+

+ {$t('models.defaultModelHint')} +

diff --git a/cptr/frontend/src/lib/components/Admin/Skills.svelte b/cptr/frontend/src/lib/components/Admin/Skills.svelte index f6d9600a..742d5d90 100644 --- a/cptr/frontend/src/lib/components/Admin/Skills.svelte +++ b/cptr/frontend/src/lib/components/Admin/Skills.svelte @@ -5,12 +5,14 @@ import { t } from '$lib/i18n'; import ToggleSwitch from '$lib/components/common/ToggleSwitch.svelte'; import Spinner from '$lib/components/common/Spinner.svelte'; + import ModelSelector from '$lib/components/common/ModelSelector.svelte'; let loading = $state(true); let saving = $state(false); let enabled = $state(true); let toolEnabled = $state(true); let backgroundReview = $state(true); + let backgroundReviewModel = $state(null); let reviewInterval = $state(10); onMount(async () => { @@ -22,6 +24,10 @@ backgroundReview = config['skills.background_review_enabled'] !== false && config['skills.background_review_enabled'] !== 'false'; + backgroundReviewModel = + typeof config['skills.background_review.model'] === 'string' + ? config['skills.background_review.model'] + : null; reviewInterval = Number(config['skills.review_interval_turns']) || 10; } catch { toast.error($t('admin.failedToLoadConfig')); @@ -36,6 +42,7 @@ 'skills.enabled': enabled, 'skills.tool_enabled': toolEnabled, 'skills.background_review_enabled': backgroundReview, + 'skills.background_review.model': backgroundReviewModel, 'skills.review_interval_turns': Math.max(1, Number(reviewInterval) || 10) }); toast.success($t('settings.saved')); @@ -94,6 +101,25 @@ }} /> + +
+
+ + {$t('admin.skillsBackgroundReviewModel')} + +
+ +
+
+

+ {$t('admin.skillsBackgroundReviewModelHint')} +

+
{/if}
diff --git a/cptr/frontend/src/lib/components/GitBar.svelte b/cptr/frontend/src/lib/components/GitBar.svelte index 16c58721..d4fe2c8e 100644 --- a/cptr/frontend/src/lib/components/GitBar.svelte +++ b/cptr/frontend/src/lib/components/GitBar.svelte @@ -1,7 +1,6 @@