Phase 10: CalDAV full lifecycle, update_note, dashboard inline streaming, keyboard shortcuts

Backend:
- caldav.py: Full event lifecycle — update_event, delete_event; VTODO suite —
  create_todo, list_todos, complete_todo, delete_todo; list_calendars; timezone
  support via ZoneInfo; reminders via VALARM; attendees; multi-calendar search
  (_get_all_calendars scans all calendars when no specific one is configured)
- tools.py: New update_note tool (find by title + replace/append modes),
  7 new CalDAV tool definitions, corresponding execute_tool cases
- llm.py: Update system prompt — add update_note guidance, full CalDAV action list
- intent.py: Confidence scoring (high/medium/low) + should_execute property;
  conversation history support for anaphora resolution; routing rules for
  update/delete events, todos, update_note vs create_note disambiguation,
  time-period → list_events (not search_events), reminder_minutes conversion
- generation_task.py: Parallel fetch of tools + intent_model setting; dedicated
  intent model (OLLAMA_INTENT_MODEL env var or per-user intent_model setting)
- config.py: Add OLLAMA_INTENT_MODEL env var

Frontend:
- HomeView.vue: Inline streaming response (no navigation); quick action chips;
  isConversational computed — prominent "Continue this conversation" CTA when
  no tool calls; auto-focus chat input on mount via chatInputRef
- DashboardChatInput.vue: defineExpose({ focus }) for external focus control
- ChatView.vue: Escape key handler — close picker → close sidebar → clear
  textarea → navigate home; onUnmounted cleanup
- App.vue: Global ? key shortcut toggles keyboard shortcuts overlay; shared
  state via useShortcuts composable; Transition animation
- AppHeader.vue: ? button for shortcuts overlay discoverability
- useShortcuts.ts (new): Shared showShortcuts ref + open/close/toggle helpers
- ToolCallCard.vue: note_updated, event_updated, event_deleted, calendars,
  todo, todos, todo_completed, todo_deleted label cases + render blocks
- SettingsView.vue: Intent model field + caldav_timezone setting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 22:04:41 -05:00
parent 75560dee4e
commit 70cba72a80
15 changed files with 1569 additions and 71 deletions
+22 -1
View File
@@ -9,6 +9,7 @@ const store = useSettingsStore();
const authStore = useAuthStore();
const toastStore = useToastStore();
const assistantName = ref("");
const intentModel = ref("");
const currentPassword = ref("");
const newPassword = ref("");
const confirmNewPassword = ref("");
@@ -63,6 +64,7 @@ onMounted(async () => {
// Load notification preferences from user settings
const allSettings = await apiGet<Record<string, string>>("/api/settings");
intentModel.value = allSettings.intent_model ?? "";
if (allSettings.notify_task_reminders !== undefined) {
notifyTaskReminders.value = allSettings.notify_task_reminders !== "false";
}
@@ -126,7 +128,10 @@ async function saveAssistant() {
saving.value = true;
saved.value = false;
try {
await store.updateSettings({ assistant_name: assistantName.value.trim() || "Fable" });
await store.updateSettings({
assistant_name: assistantName.value.trim() || "Fable",
intent_model: intentModel.value.trim(),
});
saved.value = true;
setTimeout(() => (saved.value = false), 2000);
} finally {
@@ -310,6 +315,22 @@ async function handleRestoreFile(event: Event) {
</p>
</div>
<div class="field">
<label for="intent-model">Intent Model</label>
<input
id="intent-model"
v-model="intentModel"
type="text"
placeholder="Same as chat model"
class="input"
/>
<p class="field-hint">
Optional smaller/faster model for intent routing (e.g. <code>llama3.2:3b</code>,
<code>qwen2.5:3b</code>). Leave empty to use the same model as chat.
Can also be set via <code>OLLAMA_INTENT_MODEL</code> environment variable.
</p>
</div>
<div class="actions">
<button class="btn-save" @click="saveAssistant" :disabled="saving">
{{ saving ? "Saving..." : "Save" }}