Skip to content
KAMOUniverse

Changelog

What's new.

Every improvement to the platform, as it ships.

  1. · kamo-internal

    Overhaul /messages — iframe rendering, list context menu, threading, races, a11y

    Implements the audit of the /messages page end-to-end. Reported issues: - Reply/Reply All/Forward buttons now show text labels; removed from the more-actions dropdown (they're adjacent buttons) while the new list menu keeps them. - Message-list rows now open the shared action menu on right-click and touch long-press **************** rows gain a focus ring (j/k), selection checkbox (x), and scroll-into-view. - Email now renders in a sandboxed auto-height iframe (srcdoc) instead of inline dangerouslySetInnerHTML. This isolates email CSS/layout, which fixes the reading-pane "bounce" and the mid-drag text-selection loss, closes the app-wide CSS-leak, and adds a default "external images blocked" tracking-pixel gate. Correctness/bugs: - Full-compose Reply/Reply-All now set In-Reply-To/References (unified DraftMessage type) so replies stay threaded; quick-reply threads References + parent id, HTML- escapes the body, and sends from the active shared/member mailbox. - Forward carries the original attachments; reply/forward resolve the full message (fetching body for list-row actions) instead of quoting whatever is open. - Race guards (latest-wins) on loadMessages / message select / thread load / conversation load; poll no longer yanks paged users to page 0; WS reload filtered to the active folder; searchPage resets on query change. - ThreadDetail expand state keyed by folder+uid (cross-folder collision); markRead only when unread; retry uses the folder prop. - Compose: flush typed-but-uncommitted recipient on Send/Draft; attachment dedupe + 40MB aggregate cap + functional updates; drop the redundant client OTK. - Proxy: forward Range + caching validators for attachment/inline streaming; close the multipart carve-out on guarded provisioning paths. - Email WebSocket: real stop after max retries, exponential backoff, stable-connection retry reset; memoized context value to cut consumer re-render churn. Quality: unified **************** a11y (aria-labels, aria-pressed, keyboard-operable rows/toggles); i18n (folder names, list dates, pagers, ICU plural); removed dead code and phantom keyboard-shortcut entries.

  2. · kamo-internal

    Instrument Panel redesign — accent families, masthead, section-index nav, unified glass card

    Redesigns the two most-visited landings into a permission-shaped operator's console. Foundation - Turn on the already-bundled Geist Sans/Mono (were discarded by an Arial fallback); route MUI typography through Geist. - Add white-label-safe --accent-* module-family tokens, an elevation ladder, a lit-glass top-edge, Operator-Label + .tabular utilities, and an --ease-house token. Fix --gradient-primary-transparent (was hardcoded blue → now derived from --color-primary). Shared FeatureCard (one component, keyboard-correct) - Now a real anchor (NextLink): Tab/Enter/middle-click work and the focus-visible ring can finally fire. Adds useReducedMotion guard, whileInView entrance on the house ease, family-accent lit chip, attention count pip, weight tiers, and onClick passthrough. Revives the per-module colour intent that was discarded on render. Deletes the forked inline home card. Home - Masthead (mono kicker + time/name greeting + tabular status strip) replaces the org-only banner; oklab Backdrop replaces the grayscale-multiply watermark; permission-shaped two-tier launchpad (usage-ordered primary + grouped directory) with a "Needs you today" attention strip and "Jump back in" (new useHomeRecent + global RecentRouteTracker); adaptive sparse/empty states; real <h1> + landmarks. Settings - Registry-driven render replaces 18 copy-pasted Collapse blocks: sticky section-index rail + IntersectionObserver scroll-spy + live counts, 4 super-groups, progressive disclosure (Personal open, recent-aware, last-section restore, expand/collapse all), SectionHeader instrument rows, and a fixed Quick Access (correct MAX_RECENT slice + relative-time + Start here). Preserves every permission/product-flag gate exactly. Cross-cutting: skeleton + EmptyState, prefers-reduced-transparency + reduced-motion paths, per-locale Operator-Label caps opt-out. New UI strings land in the kamo-translation-dictionary (en) in a companion commit.

  3. · kamo-shared-library

    **************** targeted update

    Async conversion (virtual threads) and scheduled sweeps hold a detached ImgDat; save()/merge() cascades into the orphanRemoval=true images collection and throws 'a collection with cascade=all-delete-orphan was no longer referenced by the owning entity instance'. A bulk UPDATE of just the thumbnail columns avoids the entity graph entirely and is safe from any thread.

  4. · kamo-internal

    Add Reply All to the messages reading pane and thread view

    Reply All was advertised (keyboard-shortcut label + i18n key) but had no handler. Wire it up: new computeReplyAllRecipients helper puts the sender in To and every other original recipient in Cc, excluding self and de-duping case-insensitively. Adds a button, the `a` shortcut, and a context-menu entry in MessageBrowser, plus a per-message button in ThreadDetailView.

  5. · InitializerService

    Partial-unique index enforcing one active closing package per loan (eClose double-close backstop)

    CREATE UNIQUE INDEX uk_mlos_closing_pkg_active_loan ON mlos_closing_package (loan_file_uid) WHERE status <> 'CANCELLED'. Durable backstop behind the assembler's in-app idempotency guard: a duplicate active closing package would drive its own MERS registration + funding (a double close). ClosingPackage lives in shared-lib with only non-unique @Index entries and can't express a partial-unique constraint via JPA, so it's hand-written DDL here (KI owns schema). Idempotent; applies on the next KI run.

  6. · MediaService

    Thumbnail scheduled-generated materials in-process

    A headless sweep can't drive ConversionService's OTK thumbnail pipeline, so generate a page-0 PNG thumbnail via ImageIO right after storing the export — scheduled materials now render in the grid like any other, no backfill needed.

  7. · kamo-internal

    Recurring generation schedules UI

    ScheduleManager: create/list/delete schedules (recipe + daily/weekly/monthly + hour/day + optional auto-post targets + caption); launched from Materials, gated on the Canva autofill (Enterprise) capability. + canvaApi + /api/canva/schedules proxies.

  8. · MediaService

    Scheduled generation — sweep runs recipes on a cadence

    - CanvaScheduleController: CRUD for org schedules (recipe + cadence + hour/day + optional social targets), computes nextRunAt - CanvaGenerationService: headless **************** as a Material (in-process ImageService; thumbnail-less until backfill) - CanvaScheduleSweep: @Scheduled sweep, atomic per-schedule claim, generates and best-effort auto-posts the result to the target social connections via the signed public-URL seam

  9. · kamo-shared-library

    CanvaSchedule entity + repo (recurring generation)

    Org-scoped recurring schedule (recipe + cadence + optional social targets) with an atomic-claim query so a MediaService sweep runs each due schedule once.

  10. · kamo-internal

    Inline expandable Whisper transcript on softphone voicemail + call-history rows

    Each recorded row (voicemail and call history) gets a 'Transcript' toggle that fetches the recording's Whisper transcript on demand and shows it inline, so members can read it without opening the player. Cached per recording.

  11. · kamo-internal

    Save + reuse autofill recipes

    TemplateAutofill: "Saved recipes" chips load a template + prefilled mapping; "Save as recipe" persists the current template + field values org-wide. + canvaApi list/save/delete + /api/canva/recipes proxies.

  12. · MediaService

    Saved autofill recipes CRUD (org-scoped)

    CanvaRecipeController: GET/POST /api/media/canva/recipes + DELETE /{id} — save/list/soft-delete a named brand-template + field mapping, scoped to the caller's org.

  13. · kamo-shared-library

    CanvaAutofillRecipe entity + repo (saved autofill recipes)

    Org-shared named binding of a Canva brand template to field values (literal text or a Materials image), so stewards can re-run a standard data-merge.

  14. · kamo-internal

    Add Material ID field to create form, details page, and imports

    Threads a nullable materialId (max 40) through the Lead type, leadsApi (create + update wire requests), and the shared leadFields system-field list — which drives both the manual import wizard and the API/vendor intake mapping UI (plus the downloadable CSV template). Adds the input to /leads/new and a view/edit row on the lead details page, wired to the realtime pane-flash map. Labels via leads.new.fields / leads.view.fields / leads.view.rows (materialId).

  15. · kamo-internal

    Enterprise autofill UI — data-merge brand templates

    TemplateAutofill: pick a Canva brand template, fill each field with literal text, an org/compliance token (NMLS #, Equal Housing disclosure, org name/phone/ website/address, member name), or a Materials image; generate a data-merged design and import it back into Materials. Gated on the Canva `autofill` (Enterprise) capability. + canvaApi methods and /api/canva/{brand-templates, **************** proxies.

  16. · MediaService

    Enterprise autofill — data-merge brand templates

    - CanvaApiClient: list brand templates (dataset=non_empty), get dataset, asset-upload jobs, create/get autofill jobs - CanvaController: GET /brand-templates, GET /brand-templates/{id}/dataset, POST /autofill — resolves image fields by uploading the referenced MATERIALS material to Canva as an asset, then data-merges; polls to the new design. Canva 403 (no autofill capability) surfaces as autofill_not_available.

  17. · SecurityService

    Persist mapped Material ID on manual import + API intake

    Both setDirectFieldOnCreateRequest switches lack a default case, so an unmapped systemFieldPath is silently dropped. Add a materialId case to the manual-import (LeadImportController) and vendor/API-intake (LeadIntakeController) dispatchers so a mapped Material ID column/field actually persists.

  18. · kamo-shared-library

    Add Material ID field end-to-end in shared-lib

    Adds a nullable, max-40-char materialId to the Lead entity (MATERIAL_ID VARCHAR(40)), registers it in LeadHistoryFieldRegistry (Profile category) so edits are audited, threads it through Create/Update/Read DTOs and LeadService create/update (trim + truncate to 40, mirroring companyName), and preserves it across base<->mortgage lead conversion.

  19. · kamo-internal

    Social attach + email-a-material + public links

    Distribution for marketing materials, built on the new signed public-URL seam. - materialsApi.publicLink(imgId) + /api/materials/link proxy - Materials: "Copy public link" and "Email this material" (attaches the file to an outbound send via the existing multipart /api/email/send, gated on org email) - MaterialPickerDialog (reusable grid over the MATERIALS assoc) - SocialComposer: the once-disabled photo button now opens the picker and attaches a material as post media via its public URL (works for Meta/Telegram/Discord)

  20. · MediaService

    Short-lived signed public URLs for marketing assets

    Lets social providers (Meta) and email fetch a specific material for a bounded window without exposing the private imaging-materials bucket. - MaterialUrlSigner: HMAC-SHA256 (platform.enc-key) token = imgId:orgId:exp, 1h TTL - MaterialsController: POST /api/media/materials/link (auth → mint URL for an org-owned MATERIALS image) + GET **************** (unauth → serve bytes for a valid token; org + assoc checked). Rides the existing sessionless /api/media/** forward — no APIService change needed.

  21. · kamo-shared-library

    First-class borrower self-submit transition ****************

    DRAFT→APPLICATION_TAKEN for a borrower (a loan party, not an operator, so no LOS_TAKE_APPLICATION). Routes through transition() with a scoped predicate that grants ONLY the take-application right and a pinned toStatusId, so the borrower submit becomes a proper pipeline transition — state-machine legality, the hash-chained milestone, the ApplicationSubmission (INITIAL_DISCLOSURE), and the afterCommit STATUS_CHANGE event — instead of a silent loan-file status poke. Loan-party membership is verified by the caller.

  22. · kamo-internal

    Delete + preview + import-file-to-Canva

    - delete a material (gated on MANAGE_MATERIALS) with confirmation - click a thumbnail to preview it in a lightbox - "Import a file" turns a PDF/PPTX/image into an editable Canva design (proxy rebuilds the multipart body like the imaging uploader)

  23. · MediaService

    Import a file into Canva as an editable design

    - **************** (binary design import) - CanvaController POST /imports (multipart) → create+poll import job → editor deep-link with correlation_state for return-nav - drop now-unused Base64 import in CanvaOAuthController

  24. · InitializerService

    Migration adds recordings.transcript TEXT column

  25. · VOIPService

    Return transcript in recording metadata for the softphone player

  26. · kamo-internal

    Show Whisper transcript in the softphone media player

  27. · ConversionService

    Shared TranscriptionService + generic endpoint; transcribe VOIP recordings

    - Extract a reusable TranscriptionService (ffmpeg normalize -> whisper-asr) and a generic POST /api/conversion/transcribe endpoint, replacing the meet-specific controller. Meet + VOIP now share one transcription path. - RecordingProcessController: transcribe each finalized VOIP recording/voicemail in-process (non-fatal on failure) and store the transcript on the Recording.

  28. · kamo-shared-library

    Transcript column on Recording + RecordingMetadata

    Adds a TRANSCRIPT TEXT column to the Recording entity and threads a transcript through **************** (with backwards-compat constructors so existing 5- and 9-arg callers are unaffected). Populated by the recording-finalize flow via Whisper; created by KI migration.

  29. · MediaService

    Verify return-nav JWT signature; tidy token refresh

    - CanvaJwtVerifier: verify the correlation_jwt's Ed25519 signature against Canva's JWKS (/connect/keys), plus aud/exp/type; keys cached by kid. Return-nav now fails closed to manual import if verification fails. - CanvaTokenService: drop the ineffective @Transactional on the self-invoked refresh/markDown helpers (Spring Data's per-call tx + the per-member lock already make token rotation correct); clarify with a comment.

  30. · KlusterServices

    Make whisper-asr independently scalable (HPA, multi-node)

    Remove the single-node pin and switch to a per-pod model cache so pods can schedule on any node, and add an HPA (min 1 / max 4, CPU 75%, deliberate scale-up given slow model cold-starts). whisper-asr is the shared transcription backend for both meeting recordings and VOIP recordings/voicemails.

Your universe is waiting.

Start free in minutes. Add products whenever you are ready — no migration, no new logins.

Free forever — no credit card required