Compare commits

..

10 Commits

Author SHA1 Message Date
hikari 5063a4f415 feat: display ConfigChange hook events in terminal
Detects [ConfigChange Hook] lines in stderr and emits them as
config-change line type with [config] prefix in purple.

Closes #151
2026-02-24 18:07:53 -08:00
hikari 54280b3920 feat: add worktree isolation support and hook event display
Closes #152, closes #150

- Add use_worktree bool to ClaudeStartOptions and HikariConfig (Rust + TS)
- Pass --worktree flag to claude in both WSL and non-WSL command paths
- Detect WorktreeCreate/WorktreeRemove hook events in stderr handler
- Emit worktree events with distinct line_type instead of error
- Add worktree line type to TerminalLine union, Terminal rendering, and tests
- Display worktree events in green with [worktree] prefix
- Add worktree isolation toggle to Agent Settings section of ConfigSidebar
- Thread use_worktree through all seven start_claude call sites
2026-02-24 17:39:09 -08:00
hikari 108a1b16b2 feat: detect prompt-too-long errors and offer compact action
Closes #158

- Detect "Prompt is too long" in assistant text blocks in wsl_bridge
- Emit error line type instead of assistant for better visibility
- Emit compact-prompt event after the error line
- Render a compact-prompt line as an action button in the terminal
- Clicking the button sends /compact to the active session
- Add getLineClass/getLinePrefix tests for compact-prompt type
2026-02-24 17:18:44 -08:00
hikari 1c02ca1bb5 feat: parse and display rate_limit_event messages from Claude CLI
Closes #155

- Add RateLimitInfo struct and RateLimitEvent variant to ClaudeMessage
- Emit rate-limit OutputEvent with human-readable message in wsl_bridge
- Add rate-limit line type to TerminalLine union and Terminal rendering
- Display rate-limit lines in amber with [rate-limit] prefix
- Add Terminal.test.ts with 28 tests for getLineClass, getLinePrefix,
  formatTime, isToolContentLong, and truncateToolContent
2026-02-24 16:28:33 -08:00
hikari ac965ebe92 feat: add auth status display and account management to settings sidebar
Implements issue #153. Adds Account section to ConfigSidebar with:
- Claude auth status (logged in/out, email, org, plan, API source)
- API key override indicator reading from local Hikari config
- Login/logout action buttons
- Refresh button for manual status updates

Adds Rust commands: get_auth_status, auth_login, auth_logout
2026-02-24 14:35:51 -08:00
hikari 5cdb4508aa feat: clean up CLI version display and add compatibility warnings
Strips the '(Claude Code)' suffix from the installed version badge so
it shows cleanly as 'CLI 2.1.50'. Also adds an italic warning message
after the supported badge when versions differ: amber text for newer
installs, red text for outdated installs.
2026-02-24 13:12:37 -08:00
hikari b914240d20 fix: extract semver before comparing CLI versions
The installed version string includes a suffix like '(Claude Code)'
which caused Number() to parse the third segment as NaN, making the
comparison always return 0 (current) instead of the correct result.
Now extracts the semver portion with a regex before comparing.
2026-02-24 12:57:39 -08:00
hikari 302963f6c0 feat: add supported CLI version badge to header
Adds a second badge alongside the existing CLI version display showing
the highest audited version (currently 2.1.33). The badge is
colour-coded: green when installed matches supported, amber when ahead,
red when behind, and neutral grey whilst loading.
2026-02-24 12:47:41 -08:00
hikari 986dd54dd9 feat: hold tab at connecting until greeting response arrives
Track greeting-pending conversations so the tab stays yellow until
Claude actually responds, rather than going green as soon as the
process starts. Also fixes the disconnect handler missing a status
update that left tabs stuck at yellow after the process died.
2026-02-24 12:31:24 -08:00
hikari 24313facca feat: collapsible tool lines in terminal
Long tool messages (e.g. Bash commands) are now stored in full and
displayed collapsed by default, with a toggle button to expand/collapse.
Removes backend truncation of Bash commands at 50 chars.
2026-02-24 10:44:22 -08:00
21 changed files with 21 additions and 315 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hikari-desktop", "name": "hikari-desktop",
"version": "1.7.0", "version": "1.6.0",
"description": "", "description": "",
"type": "module", "type": "module",
"scripts": { "scripts": {
+1 -1
View File
@@ -1636,7 +1636,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hikari-desktop" name = "hikari-desktop"
version = "1.7.0" version = "1.6.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.7.0" version = "1.6.0"
description = "Hikari - Claude Code Visual Assistant" description = "Hikari - Claude Code Visual Assistant"
authors = ["Naomi Carrigan"] authors = ["Naomi Carrigan"]
edition = "2021" edition = "2021"
-10
View File
@@ -28,9 +28,6 @@ pub struct ClaudeStartOptions {
#[serde(default)] #[serde(default)]
pub use_worktree: bool, pub use_worktree: bool,
#[serde(default)]
pub disable_1m_context: bool,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -122,9 +119,6 @@ pub struct HikariConfig {
#[serde(default)] #[serde(default)]
pub use_worktree: bool, pub use_worktree: bool,
#[serde(default)]
pub disable_1m_context: bool,
} }
impl Default for HikariConfig { impl Default for HikariConfig {
@@ -158,7 +152,6 @@ impl Default for HikariConfig {
budget_warning_threshold: 0.8, budget_warning_threshold: 0.8,
discord_rpc_enabled: true, discord_rpc_enabled: true,
use_worktree: false, use_worktree: false,
disable_1m_context: false,
} }
} }
} }
@@ -266,8 +259,6 @@ mod tests {
assert_eq!(config.budget_action, BudgetAction::Warn); assert_eq!(config.budget_action, BudgetAction::Warn);
assert!((config.budget_warning_threshold - 0.8).abs() < f32::EPSILON); assert!((config.budget_warning_threshold - 0.8).abs() < f32::EPSILON);
assert!(config.discord_rpc_enabled); assert!(config.discord_rpc_enabled);
assert!(!config.use_worktree);
assert!(!config.disable_1m_context);
} }
#[test] #[test]
@@ -301,7 +292,6 @@ mod tests {
budget_warning_threshold: 0.75, budget_warning_threshold: 0.75,
discord_rpc_enabled: true, discord_rpc_enabled: true,
use_worktree: true, use_worktree: true,
disable_1m_context: false,
}; };
let json = serde_json::to_string(&config).unwrap(); let json = serde_json::to_string(&config).unwrap();
-2
View File
@@ -305,8 +305,6 @@ pub struct AgentEndEvent {
pub duration_ms: Option<u64>, pub duration_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub num_turns: Option<u32>, pub num_turns: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_assistant_message: Option<String>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
+13 -212
View File
@@ -279,11 +279,6 @@ impl WslBridge {
} }
} }
// Disable 1M context window if requested
if options.disable_1m_context {
cmd.env("CLAUDE_CODE_DISABLE_1M_CONTEXT", "1");
}
cmd cmd
} else { } else {
// Running on Windows - use wsl with bash login shell to ensure PATH is loaded // Running on Windows - use wsl with bash login shell to ensure PATH is loaded
@@ -324,11 +319,6 @@ impl WslBridge {
} }
} }
// Disable 1M context window if requested
if options.disable_1m_context {
claude_cmd.push_str("CLAUDE_CODE_DISABLE_1M_CONTEXT=1 ");
}
claude_cmd.push_str( claude_cmd.push_str(
"claude --output-format stream-json --input-format stream-json --verbose", "claude --output-format stream-json --input-format stream-json --verbose",
); );
@@ -765,7 +755,6 @@ fn handle_stderr(
conversation_id: conversation_id.clone(), conversation_id: conversation_id.clone(),
duration_ms: None, duration_ms: None,
num_turns: None, num_turns: None,
last_assistant_message: stop_data.last_assistant_message,
}, },
); );
} }
@@ -839,78 +828,27 @@ fn parse_subagent_start_hook(line: &str) -> Option<SubagentStartData> {
#[derive(Debug)] #[derive(Debug)]
struct SubagentStopData { struct SubagentStopData {
parent_tool_use_id: Option<String>, parent_tool_use_id: Option<String>,
last_assistant_message: Option<String>,
}
/// Extracts the content of a Rust Debug-formatted `Some("...")` field from a hook line.
/// Handles escaped characters (e.g. `\"` → `"`, `\\` → `\`, `\n` → newline).
/// Returns `None` if the field is absent or formatted as `None`.
fn extract_debug_string_value(line: &str, key: &str) -> Option<String> {
let prefix = format!("{}=Some(\"", 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
} }
fn parse_subagent_stop_hook(line: &str) -> Option<SubagentStopData> { fn parse_subagent_stop_hook(line: &str) -> Option<SubagentStopData> {
// Parse: [SubagentStop Hook] ... parent_tool_use_id=Some("toolu_xxx"), last_assistant_message=Some("..."), ... // Parse: [SubagentStop Hook] ... parent_tool_use_id=Some("toolu_xxx"), ...
let parent_tool_use_id = extract_debug_string_value(line, "parent_tool_use_id"); // Extract parent_tool_use_id if present
let last_assistant_message = extract_debug_string_value(line, "last_assistant_message"); let parent_tool_use_id = if line.contains("parent_tool_use_id=Some") {
line.split("parent_tool_use_id=Some(\"")
.nth(1)?
.split('"')
.next()
.map(|s| s.to_string())
} else {
None
};
Some(SubagentStopData { Some(SubagentStopData {
parent_tool_use_id, parent_tool_use_id,
last_assistant_message,
}) })
} }
/// Extract text content from a ToolResult's `content` field.
/// 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> {
match content {
serde_json::Value::String(s) if !s.is_empty() => Some(s.clone()),
serde_json::Value::Array(blocks) => {
let texts: Vec<String> = blocks
.iter()
.filter_map(|block| {
if block.get("type")?.as_str()? == "text" {
block.get("text")?.as_str().map(String::from)
} else {
None
}
})
.collect();
if texts.is_empty() {
None
} else {
Some(texts.join("\n"))
}
}
_ => None,
}
}
fn process_json_line( fn process_json_line(
line: &str, line: &str,
app: &AppHandle, app: &AppHandle,
@@ -1212,8 +1150,8 @@ fn process_json_line(
} }
ContentBlock::ToolResult { ContentBlock::ToolResult {
tool_use_id, tool_use_id,
content,
is_error, is_error,
..
} => { } => {
// Emit agent-end for all tool results // Emit agent-end for all tool results
// The frontend will ignore IDs that don't match known agents // The frontend will ignore IDs that don't match known agents
@@ -1231,7 +1169,6 @@ fn process_json_line(
conversation_id: conversation_id.clone(), conversation_id: conversation_id.clone(),
duration_ms: None, duration_ms: None,
num_turns: None, num_turns: None,
last_assistant_message: extract_tool_result_text(content),
}, },
); );
} }
@@ -1649,8 +1586,8 @@ fn process_json_line(
for block in &message.content { for block in &message.content {
if let ContentBlock::ToolResult { if let ContentBlock::ToolResult {
tool_use_id, tool_use_id,
content,
is_error, is_error,
..
} = block } = block
{ {
let now = SystemTime::now() let now = SystemTime::now()
@@ -1667,7 +1604,6 @@ fn process_json_line(
conversation_id: conversation_id.clone(), conversation_id: conversation_id.clone(),
duration_ms: None, duration_ms: None,
num_turns: None, num_turns: None,
last_assistant_message: extract_tool_result_text(content),
}, },
); );
} }
@@ -2219,140 +2155,5 @@ mod tests {
assert!(result.is_some()); assert!(result.is_some());
let data = result.unwrap(); let data = result.unwrap();
assert_eq!(data.parent_tool_use_id, None); assert_eq!(data.parent_tool_use_id, None);
assert_eq!(data.last_assistant_message, None);
}
#[test]
fn test_parse_subagent_stop_hook_with_last_message() {
let line = r#"[SubagentStop Hook] stop_hook_active=true, parent_tool_use_id=Some("toolu_01ABC123"), last_assistant_message=Some("Task completed successfully."), session_id=123"#;
let result = parse_subagent_stop_hook(line);
assert!(result.is_some());
let data = result.unwrap();
assert_eq!(data.parent_tool_use_id, Some("toolu_01ABC123".to_string()));
assert_eq!(
data.last_assistant_message,
Some("Task completed successfully.".to_string())
);
}
#[test]
fn test_parse_subagent_stop_hook_with_last_message_none() {
let line = r#"[SubagentStop Hook] stop_hook_active=true, parent_tool_use_id=Some("toolu_01ABC123"), last_assistant_message=None, session_id=123"#;
let result = parse_subagent_stop_hook(line);
assert!(result.is_some());
let data = result.unwrap();
assert_eq!(data.last_assistant_message, None);
}
#[test]
fn test_extract_debug_string_value_simple() {
let line = r#"key=Some("hello world")"#;
assert_eq!(
extract_debug_string_value(line, "key"),
Some("hello world".to_string())
);
}
#[test]
fn test_extract_debug_string_value_with_escaped_quotes() {
let line = r#"key=Some("say \"hi\" there")"#;
assert_eq!(
extract_debug_string_value(line, "key"),
Some(r#"say "hi" there"#.to_string())
);
}
#[test]
fn test_extract_debug_string_value_none_variant() {
let line = "key=None";
assert_eq!(extract_debug_string_value(line, "key"), None);
}
#[test]
fn test_extract_debug_string_value_missing_key() {
let line = "other=Some(\"value\")";
assert_eq!(extract_debug_string_value(line, "key"), None);
}
#[test]
fn test_parse_subagent_stop_hook_with_commas_in_message() {
let line = r#"[SubagentStop Hook] parent_tool_use_id=Some("toolu_01"), last_assistant_message=Some("Found 3 files, all passing.")"#;
let result = parse_subagent_stop_hook(line);
assert!(result.is_some());
let data = result.unwrap();
assert_eq!(
data.last_assistant_message,
Some("Found 3 files, all passing.".to_string())
);
}
// extract_tool_result_text tests
#[test]
fn test_extract_tool_result_text_plain_string() {
let content = serde_json::json!("Hello from agent");
assert_eq!(
extract_tool_result_text(&content),
Some("Hello from agent".to_string())
);
}
#[test]
fn test_extract_tool_result_text_empty_string() {
let content = serde_json::json!("");
assert_eq!(extract_tool_result_text(&content), None);
}
#[test]
fn test_extract_tool_result_text_array_single_text_block() {
let content = serde_json::json!([{"type": "text", "text": "Agent completed the task."}]);
assert_eq!(
extract_tool_result_text(&content),
Some("Agent completed the task.".to_string())
);
}
#[test]
fn test_extract_tool_result_text_array_multiple_text_blocks() {
let content = serde_json::json!([
{"type": "text", "text": "First part."},
{"type": "text", "text": "Second part."}
]);
assert_eq!(
extract_tool_result_text(&content),
Some("First part.\nSecond part.".to_string())
);
}
#[test]
fn test_extract_tool_result_text_array_non_text_block() {
let content = serde_json::json!([{"type": "image", "source": {"type": "base64"}}]);
assert_eq!(extract_tool_result_text(&content), None);
}
#[test]
fn test_extract_tool_result_text_array_mixed_blocks() {
let content = serde_json::json!([
{"type": "image", "source": {}},
{"type": "text", "text": "Found results."}
]);
assert_eq!(
extract_tool_result_text(&content),
Some("Found results.".to_string())
);
}
#[test]
fn test_extract_tool_result_text_null() {
let content = serde_json::Value::Null;
assert_eq!(extract_tool_result_text(&content), None);
}
#[test]
fn test_extract_tool_result_text_empty_array() {
let content = serde_json::json!([]);
assert_eq!(extract_tool_result_text(&content), None);
} }
} }
+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.7.0", "version": "1.6.0",
"identifier": "com.naomi.hikari-desktop", "identifier": "com.naomi.hikari-desktop",
"build": { "build": {
"beforeDevCommand": "pnpm dev", "beforeDevCommand": "pnpm dev",
-2
View File
@@ -62,7 +62,6 @@ async function changeDirectory(path: string): Promise<void> {
mcp_servers_json: config.mcp_servers_json || null, mcp_servers_json: config.mcp_servers_json || null,
allowed_tools: allAllowedTools, allowed_tools: allAllowedTools,
use_worktree: config.use_worktree ?? false, use_worktree: config.use_worktree ?? false,
disable_1m_context: config.disable_1m_context ?? false,
}, },
}); });
@@ -138,7 +137,6 @@ async function startNewConversation(): Promise<void> {
mcp_servers_json: config.mcp_servers_json || null, mcp_servers_json: config.mcp_servers_json || null,
allowed_tools: allAllowedTools, allowed_tools: allAllowedTools,
use_worktree: config.use_worktree ?? false, use_worktree: config.use_worktree ?? false,
disable_1m_context: config.disable_1m_context ?? false,
}, },
}); });
@@ -318,16 +318,6 @@
<span class="text-[10px] text-red-400">Errored / Killed</span> <span class="text-[10px] text-red-400">Errored / Killed</span>
{/if} {/if}
</div> </div>
<!-- Last assistant message snippet -->
{#if agent.lastAssistantMessage}
<p
class="mt-1 text-[10px] text-[var(--text-secondary)] italic truncate"
title={agent.lastAssistantMessage}
>
{agent.lastAssistantMessage}
</p>
{/if}
</div> </div>
{/each} {/each}
{/if} {/if}
+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.50"; const SUPPORTED_CLI_VERSION = "2.1.33";
let installedVersion = $state("Loading..."); let installedVersion = $state("Loading...");
-17
View File
@@ -54,7 +54,6 @@
discord_rpc_enabled: true, discord_rpc_enabled: true,
show_thinking_blocks: true, show_thinking_blocks: true,
use_worktree: false, use_worktree: false,
disable_1m_context: false,
}); });
let showCustomThemeEditor = $state(false); let showCustomThemeEditor = $state(false);
@@ -490,22 +489,6 @@
Launch sessions with <code class="font-mono">--worktree</code> for isolated git worktree environments Launch sessions with <code class="font-mono">--worktree</code> for isolated git worktree environments
</p> </p>
</div> </div>
<!-- Disable 1M Context Window -->
<div class="mb-4">
<label class="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
bind:checked={config.disable_1m_context}
class="w-4 h-4 rounded border-[var(--border-color)] bg-[var(--bg-primary)] text-[var(--accent-primary)] focus:ring-[var(--accent-primary)]"
/>
<span class="text-sm text-[var(--text-primary)]">Disable 1M context window</span>
</label>
<p class="text-xs text-[var(--text-tertiary)] mt-1 ml-7">
Sets <code class="font-mono">CLAUDE_CODE_DISABLE_1M_CONTEXT=1</code> to opt out of the extended
context window
</p>
</div>
</section> </section>
<!-- Greeting Section --> <!-- Greeting Section -->
-1
View File
@@ -363,7 +363,6 @@ User: ${formattedMessage}`;
mcp_servers_json: config.mcp_servers_json || null, mcp_servers_json: config.mcp_servers_json || null,
allowed_tools: allAllowedTools, allowed_tools: allAllowedTools,
use_worktree: config.use_worktree ?? false, use_worktree: config.use_worktree ?? false,
disable_1m_context: config.disable_1m_context ?? false,
}, },
}); });
@@ -88,7 +88,6 @@
mcp_servers_json: config.mcp_servers_json || null, mcp_servers_json: config.mcp_servers_json || null,
allowed_tools: newGrantedTools, allowed_tools: newGrantedTools,
use_worktree: config.use_worktree ?? false, use_worktree: config.use_worktree ?? false,
disable_1m_context: config.disable_1m_context ?? false,
}, },
}); });
-3
View File
@@ -102,7 +102,6 @@
discord_rpc_enabled: true, discord_rpc_enabled: true,
show_thinking_blocks: true, show_thinking_blocks: true,
use_worktree: false, use_worktree: false,
disable_1m_context: false,
}); });
let streamerModeActive = $state(false); let streamerModeActive = $state(false);
@@ -181,7 +180,6 @@
mcp_servers_json: currentConfig.mcp_servers_json || null, mcp_servers_json: currentConfig.mcp_servers_json || null,
allowed_tools: allAllowedTools, allowed_tools: allAllowedTools,
use_worktree: currentConfig.use_worktree ?? false, use_worktree: currentConfig.use_worktree ?? false,
disable_1m_context: currentConfig.disable_1m_context ?? false,
}, },
}); });
@@ -294,7 +292,6 @@
mcp_servers_json: currentConfig.mcp_servers_json || null, mcp_servers_json: currentConfig.mcp_servers_json || null,
allowed_tools: allAllowedTools, allowed_tools: allAllowedTools,
use_worktree: currentConfig.use_worktree ?? false, use_worktree: currentConfig.use_worktree ?? false,
disable_1m_context: currentConfig.disable_1m_context ?? false,
}, },
}); });
@@ -107,7 +107,6 @@
mcp_servers_json: config.mcp_servers_json || null, mcp_servers_json: config.mcp_servers_json || null,
allowed_tools: grantedToolsList, allowed_tools: grantedToolsList,
use_worktree: config.use_worktree ?? false, use_worktree: config.use_worktree ?? false,
disable_1m_context: config.disable_1m_context ?? false,
}, },
}); });
-26
View File
@@ -177,32 +177,6 @@ describe("agents store", () => {
const agents = get(getAgentsForConversation(conversationId)); const agents = get(getAgentsForConversation(conversationId));
expect(agents[0].status).toBe("running"); // Status unchanged expect(agents[0].status).toBe("running"); // Status unchanged
}); });
it("stores lastAssistantMessage when provided", () => {
const agent = createMockAgent({ status: "running" });
agentStore.addAgent(conversationId, agent);
agentStore.endAgent(
conversationId,
agent.toolUseId,
Date.now(),
false,
"Task completed successfully."
);
const agents = get(getAgentsForConversation(conversationId));
expect(agents[0].lastAssistantMessage).toBe("Task completed successfully.");
});
it("leaves lastAssistantMessage undefined when not provided", () => {
const agent = createMockAgent({ status: "running" });
agentStore.addAgent(conversationId, agent);
agentStore.endAgent(conversationId, agent.toolUseId, Date.now(), false);
const agents = get(getAgentsForConversation(conversationId));
expect(agents[0].lastAssistantMessage).toBeUndefined();
});
}); });
describe("markAllErrored", () => { describe("markAllErrored", () => {
+1 -8
View File
@@ -45,13 +45,7 @@ function createAgentStore() {
}); });
}, },
endAgent( endAgent(conversationId: string, toolUseId: string, endedAt: number, isError: boolean) {
conversationId: string,
toolUseId: string,
endedAt: number,
isError: boolean,
lastAssistantMessage?: string
) {
agentsByConversation.update((state) => { agentsByConversation.update((state) => {
const agents = state[conversationId]; const agents = state[conversationId];
if (!agents) return state; if (!agents) return state;
@@ -68,7 +62,6 @@ function createAgentStore() {
endedAt, endedAt,
status: isError ? "errored" : "completed", status: isError ? "errored" : "completed",
durationMs, durationMs,
lastAssistantMessage,
}; };
return { return {
-3
View File
@@ -195,7 +195,6 @@ describe("config store", () => {
discord_rpc_enabled: true, discord_rpc_enabled: true,
show_thinking_blocks: true, show_thinking_blocks: true,
use_worktree: false, use_worktree: false,
disable_1m_context: false,
}; };
expect(config.model).toBe("claude-sonnet-4"); expect(config.model).toBe("claude-sonnet-4");
@@ -243,7 +242,6 @@ describe("config store", () => {
discord_rpc_enabled: true, discord_rpc_enabled: true,
show_thinking_blocks: true, show_thinking_blocks: true,
use_worktree: false, use_worktree: false,
disable_1m_context: false,
}; };
expect(config.model).toBeNull(); expect(config.model).toBeNull();
@@ -790,7 +788,6 @@ describe("config store", () => {
discord_rpc_enabled: false, discord_rpc_enabled: false,
show_thinking_blocks: true, show_thinking_blocks: true,
use_worktree: false, use_worktree: false,
disable_1m_context: false,
}; };
const mockInvokeImpl = vi.mocked(invoke); const mockInvokeImpl = vi.mocked(invoke);
-3
View File
@@ -49,8 +49,6 @@ export interface HikariConfig {
show_thinking_blocks: boolean; show_thinking_blocks: boolean;
// Worktree isolation // Worktree isolation
use_worktree: boolean; use_worktree: boolean;
// Disable 1M context window
disable_1m_context: boolean;
} }
const defaultConfig: HikariConfig = { const defaultConfig: HikariConfig = {
@@ -92,7 +90,6 @@ const defaultConfig: HikariConfig = {
discord_rpc_enabled: true, discord_rpc_enabled: true,
show_thinking_blocks: true, show_thinking_blocks: true,
use_worktree: false, use_worktree: false,
disable_1m_context: false,
}; };
function createConfigStore() { function createConfigStore() {
+2 -9
View File
@@ -474,17 +474,10 @@ export async function initializeTauriListeners() {
unlisteners.push(agentUpdateUnlisten); unlisteners.push(agentUpdateUnlisten);
const agentEndUnlisten = await listen<AgentEndPayload>("claude:agent-end", (event) => { const agentEndUnlisten = await listen<AgentEndPayload>("claude:agent-end", (event) => {
const { tool_use_id, ended_at, is_error, conversation_id, last_assistant_message } = const { tool_use_id, ended_at, is_error, conversation_id } = event.payload;
event.payload;
const targetConversationId = conversation_id || get(claudeStore.activeConversationId); const targetConversationId = conversation_id || get(claudeStore.activeConversationId);
if (targetConversationId) { if (targetConversationId) {
agentStore.endAgent( agentStore.endAgent(targetConversationId, tool_use_id, ended_at, is_error);
targetConversationId,
tool_use_id,
ended_at,
is_error,
last_assistant_message
);
} }
}); });
unlisteners.push(agentEndUnlisten); unlisteners.push(agentEndUnlisten);
-2
View File
@@ -12,7 +12,6 @@ export interface AgentInfo {
durationMs?: number; durationMs?: number;
characterName: string; characterName: string;
characterAvatar: string; characterAvatar: string;
lastAssistantMessage?: string;
} }
export interface AgentStartPayload { export interface AgentStartPayload {
@@ -32,5 +31,4 @@ export interface AgentEndPayload {
conversation_id?: string; conversation_id?: string;
duration_ms?: number; duration_ms?: number;
num_turns?: number; num_turns?: number;
last_assistant_message?: string;
} }