feat: expose prompt caching TTL setting in UI (#273)

Adds a dropdown to select ENABLE_PROMPT_CACHING_1H or FORCE_PROMPT_CACHING_5M env vars when starting Claude Code sessions. Requires Claude Code v2.1.108+.
This commit is contained in:
2026-05-06 14:01:00 -07:00
committed by Naomi Carrigan
parent 3f57937d54
commit 38692391e0
12 changed files with 67 additions and 0 deletions
+12
View File
@@ -75,6 +75,11 @@ pub struct ClaudeStartOptions {
/// Valid values: "low", "medium", "high", "xhigh" (Opus 4.7 only), "max". None uses CLI default.
#[serde(default)]
pub effort_level: Option<String>,
/// Sets `ENABLE_PROMPT_CACHING_1H=1` or `FORCE_PROMPT_CACHING_5M=1` env var (v2.1.108+).
/// Values: "1h" → 1-hour TTL, "5m" → force 5-minute TTL. None uses CLI default.
#[serde(default)]
pub prompt_caching_ttl: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -246,6 +251,11 @@ pub struct HikariConfig {
/// Valid values: "low", "medium", "high", "xhigh" (Opus 4.7 only), "max". None uses CLI default.
#[serde(default)]
pub effort_level: Option<String>,
/// Sets `ENABLE_PROMPT_CACHING_1H=1` or `FORCE_PROMPT_CACHING_5M=1` env var (v2.1.108+).
/// Values: "1h" → 1-hour TTL, "5m" → force 5-minute TTL. None uses CLI default.
#[serde(default)]
pub prompt_caching_ttl: Option<String>,
}
impl Default for HikariConfig {
@@ -302,6 +312,7 @@ impl Default for HikariConfig {
show_clear_context_on_plan_accept: true,
custom_model_option: None,
effort_level: None,
prompt_caching_ttl: None,
}
}
}
@@ -520,6 +531,7 @@ mod tests {
show_clear_context_on_plan_accept: true,
custom_model_option: None,
effort_level: None,
prompt_caching_ttl: None,
};
let json = serde_json::to_string(&config).unwrap();
+18
View File
@@ -410,6 +410,15 @@ impl WslBridge {
}
}
// Set prompt caching TTL env var if specified (v2.1.108+)
if let Some(ref ttl) = options.prompt_caching_ttl {
match ttl.as_str() {
"1h" => { cmd.env("ENABLE_PROMPT_CACHING_1H", "1"); }
"5m" => { cmd.env("FORCE_PROMPT_CACHING_5M", "1"); }
_ => {}
}
}
cmd
} else {
// Running on Windows - use wsl with bash login shell to ensure PATH is loaded
@@ -485,6 +494,15 @@ impl WslBridge {
}
}
// Set prompt caching TTL env var if specified (v2.1.108+)
if let Some(ref ttl) = options.prompt_caching_ttl {
match ttl.as_str() {
"1h" => { claude_cmd.push_str("ENABLE_PROMPT_CACHING_1H=1 "); }
"5m" => { claude_cmd.push_str("FORCE_PROMPT_CACHING_5M=1 "); }
_ => {}
}
}
claude_cmd.push_str(
"claude --output-format stream-json --input-format stream-json --verbose",
);
+2
View File
@@ -78,6 +78,7 @@ async function changeDirectory(path: string): Promise<void> {
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
custom_model_option: config.custom_model_option || null,
effort_level: config.effort_level || null,
prompt_caching_ttl: config.prompt_caching_ttl || null,
},
});
@@ -166,6 +167,7 @@ async function startNewConversation(): Promise<void> {
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
custom_model_option: config.custom_model_option || null,
effort_level: config.effort_level || null,
prompt_caching_ttl: config.prompt_caching_ttl || null,
},
});
+21
View File
@@ -79,6 +79,7 @@
task_loop_commit_prefix: "feat",
task_loop_include_summary: false,
effort_level: null,
prompt_caching_ttl: null,
});
let showCustomThemeEditor = $state(false);
@@ -740,6 +741,26 @@
</p>
</div>
<!-- Prompt Caching TTL -->
<div class="mb-4">
<label class="block text-sm text-[var(--text-primary)] mb-1" for="prompt-caching-ttl"
>Prompt caching TTL</label
>
<select
id="prompt-caching-ttl"
bind:value={config.prompt_caching_ttl}
class="w-full px-3 py-2 rounded border border-[var(--border-color)] bg-[var(--bg-primary)] text-[var(--text-primary)] text-sm focus:outline-none focus:ring-1 focus:ring-[var(--accent-primary)]"
>
<option value={null}>Default (CLI decides)</option>
<option value="1h">1-hour TTL (reduces cost for long sessions)</option>
<option value="5m">Force 5-minute TTL</option>
</select>
<p class="text-xs text-[var(--text-tertiary)] mt-1">
Sets <code class="font-mono">ENABLE_PROMPT_CACHING_1H</code> or
<code class="font-mono">FORCE_PROMPT_CACHING_5M</code>. Requires Claude Code v2.1.108+.
</p>
</div>
<!-- Bare Mode -->
<div class="mb-4">
<label class="flex items-center gap-3 cursor-pointer">
@@ -79,6 +79,7 @@
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
custom_model_option: config.custom_model_option || null,
effort_level: config.effort_level || null,
prompt_caching_ttl: config.prompt_caching_ttl || null,
},
});
+1
View File
@@ -413,6 +413,7 @@ User: ${formattedMessage}`;
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
custom_model_option: config.custom_model_option || null,
effort_level: config.effort_level || null,
prompt_caching_ttl: config.prompt_caching_ttl || null,
},
});
@@ -100,6 +100,7 @@
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
custom_model_option: config.custom_model_option || null,
effort_level: config.effort_level || null,
prompt_caching_ttl: config.prompt_caching_ttl || null,
},
});
+3
View File
@@ -98,6 +98,7 @@
show_clear_context_on_plan_accept: true,
custom_model_option: null,
effort_level: null,
prompt_caching_ttl: null,
});
let streamerModeActive = $state(false);
@@ -191,6 +192,7 @@
currentConfig.show_clear_context_on_plan_accept ?? true,
custom_model_option: currentConfig.custom_model_option || null,
effort_level: currentConfig.effort_level || null,
prompt_caching_ttl: currentConfig.prompt_caching_ttl || null,
},
});
@@ -360,6 +362,7 @@
currentConfig.show_clear_context_on_plan_accept ?? true,
custom_model_option: currentConfig.custom_model_option || null,
effort_level: currentConfig.effort_level || null,
prompt_caching_ttl: currentConfig.prompt_caching_ttl || null,
},
});
+1
View File
@@ -229,6 +229,7 @@
show_clear_context_on_plan_accept: cfg.show_clear_context_on_plan_accept ?? true,
custom_model_option: cfg.custom_model_option || null,
effort_level: cfg.effort_level || null,
prompt_caching_ttl: cfg.prompt_caching_ttl || null,
},
});
} catch (error) {
@@ -119,6 +119,7 @@
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
custom_model_option: config.custom_model_option || null,
effort_level: config.effort_level || null,
prompt_caching_ttl: config.prompt_caching_ttl || null,
},
});
+3
View File
@@ -230,6 +230,7 @@ describe("config store", () => {
show_clear_context_on_plan_accept: true,
custom_model_option: null,
effort_level: null,
prompt_caching_ttl: null,
};
expect(config.model).toBe("claude-sonnet-4");
@@ -299,6 +300,7 @@ describe("config store", () => {
show_clear_context_on_plan_accept: true,
custom_model_option: null,
effort_level: null,
prompt_caching_ttl: null,
};
expect(config.model).toBeNull();
@@ -923,6 +925,7 @@ describe("config store", () => {
show_clear_context_on_plan_accept: true,
custom_model_option: null,
effort_level: null,
prompt_caching_ttl: null,
};
const mockInvokeImpl = vi.mocked(invoke);
+3
View File
@@ -101,6 +101,8 @@ export interface HikariConfig {
custom_model_option: string | null;
// Effort level for Claude Code (v2.1.111+) — null means use CLI default
effort_level: string | null;
// Prompt caching TTL override (v2.1.108+) — null means use CLI default
prompt_caching_ttl: string | null;
}
const defaultConfig: HikariConfig = {
@@ -164,6 +166,7 @@ const defaultConfig: HikariConfig = {
show_clear_context_on_plan_accept: true,
custom_model_option: null,
effort_level: null,
prompt_caching_ttl: null,
};
function createConfigStore() {