• v0.5.0 4522567669

    v0.5.0 — Concurrent calendar search and retried timeouts
    Some checks failed
    CI / gate (push) Successful in 1m1s
    CI / image (push) Failing after 1s
    Stable

    joeyr released this 2026-08-09 09:53:14 -05:00 | 109 commits to main since this release

    Changed

    • Calendars are now searched concurrently rather than one after another. Serially, a
      slow calendar added its latency to every calendar behind it: on 2026-08-09 iCloud
      stalled three of four calendars at the 30-second read timeout, so a single tool call
      spent 90 seconds before returning a partial answer — well past the point the caller
      gives up, and indistinguishable from a hang. Concurrently the worst case is the slowest
      single calendar. Capped at 4 in flight, deliberately: enough parallelism to trip
      iCloud's throttling would trade one slow calendar for four rate-limited ones.

    • A timed-out calendar is retried once before being reported unavailable. A read
      timeout against iCloud is usually throttling rather than an outage, and it previously
      cost the entire calendar for the length of the cache TTL. Only transport-level failures
      are retried — retrying a 403 or a malformed response just doubles the latency of a
      failure that was never going to succeed. caldav's own 429/503 handling is enabled at
      the same time, capped at 10 seconds so a server-supplied Retry-After of an hour
      cannot hold a tool call open for one.

    • The CalDAV timeout is now set explicitly and configurable (CALDAV_TIMEOUT,
      default 60s). It was previously whatever niquests defaults to — 30 seconds, a number
      that first appeared in a production stack trace and had to be traced back to a library
      to find out whether anyone had chosen it. It appears on the boot line.

    Added

    • A log line before the network work starts, naming the window and how many calendars
      are being read. The existing listed events line carries the window too, but it is
      only reached once every calendar has answered — so for exactly the requests that hang,
      the one field that explains why was the one never written.

    Fixed

    • Warnings rendered uncoloured and unabbreviated in Portainer. The viewer matches
      debug/info/warn/error case-sensitively, and the formatter emitted
      record.levelname.lower() — which is warning for Python's WARNING, one letter off
      the token. debug, info and error agree by coincidence, which is what hid it: the
      one level whose entire purpose is to be noticed was the only one rendering plain. That
      covered every warning this service has emitted, including the two CALENDARS allowlist
      warnings that exist specifically to be seen. Now an explicit mapping, with critical
      folded into error because the viewer knows five tokens and that is not one.
    Downloads