From 723c2ad21f68b083b8580fd608b56de86a73c6fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 11:04:25 +0000 Subject: [PATCH] chore: release --- .changeset/dashboard-mobile-polish.md | 10 --- .changeset/khaki-eagles-shave.md | 11 --- .changeset/lucky-pans-tickle.md | 15 ---- .changeset/olive-buttons-write.md | 11 --- .changeset/olive-donuts-guess.md | 15 ---- .changeset/olive-mugs-shave.md | 14 ---- .changeset/quiet-lions-speak.md | 11 --- .changeset/rotten-melons-look.md | 15 ---- .changeset/tall-pianos-repeat.md | 15 ---- .changeset/thin-plums-clap.md | 11 --- apps/analytics-tracker/CHANGELOG.md | 2 + apps/analytics-tracker/package.json | 2 +- apps/chat-widget/CHANGELOG.md | 8 +++ apps/chat-widget/package.json | 2 +- packages/agent-host/CHANGELOG.md | 19 +++++ packages/agent-host/package.json | 2 +- packages/agent-runtime/CHANGELOG.md | 9 +++ packages/agent-runtime/package.json | 2 +- packages/backend-core/CHANGELOG.md | 100 ++++++++++++++++++++++++++ packages/backend-core/package.json | 2 +- packages/core/CHANGELOG.md | 9 +++ packages/core/package.json | 2 +- packages/dashboard-pages/CHANGELOG.md | 64 +++++++++++++++++ packages/dashboard-pages/package.json | 2 +- packages/db/CHANGELOG.md | 8 +++ packages/db/package.json | 2 +- packages/docs-pages/CHANGELOG.md | 15 ++++ packages/docs-pages/package.json | 2 +- packages/emails/CHANGELOG.md | 2 + packages/emails/package.json | 2 +- packages/inspector-app/CHANGELOG.md | 36 ++++++++++ packages/inspector-app/package.json | 2 +- packages/mcp-toolkit/CHANGELOG.md | 9 +++ packages/mcp-toolkit/package.json | 2 +- packages/sdk/CHANGELOG.md | 2 + packages/sdk/package.json | 2 +- packages/types/CHANGELOG.md | 22 ++++++ packages/types/package.json | 2 +- packages/ui/CHANGELOG.md | 2 + packages/ui/package.json | 2 +- 40 files changed, 322 insertions(+), 143 deletions(-) delete mode 100644 .changeset/dashboard-mobile-polish.md delete mode 100644 .changeset/khaki-eagles-shave.md delete mode 100644 .changeset/lucky-pans-tickle.md delete mode 100644 .changeset/olive-buttons-write.md delete mode 100644 .changeset/olive-donuts-guess.md delete mode 100644 .changeset/olive-mugs-shave.md delete mode 100644 .changeset/quiet-lions-speak.md delete mode 100644 .changeset/rotten-melons-look.md delete mode 100644 .changeset/tall-pianos-repeat.md delete mode 100644 .changeset/thin-plums-clap.md diff --git a/.changeset/dashboard-mobile-polish.md b/.changeset/dashboard-mobile-polish.md deleted file mode 100644 index 8d9f3991..00000000 --- a/.changeset/dashboard-mobile-polish.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@getmunin/dashboard-pages': patch ---- - -Fix several mobile/touch dashboard UI issues - -- Email channel dialog: the focused-input ring on the leftmost field was clipped, because `overflow-y-auto` forces `overflow-x` to compute as `auto` too, and the scrolling wrapper had no padding of its own to give the ring room. Added a small inset/outset pair so the ring renders without shifting any content. -- Settings topbar: the mobile menu button had a border (`variant="outline"`) inconsistent with the borderless cog icon elsewhere; switched to `variant="ghost"`. -- Settings topbar back arrow and dashboard topbar cog icon were styled gray-by-default with hover turning them black — correct for a mouse, but permanently gray on a touch device with no hover state. Both now force black via `[@media(hover:none)]`, leaving the hover-capable (desktop) behavior unchanged. -- Team page: the members and pending-invitations tables used a fixed `min-w-[640px]` with horizontal scroll on narrow viewports. Below `md`, both now render as a stacked card list instead — members show name/email, then role select + edit/remove actions in one row (role stays visible since it's the only place to change it, unlike the edit dialog which only renames); invitations show email, then role chip + revoke in one row, matching the same alignment pattern. diff --git a/.changeset/khaki-eagles-shave.md b/.changeset/khaki-eagles-shave.md deleted file mode 100644 index 6b85e518..00000000 --- a/.changeset/khaki-eagles-shave.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@getmunin/backend-core': minor ---- - -Inbound SMS threads into the existing conversation, and STOP unsubscribes - -Webhook-mode channels used to open a brand-new conversation for every inbound message. Two texts from the same person were two unrelated conversations, and no reply could ever be attributed to what it was replying to. Inbound messages now land in that contact's most recent open conversation on the channel, reopening it if it was snoozed; a closed conversation still starts a fresh one. New conversations also inherit the channel's `defaultAgentMode`, which the generic ingest path was dropping while the email path honoured it. - -An inbound SMS whose entire body is an opt-out keyword — `STOP`, `STOPP`, `SLUTT`, `AVMELD`, `UNSUBSCRIBE`, `END`, `QUIT`, `CANCEL`, `STOPALL`, case-insensitive, trailing punctuation ignored — now suppresses the CRM contact holding that number: `doNotContact` set, `unsubscribedAt` stamped, and a `crm_activities` note recording why. The message is still ingested so the conversation reads truthfully, and suppressed contacts drop out of `crm_list_contacts_in_segment`, so they leave every outreach audience. A message that merely contains the word ("can you cancel my order?") is an ordinary message. - -Honouring STOP is a legal requirement for SMS in the US and expected practice in the EU, so this lands before SMS outreach rather than alongside it. diff --git a/.changeset/lucky-pans-tickle.md b/.changeset/lucky-pans-tickle.md deleted file mode 100644 index ccfc7a4a..00000000 --- a/.changeset/lucky-pans-tickle.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@getmunin/backend-core': minor -'@getmunin/inspector-app': minor -'@getmunin/dashboard-pages': minor ---- - -Outreach proposals say where they are going before you approve them - -A proposal DTO now carries `delivery`: the campaign channel's type and vendor, the destination the approval would actually reach (email address for email campaigns, phone number for voice and SMS), and whether Munin will append the campaign CTA link and unsubscribe footer at send time. `contact` gains `phone` alongside `email`. - -Both review surfaces use it. The MCP App panel and the dashboard drawer state the consequence in words — "Approving places a phone call to +1 415 555 9999", "Approving emails jane@acme.com. Munin appends the campaign CTA link, an unsubscribe footer" — and warn in red when the contact has no address or number on file, which is a send that would fail. A voice proposal's approve button reads "Approve & call" rather than "Approve & send", and its missing subject renders as "(spoken call — no subject)" instead of the bare "(no subject)" an email would show. - -This matters most for voice campaigns, where approving dials a real phone number and the panel previously showed nothing but a subject-less body and an Approve button. It is worth having for email too: a reviewer approving a first-touch could not see the recipient address unless the contact happened to have no name. - -The panel's `Proposal.kind` union was also missing `'followup'`, which every follow-up proposal has carried since sequences shipped. diff --git a/.changeset/olive-buttons-write.md b/.changeset/olive-buttons-write.md deleted file mode 100644 index 8362f860..00000000 --- a/.changeset/olive-buttons-write.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@getmunin/backend-core': minor ---- - -Outreach calls work on any registered voice vendor, not only Vapi - -`loadOutreachChannel` accepted `voice:vapi` and nothing else, so a campaign could not run on a Threll channel even though Threll has shipped an adapter, admin service and dashboard support for as long as Vapi has. The restriction was an accident of the voice path being written against one vendor: `approveInitialVoice` called `VapiClientService` directly, read config with Vapi's parser, and hard-coded `vapiCallId` as the key linking a call back to its conversation. - -Voice outreach now goes through a small `OutreachVoiceCaller` seam — vendor name, the metadata key its conversations are keyed by, and one `placeOutreachCall` method — registered per vendor and resolved by `channel.vendor`. Adding a third vendor is a class implementing that interface and a line in the module; nothing in `OutreachService` needs to know it exists. A voice channel whose vendor has no registered caller is refused at campaign-create time, naming the vendors that do work, rather than failing at approval. - -Each vendor keeps the linkage its own webhook expects: Vapi carries the draft and the outreach ids in `assistantOverrides.metadata` and keys conversations on `vapiCallId`, Threll passes the draft as call `context` and keys on `threllCallId`. Both partial unique indexes already existed (0026 and 0040), so no migration was needed. diff --git a/.changeset/olive-donuts-guess.md b/.changeset/olive-donuts-guess.md deleted file mode 100644 index 86fc51fa..00000000 --- a/.changeset/olive-donuts-guess.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@getmunin/backend-core': minor -'@getmunin/inspector-app': minor -'@getmunin/dashboard-pages': patch ---- - -Outbound calls and text messages are approved only by a person in the dashboard - -A proposal whose campaign runs on a voice or SMS channel can now only be approved by a signed-in dashboard user. `outreach_approve_proposal` refuses every other caller — an MCP agent, an unrestricted admin API key on the control plane, a curator, the Slack approval button — and the proposal stays `pending`. Email approval is unchanged, including by agents and admin keys. - -The check lives in `OutreachService.approveProposal`, so it holds regardless of which surface the call arrives through. It is not an MCP tool-visibility hint: hosts that don't implement MCP Apps still list the tool, and calling it on a voice proposal fails there too. - -Slack declines these in-thread with a pointer to the dashboard rather than surfacing a service error. The Inspector panel drops the approve button for voice and SMS proposals — Dismiss stays, since dismissing sends nothing — and says where the call is actually placed. - -Quiet hours and blackout dates are now enforced when a call or text is approved. They were previously stored on the campaign and consulted only when listing due follow-ups, so nothing stopped a call at 3am. `cadenceRules` gains an optional `quietHoursTimezone` (IANA, validated) that quiet hours and blackout dates are read in; without it they are read in UTC, which is not what a Norwegian campaign means by "no calls before 08:00". diff --git a/.changeset/olive-mugs-shave.md b/.changeset/olive-mugs-shave.md deleted file mode 100644 index 586990f5..00000000 --- a/.changeset/olive-mugs-shave.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@getmunin/backend-core': minor -'@getmunin/dashboard-pages': patch ---- - -Remove the MCP tools that place outbound voice calls - -`conv_call_channel` and `conv_call_contact` no longer exist on the MCP surface, so no connected agent can place a phone call. Anthropic's MCP directory does not list connectors that let an assistant dial third parties on its own, and an in-client tool-approval click does not clear that bar. - -Nothing else changes. The `VoiceCallbackService` and `ChannelAdminService.call` service methods stay, the `/v1/conversations/channels/:id/call` endpoints stay, and the dashboard's per-channel test call keeps working — it is authenticated as a human dashboard session, not as an agent. `conv_request_callback` also stays: it is `self_service`-only, so it is reachable by an org's own end-user agents (the widget's "can you call me?" flow) and refused for admin callers by the audience gate in `dispatch.ts`. - -The dashboard action is renamed from "Place a call" to "Make a test call", matching "Send test email" and "Send test SMS" — verifying a newly configured voice channel is what it is for. - -Outbound calling as a product capability moves to `outreach`, where a voice campaign already drafts a proposal that a human approves before anything is dialed. diff --git a/.changeset/quiet-lions-speak.md b/.changeset/quiet-lions-speak.md deleted file mode 100644 index e201baee..00000000 --- a/.changeset/quiet-lions-speak.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@getmunin/backend-core': patch ---- - -Add a skill for drafting outbound calls - -`skill://outreach/draft-initial-call` covers the voice-campaign pass, which had no skill of its own — an agent had to infer from the email skill that `draftBody` on a voice campaign is what a text-to-speech agent says out loud, not a message that gets delivered. - -It says what only a spoken channel needs said: write speech rather than prose, no markdown or emoji (read aloud or mangled), no URLs or reference codes (nobody can click on a phone call), identify the caller in the first sentence, give the voice agent a goal and boundaries rather than a script, and tell it what to do when the person is busy or asks whether it is a human. It also sets a higher bar for who is worth calling at all — matching a segment filter is a reason to email, not to phone — and states that approval is dashboard-only, so filing the proposal is where the agent stops. - -Cross-links the three first-touch skills to each other and lists all of them in `review-proposals`, which named only the email ones. diff --git a/.changeset/rotten-melons-look.md b/.changeset/rotten-melons-look.md deleted file mode 100644 index e19c5c3e..00000000 --- a/.changeset/rotten-melons-look.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@getmunin/backend-core': minor ---- - -SMS outreach campaigns - -An outreach campaign can now run on an SMS channel. `outreach_propose_initial` drafts a text against a contact's phone number, and a person approving it in the dashboard creates the outbound conversation and sends the message. As with voice, an agent cannot approve one. - -Drafts are capped at 480 characters — roughly three billable segments — and rejected above it, with the limit stated in the tool schema so a curator can write to it rather than discover it. Composition is SMS-shaped: the campaign CTA is appended as a bare URL and the opt-out line as `Reply STOP to opt out.`, instead of the markdown link footer an email gets. `skill://outreach/draft-initial-sms` covers the writing rules, including why an emoji triples the cost of a message. - -**Outbound SMS was never actually delivered.** `ConvService` enqueued a `conv_message_deliveries` row only for `channelType === 'email'`, so an agent or operator replying on an SMS conversation stored a message that silently never sent — the `OutboundDeliveryWorker` and both SMS adapters were fine, nothing was ever handed to them. The gate now covers email and SMS. - -Follow-up sequences stay email-only: an SMS campaign rejects `sequenceSteps`, and `outreach_propose_followup` still refuses a text conversation. One touch, then the reply flow, which works because inbound texts thread into the outreach conversation. - -`findOrCreateContactByPhone` was duplicated in the Vapi and Threll adapters and inlined a third time in the outreach voice path; it is now one shared helper taking the source as a parameter. diff --git a/.changeset/tall-pianos-repeat.md b/.changeset/tall-pianos-repeat.md deleted file mode 100644 index 9ad05088..00000000 --- a/.changeset/tall-pianos-repeat.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@getmunin/backend-core': minor -'@getmunin/dashboard-pages': minor -'@getmunin/types': minor ---- - -SMS channels can set how the agent handles inbound texts - -`defaultAgentMode` has always been a `conv_channels` column, but only the email path could write it — the vendor-backed path that creates every SMS channel had no way to set it, and neither did the dashboard. Every SMS number was stuck on `auto`, replying to inbound texts automatically. - -It is now settable on SMS channels through `conv_configure_channel`, the vendor tools, the `/v1` SMS endpoints, and a control in the Twilio and MessageBird dialogs alongside the one email already had. Set `draft_only` on a number you only run campaigns from and inbound replies are drafted for approval instead of auto-answered. - -Voice channels reject it with an explanation rather than accepting a value that would do nothing: an inbound call is run by the vendor's assistant, not by the Munin agent, so there is no reply for the mode to govern. - -Also corrects `conv_create_channel`'s description, which claimed "the `voice` and `sms` channel types are reserved and not yet wired to an adapter". Both SMS vendors and both voice vendors have shipped adapters; those channels are created with `conv_configure_channel`, which the description now says. diff --git a/.changeset/thin-plums-clap.md b/.changeset/thin-plums-clap.md deleted file mode 100644 index bf927294..00000000 --- a/.changeset/thin-plums-clap.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@getmunin/backend-core': patch -'@getmunin/dashboard-pages': patch -'@getmunin/types': patch ---- - -SMS channel dialogs: agent-reply select on create, sender switching actually clears - -The "Agent replies" select is now on the SMS create dialog, not just edit — a new channel no longer silently starts on `auto` until someone goes back and changes it. Renamed from "Default agent mode" / "Inbound replies" to a shared "Agent replies" across email and SMS dialogs, since "agent mode" is the database column name, not something an operator configuring a phone number has a mental model for. - -Twilio's From-number and Messaging-Service-SID fields are now a single "Send from" choice instead of two always-visible inputs with an "either, both is also OK" caveat. Switching the choice on an existing channel now actually clears the field you switched away from — `updateChannel` previously merged with `?? prev`, so the old value survived a switch and both were sent on every message. diff --git a/apps/analytics-tracker/CHANGELOG.md b/apps/analytics-tracker/CHANGELOG.md index 54ecd80d..68b98428 100644 --- a/apps/analytics-tracker/CHANGELOG.md +++ b/apps/analytics-tracker/CHANGELOG.md @@ -1,5 +1,7 @@ # @getmunin/analytics-tracker +## 4.75.0 + ## 4.74.0 ### Minor Changes diff --git a/apps/analytics-tracker/package.json b/apps/analytics-tracker/package.json index 8ab2d436..93779eb2 100644 --- a/apps/analytics-tracker/package.json +++ b/apps/analytics-tracker/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/analytics-tracker", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Drop-in browser analytics tracker for self-hosted Munin. Built as a single content-hashed IIFE bundle and served by the Munin backend.", "publishConfig": { diff --git a/apps/chat-widget/CHANGELOG.md b/apps/chat-widget/CHANGELOG.md index c35bd758..4c6c7d47 100644 --- a/apps/chat-widget/CHANGELOG.md +++ b/apps/chat-widget/CHANGELOG.md @@ -1,5 +1,13 @@ # @getmunin/chat-widget +## 4.75.0 + +### Patch Changes + +- Updated dependencies [c5a05c5] +- Updated dependencies [c5a05c5] + - @getmunin/types@4.75.0 + ## 4.74.0 ### Patch Changes diff --git a/apps/chat-widget/package.json b/apps/chat-widget/package.json index ba6fd6ec..bb140779 100644 --- a/apps/chat-widget/package.json +++ b/apps/chat-widget/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/chat-widget", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Drop-in browser chat widget for self-hosted Munin. Built as a single content-hashed IIFE bundle and served by the Munin backend.", "publishConfig": { diff --git a/packages/agent-host/CHANGELOG.md b/packages/agent-host/CHANGELOG.md index b1bf716c..c6459362 100644 --- a/packages/agent-host/CHANGELOG.md +++ b/packages/agent-host/CHANGELOG.md @@ -1,5 +1,24 @@ # @getmunin/agent-host +## 4.75.0 + +### Patch Changes + +- Updated dependencies [c5a05c5] +- Updated dependencies [cc87bb6] +- Updated dependencies [3269f5c] +- Updated dependencies [b98f618] +- Updated dependencies [862b055] +- Updated dependencies [77820b0] +- Updated dependencies [eedcba5] +- Updated dependencies [c5a05c5] +- Updated dependencies [c5a05c5] + - @getmunin/backend-core@4.75.0 + - @getmunin/types@4.75.0 + - @getmunin/agent-runtime@4.75.0 + - @getmunin/core@4.75.0 + - @getmunin/db@4.75.0 + ## 4.74.0 ### Patch Changes diff --git a/packages/agent-host/package.json b/packages/agent-host/package.json index cf049b39..4f6bbada 100644 --- a/packages/agent-host/package.json +++ b/packages/agent-host/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/agent-host", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "In-process agent runner host: config CRUD, /v1/models proxy, ConversationHandler + curator-loop.", "publishConfig": { diff --git a/packages/agent-runtime/CHANGELOG.md b/packages/agent-runtime/CHANGELOG.md index 4ba6f33d..71e02790 100644 --- a/packages/agent-runtime/CHANGELOG.md +++ b/packages/agent-runtime/CHANGELOG.md @@ -1,5 +1,14 @@ # @getmunin/agent-runtime +## 4.75.0 + +### Patch Changes + +- Updated dependencies [c5a05c5] +- Updated dependencies [c5a05c5] + - @getmunin/types@4.75.0 + - @getmunin/core@4.75.0 + ## 4.74.0 ### Patch Changes diff --git a/packages/agent-runtime/package.json b/packages/agent-runtime/package.json index 96251f9d..3752bedf 100644 --- a/packages/agent-runtime/package.json +++ b/packages/agent-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/agent-runtime", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "LLM agent loop — given a config, conversation history, and an MCP tool handle, produce a reply.", "publishConfig": { diff --git a/packages/backend-core/CHANGELOG.md b/packages/backend-core/CHANGELOG.md index a2400e0e..291a2cac 100644 --- a/packages/backend-core/CHANGELOG.md +++ b/packages/backend-core/CHANGELOG.md @@ -1,5 +1,105 @@ # @getmunin/backend-core +## 4.75.0 + +### Minor Changes + +- c5a05c5: Inbound SMS threads into the existing conversation, and STOP unsubscribes + + Webhook-mode channels used to open a brand-new conversation for every inbound message. Two texts from the same person were two unrelated conversations, and no reply could ever be attributed to what it was replying to. Inbound messages now land in that contact's most recent open conversation on the channel, reopening it if it was snoozed; a closed conversation still starts a fresh one. New conversations also inherit the channel's `defaultAgentMode`, which the generic ingest path was dropping while the email path honoured it. + + An inbound SMS whose entire body is an opt-out keyword — `STOP`, `STOPP`, `SLUTT`, `AVMELD`, `UNSUBSCRIBE`, `END`, `QUIT`, `CANCEL`, `STOPALL`, case-insensitive, trailing punctuation ignored — now suppresses the CRM contact holding that number: `doNotContact` set, `unsubscribedAt` stamped, and a `crm_activities` note recording why. The message is still ingested so the conversation reads truthfully, and suppressed contacts drop out of `crm_list_contacts_in_segment`, so they leave every outreach audience. A message that merely contains the word ("can you cancel my order?") is an ordinary message. + + Honouring STOP is a legal requirement for SMS in the US and expected practice in the EU, so this lands before SMS outreach rather than alongside it. + +- cc87bb6: Outreach proposals say where they are going before you approve them + + A proposal DTO now carries `delivery`: the campaign channel's type and vendor, the destination the approval would actually reach (email address for email campaigns, phone number for voice and SMS), and whether Munin will append the campaign CTA link and unsubscribe footer at send time. `contact` gains `phone` alongside `email`. + + Both review surfaces use it. The MCP App panel and the dashboard drawer state the consequence in words — "Approving places a phone call to +1 415 555 9999", "Approving emails jane@acme.com. Munin appends the campaign CTA link, an unsubscribe footer" — and warn in red when the contact has no address or number on file, which is a send that would fail. A voice proposal's approve button reads "Approve & call" rather than "Approve & send", and its missing subject renders as "(spoken call — no subject)" instead of the bare "(no subject)" an email would show. + + This matters most for voice campaigns, where approving dials a real phone number and the panel previously showed nothing but a subject-less body and an Approve button. It is worth having for email too: a reviewer approving a first-touch could not see the recipient address unless the contact happened to have no name. + + The panel's `Proposal.kind` union was also missing `'followup'`, which every follow-up proposal has carried since sequences shipped. + +- 3269f5c: Outreach calls work on any registered voice vendor, not only Vapi + + `loadOutreachChannel` accepted `voice:vapi` and nothing else, so a campaign could not run on a Threll channel even though Threll has shipped an adapter, admin service and dashboard support for as long as Vapi has. The restriction was an accident of the voice path being written against one vendor: `approveInitialVoice` called `VapiClientService` directly, read config with Vapi's parser, and hard-coded `vapiCallId` as the key linking a call back to its conversation. + + Voice outreach now goes through a small `OutreachVoiceCaller` seam — vendor name, the metadata key its conversations are keyed by, and one `placeOutreachCall` method — registered per vendor and resolved by `channel.vendor`. Adding a third vendor is a class implementing that interface and a line in the module; nothing in `OutreachService` needs to know it exists. A voice channel whose vendor has no registered caller is refused at campaign-create time, naming the vendors that do work, rather than failing at approval. + + Each vendor keeps the linkage its own webhook expects: Vapi carries the draft and the outreach ids in `assistantOverrides.metadata` and keys conversations on `vapiCallId`, Threll passes the draft as call `context` and keys on `threllCallId`. Both partial unique indexes already existed (0026 and 0040), so no migration was needed. + +- b98f618: Outbound calls and text messages are approved only by a person in the dashboard + + A proposal whose campaign runs on a voice or SMS channel can now only be approved by a signed-in dashboard user. `outreach_approve_proposal` refuses every other caller — an MCP agent, an unrestricted admin API key on the control plane, a curator, the Slack approval button — and the proposal stays `pending`. Email approval is unchanged, including by agents and admin keys. + + The check lives in `OutreachService.approveProposal`, so it holds regardless of which surface the call arrives through. It is not an MCP tool-visibility hint: hosts that don't implement MCP Apps still list the tool, and calling it on a voice proposal fails there too. + + Slack declines these in-thread with a pointer to the dashboard rather than surfacing a service error. The Inspector panel drops the approve button for voice and SMS proposals — Dismiss stays, since dismissing sends nothing — and says where the call is actually placed. + + Quiet hours and blackout dates are now enforced when a call or text is approved. They were previously stored on the campaign and consulted only when listing due follow-ups, so nothing stopped a call at 3am. `cadenceRules` gains an optional `quietHoursTimezone` (IANA, validated) that quiet hours and blackout dates are read in; without it they are read in UTC, which is not what a Norwegian campaign means by "no calls before 08:00". + +- 862b055: Remove the MCP tools that place outbound voice calls + + `conv_call_channel` and `conv_call_contact` no longer exist on the MCP surface, so no connected agent can place a phone call. Anthropic's MCP directory does not list connectors that let an assistant dial third parties on its own, and an in-client tool-approval click does not clear that bar. + + Nothing else changes. The `VoiceCallbackService` and `ChannelAdminService.call` service methods stay, the `/v1/conversations/channels/:id/call` endpoints stay, and the dashboard's per-channel test call keeps working — it is authenticated as a human dashboard session, not as an agent. `conv_request_callback` also stays: it is `self_service`-only, so it is reachable by an org's own end-user agents (the widget's "can you call me?" flow) and refused for admin callers by the audience gate in `dispatch.ts`. + + The dashboard action is renamed from "Place a call" to "Make a test call", matching "Send test email" and "Send test SMS" — verifying a newly configured voice channel is what it is for. + + Outbound calling as a product capability moves to `outreach`, where a voice campaign already drafts a proposal that a human approves before anything is dialed. + +- eedcba5: SMS outreach campaigns + + An outreach campaign can now run on an SMS channel. `outreach_propose_initial` drafts a text against a contact's phone number, and a person approving it in the dashboard creates the outbound conversation and sends the message. As with voice, an agent cannot approve one. + + Drafts are capped at 480 characters — roughly three billable segments — and rejected above it, with the limit stated in the tool schema so a curator can write to it rather than discover it. Composition is SMS-shaped: the campaign CTA is appended as a bare URL and the opt-out line as `Reply STOP to opt out.`, instead of the markdown link footer an email gets. `skill://outreach/draft-initial-sms` covers the writing rules, including why an emoji triples the cost of a message. + + **Outbound SMS was never actually delivered.** `ConvService` enqueued a `conv_message_deliveries` row only for `channelType === 'email'`, so an agent or operator replying on an SMS conversation stored a message that silently never sent — the `OutboundDeliveryWorker` and both SMS adapters were fine, nothing was ever handed to them. The gate now covers email and SMS. + + Follow-up sequences stay email-only: an SMS campaign rejects `sequenceSteps`, and `outreach_propose_followup` still refuses a text conversation. One touch, then the reply flow, which works because inbound texts thread into the outreach conversation. + + `findOrCreateContactByPhone` was duplicated in the Vapi and Threll adapters and inlined a third time in the outreach voice path; it is now one shared helper taking the source as a parameter. + +- c5a05c5: SMS channels can set how the agent handles inbound texts + + `defaultAgentMode` has always been a `conv_channels` column, but only the email path could write it — the vendor-backed path that creates every SMS channel had no way to set it, and neither did the dashboard. Every SMS number was stuck on `auto`, replying to inbound texts automatically. + + It is now settable on SMS channels through `conv_configure_channel`, the vendor tools, the `/v1` SMS endpoints, and a control in the Twilio and MessageBird dialogs alongside the one email already had. Set `draft_only` on a number you only run campaigns from and inbound replies are drafted for approval instead of auto-answered. + + Voice channels reject it with an explanation rather than accepting a value that would do nothing: an inbound call is run by the vendor's assistant, not by the Munin agent, so there is no reply for the mode to govern. + + Also corrects `conv_create_channel`'s description, which claimed "the `voice` and `sms` channel types are reserved and not yet wired to an adapter". Both SMS vendors and both voice vendors have shipped adapters; those channels are created with `conv_configure_channel`, which the description now says. + +### Patch Changes + +- 77820b0: Add a skill for drafting outbound calls + + `skill://outreach/draft-initial-call` covers the voice-campaign pass, which had no skill of its own — an agent had to infer from the email skill that `draftBody` on a voice campaign is what a text-to-speech agent says out loud, not a message that gets delivered. + + It says what only a spoken channel needs said: write speech rather than prose, no markdown or emoji (read aloud or mangled), no URLs or reference codes (nobody can click on a phone call), identify the caller in the first sentence, give the voice agent a goal and boundaries rather than a script, and tell it what to do when the person is busy or asks whether it is a human. It also sets a higher bar for who is worth calling at all — matching a segment filter is a reason to email, not to phone — and states that approval is dashboard-only, so filing the proposal is where the agent stops. + + Cross-links the three first-touch skills to each other and lists all of them in `review-proposals`, which named only the email ones. + +- c5a05c5: SMS channel dialogs: agent-reply select on create, sender switching actually clears + + The "Agent replies" select is now on the SMS create dialog, not just edit — a new channel no longer silently starts on `auto` until someone goes back and changes it. Renamed from "Default agent mode" / "Inbound replies" to a shared "Agent replies" across email and SMS dialogs, since "agent mode" is the database column name, not something an operator configuring a phone number has a mental model for. + + Twilio's From-number and Messaging-Service-SID fields are now a single "Send from" choice instead of two always-visible inputs with an "either, both is also OK" caveat. Switching the choice on an existing channel now actually clears the field you switched away from — `updateChannel` previously merged with `?? prev`, so the old value survived a switch and both were sent on every message. + +- Updated dependencies [cc87bb6] +- Updated dependencies [b98f618] +- Updated dependencies [c5a05c5] +- Updated dependencies [c5a05c5] + - @getmunin/inspector-app@4.75.0 + - @getmunin/types@4.75.0 + - @getmunin/agent-runtime@4.75.0 + - @getmunin/core@4.75.0 + - @getmunin/db@4.75.0 + - @getmunin/mcp-toolkit@4.75.0 + - @getmunin/emails@4.75.0 + ## 4.74.0 ### Minor Changes diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 3f06b1f1..1a9f4294 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/backend-core", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Shared NestJS modules powering both OSS and cloud Munin backends.", "publishConfig": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 45a0f703..312d5687 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,14 @@ # @getmunin/core +## 4.75.0 + +### Patch Changes + +- Updated dependencies [c5a05c5] +- Updated dependencies [c5a05c5] + - @getmunin/types@4.75.0 + - @getmunin/db@4.75.0 + ## 4.74.0 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index 2c99c43f..2a1c6fd2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/core", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Munin core: actor identity, request context, mailer, storage, crypto helpers.", "publishConfig": { diff --git a/packages/dashboard-pages/CHANGELOG.md b/packages/dashboard-pages/CHANGELOG.md index a116f220..35ee4a36 100644 --- a/packages/dashboard-pages/CHANGELOG.md +++ b/packages/dashboard-pages/CHANGELOG.md @@ -1,5 +1,69 @@ # @getmunin/dashboard-pages +## 4.75.0 + +### Minor Changes + +- cc87bb6: Outreach proposals say where they are going before you approve them + + A proposal DTO now carries `delivery`: the campaign channel's type and vendor, the destination the approval would actually reach (email address for email campaigns, phone number for voice and SMS), and whether Munin will append the campaign CTA link and unsubscribe footer at send time. `contact` gains `phone` alongside `email`. + + Both review surfaces use it. The MCP App panel and the dashboard drawer state the consequence in words — "Approving places a phone call to +1 415 555 9999", "Approving emails jane@acme.com. Munin appends the campaign CTA link, an unsubscribe footer" — and warn in red when the contact has no address or number on file, which is a send that would fail. A voice proposal's approve button reads "Approve & call" rather than "Approve & send", and its missing subject renders as "(spoken call — no subject)" instead of the bare "(no subject)" an email would show. + + This matters most for voice campaigns, where approving dials a real phone number and the panel previously showed nothing but a subject-less body and an Approve button. It is worth having for email too: a reviewer approving a first-touch could not see the recipient address unless the contact happened to have no name. + + The panel's `Proposal.kind` union was also missing `'followup'`, which every follow-up proposal has carried since sequences shipped. + +- c5a05c5: SMS channels can set how the agent handles inbound texts + + `defaultAgentMode` has always been a `conv_channels` column, but only the email path could write it — the vendor-backed path that creates every SMS channel had no way to set it, and neither did the dashboard. Every SMS number was stuck on `auto`, replying to inbound texts automatically. + + It is now settable on SMS channels through `conv_configure_channel`, the vendor tools, the `/v1` SMS endpoints, and a control in the Twilio and MessageBird dialogs alongside the one email already had. Set `draft_only` on a number you only run campaigns from and inbound replies are drafted for approval instead of auto-answered. + + Voice channels reject it with an explanation rather than accepting a value that would do nothing: an inbound call is run by the vendor's assistant, not by the Munin agent, so there is no reply for the mode to govern. + + Also corrects `conv_create_channel`'s description, which claimed "the `voice` and `sms` channel types are reserved and not yet wired to an adapter". Both SMS vendors and both voice vendors have shipped adapters; those channels are created with `conv_configure_channel`, which the description now says. + +### Patch Changes + +- 8e98f2d: Fix several mobile/touch dashboard UI issues + + - Email channel dialog: the focused-input ring on the leftmost field was clipped, because `overflow-y-auto` forces `overflow-x` to compute as `auto` too, and the scrolling wrapper had no padding of its own to give the ring room. Added a small inset/outset pair so the ring renders without shifting any content. + - Settings topbar: the mobile menu button had a border (`variant="outline"`) inconsistent with the borderless cog icon elsewhere; switched to `variant="ghost"`. + - Settings topbar back arrow and dashboard topbar cog icon were styled gray-by-default with hover turning them black — correct for a mouse, but permanently gray on a touch device with no hover state. Both now force black via `[@media(hover:none)]`, leaving the hover-capable (desktop) behavior unchanged. + - Team page: the members and pending-invitations tables used a fixed `min-w-[640px]` with horizontal scroll on narrow viewports. Below `md`, both now render as a stacked card list instead — members show name/email, then role select + edit/remove actions in one row (role stays visible since it's the only place to change it, unlike the edit dialog which only renames); invitations show email, then role chip + revoke in one row, matching the same alignment pattern. + +- b98f618: Outbound calls and text messages are approved only by a person in the dashboard + + A proposal whose campaign runs on a voice or SMS channel can now only be approved by a signed-in dashboard user. `outreach_approve_proposal` refuses every other caller — an MCP agent, an unrestricted admin API key on the control plane, a curator, the Slack approval button — and the proposal stays `pending`. Email approval is unchanged, including by agents and admin keys. + + The check lives in `OutreachService.approveProposal`, so it holds regardless of which surface the call arrives through. It is not an MCP tool-visibility hint: hosts that don't implement MCP Apps still list the tool, and calling it on a voice proposal fails there too. + + Slack declines these in-thread with a pointer to the dashboard rather than surfacing a service error. The Inspector panel drops the approve button for voice and SMS proposals — Dismiss stays, since dismissing sends nothing — and says where the call is actually placed. + + Quiet hours and blackout dates are now enforced when a call or text is approved. They were previously stored on the campaign and consulted only when listing due follow-ups, so nothing stopped a call at 3am. `cadenceRules` gains an optional `quietHoursTimezone` (IANA, validated) that quiet hours and blackout dates are read in; without it they are read in UTC, which is not what a Norwegian campaign means by "no calls before 08:00". + +- 862b055: Remove the MCP tools that place outbound voice calls + + `conv_call_channel` and `conv_call_contact` no longer exist on the MCP surface, so no connected agent can place a phone call. Anthropic's MCP directory does not list connectors that let an assistant dial third parties on its own, and an in-client tool-approval click does not clear that bar. + + Nothing else changes. The `VoiceCallbackService` and `ChannelAdminService.call` service methods stay, the `/v1/conversations/channels/:id/call` endpoints stay, and the dashboard's per-channel test call keeps working — it is authenticated as a human dashboard session, not as an agent. `conv_request_callback` also stays: it is `self_service`-only, so it is reachable by an org's own end-user agents (the widget's "can you call me?" flow) and refused for admin callers by the audience gate in `dispatch.ts`. + + The dashboard action is renamed from "Place a call" to "Make a test call", matching "Send test email" and "Send test SMS" — verifying a newly configured voice channel is what it is for. + + Outbound calling as a product capability moves to `outreach`, where a voice campaign already drafts a proposal that a human approves before anything is dialed. + +- c5a05c5: SMS channel dialogs: agent-reply select on create, sender switching actually clears + + The "Agent replies" select is now on the SMS create dialog, not just edit — a new channel no longer silently starts on `auto` until someone goes back and changes it. Renamed from "Default agent mode" / "Inbound replies" to a shared "Agent replies" across email and SMS dialogs, since "agent mode" is the database column name, not something an operator configuring a phone number has a mental model for. + + Twilio's From-number and Messaging-Service-SID fields are now a single "Send from" choice instead of two always-visible inputs with an "either, both is also OK" caveat. Switching the choice on an existing channel now actually clears the field you switched away from — `updateChannel` previously merged with `?? prev`, so the old value survived a switch and both were sent on every message. + +- Updated dependencies [c5a05c5] +- Updated dependencies [c5a05c5] + - @getmunin/types@4.75.0 + - @getmunin/ui@4.75.0 + ## 4.74.0 ### Minor Changes diff --git a/packages/dashboard-pages/package.json b/packages/dashboard-pages/package.json index b9c54ead..e363f3d5 100644 --- a/packages/dashboard-pages/package.json +++ b/packages/dashboard-pages/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/dashboard-pages", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Munin dashboard page components shared between OSS and cloud Next apps.", "publishConfig": { diff --git a/packages/db/CHANGELOG.md b/packages/db/CHANGELOG.md index 3c35d64d..32b9ffec 100644 --- a/packages/db/CHANGELOG.md +++ b/packages/db/CHANGELOG.md @@ -1,5 +1,13 @@ # @getmunin/db +## 4.75.0 + +### Patch Changes + +- Updated dependencies [c5a05c5] +- Updated dependencies [c5a05c5] + - @getmunin/types@4.75.0 + ## 4.74.0 ### Minor Changes diff --git a/packages/db/package.json b/packages/db/package.json index 80b658bf..52e4e14e 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/db", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Munin database: Drizzle schema, migrations, RLS policies.", "publishConfig": { diff --git a/packages/docs-pages/CHANGELOG.md b/packages/docs-pages/CHANGELOG.md index 36b21dd7..837b4fab 100644 --- a/packages/docs-pages/CHANGELOG.md +++ b/packages/docs-pages/CHANGELOG.md @@ -1,5 +1,20 @@ # @getmunin/docs-pages +## 4.75.0 + +### Patch Changes + +- Updated dependencies [c5a05c5] +- Updated dependencies [cc87bb6] +- Updated dependencies [3269f5c] +- Updated dependencies [b98f618] +- Updated dependencies [862b055] +- Updated dependencies [77820b0] +- Updated dependencies [eedcba5] +- Updated dependencies [c5a05c5] +- Updated dependencies [c5a05c5] + - @getmunin/backend-core@4.75.0 + ## 4.74.0 ### Patch Changes diff --git a/packages/docs-pages/package.json b/packages/docs-pages/package.json index c1d70be5..822a59c1 100644 --- a/packages/docs-pages/package.json +++ b/packages/docs-pages/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/docs-pages", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Munin developer-portal route components shared between OSS and cloud Next apps.", "publishConfig": { diff --git a/packages/emails/CHANGELOG.md b/packages/emails/CHANGELOG.md index 708a622a..ebde68f1 100644 --- a/packages/emails/CHANGELOG.md +++ b/packages/emails/CHANGELOG.md @@ -1,5 +1,7 @@ # @getmunin/emails +## 4.75.0 + ## 4.74.0 ## 4.73.0 diff --git a/packages/emails/package.json b/packages/emails/package.json index 6a643f33..0a7715bb 100644 --- a/packages/emails/package.json +++ b/packages/emails/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/emails", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Munin transactional email templates (React Email).", "publishConfig": { diff --git a/packages/inspector-app/CHANGELOG.md b/packages/inspector-app/CHANGELOG.md index 84902c6b..22e3d864 100644 --- a/packages/inspector-app/CHANGELOG.md +++ b/packages/inspector-app/CHANGELOG.md @@ -1,5 +1,41 @@ # @getmunin/inspector-app +## 4.75.0 + +### Minor Changes + +- cc87bb6: Outreach proposals say where they are going before you approve them + + A proposal DTO now carries `delivery`: the campaign channel's type and vendor, the destination the approval would actually reach (email address for email campaigns, phone number for voice and SMS), and whether Munin will append the campaign CTA link and unsubscribe footer at send time. `contact` gains `phone` alongside `email`. + + Both review surfaces use it. The MCP App panel and the dashboard drawer state the consequence in words — "Approving places a phone call to +1 415 555 9999", "Approving emails jane@acme.com. Munin appends the campaign CTA link, an unsubscribe footer" — and warn in red when the contact has no address or number on file, which is a send that would fail. A voice proposal's approve button reads "Approve & call" rather than "Approve & send", and its missing subject renders as "(spoken call — no subject)" instead of the bare "(no subject)" an email would show. + + This matters most for voice campaigns, where approving dials a real phone number and the panel previously showed nothing but a subject-less body and an Approve button. It is worth having for email too: a reviewer approving a first-touch could not see the recipient address unless the contact happened to have no name. + + The panel's `Proposal.kind` union was also missing `'followup'`, which every follow-up proposal has carried since sequences shipped. + +- b98f618: Outbound calls and text messages are approved only by a person in the dashboard + + A proposal whose campaign runs on a voice or SMS channel can now only be approved by a signed-in dashboard user. `outreach_approve_proposal` refuses every other caller — an MCP agent, an unrestricted admin API key on the control plane, a curator, the Slack approval button — and the proposal stays `pending`. Email approval is unchanged, including by agents and admin keys. + + The check lives in `OutreachService.approveProposal`, so it holds regardless of which surface the call arrives through. It is not an MCP tool-visibility hint: hosts that don't implement MCP Apps still list the tool, and calling it on a voice proposal fails there too. + + Slack declines these in-thread with a pointer to the dashboard rather than surfacing a service error. The Inspector panel drops the approve button for voice and SMS proposals — Dismiss stays, since dismissing sends nothing — and says where the call is actually placed. + + Quiet hours and blackout dates are now enforced when a call or text is approved. They were previously stored on the campaign and consulted only when listing due follow-ups, so nothing stopped a call at 3am. `cadenceRules` gains an optional `quietHoursTimezone` (IANA, validated) that quiet hours and blackout dates are read in; without it they are read in UTC, which is not what a Norwegian campaign means by "no calls before 08:00". + +### Patch Changes + +- Updated dependencies [8e98f2d] +- Updated dependencies [cc87bb6] +- Updated dependencies [b98f618] +- Updated dependencies [862b055] +- Updated dependencies [c5a05c5] +- Updated dependencies [c5a05c5] + - @getmunin/dashboard-pages@4.75.0 + - @getmunin/types@4.75.0 + - @getmunin/ui@4.75.0 + ## 4.74.0 ### Patch Changes diff --git a/packages/inspector-app/package.json b/packages/inspector-app/package.json index a5213e4d..268e6442 100644 --- a/packages/inspector-app/package.json +++ b/packages/inspector-app/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/inspector-app", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Munin Inspector MCP App: the ui://munin/inspector panel bundle rendered inline by MCP App hosts.", "publishConfig": { diff --git a/packages/mcp-toolkit/CHANGELOG.md b/packages/mcp-toolkit/CHANGELOG.md index 45da5d44..7648e688 100644 --- a/packages/mcp-toolkit/CHANGELOG.md +++ b/packages/mcp-toolkit/CHANGELOG.md @@ -1,5 +1,14 @@ # @getmunin/mcp-toolkit +## 4.75.0 + +### Patch Changes + +- Updated dependencies [c5a05c5] +- Updated dependencies [c5a05c5] + - @getmunin/types@4.75.0 + - @getmunin/core@4.75.0 + ## 4.74.0 ### Patch Changes diff --git a/packages/mcp-toolkit/package.json b/packages/mcp-toolkit/package.json index 74be8e81..7e757022 100644 --- a/packages/mcp-toolkit/package.json +++ b/packages/mcp-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/mcp-toolkit", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Decorators + registry that turn NestJS providers into MCP tools.", "publishConfig": { diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index 1137421b..2a2faab6 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,5 +1,7 @@ # @getmunin/sdk +## 4.75.0 + ## 4.74.0 ## 4.73.0 diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 1d4358fd..f1d5dad7 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/sdk", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "TypeScript client for the Munin REST API (token minting, end-user lookup, webhooks).", "publishConfig": { diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 7344e78d..dcec51a7 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,27 @@ # @getmunin/types +## 4.75.0 + +### Minor Changes + +- c5a05c5: SMS channels can set how the agent handles inbound texts + + `defaultAgentMode` has always been a `conv_channels` column, but only the email path could write it — the vendor-backed path that creates every SMS channel had no way to set it, and neither did the dashboard. Every SMS number was stuck on `auto`, replying to inbound texts automatically. + + It is now settable on SMS channels through `conv_configure_channel`, the vendor tools, the `/v1` SMS endpoints, and a control in the Twilio and MessageBird dialogs alongside the one email already had. Set `draft_only` on a number you only run campaigns from and inbound replies are drafted for approval instead of auto-answered. + + Voice channels reject it with an explanation rather than accepting a value that would do nothing: an inbound call is run by the vendor's assistant, not by the Munin agent, so there is no reply for the mode to govern. + + Also corrects `conv_create_channel`'s description, which claimed "the `voice` and `sms` channel types are reserved and not yet wired to an adapter". Both SMS vendors and both voice vendors have shipped adapters; those channels are created with `conv_configure_channel`, which the description now says. + +### Patch Changes + +- c5a05c5: SMS channel dialogs: agent-reply select on create, sender switching actually clears + + The "Agent replies" select is now on the SMS create dialog, not just edit — a new channel no longer silently starts on `auto` until someone goes back and changes it. Renamed from "Default agent mode" / "Inbound replies" to a shared "Agent replies" across email and SMS dialogs, since "agent mode" is the database column name, not something an operator configuring a phone number has a mental model for. + + Twilio's From-number and Messaging-Service-SID fields are now a single "Send from" choice instead of two always-visible inputs with an "either, both is also OK" caveat. Switching the choice on an existing channel now actually clears the field you switched away from — `updateChannel` previously merged with `?? prev`, so the old value survived a switch and both were sent on every message. + ## 4.74.0 ### Minor Changes diff --git a/packages/types/package.json b/packages/types/package.json index 8553c3ab..2237edbd 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/types", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Shared Zod schemas + TypeScript types for the Munin platform.", "publishConfig": { diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 86d75d8f..3e1ac30f 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,5 +1,7 @@ # @getmunin/ui +## 4.75.0 + ## 4.74.0 ## 4.73.0 diff --git a/packages/ui/package.json b/packages/ui/package.json index 0185a919..431106a8 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@getmunin/ui", - "version": "4.74.0", + "version": "4.75.0", "license": "MIT", "description": "Munin design-system primitives (shadcn-style) shared between OSS and cloud Next apps.", "publishConfig": {