`main` moved next 16.2.6 → 16.2.12 (#441) and applied transitive audit
fixes (#442) since this branch was cut, so both manifests conflicted.
Resolved by taking main's manifests and re-applying the group's bumps on
top: prettier ^3.9.6 and prettier-plugin-tailwindcss ^0.8.1 in
package.json, plus tailwindcss and @tailwindcss/postcss 4.3.2 → 4.3.3,
which are lockfile-only here because both are specced as `^4`. Lockfile
regenerated with npm 10 — the version CI's Node 20 ships, so the nested
optional-peer @swc/helpers entry `npm ci` needs is preserved.
Verified next 16.2.12 and #442's transitive versions all hold, and
`npm ci` succeeds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`main` moved next 16.2.6 → 16.2.12 (#441) and applied transitive audit
fixes (#442) since this branch was cut, so both manifests conflicted.
Resolved by taking main's manifests and re-applying only this branch's
`lucide-react` bump, then regenerating package-lock.json with npm 10 —
the version CI's Node 20 ships, so the nested optional-peer @swc/helpers
entry `npm ci` needs is preserved. Verified next 16.2.12, lucide-react
1.27.0, and #442's transitive versions all hold, and `npm ci` succeeds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two defects that together produce the reported symptom — automation logs
that read `status: 'success'` with `steps_executed: []`.
1. The dispatch loop in the inbound webhook was fire-and-forget inside the
route's `after()` block. `after()` only keeps the function alive for
promises it can see, so the run could be frozen after the log row was
inserted but before any step executed. This is the same failure mode as
issue #301, recurring one level down — the comments on the AI auto-reply
and `message.received` dispatches immediately below already spell out
the invariant this violated. Now awaited; the per-iteration `.catch` is
kept so one trigger type can't skip the rest of the loop.
2. `executeAutomation` seeded the log row with `status: 'success'` before
running anything. A run that died mid-flight therefore left a permanent
success with no steps, indistinguishable from an automation that
genuinely had nothing to do. Seeded as `'failed'` instead, so the status
only becomes success if execution actually reaches a terminal path. No
migration needed — 'failed' is already in the column's CHECK constraint,
and every terminal path overwrites it.
Two more points from the issue are deliberately not addressed here, as
both change existing behaviour and want a maintainer decision: switching
`contains` keyword matching to word boundaries (would alter which
automations fire for current users) and re-opening a closed conversation
on an inbound reply.
Refs #409.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI installs with Node 20 (npm 10), which keeps next-intl's nested
optional-peer @swc/helpers entry; Dependabot generates the lockfile with
npm 11, which drops it, so `npm ci` fails as out-of-sync before any of
lint/typecheck/test/build runs. Regenerated with npm 10 — the only diff
is re-adding that nested entry.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI installs with Node 20 (npm 10), which keeps next-intl's nested
optional-peer @swc/helpers entry; Dependabot generates the lockfile with
npm 11, which drops it, so `npm ci` fails as out-of-sync before any of
lint/typecheck/test/build runs. Regenerated with npm 10 — the only diff
is re-adding that nested entry.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI installs with Node 20 (npm 10), which keeps next-intl's nested
optional-peer @swc/helpers entry; Dependabot generates the lockfile with
npm 11, which drops it, so `npm ci` fails as out-of-sync before any of
lint/typecheck/test/build runs. Regenerated with npm 10 — the only diff
is re-adding that nested entry.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lockfile-only bumps of indirect dependencies flagged by Dependabot;
no direct dependency or `package.json` entry changes.
- fast-uri 3.1.3 → 3.1.4 (two host-confusion advisories, high)
- js-yaml → 4.3.0 (quadratic CPU via YAML merge-key chains, high)
- hono → 4.12.32 and @hono/node-server → 2.0.12 (cross-request context
disclosure in hono/jsx, serve-static path traversal on Windows)
- body-parser → 2.3.0 (size enforcement silently disabled on an invalid
limit value)
- postcss → 8.5.24
Regenerated with npm 10 to match CI's Node 20, so the nested
`@swc/helpers` optional-peer entry `npm ci` needs is preserved.
Verified: `npm ci`, lint (39 warnings, unchanged from main), typecheck,
645 tests, and `next build` all pass.
Still open after this, and deliberately left alone: `next` (needs a
direct bump — separate branch), `sharp` (major, `--force` only), and
`brace-expansion` nested under the dev-only `@ts-morph/common`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
16.2.6 is in the vulnerable range for eight advisories, all patched in
16.2.11: SSRF in rewrites via an attacker-controlled destination
hostname, SSRF in Server Actions on custom servers, middleware/proxy
bypass in App Router, DoS in App Router Server Actions, DoS in the image
optimizer via SVG, unauthenticated disclosure of internal Server
Function endpoints, and two response-body cache-confusion issues.
`eslint-config-next` moves with it to keep the pair on one version.
Lockfile regenerated with npm 10 to match CI's Node 20 — the default
npm 11 drops next-intl's nested optional-peer `@swc/helpers` entry and
breaks `npm ci`.
Verified: lint (39 warnings, unchanged from main), typecheck, 645 tests,
and `next build` all pass on 16.2.12.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`ensureImageHeaderHandle` fetched the caller-supplied `header_media_url`
server-side with no validation, so a template submit — an action any
authenticated member can perform — could reach loopback, RFC1918 and
cloud-metadata addresses, with the response status and content-type
echoed back through the thrown error.
Wire in the project's existing `isDeliverableUrl` guard, which the two
other outbound-fetch call sites (webhook delivery, automation
`send_webhook`) already use, and reuse their `redirect: 'manual'` +
timeout so a public URL can't 3xx-bounce to an internal one. The
rejection reuses the unreachable-host message verbatim so the failure
doesn't distinguish "blocked" from "down".
Addresses GHSA-6fr5-pxw7-r3xw.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The settings page derived its active panel from `useSearchParams()` but
wasn't wrapped in a Suspense boundary. In Next 16 that fails the
production build ("missing Suspense with CSR bailout") and forces the
page to bail to full client-side rendering — shipping a settings screen
whose rail click handlers never wire up. Because the account-menu
Settings link targets `?tab=whatsapp`, users land on the WhatsApp panel
and can't navigate to any other section ("locked on WhatsApp").
Wrap the `useSearchParams` consumer in `<Suspense>` on the three
affected dashboard pages — settings, inbox, automations/new — mirroring
the split login/signup already use. Also add the missing
`Settings.sections.quick-replies` label, which was rendering as a raw
translation key in the settings rail.
Verified: production build now succeeds (previously errored out during
static generation); every settings rail section navigates correctly in a
production build.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dependabot's lockfile regeneration drops the nested
node_modules/next-intl/node_modules/@swc/helpers@0.5.23 optional-peer
entry that CI's `npm ci` (Node 20 / npm 10) requires, so the job failed
at install before lint/typecheck/test/build ran. Regenerated
package-lock.json with npm 10 to restore it; no dependency versions change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dependabot's lockfile regeneration drops the nested
node_modules/next-intl/node_modules/@swc/helpers@0.5.23 optional-peer
entry that CI's `npm ci` (Node 20 / npm 10) requires, so the job failed
at install before lint/typecheck/test/build ran. Regenerated
package-lock.json with npm 10 to restore it; no dependency versions change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dependabot's lockfile regeneration drops the nested
node_modules/next-intl/node_modules/@swc/helpers@0.5.23 optional-peer
entry that CI's `npm ci` (Node 20 / npm 10) requires, so the job failed
at install before lint/typecheck/test/build ran. Regenerated
package-lock.json with npm 10 to restore it; no dependency versions change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Login signed in client-side then soft-navigated with router.push into
/dashboard, a route the middleware gates on the Supabase session cookie.
The soft navigation can reach the protected route before the server
observes the freshly written cookies, so the middleware bounces it back
to /login -- which looks like the page "just refreshing" instead of
signing in. It surfaced over ngrok/HTTPS tunnels (added latency widens
the window) while staying hidden on localhost.
- login: navigate with window.location.href after signInWithPassword,
mirroring the deliberate full reload the invite-accept flow already
uses in join/[token]/page.tsx; drops the now-unused useRouter.
- next.config: allow-list common dev tunnel origins (ngrok, cloudflared,
localtunnel) so Next 16 stops 403-ing dev-only resources over the
tunnel, extendable via ALLOWED_DEV_ORIGINS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds messages/ko.json — full next-intl translation covering all 12
namespaces (LoginPage, Sidebar, Header, ModeToggle, Dashboard, Inbox,
Contacts, Pipelines, Broadcasts, Automations, Flows, Settings).
Scoped to the language file only, per maintainer guidance on PR #362:
no changes to the default locale, component text, or any other file.
Verified 1:1 key parity with messages/en.json (1426/1426 leaf keys)
and exact preservation of all interpolation variables and HTML-ish
tags across every message.
An inbound message could create a second conversation for a contact
under a race (Meta retries a delivery, or a batch fans out to concurrent
after() runs). There was no UNIQUE index and no unique-violation
backstop on conversations, unlike contacts (migration 022). Worse, once
two conversations existed the `.single()` lookup errored on every later
message and the webhook created yet another conversation each time,
snowballing into a wall of duplicate chats.
- findOrCreateConversation now resolves oldest-first with .limit(1)
instead of .single(), so pre-existing duplicates converge, and catches
23505 on insert to re-resolve the winning row.
- Same hardening applied to the public-API resolver (resolve-conversation).
- Migration 036 merges existing duplicate conversations into the oldest
thread (re-pointing messages, reactions, deals, flow_runs,
notifications, ai_usage_log — no data loss) then adds a
UNIQUE (account_id, contact_id) index as the authoritative guarantee.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add full support for WhatsApp Cloud API interactive messages across the
inbox, automations, and a new quick-replies feature. Extends the
interactive plumbing that previously existed only for the Flows engine.
Sending
- send-message core handles `interactive` (reply buttons ≤3, list ≤10
rows) via the existing Meta senders; validated server-side before send.
- Inbox composer gains a "+" menu → builder dialog with live WhatsApp-style
preview + Meta char limits (button 20, list row 24, body 1024).
- Automation "Send Buttons" / "Send List" steps in the builder.
- Public v1 /messages accepts type=interactive.
Storage / round-trip
- migration 035: messages.interactive_payload JSONB + quick_replies table.
- Outbound interactive persists the structured payload (composer,
automations, and Flows) so the thread re-renders the buttons/rows.
Receiving
- Bubble renders outbound interactive via the shared preview; inbound taps
(button_reply/list_reply) keep the "chosen option" affordance.
Automations
- New `interactive_reply` trigger matches on tapped button/list IDs
(exact), enabling chained multi-step menus.
Quick replies
- New account-scoped quick_replies (text or interactive snippets), managed
in Settings and insertable from the composer; "Save as quick reply" in
the builder.
Shared core in src/lib/whatsapp/interactive.ts + src/components/interactive/.
Includes unit tests for the validator, send-param validation, the
interactive_reply trigger match, and the new automation step/trigger
validation. typecheck + lint clean; full suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The MCP registry namespace is case-sensitive and keyed to the GitHub
login: the owner is `ArnasDon`, not `arnasdon`. Publishing as
io.github.arnasdon/wacrm-mcp is rejected 403. Fix the name in
server.json and the matching mcpName in package.json to
io.github.ArnasDon/wacrm-mcp.
Bump to 0.1.1 because the published 0.1.0 baked in the lowercase
mcpName; the registry validates the published package's mcpName
against server.json, so a fresh npm version is required.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>