Compare commits

..

1 Commits

Author SHA1 Message Date
minori fab350b71d deps: update @codemirror/state to 6.6.0
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m23s
CI / Lint & Test (pull_request) Successful in 16m50s
CI / Build Linux (pull_request) Successful in 20m22s
CI / Build Windows (cross-compile) (pull_request) Successful in 30m13s
2026-03-22 07:02:51 -07:00
26 changed files with 81 additions and 1134 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "hikari-desktop", "name": "hikari-desktop",
"version": "1.13.0", "version": "1.12.0",
"description": "", "description": "",
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -47,9 +47,9 @@
"@codemirror/lang-wast": "6.0.2", "@codemirror/lang-wast": "6.0.2",
"@codemirror/lang-xml": "6.1.0", "@codemirror/lang-xml": "6.1.0",
"@codemirror/lang-yaml": "6.1.2", "@codemirror/lang-yaml": "6.1.2",
"@codemirror/language": "6.12.3", "@codemirror/language": "6.12.2",
"@codemirror/legacy-modes": "6.5.2", "@codemirror/legacy-modes": "6.5.2",
"@codemirror/state": "6.5.4", "@codemirror/state": "6.6.0",
"@codemirror/theme-one-dark": "6.1.3", "@codemirror/theme-one-dark": "6.1.3",
"@codemirror/view": "6.39.15", "@codemirror/view": "6.39.15",
"@lezer/highlight": "1.2.3", "@lezer/highlight": "1.2.3",
+53 -53
View File
@@ -69,14 +69,14 @@ importers:
specifier: 6.1.2 specifier: 6.1.2
version: 6.1.2 version: 6.1.2
'@codemirror/language': '@codemirror/language':
specifier: 6.12.3 specifier: 6.12.2
version: 6.12.3 version: 6.12.2
'@codemirror/legacy-modes': '@codemirror/legacy-modes':
specifier: 6.5.2 specifier: 6.5.2
version: 6.5.2 version: 6.5.2
'@codemirror/state': '@codemirror/state':
specifier: 6.5.4 specifier: 6.6.0
version: 6.5.4 version: 6.6.0
'@codemirror/theme-one-dark': '@codemirror/theme-one-dark':
specifier: 6.1.3 specifier: 6.1.3
version: 6.1.3 version: 6.1.3
@@ -310,8 +310,8 @@ packages:
'@codemirror/lang-yaml@6.1.2': '@codemirror/lang-yaml@6.1.2':
resolution: {integrity: sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==} resolution: {integrity: sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==}
'@codemirror/language@6.12.3': '@codemirror/language@6.12.2':
resolution: {integrity: sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==} resolution: {integrity: sha512-jEPmz2nGGDxhRTg3lTpzmIyGKxz3Gp3SJES4b0nAuE5SWQoKdT5GoQ69cwMmFd+wvFUhYirtDTr0/DRHpQAyWg==}
'@codemirror/legacy-modes@6.5.2': '@codemirror/legacy-modes@6.5.2':
resolution: {integrity: sha512-/jJbwSTazlQEDOQw2FJ8LEEKVS72pU0lx6oM54kGpL8t/NJ2Jda3CZ4pcltiKTdqYSRk3ug1B3pil1gsjA6+8Q==} resolution: {integrity: sha512-/jJbwSTazlQEDOQw2FJ8LEEKVS72pU0lx6oM54kGpL8t/NJ2Jda3CZ4pcltiKTdqYSRk3ug1B3pil1gsjA6+8Q==}
@@ -322,8 +322,8 @@ packages:
'@codemirror/search@6.6.0': '@codemirror/search@6.6.0':
resolution: {integrity: sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==} resolution: {integrity: sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==}
'@codemirror/state@6.5.4': '@codemirror/state@6.6.0':
resolution: {integrity: sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==} resolution: {integrity: sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==}
'@codemirror/theme-one-dark@6.1.3': '@codemirror/theme-one-dark@6.1.3':
resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==}
@@ -2200,15 +2200,15 @@ snapshots:
'@codemirror/autocomplete@6.20.0': '@codemirror/autocomplete@6.20.0':
dependencies: dependencies:
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@codemirror/commands@6.10.2': '@codemirror/commands@6.10.2':
dependencies: dependencies:
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
@@ -2216,29 +2216,29 @@ snapshots:
dependencies: dependencies:
'@codemirror/lang-html': 6.4.11 '@codemirror/lang-html': 6.4.11
'@codemirror/lang-javascript': 6.2.4 '@codemirror/lang-javascript': 6.2.4
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/highlight': 1.2.3 '@lezer/highlight': 1.2.3
'@lezer/lr': 1.4.8 '@lezer/lr': 1.4.8
'@codemirror/lang-cpp@6.0.3': '@codemirror/lang-cpp@6.0.3':
dependencies: dependencies:
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@lezer/cpp': 1.1.5 '@lezer/cpp': 1.1.5
'@codemirror/lang-css@6.3.1': '@codemirror/lang-css@6.3.1':
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.0 '@codemirror/autocomplete': 6.20.0
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/css': 1.3.0 '@lezer/css': 1.3.0
'@codemirror/lang-go@6.0.1': '@codemirror/lang-go@6.0.1':
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.0 '@codemirror/autocomplete': 6.20.0
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/go': 1.0.1 '@lezer/go': 1.0.1
@@ -2247,8 +2247,8 @@ snapshots:
'@codemirror/autocomplete': 6.20.0 '@codemirror/autocomplete': 6.20.0
'@codemirror/lang-css': 6.3.1 '@codemirror/lang-css': 6.3.1
'@codemirror/lang-javascript': 6.2.4 '@codemirror/lang-javascript': 6.2.4
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/css': 1.3.0 '@lezer/css': 1.3.0
@@ -2256,28 +2256,28 @@ snapshots:
'@codemirror/lang-java@6.0.2': '@codemirror/lang-java@6.0.2':
dependencies: dependencies:
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@lezer/java': 1.1.3 '@lezer/java': 1.1.3
'@codemirror/lang-javascript@6.2.4': '@codemirror/lang-javascript@6.2.4':
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.0 '@codemirror/autocomplete': 6.20.0
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/lint': 6.9.3 '@codemirror/lint': 6.9.3
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/javascript': 1.5.4 '@lezer/javascript': 1.5.4
'@codemirror/lang-json@6.0.2': '@codemirror/lang-json@6.0.2':
dependencies: dependencies:
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@lezer/json': 1.0.3 '@lezer/json': 1.0.3
'@codemirror/lang-less@6.0.2': '@codemirror/lang-less@6.0.2':
dependencies: dependencies:
'@codemirror/lang-css': 6.3.1 '@codemirror/lang-css': 6.3.1
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/highlight': 1.2.3 '@lezer/highlight': 1.2.3
'@lezer/lr': 1.4.8 '@lezer/lr': 1.4.8
@@ -2286,8 +2286,8 @@ snapshots:
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.0 '@codemirror/autocomplete': 6.20.0
'@codemirror/lang-html': 6.4.11 '@codemirror/lang-html': 6.4.11
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/markdown': 1.6.3 '@lezer/markdown': 1.6.3
@@ -2295,37 +2295,37 @@ snapshots:
'@codemirror/lang-php@6.0.2': '@codemirror/lang-php@6.0.2':
dependencies: dependencies:
'@codemirror/lang-html': 6.4.11 '@codemirror/lang-html': 6.4.11
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/php': 1.0.5 '@lezer/php': 1.0.5
'@codemirror/lang-python@6.2.1': '@codemirror/lang-python@6.2.1':
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.0 '@codemirror/autocomplete': 6.20.0
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/python': 1.1.18 '@lezer/python': 1.1.18
'@codemirror/lang-rust@6.0.2': '@codemirror/lang-rust@6.0.2':
dependencies: dependencies:
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@lezer/rust': 1.0.2 '@lezer/rust': 1.0.2
'@codemirror/lang-sass@6.0.2': '@codemirror/lang-sass@6.0.2':
dependencies: dependencies:
'@codemirror/lang-css': 6.3.1 '@codemirror/lang-css': 6.3.1
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/sass': 1.1.0 '@lezer/sass': 1.1.0
'@codemirror/lang-sql@6.10.0': '@codemirror/lang-sql@6.10.0':
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.0 '@codemirror/autocomplete': 6.20.0
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/highlight': 1.2.3 '@lezer/highlight': 1.2.3
'@lezer/lr': 1.4.8 '@lezer/lr': 1.4.8
@@ -2334,14 +2334,14 @@ snapshots:
dependencies: dependencies:
'@codemirror/lang-html': 6.4.11 '@codemirror/lang-html': 6.4.11
'@codemirror/lang-javascript': 6.2.4 '@codemirror/lang-javascript': 6.2.4
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/highlight': 1.2.3 '@lezer/highlight': 1.2.3
'@lezer/lr': 1.4.8 '@lezer/lr': 1.4.8
'@codemirror/lang-wast@6.0.2': '@codemirror/lang-wast@6.0.2':
dependencies: dependencies:
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/highlight': 1.2.3 '@lezer/highlight': 1.2.3
'@lezer/lr': 1.4.8 '@lezer/lr': 1.4.8
@@ -2349,8 +2349,8 @@ snapshots:
'@codemirror/lang-xml@6.1.0': '@codemirror/lang-xml@6.1.0':
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.0 '@codemirror/autocomplete': 6.20.0
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/xml': 1.0.6 '@lezer/xml': 1.0.6
@@ -2358,16 +2358,16 @@ snapshots:
'@codemirror/lang-yaml@6.1.2': '@codemirror/lang-yaml@6.1.2':
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.0 '@codemirror/autocomplete': 6.20.0
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/highlight': 1.2.3 '@lezer/highlight': 1.2.3
'@lezer/lr': 1.4.8 '@lezer/lr': 1.4.8
'@lezer/yaml': 1.0.3 '@lezer/yaml': 1.0.3
'@codemirror/language@6.12.3': '@codemirror/language@6.12.2':
dependencies: dependencies:
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
'@lezer/common': 1.5.0 '@lezer/common': 1.5.0
'@lezer/highlight': 1.2.3 '@lezer/highlight': 1.2.3
@@ -2376,34 +2376,34 @@ snapshots:
'@codemirror/legacy-modes@6.5.2': '@codemirror/legacy-modes@6.5.2':
dependencies: dependencies:
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/lint@6.9.3': '@codemirror/lint@6.9.3':
dependencies: dependencies:
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
crelt: 1.0.6 crelt: 1.0.6
'@codemirror/search@6.6.0': '@codemirror/search@6.6.0':
dependencies: dependencies:
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
crelt: 1.0.6 crelt: 1.0.6
'@codemirror/state@6.5.4': '@codemirror/state@6.6.0':
dependencies: dependencies:
'@marijn/find-cluster-break': 1.0.2 '@marijn/find-cluster-break': 1.0.2
'@codemirror/theme-one-dark@6.1.3': '@codemirror/theme-one-dark@6.1.3':
dependencies: dependencies:
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
'@lezer/highlight': 1.2.3 '@lezer/highlight': 1.2.3
'@codemirror/view@6.39.15': '@codemirror/view@6.39.15':
dependencies: dependencies:
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
crelt: 1.0.6 crelt: 1.0.6
style-mod: 4.1.3 style-mod: 4.1.3
w3c-keyname: 2.2.8 w3c-keyname: 2.2.8
@@ -3232,10 +3232,10 @@ snapshots:
dependencies: dependencies:
'@codemirror/autocomplete': 6.20.0 '@codemirror/autocomplete': 6.20.0
'@codemirror/commands': 6.10.2 '@codemirror/commands': 6.10.2
'@codemirror/language': 6.12.3 '@codemirror/language': 6.12.2
'@codemirror/lint': 6.9.3 '@codemirror/lint': 6.9.3
'@codemirror/search': 6.6.0 '@codemirror/search': 6.6.0
'@codemirror/state': 6.5.4 '@codemirror/state': 6.6.0
'@codemirror/view': 6.39.15 '@codemirror/view': 6.39.15
color-convert@2.0.1: color-convert@2.0.1:
+1 -1
View File
@@ -1648,7 +1648,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hikari-desktop" name = "hikari-desktop"
version = "1.13.0" version = "1.12.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"dirs 5.0.1", "dirs 5.0.1",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "hikari-desktop" name = "hikari-desktop"
version = "1.13.0" version = "1.12.0"
description = "Hikari - Claude Code Visual Assistant" description = "Hikari - Claude Code Visual Assistant"
authors = ["Naomi Carrigan"] authors = ["Naomi Carrigan"]
edition = "2021" edition = "2021"
+3 -17
View File
@@ -1464,7 +1464,6 @@ pub async fn close_application(app_handle: AppHandle) -> Result<(), String> {
pub struct MemoryFileInfo { pub struct MemoryFileInfo {
pub path: String, pub path: String,
pub heading: Option<String>, pub heading: Option<String>,
pub last_modified: Option<String>, // Unix timestamp in seconds as a string
} }
#[derive(serde::Serialize)] #[derive(serde::Serialize)]
@@ -1536,11 +1535,7 @@ async fn list_memory_files_via_wsl() -> Result<MemoryFilesResponse, String> {
let mut files = Vec::new(); let mut files = Vec::new();
for path in paths { for path in paths {
let heading = read_wsl_file_first_heading(&path); let heading = read_wsl_file_first_heading(&path);
files.push(MemoryFileInfo { files.push(MemoryFileInfo { path, heading });
path,
heading,
last_modified: None,
});
} }
Ok(MemoryFilesResponse { files }) Ok(MemoryFilesResponse { files })
@@ -1610,23 +1605,14 @@ async fn list_memory_files_native() -> Result<MemoryFilesResponse, String> {
// Sort files alphabetically // Sort files alphabetically
memory_paths.sort(); memory_paths.sort();
// Read first heading and modification time from each file // Read first heading from each file
let files = memory_paths let files = memory_paths
.into_iter() .into_iter()
.map(|path| { .map(|path| {
let heading = fs::read_to_string(&path) let heading = fs::read_to_string(&path)
.ok() .ok()
.and_then(|content| extract_first_heading(&content)); .and_then(|content| extract_first_heading(&content));
let last_modified = fs::metadata(&path) MemoryFileInfo { path, heading }
.ok()
.and_then(|m| m.modified().ok())
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.as_secs().to_string());
MemoryFileInfo {
path,
heading,
last_modified,
}
}) })
.collect(); .collect();
-3
View File
@@ -51,9 +51,6 @@ pub struct ClaudeStartOptions {
#[serde(default)] #[serde(default)]
pub model_overrides: Option<HashMap<String, String>>, pub model_overrides: Option<HashMap<String, String>>,
#[serde(default)]
pub session_name: Option<String>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
-175
View File
@@ -280,44 +280,6 @@ pub struct UserQuestionEvent {
pub conversation_id: Option<String>, pub conversation_id: Option<String>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ElicitationEvent {
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub conversation_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ElicitationResultEvent {
pub action: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub conversation_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StopFailureEvent {
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub conversation_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PostCompactEvent {
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub conversation_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentStartEvent { pub struct AgentStartEvent {
pub tool_use_id: String, pub tool_use_id: String,
@@ -604,141 +566,4 @@ mod tests {
panic!("Expected RateLimitEvent variant"); panic!("Expected RateLimitEvent variant");
} }
} }
#[test]
fn test_elicitation_event_serialization() {
let event = ElicitationEvent {
message: "Please provide the API endpoint".to_string(),
server_name: Some("my-server".to_string()),
request_id: Some("req-123".to_string()),
conversation_id: Some("conv-abc".to_string()),
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(serialized.contains("\"message\":\"Please provide the API endpoint\""));
assert!(serialized.contains("\"server_name\":\"my-server\""));
assert!(serialized.contains("\"request_id\":\"req-123\""));
assert!(serialized.contains("\"conversation_id\":\"conv-abc\""));
}
#[test]
fn test_elicitation_event_omits_none_fields() {
let event = ElicitationEvent {
message: "Enter your token".to_string(),
server_name: None,
request_id: None,
conversation_id: None,
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(serialized.contains("\"message\":\"Enter your token\""));
assert!(!serialized.contains("server_name"));
assert!(!serialized.contains("request_id"));
assert!(!serialized.contains("conversation_id"));
}
#[test]
fn test_elicitation_result_event_serialization() {
let event = ElicitationResultEvent {
action: "accept".to_string(),
request_id: Some("req-123".to_string()),
conversation_id: Some("conv-abc".to_string()),
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(serialized.contains("\"action\":\"accept\""));
assert!(serialized.contains("\"request_id\":\"req-123\""));
}
#[test]
fn test_elicitation_result_event_cancel_omits_none_fields() {
let event = ElicitationResultEvent {
action: "cancel".to_string(),
request_id: None,
conversation_id: None,
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(serialized.contains("\"action\":\"cancel\""));
assert!(!serialized.contains("request_id"));
assert!(!serialized.contains("conversation_id"));
}
#[test]
fn test_stop_failure_event_serialization() {
let event = StopFailureEvent {
stop_reason: Some("api_error".to_string()),
error_type: Some("rate_limit".to_string()),
conversation_id: Some("conv-abc".to_string()),
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(serialized.contains("\"stop_reason\":\"api_error\""));
assert!(serialized.contains("\"error_type\":\"rate_limit\""));
assert!(serialized.contains("\"conversation_id\":\"conv-abc\""));
}
#[test]
fn test_stop_failure_event_omits_none_fields() {
let event = StopFailureEvent {
stop_reason: None,
error_type: None,
conversation_id: None,
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(!serialized.contains("stop_reason"));
assert!(!serialized.contains("error_type"));
assert!(!serialized.contains("conversation_id"));
}
#[test]
fn test_stop_failure_event_partial_fields() {
let event = StopFailureEvent {
stop_reason: Some("api_error".to_string()),
error_type: None,
conversation_id: None,
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(serialized.contains("\"stop_reason\":\"api_error\""));
assert!(!serialized.contains("error_type"));
assert!(!serialized.contains("conversation_id"));
}
#[test]
fn test_post_compact_event_serialization() {
let event = PostCompactEvent {
session_id: Some("sess-abc".to_string()),
conversation_id: Some("conv-123".to_string()),
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(serialized.contains("\"session_id\":\"sess-abc\""));
assert!(serialized.contains("\"conversation_id\":\"conv-123\""));
}
#[test]
fn test_post_compact_event_omits_none_fields() {
let event = PostCompactEvent {
session_id: None,
conversation_id: None,
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(!serialized.contains("session_id"));
assert!(!serialized.contains("conversation_id"));
}
#[test]
fn test_post_compact_event_partial_fields() {
let event = PostCompactEvent {
session_id: Some("sess-xyz".to_string()),
conversation_id: None,
};
let serialized = serde_json::to_string(&event).unwrap();
assert!(serialized.contains("\"session_id\":\"sess-xyz\""));
assert!(!serialized.contains("conversation_id"));
}
} }
+9 -458
View File
@@ -15,10 +15,9 @@ use crate::process_ext::HideWindow;
use crate::stats::{calculate_cost, StatsUpdateEvent, UsageStats}; use crate::stats::{calculate_cost, StatsUpdateEvent, UsageStats};
use crate::types::{ use crate::types::{
AgentEndEvent, AgentStartEvent, CharacterState, ClaudeMessage, ConnectionEvent, AgentEndEvent, AgentStartEvent, CharacterState, ClaudeMessage, ConnectionEvent,
ConnectionStatus, ContentBlock, ElicitationEvent, ElicitationResultEvent, MessageCost, ConnectionStatus, ContentBlock, MessageCost, OutputEvent, PermissionPromptEvent,
OutputEvent, PermissionPromptEvent, PermissionPromptEventItem, QuestionOption, SessionEvent, PermissionPromptEventItem, QuestionOption, SessionEvent, StateChangeEvent, TodoItem,
PostCompactEvent, StateChangeEvent, StopFailureEvent, TodoItem, TodoUpdateEvent, TodoUpdateEvent, UserQuestionEvent, WorkingDirectoryEvent, WorktreeEvent, WorktreeInfo,
UserQuestionEvent, WorkingDirectoryEvent, WorktreeEvent, WorktreeInfo,
}; };
use parking_lot::RwLock; use parking_lot::RwLock;
use std::cell::RefCell; use std::cell::RefCell;
@@ -287,13 +286,6 @@ impl WslBridge {
} }
} }
// Pass session name to Claude Code if specified
if let Some(ref name) = options.session_name {
if !name.is_empty() {
cmd.args(["--name", name]);
}
}
// Add worktree flag if requested // Add worktree flag if requested
if options.use_worktree { if options.use_worktree {
cmd.arg("--worktree"); cmd.arg("--worktree");
@@ -473,14 +465,6 @@ impl WslBridge {
} }
} }
// Pass session name to Claude Code if specified
if let Some(ref name) = options.session_name {
if !name.is_empty() {
let escaped = name.replace('\'', "'\\''");
claude_cmd.push_str(&format!(" --name '{}'", escaped));
}
}
// Add worktree flag if requested // Add worktree flag if requested
if options.use_worktree { if options.use_worktree {
claude_cmd.push_str(" --worktree"); claude_cmd.push_str(" --worktree");
@@ -603,13 +587,12 @@ impl WslBridge {
); );
// Watchdog: if system:init never arrives the process is truly hung (e.g. a silent crash // Watchdog: if system:init never arrives the process is truly hung (e.g. a silent crash
// after spawning). After 30 seconds we kill it so the user is not stuck forever. // after spawning). After 5 minutes we kill it so the user isn't stuck forever.
// The CLI v2.1.79 stdin-hang fix means startup is reliably fast; 30 s is a generous // handle_stdout will surface the error when stdout closes after the kill.
// safety net rather than a workaround for a known hang.
let process_watchdog = self.process.clone(); let process_watchdog = self.process.clone();
let received_init_watchdog = self.received_init.clone(); let received_init_watchdog = self.received_init.clone();
thread::spawn(move || { thread::spawn(move || {
thread::sleep(Duration::from_secs(30)); thread::sleep(Duration::from_secs(60));
if !received_init_watchdog.load(Ordering::SeqCst) { if !received_init_watchdog.load(Ordering::SeqCst) {
if let Some(mut proc) = process_watchdog.lock().take() { if let Some(mut proc) = process_watchdog.lock().take() {
let _ = proc.kill(); let _ = proc.kill();
@@ -630,9 +613,6 @@ impl WslBridge {
let process_mid_watchdog = self.process.clone(); let process_mid_watchdog = self.process.clone();
let pending_since_watchdog = self.pending_since.clone(); let pending_since_watchdog = self.pending_since.clone();
let generation_watchdog = self.watchdog_generation.clone(); let generation_watchdog = self.watchdog_generation.clone();
// 5-minute stuck timeout is intentionally larger than the CLI's 2-minute per-attempt
// non-streaming fallback (added in v2.1.79). A non-streaming response will either
// succeed or fail with a Result message well within this window.
const STUCK_TIMEOUT: Duration = Duration::from_secs(5 * 60); const STUCK_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const POLL_INTERVAL: Duration = Duration::from_secs(30); const POLL_INTERVAL: Duration = Duration::from_secs(30);
thread::spawn(move || { thread::spawn(move || {
@@ -803,17 +783,14 @@ impl WslBridge {
} }
let input_chars = stats.current_request_input.as_ref().map(|s| s.len() as u64).unwrap_or(0); let input_chars = stats.current_request_input.as_ref().map(|s| s.len() as u64).unwrap_or(0);
let model = stats.model.clone().unwrap_or_else(|| "claude-sonnet-4-6".to_string()); let model = stats.model.clone().unwrap_or_else(|| "claude-sonnet-4-5-20250929".to_string());
(input_chars, stats.current_request_output_chars, stats.current_request_thinking_chars, stats.current_request_tools.clone(), model) (input_chars, stats.current_request_output_chars, stats.current_request_thinking_chars, stats.current_request_tools.clone(), model)
}; };
tracing::info!("[COST ESTIMATION] Estimating cost for interrupted request"); tracing::info!("[COST ESTIMATION] Estimating cost for interrupted request");
// Char-based estimation: 3.5 chars/token is intentionally conservative (standard English // Use conservative 3.5 chars/token for estimation (vs standard 4)
// averages ~4 chars/token). CLI v2.1.75 fixed over-counting in Claude Code's compaction
// logic but does not affect this heuristic — we count characters ourselves, independently
// of Claude Code's internal token tracking. The 1.2 safety margin avoids undercharging.
let estimated_input_tokens = (input_chars as f64 / 3.5).ceil() as u64; let estimated_input_tokens = (input_chars as f64 / 3.5).ceil() as u64;
let estimated_output_tokens = ((output_chars as f64 / 3.5).ceil() as u64) let estimated_output_tokens = ((output_chars as f64 / 3.5).ceil() as u64)
+ ((thinking_chars as f64 / 3.5).ceil() as u64); + ((thinking_chars as f64 / 3.5).ceil() as u64);
@@ -1074,21 +1051,11 @@ fn handle_stderr(
// Hook events are informational — emit with distinct types instead of error // Hook events are informational — emit with distinct types instead of error
let is_worktree_create = line.contains("[WorktreeCreate Hook]"); let is_worktree_create = line.contains("[WorktreeCreate Hook]");
let is_worktree_remove = line.contains("[WorktreeRemove Hook]"); let is_worktree_remove = line.contains("[WorktreeRemove Hook]");
let is_elicitation = line.contains("[Elicitation Hook]");
let is_elicitation_result = line.contains("[ElicitationResult Hook]");
let is_stop_failure = line.contains("[StopFailure Hook]");
let is_post_compact = line.contains("[PostCompact Hook]");
let line_type = if is_worktree_create || is_worktree_remove { let line_type = if is_worktree_create || is_worktree_remove {
"worktree" "worktree"
} else if line.contains("[ConfigChange Hook]") { } else if line.contains("[ConfigChange Hook]") {
"config-change" "config-change"
} else if is_elicitation || is_elicitation_result {
"elicitation"
} else if is_stop_failure {
"error"
} else if is_post_compact {
"compact-prompt"
} else { } else {
"error" "error"
}; };
@@ -1130,103 +1097,6 @@ fn handle_stderr(
parent_tool_use_id: None, parent_tool_use_id: None,
}, },
); );
} else if is_elicitation {
let data = parse_elicitation_hook(&line);
let friendly_content =
format!("MCP server requesting input: {}", data.message);
let _ = app.emit(
"claude:elicitation",
ElicitationEvent {
message: data.message,
server_name: data.server_name,
request_id: data.request_id,
conversation_id: conversation_id.clone(),
},
);
let _ = app.emit(
"claude:output",
OutputEvent {
line_type: "elicitation".to_string(),
content: friendly_content,
tool_name: None,
conversation_id: conversation_id.clone(),
cost: None,
parent_tool_use_id: None,
},
);
} else if is_elicitation_result {
let data = parse_elicitation_result_hook(&line);
let friendly_content =
format!("MCP elicitation completed: {}", data.action);
let _ = app.emit(
"claude:elicitation-result",
ElicitationResultEvent {
action: data.action,
request_id: data.request_id,
conversation_id: conversation_id.clone(),
},
);
let _ = app.emit(
"claude:output",
OutputEvent {
line_type: "elicitation".to_string(),
content: friendly_content,
tool_name: None,
conversation_id: conversation_id.clone(),
cost: None,
parent_tool_use_id: None,
},
);
} else if is_stop_failure {
let data = parse_stop_failure_hook(&line);
let friendly_content = build_stop_failure_message(&data);
let _ = app.emit(
"claude:stop-failure",
StopFailureEvent {
stop_reason: data.stop_reason,
error_type: data.error_type,
conversation_id: conversation_id.clone(),
},
);
let _ = app.emit(
"claude:output",
OutputEvent {
line_type: "error".to_string(),
content: friendly_content,
tool_name: None,
conversation_id: conversation_id.clone(),
cost: None,
parent_tool_use_id: None,
},
);
} else if is_post_compact {
let data = parse_post_compact_hook(&line);
let _ = app.emit(
"claude:post-compact",
PostCompactEvent {
session_id: data.session_id,
conversation_id: conversation_id.clone(),
},
);
let _ = app.emit(
"claude:output",
OutputEvent {
line_type: "compact-prompt".to_string(),
content: "Context compacted — conversation history has been summarised to free up space.".to_string(),
tool_name: None,
conversation_id: conversation_id.clone(),
cost: None,
parent_tool_use_id: None,
},
);
} else { } else {
let _ = app.emit( let _ = app.emit(
"claude:output", "claude:output",
@@ -1365,111 +1235,6 @@ fn parse_subagent_stop_hook(line: &str) -> Option<SubagentStopData> {
}) })
} }
#[derive(Debug)]
struct ElicitationData {
message: String,
server_name: Option<String>,
request_id: Option<String>,
}
fn parse_elicitation_hook(line: &str) -> ElicitationData {
let message = extract_quoted_value(line, "message").unwrap_or_else(|| {
line.split("[Elicitation Hook]")
.nth(1)
.unwrap_or("")
.trim()
.to_string()
});
let server_name = extract_debug_string_value(line, "server_name");
let request_id = extract_debug_string_value(line, "request_id");
ElicitationData { message, server_name, request_id }
}
#[derive(Debug)]
struct ElicitationResultData {
action: String,
request_id: Option<String>,
}
fn parse_elicitation_result_hook(line: &str) -> ElicitationResultData {
let action =
extract_quoted_value(line, "action").unwrap_or_else(|| "unknown".to_string());
let request_id = extract_debug_string_value(line, "request_id");
ElicitationResultData { action, request_id }
}
#[derive(Debug)]
struct StopFailureData {
stop_reason: Option<String>,
error_type: Option<String>,
}
fn parse_stop_failure_hook(line: &str) -> StopFailureData {
let stop_reason = extract_quoted_value(line, "stop_reason");
let error_type = extract_debug_string_value(line, "error_type");
StopFailureData { stop_reason, error_type }
}
/// Builds a user-friendly message from a `StopFailureData` instance.
fn build_stop_failure_message(data: &StopFailureData) -> String {
match data.stop_reason.as_deref() {
Some("rate_limit") => "Session stopped: rate limit reached".to_string(),
Some("auth_failure") | Some("authentication") => {
"Session stopped: authentication failed".to_string()
}
Some(reason) => format!("Session stopped due to API error: {}", reason),
None => match data.error_type.as_deref() {
Some(et) => format!("Session stopped due to API error: {}", et),
None => "Session stopped due to an unknown API error".to_string(),
},
}
}
#[derive(Debug)]
struct PostCompactData {
session_id: Option<String>,
}
fn parse_post_compact_hook(line: &str) -> PostCompactData {
let session_id = extract_debug_string_value(line, "session_id");
PostCompactData { session_id }
}
/// Extracts a double-quoted string value from a `key="value"` pair in a hook line.
/// Handles escape sequences within the quoted value.
fn extract_quoted_value(line: &str, key: &str) -> Option<String> {
let prefix = format!("{}=\"", key);
let start_idx = line.find(&prefix)? + prefix.len();
let rest = &line[start_idx..];
let mut result = String::new();
let mut chars = rest.chars();
loop {
match chars.next() {
Some('"') => return Some(result),
Some('\\') => match chars.next() {
Some('n') => result.push('\n'),
Some('t') => result.push('\t'),
Some('"') => result.push('"'),
Some('\\') => result.push('\\'),
Some(c) => {
result.push('\\');
result.push(c);
}
None => break,
},
Some(c) => result.push(c),
None => break,
}
}
None
}
/// Extract text content from a ToolResult's `content` field. /// Extract text content from a ToolResult's `content` field.
/// The content may be a JSON string or an array of typed content blocks. /// The content may be a JSON string or an array of typed content blocks.
fn extract_tool_result_text(content: &serde_json::Value) -> Option<String> { fn extract_tool_result_text(content: &serde_json::Value) -> Option<String> {
@@ -1591,7 +1356,7 @@ fn process_json_line(
stats_guard.model.clone() stats_guard.model.clone()
}).unwrap_or_else(|| { }).unwrap_or_else(|| {
tracing::warn!("No model info available for cost calculation, using default"); tracing::warn!("No model info available for cost calculation, using default");
"claude-sonnet-4-6".to_string() "claude-sonnet-4-5-20250929".to_string()
}); });
// Calculate cost for historical tracking (including cache tokens) // Calculate cost for historical tracking (including cache tokens)
@@ -3426,218 +3191,4 @@ mod tests {
let result = build_combined_settings_arg(Some(""), None); let result = build_combined_settings_arg(Some(""), None);
assert_eq!(result, "{}"); assert_eq!(result, "{}");
} }
#[test]
fn test_extract_quoted_value_basic() {
let line = r#"[Elicitation Hook] message="Hello world", server_name=Some("srv")"#;
let result = extract_quoted_value(line, "message");
assert_eq!(result, Some("Hello world".to_string()));
}
#[test]
fn test_extract_quoted_value_with_escapes() {
let line = r#"[Elicitation Hook] message="Line one\nLine two", request_id=Some("r1")"#;
let result = extract_quoted_value(line, "message");
assert_eq!(result, Some("Line one\nLine two".to_string()));
}
#[test]
fn test_extract_quoted_value_missing() {
let line = r#"[Elicitation Hook] server_name=Some("srv")"#;
let result = extract_quoted_value(line, "message");
assert_eq!(result, None);
}
#[test]
fn test_parse_elicitation_hook_with_all_fields() {
let line = r#"[Elicitation Hook] message="Please enter your API key", server_name=Some("my-mcp"), request_id=Some("req-456")"#;
let data = parse_elicitation_hook(line);
assert_eq!(data.message, "Please enter your API key");
assert_eq!(data.server_name, Some("my-mcp".to_string()));
assert_eq!(data.request_id, Some("req-456".to_string()));
}
#[test]
fn test_parse_elicitation_hook_missing_optional_fields() {
let line = r#"[Elicitation Hook] message="What is the endpoint?", server_name=None, request_id=None"#;
let data = parse_elicitation_hook(line);
assert_eq!(data.message, "What is the endpoint?");
assert_eq!(data.server_name, None);
assert_eq!(data.request_id, None);
}
#[test]
fn test_parse_elicitation_hook_invalid_line() {
let line = "[Elicitation Hook] some unstructured data";
let data = parse_elicitation_hook(line);
assert_eq!(data.message, "some unstructured data");
assert_eq!(data.server_name, None);
assert_eq!(data.request_id, None);
}
#[test]
fn test_parse_elicitation_result_hook_accept() {
let line = r#"[ElicitationResult Hook] action="accept", request_id=Some("req-789")"#;
let data = parse_elicitation_result_hook(line);
assert_eq!(data.action, "accept");
assert_eq!(data.request_id, Some("req-789".to_string()));
}
#[test]
fn test_parse_elicitation_result_hook_cancel() {
let line = r#"[ElicitationResult Hook] action="cancel", request_id=None"#;
let data = parse_elicitation_result_hook(line);
assert_eq!(data.action, "cancel");
assert_eq!(data.request_id, None);
}
#[test]
fn test_parse_stop_failure_hook_with_all_fields() {
let line = r#"[StopFailure Hook] stop_reason="api_error", error_type=Some("rate_limit"), conversation_id=Some("conv-123")"#;
let data = parse_stop_failure_hook(line);
assert_eq!(data.stop_reason, Some("api_error".to_string()));
assert_eq!(data.error_type, Some("rate_limit".to_string()));
}
#[test]
fn test_parse_stop_failure_hook_missing_optional_error_type() {
let line = r#"[StopFailure Hook] stop_reason="api_error", error_type=None"#;
let data = parse_stop_failure_hook(line);
assert_eq!(data.stop_reason, Some("api_error".to_string()));
assert_eq!(data.error_type, None);
}
#[test]
fn test_parse_stop_failure_hook_invalid_line() {
let line = "[StopFailure Hook] some unstructured data";
let data = parse_stop_failure_hook(line);
assert_eq!(data.stop_reason, None);
assert_eq!(data.error_type, None);
}
#[test]
fn test_build_stop_failure_message_rate_limit() {
let data = StopFailureData {
stop_reason: Some("rate_limit".to_string()),
error_type: None,
};
assert_eq!(build_stop_failure_message(&data), "Session stopped: rate limit reached");
}
#[test]
fn test_build_stop_failure_message_auth_failure() {
let data = StopFailureData {
stop_reason: Some("auth_failure".to_string()),
error_type: None,
};
assert_eq!(build_stop_failure_message(&data), "Session stopped: authentication failed");
}
#[test]
fn test_build_stop_failure_message_authentication() {
let data = StopFailureData {
stop_reason: Some("authentication".to_string()),
error_type: None,
};
assert_eq!(build_stop_failure_message(&data), "Session stopped: authentication failed");
}
#[test]
fn test_build_stop_failure_message_unknown_reason() {
let data = StopFailureData {
stop_reason: Some("server_error".to_string()),
error_type: None,
};
assert_eq!(
build_stop_failure_message(&data),
"Session stopped due to API error: server_error"
);
}
#[test]
fn test_build_stop_failure_message_no_reason_with_error_type() {
let data = StopFailureData {
stop_reason: None,
error_type: Some("timeout".to_string()),
};
assert_eq!(
build_stop_failure_message(&data),
"Session stopped due to API error: timeout"
);
}
#[test]
fn test_parse_post_compact_hook_with_session_id() {
let line =
r#"[PostCompact Hook] session_id=Some("sess-abc123"), conversation_id=Some("conv-xyz")"#;
let data = parse_post_compact_hook(line);
assert_eq!(data.session_id, Some("sess-abc123".to_string()));
}
#[test]
fn test_parse_post_compact_hook_without_session_id() {
let line = "[PostCompact Hook] session_id=None";
let data = parse_post_compact_hook(line);
assert_eq!(data.session_id, None);
}
#[test]
fn test_parse_post_compact_hook_empty_line() {
let line = "[PostCompact Hook]";
let data = parse_post_compact_hook(line);
assert_eq!(data.session_id, None);
}
#[test]
fn test_build_stop_failure_message_no_fields() {
let data = StopFailureData {
stop_reason: None,
error_type: None,
};
assert_eq!(
build_stop_failure_message(&data),
"Session stopped due to an unknown API error"
);
}
/// Build the --name argument string without executing a command (for testing)
#[cfg(test)]
fn build_session_name_arg(name: &str) -> Option<(String, String)> {
if name.is_empty() {
return None;
}
Some(("--name".to_string(), name.to_string()))
}
#[test]
fn test_e2e_session_name_passed_when_set() {
let name = "Sakura";
let result = build_session_name_arg(name);
assert!(result.is_some());
let (flag, value) = result.unwrap();
assert_eq!(flag, "--name");
assert_eq!(value, "Sakura");
}
#[test]
fn test_e2e_session_name_not_passed_when_none() {
let options = ClaudeStartOptions {
session_name: None,
..Default::default()
};
// When session_name is None, no --name arg should be produced
let has_name = options.session_name.as_deref().map(|n| !n.is_empty()).unwrap_or(false);
assert!(!has_name);
}
#[test]
fn test_e2e_session_name_not_passed_when_empty() {
let options = ClaudeStartOptions {
session_name: Some(String::new()),
..Default::default()
};
// When session_name is Some(""), no --name arg should be produced
let has_name = options.session_name.as_deref().map(|n| !n.is_empty()).unwrap_or(false);
assert!(!has_name);
}
} }
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "hikari-desktop", "productName": "hikari-desktop",
"version": "1.13.0", "version": "1.12.0",
"identifier": "com.naomi.hikari-desktop", "identifier": "com.naomi.hikari-desktop",
"build": { "build": {
"beforeDevCommand": "pnpm dev", "beforeDevCommand": "pnpm dev",
-2
View File
@@ -71,7 +71,6 @@ async function changeDirectory(path: string): Promise<void> {
enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true, enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true,
auto_memory_directory: config.auto_memory_directory || null, auto_memory_directory: config.auto_memory_directory || null,
model_overrides: config.model_overrides || null, model_overrides: config.model_overrides || null,
session_name: null,
}, },
}); });
@@ -153,7 +152,6 @@ async function startNewConversation(): Promise<void> {
enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true, enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true,
auto_memory_directory: config.auto_memory_directory || null, auto_memory_directory: config.auto_memory_directory || null,
model_overrides: config.model_overrides || null, model_overrides: config.model_overrides || null,
session_name: null,
}, },
}); });
+1 -1
View File
@@ -2,7 +2,7 @@
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { onMount } from "svelte"; import { onMount } from "svelte";
const SUPPORTED_CLI_VERSION = "2.1.80"; const SUPPORTED_CLI_VERSION = "2.1.74";
let installedVersion = $state("Loading..."); let installedVersion = $state("Loading...");
let latestNpmVersion = $state<string | null>(null); let latestNpmVersion = $state<string | null>(null);
+3 -5
View File
@@ -610,15 +610,13 @@
id="max-output-tokens" id="max-output-tokens"
type="number" type="number"
min="1" min="1"
max="128000" placeholder="Default (32000)"
placeholder="Default (model-dependent)"
bind:value={config.max_output_tokens} bind:value={config.max_output_tokens}
class="w-full px-3 py-2 text-sm bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:border-[var(--accent-primary)]" class="w-full px-3 py-2 text-sm bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:border-[var(--accent-primary)]"
/> />
<p class="text-xs text-[var(--text-tertiary)] mt-1"> <p class="text-xs text-[var(--text-tertiary)] mt-1">
Sets <code class="font-mono">CLAUDE_CODE_MAX_OUTPUT_TOKENS</code>. Maximum: 128k tokens Sets <code class="font-mono">CLAUDE_CODE_MAX_OUTPUT_TOKENS</code> — increase if responses are
for Opus 4.6 and Sonnet 4.6 (64k default for Opus 4.6, 32k for other models). Increase if being cut off mid-reply
responses are being cut off.
</p> </p>
</div> </div>
-188
View File
@@ -1,188 +0,0 @@
<script lang="ts">
import { invoke } from "@tauri-apps/api/core";
import { get } from "svelte/store";
import { claudeStore, hasElicitationPending } from "$lib/stores/claude";
import { characterState } from "$lib/stores/character";
import type { ElicitationEvent } from "$lib/types/messages";
import { updateDiscordRpc, setSkipNextGreeting } from "$lib/tauri";
import { conversationsStore } from "$lib/stores/conversations";
import { configStore } from "$lib/stores/config";
let isVisible = $state(false);
let elicitation: ElicitationEvent | null = $state(null);
let response = $state("");
let grantedToolsList: string[] = $state([]);
let workingDirectory = $state("");
hasElicitationPending.subscribe((pending) => {
isVisible = pending;
if (!pending) {
response = "";
}
});
claudeStore.pendingElicitation.subscribe((e) => {
elicitation = e;
if (e) {
characterState.setState("permission");
}
});
claudeStore.grantedTools.subscribe((tools) => {
grantedToolsList = Array.from(tools);
});
claudeStore.currentWorkingDirectory.subscribe((dir) => {
workingDirectory = dir;
});
async function handleSubmitAndReconnect() {
if (!elicitation || !response.trim()) return;
const conversationId = get(claudeStore.activeConversationId);
if (!conversationId) return;
const responseText = response.trim();
const elicitationMessage = elicitation.message;
const conversationHistory = claudeStore.getConversationHistory();
claudeStore.addLine("system", `MCP response submitted. Reconnecting with context...`);
claudeStore.clearElicitation();
try {
setSkipNextGreeting(true);
await invoke("stop_claude", { conversationId });
await new Promise((resolve) => setTimeout(resolve, 500));
const config = configStore.getConfig();
await invoke("start_claude", {
conversationId,
options: {
working_dir: workingDirectory || "/home/naomi",
model: config.model || null,
api_key: config.api_key || null,
custom_instructions: config.custom_instructions || null,
mcp_servers_json: config.mcp_servers_json || null,
allowed_tools: grantedToolsList,
use_worktree: config.use_worktree ?? false,
disable_1m_context: config.disable_1m_context ?? false,
include_git_instructions: config.include_git_instructions ?? true,
enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true,
auto_memory_directory: config.auto_memory_directory || null,
model_overrides: config.model_overrides || null,
session_name: null,
},
});
const activeConversation = get(conversationsStore.activeConversation);
if (activeConversation) {
await updateDiscordRpc(
activeConversation.name,
config.model || "claude",
activeConversation.startedAt
);
}
await new Promise((resolve) => setTimeout(resolve, 1000));
if (conversationHistory) {
const contextMessage = `[CONTEXT RESTORATION]
I just responded to an MCP server elicitation request. Here's our conversation so far:
${conversationHistory}
The MCP server asked: "${elicitationMessage}"
My response: "${responseText}"
Please continue where we left off, taking my response into account.`;
await invoke("send_prompt", {
conversationId,
message: contextMessage,
});
}
characterState.setTemporaryState("success", 2000);
} catch (error) {
console.error("Failed to reconnect:", error);
claudeStore.addLine("error", `Reconnect failed: ${error}`);
characterState.setTemporaryState("error", 3000);
}
}
function handleDismiss() {
claudeStore.clearElicitation();
claudeStore.addLine("system", "MCP elicitation dismissed");
characterState.setTemporaryState("idle", 1000);
}
function handleKeydown(event: KeyboardEvent) {
if (!isVisible || !elicitation) return;
if (event.key === "Escape") {
event.preventDefault();
handleDismiss();
}
}
function canSubmit(): boolean {
return response.trim().length > 0;
}
</script>
<svelte:window onkeydown={handleKeydown} />
{#if isVisible && elicitation}
<div
class="elicitation-overlay fixed inset-0 bg-black/70 flex items-center justify-center z-50 backdrop-blur-sm"
>
<div
class="elicitation-modal bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-xl p-6 max-w-md w-full mx-4 shadow-2xl"
>
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 rounded-full bg-blue-500/20 flex items-center justify-center">
<span class="text-xl">đź’¬</span>
</div>
<div>
<h2 class="text-lg font-semibold text-[var(--text-primary)]">MCP Server Request</h2>
{#if elicitation.server_name}
<p class="text-sm text-[var(--text-secondary)]">from: {elicitation.server_name}</p>
{:else}
<p class="text-sm text-[var(--text-secondary)]">Input required from MCP server</p>
{/if}
</div>
</div>
<div class="mb-4">
<p class="text-[var(--text-primary)]">{elicitation.message}</p>
</div>
<div class="mb-4">
<textarea
bind:value={response}
placeholder="Type your response here..."
class="w-full px-4 py-3 bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded-lg text-[var(--text-primary)] placeholder-[var(--text-secondary)] resize-none focus:outline-none focus:border-[var(--accent-primary)]"
rows="4"
></textarea>
</div>
<div class="flex gap-3">
<button
onclick={handleDismiss}
class="flex-1 px-4 py-2 bg-gray-500/20 hover:bg-gray-500/30 text-[var(--text-secondary)] rounded-lg transition-colors font-medium"
>
Dismiss
</button>
<button
onclick={handleSubmitAndReconnect}
disabled={!canSubmit()}
class="flex-1 px-4 py-2 bg-blue-500/20 hover:bg-blue-500/30 text-blue-400 rounded-lg transition-colors font-medium disabled:opacity-50 disabled:cursor-not-allowed"
>
Submit & Reconnect
</button>
</div>
</div>
</div>
{/if}
-1
View File
@@ -406,7 +406,6 @@ User: ${formattedMessage}`;
enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true, enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true,
auto_memory_directory: config.auto_memory_directory || null, auto_memory_directory: config.auto_memory_directory || null,
model_overrides: config.model_overrides || null, model_overrides: config.model_overrides || null,
session_name: null,
}, },
}); });
+2 -34
View File
@@ -14,7 +14,6 @@
interface MemoryFileInfo { interface MemoryFileInfo {
path: string; path: string;
heading: string | null; heading: string | null;
last_modified?: string; // Unix timestamp in seconds as a string, optional for backwards compat
} }
interface MemoryFilesResponse { interface MemoryFilesResponse {
@@ -66,16 +65,6 @@
return file.heading ?? getFileName(file.path); return file.heading ?? getFileName(file.path);
} }
function formatLastModified(ts: string | undefined): string {
if (!ts) return "";
const date = new Date(Number(ts) * 1000);
return date.toLocaleDateString(undefined, {
year: "numeric",
month: "short",
day: "numeric",
});
}
async function sendMemoryCommand() { async function sendMemoryCommand() {
const conversationId = get(claudeStore.activeConversationId); const conversationId = get(claudeStore.activeConversationId);
if (!conversationId) return; if (!conversationId) return;
@@ -216,12 +205,7 @@
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/> />
</svg> </svg>
<div class="file-info"> <span class="file-name">{getDisplayName(file)}</span>
<span class="file-name">{getDisplayName(file)}</span>
{#if file.last_modified}
<span class="file-date">{formatLastModified(file.last_modified)}</span>
{/if}
</div>
</button> </button>
{/each} {/each}
</div> </div>
@@ -432,7 +416,7 @@
.file-item { .file-item {
display: flex; display: flex;
align-items: flex-start; align-items: center;
gap: 0.75rem; gap: 0.75rem;
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
background: var(--bg-secondary); background: var(--bg-secondary);
@@ -461,13 +445,6 @@
flex-shrink: 0; flex-shrink: 0;
} }
.file-info {
display: flex;
flex-direction: column;
gap: 0.125rem;
overflow: hidden;
}
.file-name { .file-name {
font-size: 0.875rem; font-size: 0.875rem;
font-weight: 500; font-weight: 500;
@@ -476,15 +453,6 @@
white-space: nowrap; white-space: nowrap;
} }
.file-date {
font-size: 0.75rem;
color: var(--text-secondary);
}
.file-item.active .file-date {
color: rgba(255, 255, 255, 0.75);
}
.file-viewer { .file-viewer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -93,7 +93,6 @@
enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true, enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true,
auto_memory_directory: config.auto_memory_directory || null, auto_memory_directory: config.auto_memory_directory || null,
model_overrides: config.model_overrides || null, model_overrides: config.model_overrides || null,
session_name: null,
}, },
}); });
-3
View File
@@ -160,7 +160,6 @@
if (!conversationId) { if (!conversationId) {
throw new Error("No active conversation"); throw new Error("No active conversation");
} }
const activeConversationForName = get(conversationsStore.activeConversation);
await invoke("start_claude", { await invoke("start_claude", {
conversationId, conversationId,
options: { options: {
@@ -178,7 +177,6 @@
enable_claudeai_mcp_servers: currentConfig.enable_claudeai_mcp_servers ?? true, enable_claudeai_mcp_servers: currentConfig.enable_claudeai_mcp_servers ?? true,
auto_memory_directory: currentConfig.auto_memory_directory || null, auto_memory_directory: currentConfig.auto_memory_directory || null,
model_overrides: currentConfig.model_overrides || null, model_overrides: currentConfig.model_overrides || null,
session_name: activeConversationForName?.name || null,
}, },
}); });
@@ -340,7 +338,6 @@
enable_claudeai_mcp_servers: currentConfig.enable_claudeai_mcp_servers ?? true, enable_claudeai_mcp_servers: currentConfig.enable_claudeai_mcp_servers ?? true,
auto_memory_directory: currentConfig.auto_memory_directory || null, auto_memory_directory: currentConfig.auto_memory_directory || null,
model_overrides: currentConfig.model_overrides || null, model_overrides: currentConfig.model_overrides || null,
session_name: null,
}, },
}); });
-1
View File
@@ -222,7 +222,6 @@
enable_claudeai_mcp_servers: cfg.enable_claudeai_mcp_servers ?? true, enable_claudeai_mcp_servers: cfg.enable_claudeai_mcp_servers ?? true,
auto_memory_directory: cfg.auto_memory_directory || null, auto_memory_directory: cfg.auto_memory_directory || null,
model_overrides: cfg.model_overrides || null, model_overrides: cfg.model_overrides || null,
session_name: null,
}, },
}); });
} catch (error) { } catch (error) {
@@ -112,7 +112,6 @@
enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true, enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true,
auto_memory_directory: config.auto_memory_directory || null, auto_memory_directory: config.auto_memory_directory || null,
model_overrides: config.model_overrides || null, model_overrides: config.model_overrides || null,
session_name: null,
}, },
}); });
-11
View File
@@ -22,7 +22,6 @@ export const claudeStore = {
terminalLines: conversationsStore.terminalLines, terminalLines: conversationsStore.terminalLines,
pendingPermission: conversationsStore.pendingPermission, pendingPermission: conversationsStore.pendingPermission,
pendingQuestion: conversationsStore.pendingQuestion, pendingQuestion: conversationsStore.pendingQuestion,
pendingElicitation: conversationsStore.pendingElicitation,
isProcessing: conversationsStore.isProcessing, isProcessing: conversationsStore.isProcessing,
grantedTools: conversationsStore.grantedTools, grantedTools: conversationsStore.grantedTools,
pendingRetryMessage: conversationsStore.pendingRetryMessage, pendingRetryMessage: conversationsStore.pendingRetryMessage,
@@ -58,10 +57,6 @@ export const claudeStore = {
clearQuestion: conversationsStore.clearQuestion, clearQuestion: conversationsStore.clearQuestion,
requestQuestionForConversation: conversationsStore.requestQuestionForConversation, requestQuestionForConversation: conversationsStore.requestQuestionForConversation,
clearQuestionForConversation: conversationsStore.clearQuestionForConversation, clearQuestionForConversation: conversationsStore.clearQuestionForConversation,
requestElicitation: conversationsStore.requestElicitation,
clearElicitation: conversationsStore.clearElicitation,
requestElicitationForConversation: conversationsStore.requestElicitationForConversation,
clearElicitationForConversation: conversationsStore.clearElicitationForConversation,
grantTool: conversationsStore.grantTool, grantTool: conversationsStore.grantTool,
revokeAllTools: conversationsStore.revokeAllTools, revokeAllTools: conversationsStore.revokeAllTools,
isToolGranted: conversationsStore.isToolGranted, isToolGranted: conversationsStore.isToolGranted,
@@ -131,12 +126,6 @@ export const hasQuestionPending = derived(
($conversation) => $conversation?.pendingQuestion !== null ($conversation) => $conversation?.pendingQuestion !== null
); );
export const hasElicitationPending = derived(
claudeStore.activeConversation,
($conversation) =>
$conversation?.pendingElicitation !== null && $conversation?.pendingElicitation !== undefined
);
// Derived store to check if Claude is currently processing (can be interrupted) // Derived store to check if Claude is currently processing (can be interrupted)
export const isClaudeProcessing = derived( export const isClaudeProcessing = derived(
[claudeStore.connectionStatus, characterState], [claudeStore.connectionStatus, characterState],
-54
View File
@@ -2,7 +2,6 @@ import { writable, derived, get } from "svelte/store";
import type { import type {
TerminalLine, TerminalLine,
ConnectionStatus, ConnectionStatus,
ElicitationEvent,
PermissionRequest, PermissionRequest,
UserQuestionEvent, UserQuestionEvent,
Attachment, Attachment,
@@ -33,7 +32,6 @@ export interface Conversation {
grantedTools: Set<string>; grantedTools: Set<string>;
pendingPermissions: PermissionRequest[]; pendingPermissions: PermissionRequest[];
pendingQuestion: UserQuestionEvent | null; pendingQuestion: UserQuestionEvent | null;
pendingElicitation: ElicitationEvent | null;
scrollPosition: number; scrollPosition: number;
createdAt: Date; createdAt: Date;
lastActivityAt: Date; lastActivityAt: Date;
@@ -159,7 +157,6 @@ function createConversationsStore() {
grantedTools: new Set(), grantedTools: new Set(),
pendingPermissions: [], pendingPermissions: [],
pendingQuestion: null, pendingQuestion: null,
pendingElicitation: null,
scrollPosition: -1, // -1 means "scroll to bottom" (auto-scroll) scrollPosition: -1, // -1 means "scroll to bottom" (auto-scroll)
createdAt: new Date(), createdAt: new Date(),
lastActivityAt: new Date(), lastActivityAt: new Date(),
@@ -224,10 +221,6 @@ function createConversationsStore() {
($conv) => $conv?.pendingPermissions || [] ($conv) => $conv?.pendingPermissions || []
); );
const pendingQuestion = derived(activeConversation, ($conv) => $conv?.pendingQuestion || null); const pendingQuestion = derived(activeConversation, ($conv) => $conv?.pendingQuestion || null);
const pendingElicitation = derived(
activeConversation,
($conv) => $conv?.pendingElicitation ?? null
);
const scrollPosition = derived(activeConversation, ($conv) => $conv?.scrollPosition ?? -1); const scrollPosition = derived(activeConversation, ($conv) => $conv?.scrollPosition ?? -1);
const attachments = derived(activeConversation, ($conv) => $conv?.attachments || []); const attachments = derived(activeConversation, ($conv) => $conv?.attachments || []);
const worktreeInfo = derived(activeConversation, ($conv) => $conv?.worktreeInfo ?? null); const worktreeInfo = derived(activeConversation, ($conv) => $conv?.worktreeInfo ?? null);
@@ -241,7 +234,6 @@ function createConversationsStore() {
pendingPermission: { subscribe: pendingPermission.subscribe }, pendingPermission: { subscribe: pendingPermission.subscribe },
pendingPermissions: { subscribe: pendingPermissions.subscribe }, pendingPermissions: { subscribe: pendingPermissions.subscribe },
pendingQuestion: { subscribe: pendingQuestion.subscribe }, pendingQuestion: { subscribe: pendingQuestion.subscribe },
pendingElicitation: { subscribe: pendingElicitation.subscribe },
isProcessing: { subscribe: isProcessing.subscribe }, isProcessing: { subscribe: isProcessing.subscribe },
grantedTools: { subscribe: grantedTools.subscribe }, grantedTools: { subscribe: grantedTools.subscribe },
pendingRetryMessage: { subscribe: pendingRetryMessage.subscribe }, pendingRetryMessage: { subscribe: pendingRetryMessage.subscribe },
@@ -407,52 +399,6 @@ function createConversationsStore() {
return convs; return convs;
}); });
}, },
requestElicitation: (elicitation: ElicitationEvent) => {
const activeId = get(activeConversationId);
if (!activeId) return;
conversations.update((convs) => {
const conv = convs.get(activeId);
if (conv) {
conv.pendingElicitation = elicitation;
conv.lastActivityAt = new Date();
}
return convs;
});
},
clearElicitation: () => {
const activeId = get(activeConversationId);
if (!activeId) return;
conversations.update((convs) => {
const conv = convs.get(activeId);
if (conv) {
conv.pendingElicitation = null;
conv.lastActivityAt = new Date();
}
return convs;
});
},
requestElicitationForConversation: (conversationId: string, elicitation: ElicitationEvent) => {
conversations.update((convs) => {
const conv = convs.get(conversationId);
if (conv) {
conv.pendingElicitation = elicitation;
conv.lastActivityAt = new Date();
}
return convs;
});
},
clearElicitationForConversation: (conversationId: string) => {
conversations.update((convs) => {
const conv = convs.get(conversationId);
if (conv) {
conv.pendingElicitation = null;
conv.lastActivityAt = new Date();
}
return convs;
});
},
setPendingRetryMessage: (message: string | null) => pendingRetryMessage.set(message), setPendingRetryMessage: (message: string | null) => pendingRetryMessage.set(message),
// Conversation management // Conversation management
-27
View File
@@ -187,33 +187,6 @@ describe("toastStore", () => {
}); });
}); });
describe("addError", () => {
it("adds an error toast with the warning icon", () => {
toastStore.addError("Something went wrong");
const toasts = get(toastStore);
expect(toasts).toHaveLength(1);
const toast = toasts[0];
expect(toast.kind).toBe("info");
if (toast.kind === "info") {
expect(toast.message).toBe("Something went wrong");
expect(toast.icon).toBe("⚠️");
expect(typeof toast.id).toBe("string");
expect(toast.id.length).toBeGreaterThan(0);
}
});
it("auto-dismisses after 6000ms", () => {
toastStore.addError("Rate limit reached");
expect(get(toastStore)).toHaveLength(1);
vi.advanceTimersByTime(5999);
expect(get(toastStore)).toHaveLength(1);
vi.advanceTimersByTime(1);
expect(get(toastStore)).toHaveLength(0);
});
});
describe("addUpdate", () => { describe("addUpdate", () => {
it("adds a persistent update toast with the correct fields", () => { it("adds a persistent update toast with the correct fields", () => {
toastStore.addUpdate("2.0.0", "1.9.0", "https://example.com/release"); toastStore.addUpdate("2.0.0", "1.9.0", "https://example.com/release");
+1 -8
View File
@@ -68,13 +68,6 @@ function createToastStore() {
setTimeout(() => remove(id), 4000); setTimeout(() => remove(id), 4000);
} }
function addError(message: string) {
const id = crypto.randomUUID();
const toast: InfoToast = { id, kind: "info", message, icon: "⚠️" };
update((toasts) => [...toasts, toast]);
setTimeout(() => remove(id), 6000);
}
function addAchievement(achievement: AchievementUnlockedEvent["achievement"]) { function addAchievement(achievement: AchievementUnlockedEvent["achievement"]) {
const id = crypto.randomUUID(); const id = crypto.randomUUID();
const toast: AchievementToast = { id, kind: "achievement", achievement }; const toast: AchievementToast = { id, kind: "achievement", achievement };
@@ -89,7 +82,7 @@ function createToastStore() {
// Update toasts are persistent — no auto-dismiss // Update toasts are persistent — no auto-dismiss
} }
return { subscribe, addInfo, addError, addAchievement, addUpdate, remove }; return { subscribe, addInfo, addAchievement, addUpdate, remove };
} }
export const toastStore = createToastStore(); export const toastStore = createToastStore();
+2 -57
View File
@@ -8,10 +8,7 @@ import { initStatsListener, resetSessionStats } from "$lib/stores/stats";
import { initAchievementsListener } from "$lib/stores/achievements"; import { initAchievementsListener } from "$lib/stores/achievements";
import type { import type {
ConnectionStatus, ConnectionStatus,
ElicitationEvent,
PermissionPromptEvent, PermissionPromptEvent,
PostCompactEvent,
StopFailureEvent,
UserQuestionEvent, UserQuestionEvent,
} from "$lib/types/messages"; } from "$lib/types/messages";
import type { CharacterState } from "$lib/types/states"; import type { CharacterState } from "$lib/types/states";
@@ -409,8 +406,7 @@ export async function initializeTauriListeners() {
| "rate-limit" | "rate-limit"
| "compact-prompt" | "compact-prompt"
| "worktree" | "worktree"
| "config-change" | "config-change",
| "elicitation",
content, content,
tool_name || undefined, tool_name || undefined,
costData, costData,
@@ -429,8 +425,7 @@ export async function initializeTauriListeners() {
| "rate-limit" | "rate-limit"
| "compact-prompt" | "compact-prompt"
| "worktree" | "worktree"
| "config-change" | "config-change",
| "elicitation",
content, content,
tool_name || undefined, tool_name || undefined,
costData, costData,
@@ -616,56 +611,6 @@ export async function initializeTauriListeners() {
} }
}); });
unlisteners.push(questionUnlisten); unlisteners.push(questionUnlisten);
const elicitationUnlisten = await listen<ElicitationEvent>("claude:elicitation", (event) => {
const elicitationEvent = event.payload;
if (elicitationEvent.conversation_id) {
claudeStore.requestElicitationForConversation(
elicitationEvent.conversation_id,
elicitationEvent
);
} else {
claudeStore.requestElicitation(elicitationEvent);
}
});
unlisteners.push(elicitationUnlisten);
const elicitationResultUnlisten = await listen<{ conversation_id?: string }>(
"claude:elicitation-result",
(event) => {
const { conversation_id } = event.payload;
if (conversation_id) {
claudeStore.clearElicitationForConversation(conversation_id);
} else {
claudeStore.clearElicitation();
}
}
);
unlisteners.push(elicitationResultUnlisten);
const stopFailureUnlisten = await listen<StopFailureEvent>("claude:stop-failure", (event) => {
const { stop_reason, error_type } = event.payload;
characterState.setTemporaryState("error", 3000);
let message: string;
if (stop_reason === "rate_limit") {
message = "Rate limit reached";
} else if (stop_reason === "auth_failure" || stop_reason === "authentication") {
message = "Authentication failed";
} else {
message = `API error: ${stop_reason ?? error_type ?? "unknown"}`;
}
toastStore.addError(message);
});
unlisteners.push(stopFailureUnlisten);
const postCompactUnlisten = await listen<PostCompactEvent>("claude:post-compact", () => {
toastStore.addInfo("Context compacted", "🗜️");
characterState.setTemporaryState("success", 2000);
});
unlisteners.push(postCompactUnlisten);
} }
export function cleanupTauriListeners() { export function cleanupTauriListeners() {
+1 -26
View File
@@ -10,8 +10,7 @@ export interface TerminalLine {
| "rate-limit" | "rate-limit"
| "compact-prompt" | "compact-prompt"
| "worktree" | "worktree"
| "config-change" | "config-change";
| "elicitation";
content: string; content: string;
timestamp: Date; timestamp: Date;
toolName?: string; toolName?: string;
@@ -163,30 +162,6 @@ export interface UserQuestionEvent {
conversation_id?: string; conversation_id?: string;
} }
export interface ElicitationEvent {
message: string;
server_name?: string;
request_id?: string;
conversation_id?: string;
}
export interface ElicitationResultEvent {
action: string;
request_id?: string;
conversation_id?: string;
}
export interface StopFailureEvent {
stop_reason?: string;
error_type?: string;
conversation_id?: string;
}
export interface PostCompactEvent {
session_id?: string;
conversation_id?: string;
}
export type ConnectionStatus = "disconnected" | "connecting" | "connected" | "error"; export type ConnectionStatus = "disconnected" | "connecting" | "connected" | "error";
export interface Attachment { export interface Attachment {
-2
View File
@@ -36,7 +36,6 @@
import type { CharacterState } from "$lib/types/states"; import type { CharacterState } from "$lib/types/states";
import PermissionModal from "$lib/components/PermissionModal.svelte"; import PermissionModal from "$lib/components/PermissionModal.svelte";
import UserQuestionModal from "$lib/components/UserQuestionModal.svelte"; import UserQuestionModal from "$lib/components/UserQuestionModal.svelte";
import ElicitationModal from "$lib/components/ElicitationModal.svelte";
import ConfigSidebar from "$lib/components/ConfigSidebar.svelte"; import ConfigSidebar from "$lib/components/ConfigSidebar.svelte";
import AchievementsPanel from "$lib/components/AchievementsPanel.svelte"; import AchievementsPanel from "$lib/components/AchievementsPanel.svelte";
import ToastContainer from "$lib/components/ToastContainer.svelte"; import ToastContainer from "$lib/components/ToastContainer.svelte";
@@ -594,7 +593,6 @@
<PermissionModal /> <PermissionModal />
<UserQuestionModal /> <UserQuestionModal />
<ElicitationModal />
<ConfigSidebar /> <ConfigSidebar />
<AchievementsPanel <AchievementsPanel
bind:isOpen={achievementPanelOpen} bind:isOpen={achievementPanelOpen}