componentsfix(mail): a link in a chat thread is a link LinkedIn and WhatsApp send their words as plain text, so a pasted URL rendered as characters. Slack linkified only its own <url|label> tokens — which its composer emits and a bot, an unfurl field or pasted text do not. Bare URLs are now linkified in both, before the mrkdwn formatting pass so the parser cannot read a URL's underscores as italics. The splitter is lifted out of the calendar's LinkifiedEventText into lib/linkify. Links are painted with the action ramp, not primary: primary is near-black on light and near-white on dark, the colour of the words around them. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 18, 2026, 9:38 AM1 defecthooksMerge remote-tracking branch 'origin/staging' into fix/dm-container-kind # Conflicts: # apps/mail/modules/home/widgets/HomeAgentsWidget.tsxSep 2, 2026, 12:32 AMstorerefactor(agenda): a type that says "this event has a start and an end", so nothing has to assert it Greptile, on the merged head: the agenda casts a filtered array and then asserts its way through the dates — `ev.start!.dateTime!` at a dozen sites — which is the one thing CLAUDE.md says never to do, and for the usual reason. `CalendarEvent.start`/`end` are optional and nullable because Google allows an all-day event and a malformed one; every agenda surface filters those out and then does arithmetic on what is left, and each `!` is a claim the compiler cannot check, repeated until one of them is wrong. Named the post-filter shape instead. `TimedCalendarEvent` is an event with both ends present and timed, reachable only through `isTimedCalendarEvent` — a real predicate, checked at runtime. The filters that already performed that check now SAY so in their signature, and `visibleEventsForDay` being generic carries the narrowing all the way to the row renderer, so every assertion downstream simply became legal code. `endOfEvent`'s `POSITIVE_INFINITY` fallback is gone with them: its own comment said the branch should never be reached, and now the type is what guarantees it. The `event as CalendarEvent` casts at the render seams were the same mistake wearing a different hat — the callbacks declared the NARROWER type while the data always carried its `calendarId`, so every call site cast back. That hid a real consumer: `AttendeeIndicator` needs `CalendarEventWithCalendarId`, which `AgendaEventBlock` was reaching by way of `as unknown as`. The callbacks now take what the lists actually hold, and all eleven casts and assertions are gone rather than relocated. Not taken: Greptile also asked for `./contextKinds` to become an absolute import. Every sibling in that directory imports its neighbours the same way, and `cedar-os` is a vendored module with its own `@/` root — the convention is about crossing module boundaries, which this does not. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 7, 2026, 9:59 PMtypesfeat(calendar,tasks): mint real Zoom meetings, and two fixes for surfaces that lied Zoom reaches a Google Calendar event through the vendor's Workspace Add-on running INSIDE Google, never through an external API client. Probed live against two accounts: all 17 calendars report `allowedConferenceSolutionTypes: ["hangoutsMeet"]`, so the `addOn` option added in ead6b26a4 was unreachable code — and had it rendered, Google would have answered "Invalid conference type value". Cedar now holds a Zoom OAuth token per user, mints the meeting through Zoom's own API, and writes the join details onto the event. That is what Superhuman does, and there is no Calendar-API shortcut they know that we do not. The driver implements the existing OAuth interface, which buys the sealed AES-GCM state, `initiateOAuth`'s URL builder, and `refreshMcpConnection`. The last is load-bearing rather than convenient: Zoom rotates the refresh token on every refresh and kills the old one, so two concurrent refreshes presenting the same stored token leave it dead — exactly the failure the single-flight coalescing there already exists for. The row is written `direct_oauth`, never `external_mcp`, because every MCP surface selects on that column and an agent enumerating tools from api.zoom.us should not be possible. Attachment goes to `location` (the only field that renders on every Google client), `description` (a fenced, idempotent dial-in block — recreating the meeting REPLACES it rather than appending beside it, which is what a "does it already contain this URL?" check gets wrong), and `conferenceData` best-effort. `addOn` is gone from both `createRequest` enums, the agent tool, the chat route, and `CalendarEvent.conferenceType`; describing an existing conference is still allowed, because that is what a Zoom meeting is by the time it reaches the event body. Inert until ZOOM_CLIENT_ID is set. Two unrelated fixes, both reported by the same customer: - "Search a deal…" searched every conversation the user owned. Cedar mints one per unrecognised correspondent, so on the reporting account 117 conversations held 6 deals and typing "gmail" returned a column of recruiters' addresses. `dealsOnly` narrows to the AOPs the user actually works — `isNoOp = false` being their own declaration that Cedar does work there — and falls back to today's behaviour for the 18 of 109 active accounts that have no workable AOP. - Every task surface splits due-now from upcoming at `endOfToday()`, read inside memos whose dependencies are all data. Nothing in them moves when the day does, so a tab left open past midnight files everything due TODAY under Upcoming, out of its group column, which then collapses into the hidden-columns rail for being empty. `useDayKey` is the missing dependency. Staged whole-tree, not session-scoped: this branch is shared with a concurrent session whose in-flight work (the board's Done lane, the calendar drag-settle animation, the onboarding setup flow, the task-groups CLI) is entangled with these changes both within files and across them — CalendarView passes props DayColumn only accepts in its uncommitted form — so a scoped commit would not have compiled. That session likewise swept this change's crm.ts and NewTaskDialog edits into a5fc7b295. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 1, 2026, 10:35 PMutilsfix(calendar,ui): a Zoom connect dialog, and four things the review found The Zoom work lands its last piece: picking Zoom on an unconnected account used to open a bare browser window with no explanation and no way back. ZoomConnectDialog owns everything around the consent screen — why Cedar is asking, the wait, and the four different reasons a popup ends without a connection — and resolves back to the composer so one click on "Change to Zoom" still ends with Zoom on the event. The OAuth callback URL now falls back to the server's own origin, so an unset env var is no longer the difference between a working integration and a message telling a user to edit a file they have never heard of. Agent avatars move to components/icons/agent-avatar.tsx as one component, and the conversation/agent reading column narrows from 100ch to 80ch across all four places that hold the measure. Review fixes: - Breadcrumb decided "you are here" positionally, so it rendered the last crumb as an inert span and silently DISCARDED any href it was handed. A task's bar is `Tasks / <lane>` above the task's own title — the h1 below is the page, and the lane is somewhere to go — so its link had become dead markup. The current crumb is now the last one with nowhere to go, which every other call site already is. - modifyCalendarEventTool passed a start time but no duration to Zoom, which silently books 60 minutes: an AI-updated 25-minute event held an hour on the host's own account. - schema.ts declared two_factor_required without .notNull(), drifting from the NOT NULL the migration actually applied. - HomeAgentsWidget cast a tRPC result that was already typed — the redundant-cast shape that took down the home rail once before. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 1, 2026, 11:54 PMcalendarUtils.tsfix(calendar): stop event chips at the column edge, so a busy hour stays clickable Chips ran the full width of their day column, leaving nowhere to click or drag to book over an existing event. Lay them out inside the column minus an 8px gutter — the strip Google leaves for exactly that — including the rightmost lane and the drag previews. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 26, 2026, 12:54 PMindex.tsfeat: superior /mail & pipeline designApr 19, 2026, 9:00 PM