1.6
Hardening releaseCurrent stable2026-06-01Daisy got a full security and reliability audit, and everything found got fixed. The biggest changes you'll feel: "undo" now survives restarts, the app window finally shows YOUR real conversation and tasks (not demo content), there's a real settings screen (API keys, voice, provider — no terminal needed), and the confirmation prompts for risky actions now actually fire every time. Under the hood she's much harder to trick: quotes in calendar titles or emails can't break out into script execution anymore, and random websites can't poke her local API.
Technical details
- Confirmation layer enforced: per-action confirmation can no longer be
bypassed by the LLM's intent flag (daisy.py no longer derives auto_approve from intent.requires_confirmation); the flag can only ADD confirmation. Auto-approval is an explicit per-request API opt-in, audit-logged, with no shared-state mutation (the old provider-swap race is gone).
- AppleScript injection closed: shared
_escape()(backslash-first) in
mac_calendar.py, mac_mail.py (incl. to/cc), daisy_mcp_server, mcp-desktop-automation/server.js (now execFile, no shell); locale-safe numeric AppleScript dates; 30s osascript timeouts.
- Cross-origin guard: middleware rejects browser requests with a foreign
Origin header; auto_approve removed from the GET /api/turn-stream interface.
- Persistent undo: stack survives restarts (
~/.daisy/undo.json, atomic
writes); note-undo uses the exact written path from ActionResult.metadata; reminder-undo deletes only the one created reminder (created_at match).
- Thread safety: turns serialized with a pipeline lock;
reminders.json
guarded by a shared lock + atomic replace (services/reminders_store.py); SQLite uses one WAL connection behind a lock instead of per-call connects.
- Safety checker hardened: blocks nested shells (
bash -c …), command
substitution ($(…), backticks), and structural rm -rf variants (-fr, --recursive --force, -Rf).
- Brain parser completed:
mcp_tool_call,create_calendar_event,
create_mac_reminder, send_email now parse from LLM output (previously silently dropped); JSON-parse total failure no longer reads raw provider errors aloud.
- MCP plumbing: real read timeouts, stderr drain thread,
atexitcleanup
of child servers; computer-use kills the whole process group on timeout.
- App shell: default port unified to 5188; uvicorn shuts down gracefully on
window close; menubar polls via rumps.Timer (main thread); launchd uses bootstrap/bootout with KeepAlive SuccessfulExit=false.
- Frontend rebuilt: demo placeholder content removed; real tasks sidebar,
undo button, settings panel (Keychain keys, provider/voice, PATCH /api/settings), memory viewer, audit-log viewer, SSE auto-retry + offline banner, aria-live + contrast fixes.
- New — provider circuit breaker + cost tracking (
services/provider_health.py,
ported from the AI-OS project): a provider that fails 3× in a row is skipped for a 2-minute cooldown instead of timing out every turn; a rough running spend estimate per provider persists to ~/.daisy/costs.json. Exposed at GET /api/providers/health and GET /api/costs.
- New — free-tier LLM rotation: set
llm.provider: freeto route through
llm-free-rotator (Groq / OpenRouter / NVIDIA) for $0 operation.
- New — portable memory:
GET /api/memory/export(SecondBrain markdown +
YAML frontmatter) and POST /api/memory/import.
- New — writable settings:
PATCH /api/settingspersists provider, model,
voice and toggles to ~/.daisy/config.yaml.
- Pydantic v2 clean:
model_dump(mode="json"),ConfigDict— zero
deprecation warnings.
- Stale 0.6-era docs rewritten (QUICKSTART, START_DAISY); dead
START_INTERACTIVE.sh removed.