generated from nhcarrigan/template
38692391e0
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+.
196 lines
6.7 KiB
Svelte
196 lines
6.7 KiB
Svelte
<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,
|
|
disable_cron: config.disable_cron ?? false,
|
|
disable_skill_shell_execution: config.disable_skill_shell_execution ?? 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,
|
|
bare_mode: config.bare_mode ?? false,
|
|
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,
|
|
},
|
|
});
|
|
|
|
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}
|