Skip to content

SupportOS v1.4.0 — the real-time release

Choose a tag to compare

@kimpearce888 kimpearce888 released this 27 Sep 08:01
· 17 commits to main since this release

SupportOS v1.4.0 — the real-time release

The three follow-up roadmap items are shipped — plus two serious latent bugs found and fixed in the job pipeline underneath the webhook path. 224/224 tests green (+52).

🪝 Incoming webhook push for conversations

  • Register webhooks from the app (Sync Health → Webhook push): POST /api/webhooks/register creates the webhook in Help Scout with your locally configured secret; delete just as easily
  • Conversation changes push in seconds: a convo.* event enqueues a single-conversation sync, and when it lands every connected client receives a real-time conversation-updated SSE event (id, number, subject + an honest reason: webhook)
  • Restart-safe: persisted-but-unprocessed webhook events are drained automatically on boot
  • Demo it end-to-end: POST /api/demo/simulate-webhook drives an event through the exact production path — HMAC-signed self-POST → persist → dedup → job → worker tick → mirror update → SSE

🔎 Semantic docs search (local embeddings + optional Qdrant)

  • Docs mirror articles are chunked and embedded by a background job; vectors are stored locally in SQLite, so semantic search works without Qdrant — and gets ANN speed when Qdrant is running
  • Hybrid retrieval: GET /api/docs/search fuses FTS5 keyword and semantic vector result lists with Reciprocal Rank Fusion; every hit records whether keyword search, semantic search, or both found it, and a mode note explains exactly what ran
  • Honest degradation at every layer (no model → FTS only with setup instructions; Qdrant down → local cosine scan; provider unreachable → FTS only, retried next query)

⏱️ SLA / business-hours reporting per mailbox

  • Per-mailbox schedules: IANA timezone, active weekdays, open window — plus first-response and resolution SLA targets — edited in Settings → Business hours
  • Reports → SLA & business hours: first-response and resolution in wall AND business minutes (median included), met/missed against targets, and live "currently waiting" aging with at-risk counts
  • The business-minutes engine is pure and DST-safe (built on the platform timezone database, unit-tested across a spring-forward transition and a half-hour zone); invalid input returns null instead of a fabricated number

🐛 Two latent job-pipeline bugs fixed (found while wiring the webhook e2e)

  • Every background job was permanently unclaimable at runtime: jobs.run_at was written in ISO-8601 (...T...Z) while the claim loop compares against SQLite datetime('now') (... ...) — 'T' > ' ' lexicographically, so webhook-triggered syncs, attachment downloads, AI jobs and embedding passes all sat queued forever since v1.0.0. Tests stayed green because they called components directly, skipping the claim loop.
  • Job payloads reached the worker as unparsed JSON strings, so payload.remoteId read as undefined and sync jobs "completed" without syncing anything.
  • Both fixes ship with regression tests that enqueue → claim → execute exactly as the worker does.

Full changelog: CHANGELOG.md