toga-ai 1.0.653 → 1.0.654

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,9 +6,10 @@
6
6
  | [BDR Web Funnel — Full Implementation Plan](features/bdr-web-funnel-plan.md) | > **Execution status (2026-07-16):** PLAN **Phases 0–5 are BUILT and QA'd**, Phase 6 > was **mostly already built** (the `info` contract was implemented at comm | bdr/PLAN.md, bdr/mockup/app.jsx, bdr/mockup/screens.jsx, bdr/mockup/components.jsx |
7
7
  | [Call Orchestration — PHP Worker ↔ Vapi (the integration seam)](features/call-orchestration.md) | The **PHP worker** is the orchestrator; **Vapi** is the actor. | ai-bdr/docs/client-onboarding-sop.md, ai-bdr/docs/vapi-firstmessage-timing-fix.md, ai-bdr/docs/vapi-inbound-callback-assistant-request.md, ai-bdr/docs/vapi-voicemail-iphone-screening.md, ai-bdr/scripts/update_structured_output.py |
8
8
  | [AI-BDR Campaign Chat — Backend Contract + Frontend Widget Handoff](features/campaign-chat-handoff.md) | The consolidated backend + frontend handoff for the **AI-BDR campaign chat** — a grounded AI-BDR chat widget for landing pages, powered by the Talos backend at | ai-bdr-campaign-handoff.md, bdr/src/lib/talosChat.ts |
9
+ | [Landing "Call me now" Flow (/landing) — screens + the dormant real callback route](features/landing-call-flow.md) | The **"Call me now" path on `/landing`**: a two-screen flow (call form → calling/recap) plus a **real, wired, but deliberately dormant** backend route (`POST /a | bdr/src/flow/screens/SecurityCallNow.tsx, bdr/src/flow/screens/SecurityCalling.tsx, bdr/src/lib/securityCallFlow.ts, bdr/src/lib/leadName.ts, bdr/src/app/api/landing-call/route.ts, bdr/src/flow/landingCallApi.ts, bdr/src/flow/screens/SecurityLanding.tsx, bdr/src/components/ChatPanel.tsx, bdr/src/content/security.ts, bdr/src/app/security.css, bdr/src/proxy.ts, bdr/.env.example, bdr/test/securityCallFlow.test.ts, bdr/test/landingCallRoute.test.ts |
9
10
  | [Talos Chat Drawer on /landing (first campaign-chat consumer)](features/landing-chat-drawer.md) | The **right-anchored chat drawer on `/landing`**, wired to the **real** Talos campaign-chat backend at `https://api.togaiq.com`. | bdr/src/components/ChatPanel.tsx, bdr/src/components/ChatMarkdown.tsx, bdr/src/lib/talosChat.ts, bdr/src/content/security.ts, bdr/src/flow/screens/SecurityLanding.tsx, bdr/src/app/security.css, bdr/test/talosChat.test.ts, bdr/test/chatMarkdown.test.ts |
10
11
  | [Live Call Status — Vapi call API → funnel Success screen lifecycle](features/live-call-status.md) | The BDR funnel's Success screen shows the **real** state of the call it just requested — dialing / ringing / answered-and-talking / ended / not-answered — inste | bdr/src/server/vapiCall.ts, bdr/src/server/callStatus.ts, bdr/src/server/statusToken.ts, bdr/src/server/callbackService.ts, bdr/src/server/workerDialer.ts, bdr/src/lib/callStatus.ts, bdr/src/app/api/call-status/route.ts, bdr/src/app/api/call-now/route.ts, bdr/src/flow/useCallStatusViewModel.ts, bdr/src/flow/callStatusApi.ts, bdr/src/flow/screens/Success.tsx, bdr/src/content/schema.ts, bdr/src/content/default.ts, bdr/src/proxy.ts |
11
- | [Cybersecurity Landing Page (/landing — the site's current main page)](features/security-landing-page.md) | A single-page **cybersecurity services marketing landing page** at **`/landing`** in the `bdr` repo — and, as of 2026-08-24, **the site's main page**: `/` **307 | bdr/src/app/landing/page.tsx, bdr/src/app/security.css, bdr/src/flow/screens/SecurityLanding.tsx, bdr/src/content/security.ts, bdr/src/components/LogoSymbol.tsx, bdr/src/components/AssistantMark.tsx, bdr/src/components/Ico.tsx, bdr/src/components/Particles.tsx, bdr/src/components/ChatPanel.tsx, bdr/src/components/ChatMarkdown.tsx, bdr/src/lib/talosChat.ts, bdr/next.config.ts, bdr/eslint.config.mjs |
12
+ | [Cybersecurity Landing Page (/landing — the site's current main page)](features/security-landing-page.md) | A single-page **cybersecurity services marketing landing page** at **`/landing`** in the `bdr` repo — and, as of 2026-08-24, **the site's main page**: `/` **307 | bdr/src/app/landing/page.tsx, bdr/src/app/security.css, bdr/src/flow/screens/SecurityLanding.tsx, bdr/src/flow/screens/SecurityCallNow.tsx, bdr/src/flow/screens/SecurityCalling.tsx, bdr/src/lib/securityCallFlow.ts, bdr/src/content/security.ts, bdr/src/components/LogoSymbol.tsx, bdr/src/components/AssistantMark.tsx, bdr/src/components/Ico.tsx, bdr/src/components/Particles.tsx, bdr/src/components/ChatPanel.tsx, bdr/src/components/ChatMarkdown.tsx, bdr/src/lib/talosChat.ts, bdr/next.config.ts, bdr/eslint.config.mjs |
12
13
  | [Vapi Integration — Assistants, Tools, Structured Output](features/vapi-integration.md) | Everything inside Vapi: the three assistants, the three shared tools, the shared structured-output schema, the Liquid-templated system prompt, and the Python sc | ai-bdr/vapi/templates/assistant.template.json, ai-bdr/vapi/templates/system-prompt.template.md, ai-bdr/prompts/archive/prompt-may-15.txt, ai-bdr/prompts/campaigns/healthcare-2026-03-18.md, ai-bdr/prompts/campaigns/healthcare-v2-2026-03-25.md, ai-bdr/prompts/templates/campaign-template.md, ai-bdr/scripts/update_assistant.py, ai-bdr/scripts/update_system_prompt.py, ai-bdr/scripts/update_structured_output.py, ai-bdr/scripts/update_call_summary_context.py, ai-bdr/scripts/fix_booking_guardrails.py, ai-bdr/scripts/get_assistant.py, ai-bdr/docs/vapi-firstmessage-timing-fix.md, ai-bdr/docs/vapi-voicemail-iphone-screening.md |
13
14
  | [Web Funnel — Built Next.js App (structure, stack, how to run)](features/web-funnel-app.md) | > **READ FIRST (2026-08-24) — the funnel is currently UNREACHABLE.** `/` now > **307-redirects to `/landing`**, the new cybersecurity landing page > ([security- | bdr/PLAN.md, bdr/next.config.ts, bdr/.gitignore, bdr/src/server/hubspot.ts, bdr/src/server/leadSink.ts, bdr/src/server/callbackService.ts, bdr/src/server/workerDialer.ts, bdr/src/server/mailer.ts, bdr/src/server/statusToken.ts, bdr/src/server/vapiCall.ts, bdr/src/server/callStatus.ts, bdr/src/lib/summaryText.ts, bdr/src/lib/emailFormat.ts, bdr/src/lib/callStatus.ts, bdr/src/app/api/share-summary/route.ts, bdr/src/app/api/call-status/route.ts, bdr/src/flow/shareSummaryApi.ts, bdr/src/flow/callStatusApi.ts, bdr/src/flow/useCallStatusViewModel.ts, bdr/src/flow/screens/Success.tsx, bdr/src/flow/screens/CallNow.tsx, bdr/src/flow/screens/Schedule.tsx, bdr/src/flow/screens/CallSummary.tsx, bdr/src/content/schema.ts, bdr/src/content/default.ts, bdr/test/togaEnrichment.test.ts, bdr/test/fastDial.test.ts, bdr/eslint.config.mjs, bdr/amplify.yml, bdr/src/app, bdr/src/app/layout.tsx, bdr/src/app/page.tsx, bdr/src/app/studio.css, bdr/src/lib/resolveCampaignId.ts, bdr/src/lib/scheduleSlots.ts, bdr/src/lib/formatPhone.ts, bdr/src/lib/analytics.ts, bdr/src/lib/rateLimit.ts, bdr/src/lib/clientIp.ts, bdr/src/proxy.ts, bdr/.env.example, bdr/src/components/GoogleAnalytics.tsx, bdr/src/components/RichText.tsx, bdr/src/content, bdr/src/components, bdr/src/flow, bdr/src/server, bdr/src/server/toga.ts, bdr/src/app/api, bdr/test, bdr/test/togaPrimaryPhone.test.ts, bdr/public, bdr/mockup/styles.css |
14
15
  | [Web Funnel — Next.js UI + Config-Driven Campaign Content](features/web-funnel-content-model.md) | > **Status note (RESOLVED 2026-07-16):** the content **contract** below (schema, > provider seam, adapters) is BUILT and QA'd (PLAN Phases 0–5). | bdr/src/content/schema.ts, bdr/src/content/provider.ts, bdr/src/content/useCampaign.ts, bdr/src/content/default.ts, bdr/src/lib/resolveCampaignId.ts, bdr/src/server/leadSink.ts, bdr/src/server/callbackService.ts, bdr/src/app/api/call-now/route.ts, bdr/src/app/api/call-later/route.ts, bdr/src/app/api/contact/route.ts |
@@ -0,0 +1,226 @@
1
+ ---
2
+ title: Landing "Call me now" Flow (/landing) — screens + the dormant real callback route
3
+ framework: "2.0"
4
+ repo: ai-bdr
5
+ project: AI-BDR
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-08-26
10
+ owners: [tcox]
11
+ files:
12
+ - bdr/src/flow/screens/SecurityCallNow.tsx
13
+ - bdr/src/flow/screens/SecurityCalling.tsx
14
+ - bdr/src/lib/securityCallFlow.ts
15
+ - bdr/src/lib/leadName.ts
16
+ - bdr/src/app/api/landing-call/route.ts
17
+ - bdr/src/flow/landingCallApi.ts
18
+ - bdr/src/flow/screens/SecurityLanding.tsx
19
+ - bdr/src/components/ChatPanel.tsx
20
+ - bdr/src/content/security.ts
21
+ - bdr/src/app/security.css
22
+ - bdr/src/proxy.ts
23
+ - bdr/.env.example
24
+ - bdr/test/securityCallFlow.test.ts
25
+ - bdr/test/landingCallRoute.test.ts
26
+ related:
27
+ - security-landing-page.md
28
+ - landing-chat-drawer.md
29
+ - live-call-status.md
30
+ - call-orchestration.md
31
+ - web-funnel-app.md
32
+ ---
33
+
34
+ ## What this is
35
+
36
+ The **"Call me now" path on `/landing`**: a two-screen flow (call form → calling/recap)
37
+ plus a **real, wired, but deliberately dormant** backend route
38
+ (`POST /api/landing-call`) that requests an actual Vapi callback for an **anonymous**
39
+ visitor.
40
+
41
+ It was built 2026-08-26 on the design lead's UI review of the landing page: *"mock is
42
+ right; missing a Call me now button + flow; book a meeting slightly large; check with
43
+ Alex re shared component."*
44
+
45
+ **Read this first:** the route is **off by default**. It requires
46
+ `TOGA_SECURITY_CAMPAIGN_UUID`, and **no cybersecurity campaign exists in Toga yet**, so
47
+ in every environment today the flow runs its **simulation**. That is the intended end
48
+ state for now — *"wired up and ready for the campaign"* — not an unfinished build.
49
+
50
+ ## The two campaigns are NOT the same thing (read before wiring anything)
51
+
52
+ AI-BDR has **two independent campaign systems** with unrelated ids. Confusing them is the
53
+ easiest mistake to make on this page, because both serve the same cybersecurity offer.
54
+
55
+ | | **Talos chat campaign** | **Toga dialer campaign** |
56
+ |---|---|---|
57
+ | Lives in | `api.togaiq.com` → `campaign_registry` | Toga platform → `Client_True.Campaigns` |
58
+ | Used by | the chat drawer ([landing-chat-drawer.md](landing-chat-drawer.md)) | the callback/dialer path (this doc) |
59
+ | Security id | `43fdf2b6-7a5d-4e85-be6b-911f42aa787f` ("External Pen Testing") | **none exists** |
60
+ | Env var | `NEXT_PUBLIC_TALOS_API_BASE` (base URL only) | `TOGA_SECURITY_CAMPAIGN_UUID` |
61
+
62
+ **`43fdf2b6-7a5d-4e85-be6b-911f42aa787f` cannot be used for
63
+ `TOGA_SECURITY_CAMPAIGN_UUID`.** It is a Talos chat-grounding campaign, not a dialer
64
+ campaign, and Toga has never heard of it.
65
+
66
+ `Client_True.Campaigns` in **prod** holds exactly two rows (verified 2026-08-26), both
67
+ **healthcare**, both `c_meetingType = discovery-call`:
68
+
69
+ - `15d0c02f-501e-e081-2ead-61b78c854223` — "26.05 - AI BDR - Ryan Nitti"
70
+ - `d8f7abe6-8f06-0218-03d1-50db575` — "26.04 - AI BDR TEST CAMPAIGN"
71
+
72
+ So there is **no cybersecurity dialer campaign and no cybersecurity Vapi assistant**.
73
+ That is the single blocker for real calls from `/landing`, and it is owned **outside the
74
+ `bdr` repo** (Toga platform + Vapi config).
75
+
76
+ ## Screen flow
77
+
78
+ `SecurityLanding.tsx` gained a page-level `screen` state — `landing | call | calling` —
79
+ swapped with `SwapFade` using the **mock's app-shell timings** (`outMs 240`, `inMs 460`,
80
+ `rise 12`, `drop 18`). `backToOverview` resets the recap rotation and scrolls to top.
81
+
82
+ 1. **`SecurityCallNow.tsx`** (new) — the mock's security *CallNow* screen: trust column,
83
+ **business email** field with a live hint (*"we'll assess the domain **acme.com**"*,
84
+ derived by `emailDomain`), and a masked phone field. Submit is enabled only when
85
+ `isValidEmail` **and** `isViablePhone` (`isCallFormComplete`).
86
+ 2. **`SecurityCalling.tsx`** (new) — the mock's *Success* screen in calling mode:
87
+ `dialing` → **Connected** with an `mm:ss` timer → **End call** → **Call ended** with a
88
+ recap, **Copy**, and three exits: *Call me again* / *Book a meeting* (booking link) /
89
+ *Back to overview*.
90
+ 3. **`lib/securityCallFlow.ts`** (new) — the pure helpers, all unit-tested:
91
+ `fillTokens`, `emailDomain`, `isCallFormComplete`, `formatCallTimer`, and the
92
+ recap-summary rotation (short / medium / large by visit count).
93
+
94
+ Entry points: the drawer's new **"Call me now"** button (see
95
+ [landing-chat-drawer.md](landing-chat-drawer.md)) via `ChatPanel`'s new `onCall` prop.
96
+ All copy lives in `src/content/security.ts` under the new `callNow` and `calling` groups
97
+ plus `talos.callCta`.
98
+
99
+ ### Why new screens instead of reusing the studio ones
100
+
101
+ `src/flow/screens/CallNow.tsx` and `Success.tsx` (the parked studio funnel) read the
102
+ studio **`CampaignBundle` + lead contexts**, which `/landing` does not provide, and the
103
+ studio `CallNow` has **no email field** at all (the studio funnel primes identity from
104
+ `?hsContactId`). Reuse would have meant threading bundle/lead plumbing into a static
105
+ marketing page.
106
+
107
+ ### Deliberate deviations from the mock
108
+
109
+ - **No `AmbientQuestions`** — that component is bundle-driven.
110
+ - Recap has **Copy** but **no Share sheet**.
111
+ - The mock's *"Schedule a call"* became **"Book a meeting"** (booking link), because
112
+ `/landing` has no schedule screen.
113
+ - Footnote **Privacy / Terms are live `togatech.com` links** (funnel precedent), not the
114
+ mock's inert `#` hashes.
115
+
116
+ ## The real path — `POST /api/landing-call` (wired, dormant)
117
+
118
+ Built as a **separate route from `/api/call-now`** because the landing visitor is
119
+ **anonymous**: there is no `?hsContactId` priming on `/landing`, so there is no HubSpot
120
+ contact and no lead ref to work from — only the email and phone the visitor just typed.
121
+
122
+ Request `{ email, phoneNumber }`. Order of operations:
123
+
124
+ 1. **Validate** — `isValidEmail` + `MAX_EMAIL_LENGTH`, `isValidUSPhone`.
125
+ 2. **Config gate** — require `TOGA_SECURITY_CAMPAIGN_UUID` (must be a Toga uuid) or
126
+ return a **503 envelope** *"Live callback is not configured"* **before any upstream
127
+ call**. This is what keeps the route inert without a feature flag.
128
+ 3. **Lead** — `togaLeadSink.upsertLead`, with `firstName`/`lastName` derived from the
129
+ email local part by the new `src/lib/leadName.ts` (`nameFromEmail`):
130
+ `jane.doe@…` → `Jane` `Doe`; `jdoe@…` → `Jdoe` + an **empty** lastName. Links the
131
+ campaign.
132
+ 4. **Dial** — `togaCallbackService.requestCall(uuid, { type: "now" }, phone)` — the
133
+ **same code path as `/api/call-now`** (contact write + worker fast dial; see
134
+ [live-call-status.md](live-call-status.md)).
135
+ 5. **Tokens** — mints the same purpose-scoped status/share tokens as the studio flow.
136
+
137
+ `src/flow/landingCallApi.ts` (`submitLandingCall`) is the client seam and returns
138
+ `{ live, statusHandle }`. **Any** non-success — 503, 400, 500, network — resolves to
139
+ `live: false` and the screen runs the **simulation**. `SecurityCallNow`'s submit posts
140
+ the request but **never blocks the screen advance**.
141
+
142
+ ### Live vs. simulation in `SecurityCalling`
143
+
144
+ Mirrors the studio `Success.tsx` split, using the existing
145
+ `useCallStatusViewModel` (see [live-call-status.md](live-call-status.md)):
146
+
147
+ | | Simulation (today) | Live (once configured) |
148
+ |---|---|---|
149
+ | Phases | scripted dialing → connected → ended | real Vapi states: dialing → on call (real elapsed) → ended / *"We could not reach you"* |
150
+ | Recap | static, rotating short/medium/large | the **real Vapi recap** replaces it |
151
+ | "End call" button | shown | **hidden** — a browser cannot hang up a phone call |
152
+ | Status unavailable | n/a | **holds at dialing** with a note; never fakes progress |
153
+
154
+ Rate limiting: `/api/landing-call` was added to `src/proxy.ts`'s **call-limiter branch and
155
+ matcher**, sharing the `/api/call-now` bucket.
156
+
157
+ ## Turning it on later (config only — no code change)
158
+
159
+ 1. Create the **cybersecurity campaign + Vapi assistant** in Toga — runbook:
160
+ `ai-bdr/docs/client-onboarding-sop.md` (see [call-orchestration.md](call-orchestration.md)).
161
+ 2. Set `TOGA_SECURITY_CAMPAIGN_UUID` in `.env.local` and in the **Amplify console** (it is
162
+ already covered by the build-time `env | grep` recipe in `amplify.yml` that materializes
163
+ `TOGA_*` into `.env.production` — see [web-funnel-app.md](web-funnel-app.md)).
164
+ 3. Redeploy (console vars only reach a build that starts **after** they are saved).
165
+
166
+ **Rejected alternatives (do not redo them):**
167
+
168
+ - *Remove the route until the campaign exists* — it would simply be rebuilt later; the
169
+ env gate already makes it inert.
170
+ - *Point it at a healthcare campaign to test with a real phone* — **do not**. The
171
+ assistant would pitch the wrong offer to a real person.
172
+
173
+ ## Open items before the first live run
174
+
175
+ - **An empty `lastName` on an email-only lead is UNVERIFIED against Toga.** `jdoe@…`
176
+ produces `Jdoe` + `""`; nobody has confirmed Toga accepts that on `upsertLead`.
177
+ - **The deployed environment needs `TOGA_VAPI_TOKEN`** for live status to render. Without
178
+ it the call still happens, but the screen **holds at dialing with the unavailable note**
179
+ — by design, not a bug.
180
+ - The recap's "Book a meeting" inherits the landing page's booking-link problem (see
181
+ [security-landing-page.md](security-landing-page.md)).
182
+
183
+ ## Gotchas
184
+
185
+ - **Two campaign systems, same product.** See the table above. A Talos `campaign_id` in
186
+ `TOGA_SECURITY_CAMPAIGN_UUID` fails the uuid/Toga check and the route 503s — which
187
+ looks exactly like "not configured," so check *which* id you pasted before debugging.
188
+ - **The 503 is a designed state, not an error to fix.** A dormant route returning
189
+ *"Live callback is not configured"* is the expected production behavior today.
190
+ - **Never let the network decide the UX.** `submitLandingCall` is fire-and-continue: the
191
+ screen advances regardless, and only the *content* (real vs. simulated) depends on the
192
+ response.
193
+ - **No "End call" on the live path.** If you see one during a live call, the live/sim
194
+ branch is wrong — the browser has no way to hang up a Vapi phone call.
195
+ - **An anchor styled as `.btn` renders smaller than a real `<button>` sibling** unless
196
+ `font-size` is pinned — see the drawer doc's gotcha; the same trap produced the
197
+ oversized "Book a meeting" button this session.
198
+
199
+ ## How to work on it
200
+
201
+ - 18 new Vitest tests: `test/securityCallFlow.test.ts` (9) and
202
+ `test/landingCallRoute.test.ts` (9).
203
+ - Verified 2026-08-26: `tsc` clean, `eslint` clean, **vitest 385/385**, `next build`
204
+ clean — the route builds as `ƒ /api/landing-call` and **`/landing` stays prerendered
205
+ static** (the call screens are client-side; the POST only happens on submit).
206
+ - **Nothing is committed** — all of it lives in the `bdr` working tree; the developer
207
+ merges and commits.
208
+
209
+ ## Change history
210
+ - 2026-08-26 — Initial doc. BUILT the `/landing` "Call me now" flow: `ChatPanel` gained an
211
+ `onCall` prop and a "Call me now" action above "Book a meeting"; new `SecurityCallNow` /
212
+ `SecurityCalling` screens driven by a page-level `landing|call|calling` state in
213
+ `SecurityLanding` (SwapFade on the mock's app-shell timings), new pure helpers in
214
+ `lib/securityCallFlow.ts`, and new `callNow`/`calling` content groups. BUILT the REAL
215
+ callback path — `POST /api/landing-call` for ANONYMOUS visitors (validate → require
216
+ `TOGA_SECURITY_CAMPAIGN_UUID` else 503 before any upstream call → `upsertLead` with a
217
+ name derived from the email local part via new `lib/leadName.ts` → `requestCall` on the
218
+ same path as `/api/call-now` → purpose-scoped status/share tokens), the
219
+ `submitLandingCall` client seam, the live/sim split in `SecurityCalling`, and the
220
+ `/api/call-now` rate-limit bucket. DECIDED to keep it **dormant** until the campaign
221
+ exists (enabling is config only); rejected deleting the route and rejected testing
222
+ against a healthcare campaign. DISCOVERED the Talos chat campaign id is NOT a Toga
223
+ dialer campaign and that prod has only two healthcare dialer campaigns — no
224
+ cybersecurity campaign or Vapi assistant exists, which is the real blocker. Open:
225
+ empty-`lastName` acceptance and `TOGA_VAPI_TOKEN` in the deployed env. tsc/eslint clean,
226
+ 385/385 tests, `/landing` still prerendered static. Uncommitted at capture. (tcox)
@@ -6,7 +6,7 @@ project: AI-BDR
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-08-25
9
+ updated: 2026-08-26
10
10
  owners: [tcox]
11
11
  files:
12
12
  - bdr/src/components/ChatPanel.tsx
@@ -19,6 +19,7 @@ files:
19
19
  - bdr/test/chatMarkdown.test.ts
20
20
  related:
21
21
  - security-landing-page.md
22
+ - landing-call-flow.md
22
23
  - campaign-chat-handoff.md
23
24
  - web-funnel-app.md
24
25
  - ../../toga-blox/features/talos-assistant.md
@@ -92,11 +93,22 @@ in the adapter).
92
93
  into the response type by `toTalosResponse()`. The backend's **silent
93
94
  lead/qualification fields are never read** — deliberate, per the handoff.
94
95
 
96
+ ## Drawer actions (both render as of 2026-08-26)
97
+
98
+ The drawer's action row under the message list now carries **two** buttons, in the
99
+ mockup's order:
100
+
101
+ 1. **"Call me now"** — new 2026-08-26, driven by `ChatPanel`'s new **`onCall` prop**. It
102
+ swaps `/landing` to the call screens; the flow, its dormant real backend, and the
103
+ Talos-chat-vs-Toga-dialer campaign distinction live in
104
+ [landing-call-flow.md](landing-call-flow.md). **(This corrects the previous "Call me
105
+ now is NOT rendered" note** — that was true only while the sole destination was the
106
+ parked studio call screen. Purpose-built landing screens replaced that dead end.)
107
+ 2. **"Book a meeting"** — an `<a>` to the same booking href as the page's primary CTA
108
+ (still the dead Calendly link; see [security-landing-page.md](security-landing-page.md)).
109
+
95
110
  ## Deliberate deviations from the mockup drawer
96
111
 
97
- - **"Call me now" is NOT rendered.** Its destination is the parked studio call screen,
98
- unreachable from this page. The standing drawer action is **"Book a meeting"** →
99
- the same Calendly href as the page's primary CTA.
100
112
  - **`cta` and `cited_slides` are captured and typed but not rendered yet.** Both are
101
113
  clean follow-ups; `cited_slides` additionally needs `GET /api/ai/campaign/{id}` plus
102
114
  presigned-URL freshness handling (those URLs expire ~7 days — see the handoff).
@@ -128,15 +140,36 @@ So the decision: build the shell now, swap it later.
128
140
  documented theming ceiling), so even fully themed it will **not** match the security
129
141
  mockup drawer pixel-for-pixel. Accept that at swap time or fund the spacing-token pass.
130
142
 
143
+ **Status 2026-08-26 — decision re-affirmed: keep the disposable bridge shell.** A note to
144
+ **apeterson** is **drafted but not yet sent** (the developer will send it). What it has to
145
+ cover, so the alignment conversation starts from facts:
146
+
147
+ - our adapter **already matches his `TalosAdapter` seam** and is **wired live**;
148
+ - the two blockers are still blox-side and unscheduled — **unpublished to npm**, and
149
+ **React `^18` peers vs. BDR's React 19**;
150
+ - the **design divergence** the security mock introduces versus blox `TalosPanel`: the
151
+ **"Call me now" / "Book a meeting" action buttons under the message list**, and the
152
+ cobalt **`.security-shell` theme**. Those must be reconciled as **blox props/theme vs.
153
+ app-side composition** before a swap is possible — that decision is the real gate now,
154
+ not just publishing.
155
+
131
156
  ## Styling
132
157
 
133
158
  The `.chatveil` / `.chatpanel` block was ported from the security mockup's `styles.css`
134
159
  into `security.css` and is **scoped under `.security-shell`** like every other landing
135
- rule. (The source mockup folder was deleted from the repo on 2026-08-25; `security.css`
136
- is now the only copy of these styles.) `z-index: 120`, width `min(400px, 92vw)`, reduced-motion respected.
160
+ rule. (**Correction to an earlier note:** the mockup folder was deleted 2026-08-25 but
161
+ **re-added 2026-08-26**, so `security.css` is *not* the only copy — the mockup is back in
162
+ the repo as the design reference. `security.css` remains the only *shipped* copy.)
163
+ `z-index: 120`, width `min(400px, 92vw)`, reduced-motion respected.
137
164
  `.chatmd-p` / `.chatmd-list` carry the reply rhythm. Escape and scrim click close the
138
165
  drawer; focus moves into the composer on open and is restored to the opener on close.
139
166
 
167
+ **Action-row sizing (fixed 2026-08-26).** "Book a meeting" rendered visibly larger than the
168
+ mockup's. Root cause: the mockup's chat actions are `<button>`s, which browsers draw at the
169
+ ~13.3px default button font because `.btn` never sets `font: inherit`; ours is an `<a>`
170
+ inheriting the panel's body size. Both are now pinned:
171
+ `.security-shell .chatactions .btn{font-size:13.5px;padding:0 16px;min-height:44px}`.
172
+
140
173
  ## Gotchas
141
174
 
142
175
  - **`api.togaiq.com` chat endpoints are origin/UA-gated — verified 2026-08-25 with
@@ -157,21 +190,38 @@ drawer; focus moves into the composer on open and is restored to the opener on c
157
190
  any merged/trimmed turns would break the strict alternating shape and return a 422.
158
191
  - **The chat is fully client-side**, which is why `/landing` still builds as a
159
192
  **prerendered static** route. Adding a server call here changes that.
160
- - **`/landing` is not rate-limited** (`src/proxy.ts` matches only `/` and the five API
161
- routes). The drawer talks to a third-party origin, not a BDR API route, so the
162
- limiter still does not apply — but the abuse surface now exists on the page.
193
+ - **`/landing` is not rate-limited** (`src/proxy.ts` matches `/` and the API routes, not
194
+ the page). The drawer talks to a third-party origin, not a BDR API route, so the limiter
195
+ still does not apply to *chat* — but the abuse surface exists on the page. The drawer's
196
+ new "Call me now" action does hit a BDR route, and that one **is** limited
197
+ (`/api/landing-call`, on the `/api/call-now` bucket).
163
198
 
164
199
  ## How to work on it
165
200
 
166
201
  - 30 new Vitest tests across `test/talosChat.test.ts` and `test/chatMarkdown.test.ts`
167
202
  (message-shape rules including the 30-cap alternation, 401 re-mint-once, fallback on
168
203
  network error and on 5xx, alternation preserved across a fallback turn, adapter
169
- reset, markdown safety). Suite **367/367 green**; `tsc`, `eslint` (incl. the
170
- no-em-dash rule), and `next build` all clean as of 2026-08-24.
171
- - **Nothing is committed** — the drawer lives in the `bdr` working tree only, awaiting
172
- the developer's branch/commit go-ahead.
204
+ reset, markdown safety). Suite **385/385 green** as of 2026-08-26 (was 367 before the
205
+ call-flow tests); `tsc`, `eslint` (incl. the no-em-dash rule), and `next build` all clean.
206
+ - **Shipped state:** the drawer landed on `_dev-sandbox` via **TRUE-80965**, merged by the
207
+ developer 2026-08-25. **TRUE-80966** ("Connect Talos UI to Backend Services") is
208
+ engineering-complete — chat is live from allowlisted origins — but **acceptance is
209
+ pending** on three items: the sandbox **origin allowlist**, rendering of **`cta` /
210
+ `cited_slides`**, and the **absent CRM lead sink**. The 2026-08-26 action-row changes are
211
+ uncommitted working tree.
173
212
 
174
213
  ## Change history
214
+ - 2026-08-26 — The drawer now renders **both** mockup actions: a new **"Call me now"**
215
+ button (via `ChatPanel`'s new `onCall` prop) above "Book a meeting" — **reversing** the
216
+ 2026-08-24 decision not to render it, which only held while the destination was the
217
+ parked studio call screen (see [landing-call-flow.md](landing-call-flow.md) for the new
218
+ screens). FIXED "Book a meeting" rendering too large (`<a>` inherits body size, sibling
219
+ `<button>`s use the ~13.3px browser default; both pinned in `.chatactions .btn`).
220
+ CORRECTED the styling note — the security mockup was re-added to the repo on 2026-08-26,
221
+ so `security.css` is only the *shipped* copy, not the *only* copy. Re-affirmed the
222
+ TRUE-80692 "keep the disposable bridge shell" decision and recorded the drafted (not yet
223
+ sent) note to apeterson, including the new design divergence — action buttons + cobalt
224
+ theme — that must be reconciled as blox props/theme vs. app-side. (tcox)
175
225
  - 2026-08-25 — Verified the origin gate with browser-shaped probes: `localhost:3000` is
176
226
  allowed and returns live replies end-to-end; `bdr.dev.sandbox.togatech.com` gets 403 at
177
227
  session mint (not allowlisted → deployed drawer runs on scripted fallback). Recorded the
@@ -6,7 +6,7 @@ project: AI-BDR
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-07-29
9
+ updated: 2026-08-26
10
10
  owners: [tcox]
11
11
  files:
12
12
  - bdr/src/server/vapiCall.ts
@@ -24,6 +24,7 @@ files:
24
24
  - bdr/src/content/default.ts
25
25
  - bdr/src/proxy.ts
26
26
  related:
27
+ - landing-call-flow.md
27
28
  - web-funnel-app.md
28
29
  - vapi-integration.md
29
30
  - call-orchestration.md
@@ -144,6 +145,13 @@ rules that are specific to this feature:
144
145
  looks fake in this environment" is an **env** symptom, not a code bug.
145
146
 
146
147
  ## Change history
148
+ - 2026-08-26 — **Second consumer:** `/landing`'s new `SecurityCalling` screen reuses this
149
+ pipeline unchanged (`useCallStatusViewModel` + `/api/call-status` + the same
150
+ purpose-scoped status token), fed by the new `POST /api/landing-call` for anonymous
151
+ visitors. Two behavior notes specific to it: it shows **no "End call" button on the live
152
+ path**, and an **unavailable** status **holds at dialing** rather than faking progress.
153
+ The route is env-gated and currently dormant — see
154
+ [landing-call-flow.md](landing-call-flow.md). No change to this feature's own code. (tcox)
147
155
  - 2026-07-29 — Created. BUILT the live call-status pipeline: `src/server/vapiCall.ts` reads
148
156
  `GET https://api.vapi.ai/call/{id}` with the worker's Vapi bearer credential and drives a real
149
157
  dialing / active / ended / not-answered lifecycle on the Success screen (replacing the ~5s
@@ -6,12 +6,15 @@ project: AI-BDR
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-08-25
9
+ updated: 2026-08-26
10
10
  owners: [tcox]
11
11
  files:
12
12
  - bdr/src/app/landing/page.tsx
13
13
  - bdr/src/app/security.css
14
14
  - bdr/src/flow/screens/SecurityLanding.tsx
15
+ - bdr/src/flow/screens/SecurityCallNow.tsx
16
+ - bdr/src/flow/screens/SecurityCalling.tsx
17
+ - bdr/src/lib/securityCallFlow.ts
15
18
  - bdr/src/content/security.ts
16
19
  - bdr/src/components/LogoSymbol.tsx
17
20
  - bdr/src/components/AssistantMark.tsx
@@ -24,6 +27,7 @@ files:
24
27
  - bdr/eslint.config.mjs
25
28
  related:
26
29
  - landing-chat-drawer.md
30
+ - landing-call-flow.md
27
31
  - campaign-chat-handoff.md
28
32
  - web-funnel-app.md
29
33
  - web-funnel-content-model.md
@@ -38,19 +42,21 @@ and the 7-screen Agent Studio funnel is parked (see `web-funnel-app.md`). Its jo
38
42
  conversion: *"Book your free risk assessment."*
39
43
 
40
44
  It is a **pixel-faithful port of a designer mockup**, not new design work. The source
41
- mockup lived at `BDR/AI BDR - Security/` (a React 18 UMD + in-browser Babel prototype —
42
- `app.jsx`, `screens.jsx`, `components.jsx`, `styles.css`, plus a
43
- `ground-truth-tokens-and-stack.md` token spec) but **was deleted from the repo on
44
- 2026-08-25 — the port is complete and the shipped code in `src/` is now the only
45
- reference.** The landing is that mockup's **`Capabilities` screen**; because
46
- `src/flow/screens/Capabilities.tsx` already exists as a studio screen, it was ported
47
- under the name **`SecurityLanding`**.
48
-
49
- **Status (updated 2026-08-24): one live backend, nothing else.** The page now carries a
50
- **Talos chat drawer wired to the real campaign-chat backend** — see
51
- [landing-chat-drawer.md](landing-chat-drawer.md), which owns that subject. Everything
52
- else is still UI only: no form, no lead capture, no Toga/HubSpot call, no analytics
53
- events of its own.
45
+ mockup lives at **`BDR/AI BDR - Security/`** — a React 18 UMD + in-browser Babel prototype
46
+ (`app.jsx`, `screens.jsx`, `components.jsx`, `styles.css`, plus a
47
+ `ground-truth-tokens-and-stack.md` token spec). **History, because an earlier capture got
48
+ it wrong:** the folder was deleted on 2026-08-25 (port believed complete) and then
49
+ **RE-ADDED by the developer on 2026-08-26** — it is **in the repo today** and is the
50
+ standing design reference for further porting. The landing is that mockup's
51
+ **`Capabilities` screen**; because `src/flow/screens/Capabilities.tsx` already exists as a
52
+ studio screen, it was ported under the name **`SecurityLanding`**.
53
+
54
+ **Status (updated 2026-08-26): two backends wired, one of them dormant.** The page carries
55
+ a **Talos chat drawer wired to the real campaign-chat backend** — see
56
+ [landing-chat-drawer.md](landing-chat-drawer.md) — and, as of 2026-08-26, a **"Call me
57
+ now" flow with a real (but env-gated, currently inert) callback route**, which is its own
58
+ subject in [landing-call-flow.md](landing-call-flow.md). That call flow is the page's
59
+ **first form and first lead capture**. Still absent: landing-specific analytics events.
54
60
 
55
61
  ## Page structure
56
62
 
@@ -61,6 +67,11 @@ events of its own.
61
67
  screen — **carries its own page shell**: the noise layer, the mote field, the topbar
62
68
  lockup, and the header CTA. There is no shared layout wrapper for it.
63
69
 
70
+ Since 2026-08-26 `SecurityLanding` is also a **tiny router**: a page-level `screen` state
71
+ (`landing | call | calling`) swaps the marketing page for the call screens via `SwapFade`.
72
+ The marketing sections below are the `landing` case; see
73
+ [landing-call-flow.md](landing-call-flow.md) for the other two.
74
+
64
75
  Section order inside the shell:
65
76
 
66
77
  1. **Hero** (`.lp-hero`) — eyebrow, headline whose closing clause is the TOGA gradient
@@ -127,9 +138,12 @@ does not define or defines differently.
127
138
  was verified byte-identical**, so the 7 existing studio screens do not shift. Any future
128
139
  change here must keep that property.
129
140
  - **`LogoSymbol.tsx`** / **`AssistantMark.tsx`** — new inline-SVG marks.
130
- - **`eslint.config.mjs`** — a `"AI BDR - Security/**"` `globalIgnores` entry existed
131
- while the mockup was in the repo; it was removed with the mockup on 2026-08-25 (the
132
- studio flow's `mockup/**` entry remains).
141
+ - **`eslint.config.mjs`** — the `"AI BDR - Security/**"` `globalIgnores` entry is
142
+ **present and required**. It was removed alongside the 2026-08-25 mockup deletion and
143
+ **restored on 2026-08-26** when the mockup came back; lint is clean with it. (The studio
144
+ flow's `mockup/**` entry is separate and has always been there.) If the mockup folder is
145
+ ever removed again, remove this entry with it — and if you see lint errors in
146
+ Babel-flavored `.jsx`, this entry is what is missing.
133
147
 
134
148
  ## CTA wiring (read this before assuming the page converts)
135
149
 
@@ -153,6 +167,13 @@ does not define or defines differently.
153
167
  the mockup's "Ask Me Anything") both **open the Talos chat drawer** as of 2026-08-24.
154
168
  Neither is inert any more. The drawer, its backend adapter, and its offline fallback
155
169
  are documented in [landing-chat-drawer.md](landing-chat-drawer.md).
170
+ - **"Call me now"** (new 2026-08-26, in the drawer's action row above "Book a meeting")
171
+ swaps the page to the call screens. The backend behind it is **real but dormant** —
172
+ `POST /api/landing-call` 503s until `TOGA_SECURITY_CAMPAIGN_UUID` is set, and no
173
+ cybersecurity Toga campaign exists yet, so the flow currently runs its **simulation**.
174
+ Full detail, including the *do-not-confuse-these* Talos-chat-vs-Toga-dialer campaign
175
+ distinction and the enable-later runbook, in
176
+ [landing-call-flow.md](landing-call-flow.md).
156
177
 
157
178
  ## Responsive / mobile pass (original work, not ported)
158
179
 
@@ -171,11 +192,16 @@ ported. All of it is scoped under `.security-shell`.
171
192
 
172
193
  ## How to work on it
173
194
 
174
- - `npx tsc --noEmit`, `npm run lint`, `npm run build` — all clean as of 2026-08-24;
175
- `/landing` builds as a **prerendered static** route. Local prod smoke test:
176
- `GET /` → `307` with `Location: /landing`, `GET /landing` → `200`.
177
- - **Nothing is committed** — as of capture the whole landing lives in the `bdr` working
178
- tree only. It is not shipped or deployed.
195
+ - `npx tsc --noEmit`, `npm run lint`, `npm run build`, `npx vitest run` — all clean as of
196
+ 2026-08-26 (385/385 tests); `/landing` still builds as a **prerendered static** route.
197
+ Local prod smoke test: `GET /` → `307` with `Location: /landing`, `GET /landing` → `200`.
198
+ - **Shipping state:** the landing + chat drawer landed on `_dev-sandbox` via **TRUE-80965**
199
+ ("integrate Talos UI into landing"), merged by the developer 2026-08-25 — one conflict in
200
+ `security.ts`, resolved to the TRUE-80965 side. **TRUE-80966** ("Connect Talos UI to
201
+ Backend Services") is engineering-complete (chat is live from allowlisted origins) but
202
+ **acceptance is pending**: the sandbox origin allowlist, rendering of `cta` /
203
+ `cited_slides`, and the absent CRM lead sink. The 2026-08-26 call-flow work is
204
+ **uncommitted working tree** — the developer merges and commits.
179
205
 
180
206
  ## Known gaps / pending work
181
207
 
@@ -186,7 +212,12 @@ ported. All of it is scoped under `.security-shell`.
186
212
  - **Chat drawer's origin allowlist is not done** — `api.togaiq.com` is origin/UA gated,
187
213
  so until localhost / sandbox / prod domains are allowlisted the drawer silently answers
188
214
  from its scripted fallback (see [landing-chat-drawer.md](landing-chat-drawer.md)).
189
- - No form, no lead capture, no landing-specific analytics.
215
+ - **No cybersecurity Toga campaign / Vapi assistant exists**, so the "Call me now" path
216
+ cannot place real calls. Owned outside the `bdr` repo; see
217
+ [landing-call-flow.md](landing-call-flow.md).
218
+ - No landing-specific analytics events.
219
+ - ~~No form, no lead capture~~ — **the call flow added both 2026-08-26** (email + phone →
220
+ `upsertLead`), though the lead write only fires once the campaign env var is set.
190
221
  - ~~Mobile pass pending~~ — **done 2026-08-24** (see *Responsive / mobile pass* above).
191
222
  - ~~Chat drawer not ported~~ — **done 2026-08-24**, and wired to the real backend rather
192
223
  than re-creating the mockup's scripted-only behavior.
@@ -201,36 +232,66 @@ ported. All of it is scoped under `.security-shell`.
201
232
  `Capabilities.tsx` is a *studio* screen — hence `SecurityLanding.tsx`. Likewise
202
233
  `src/flow/screens/Landing.tsx` is a **studio** screen and has nothing to do with the
203
234
  `/landing` route. Check which is meant before editing either.
204
- - **`.btn` does not reset anchor styling.** Every studio `.btn` is a `<button>`, so
205
- studio.css never needed `text-decoration:none`. The landing's primary CTA is an `<a>`,
206
- which arrives underlined — a scoped `.security-shell a.btn{text-decoration:none}` rule
207
- fixes it. Any future anchor-as-button elsewhere hits the same trap.
235
+ - **`.btn` does not reset anchor styling — in TWO ways.** Every studio `.btn` is a
236
+ `<button>`, so studio.css never needed to normalize anchors.
237
+ 1. *Underline.* An `<a class="btn">` arrives underlined; scoped
238
+ `.security-shell a.btn{text-decoration:none}` fixes it (and a matching
239
+ `.ended-cta a.btn` reset was needed for the call-recap buttons, 2026-08-26).
240
+ 2. *Font size (found 2026-08-26).* A real `<button>` renders at the browser's ~13.3px
241
+ default button font unless `font: inherit` is set, while an `<a>` inherits its
242
+ container's body size — so **an anchor-as-`.btn` sitting next to real buttons looks
243
+ visibly larger**. That was the root cause of the "Book a meeting" drawer button being
244
+ oversized; the fix pins the size for both:
245
+ `.security-shell .chatactions .btn{font-size:13.5px;padding:0 16px;min-height:44px}`.
246
+ Whenever you mix anchor and button actions in one row, pin `font-size`.
208
247
  - **`Particles` default behavior must stay byte-identical.** The new `palette`/`bold`/
209
248
  `radiate` props are additive on purpose; the 7 studio screens depend on the untouched
210
249
  default path.
211
- - **Nothing further can be ported from the security mockup — it was deleted from the
212
- repo (2026-08-25).** The chat drawer (see `landing-chat-drawer.md`) was the last piece
213
- ported before deletion. Historical note: that mockup's `styles.css` carried the same
214
- literal-`\n` corruption class as `mockup/styles.css` (a JS-string generation artifact,
215
- see `web-funnel-app.md`) — the standing gotcha still applies to the studio `mockup/`
216
- folder, which remains in the repo.
250
+ - **The security mockup IS in the repo — do not trust the 2026-08-25 "deleted" note.**
251
+ It was deleted on 2026-08-25 and **re-added on 2026-08-26**; `BDR/AI BDR - Security/` is
252
+ the live design reference again (the call screens were ported from it). Check the working
253
+ tree before assuming either way. Its `styles.css` carries the same literal-`\n`
254
+ corruption class as `mockup/styles.css` (a JS-string generation artifact, see
255
+ `web-funnel-app.md`) — treat ported CSS accordingly.
217
256
  - **No `<head>` resource surprises this time**, but the standing rule from the first port
218
257
  still applies: a font named in ported CSS is not a loaded font. The landing rides
219
258
  `layout.tsx`'s existing Plus Jakarta Sans.
220
- - **`/landing` is not rate-limited.** `src/proxy.ts`'s matcher covers only `/` and the
221
- five API routes, so the landing bypasses the limiter entirely. That is correct today
222
- (the page is static and reads no contact data), but it stops being correct the moment
223
- the page gains a form or reads a query param.
259
+ - **`/landing` itself is still not rate-limited** — `src/proxy.ts`'s matcher covers `/`
260
+ and the API routes, not the page. The page now **has a form** (the call flow), but the
261
+ abuse surface is the API route, and **`/api/landing-call` WAS added to the matcher and
262
+ shares the `/api/call-now` limiter bucket** (2026-08-26). The rule stands for anything
263
+ new: a page-level read of a query param or contact data would still need the matcher
264
+ widened.
265
+ - **`src/content/security.ts` is CRLF.** A `\n`-only regex will not match its line
266
+ endings — that silently defeated a scripted merge-conflict resolution during the
267
+ TRUE-80965 merge (resolved by hand to the TRUE-80965 side). Use `\r?\n` in any script
268
+ that rewrites this file.
224
269
 
225
270
  ## Change history
271
+ - 2026-08-26 — **CORRECTION to the 2026-08-25 entry below:** the developer **re-added**
272
+ `BDR/AI BDR - Security/` on 2026-08-26. The mockup is in the repo and is the standing
273
+ design reference; the `"AI BDR - Security/**"` `globalIgnores` entry in
274
+ `eslint.config.mjs` was **restored** with it (lint clean). The porting gotchas are live
275
+ again, not retired. (tcox)
276
+ - 2026-08-26 — BUILT the **"Call me now" flow** on the page (design-lead review) and the
277
+ real-but-dormant `POST /api/landing-call` behind it; `SecurityLanding` became a
278
+ `landing|call|calling` SwapFade router over the new `SecurityCallNow`/`SecurityCalling`
279
+ screens. This gives the page its **first form and first lead capture**. Own doc:
280
+ [landing-call-flow.md](landing-call-flow.md). FIXED the oversized drawer "Book a meeting"
281
+ button — root cause was `<a class="btn">` inheriting the panel body size while sibling
282
+ `<button>`s render at the ~13.3px browser default; both are now pinned. Recorded that
283
+ `/api/landing-call` joined the `proxy.ts` call-limiter, and that `security.ts` is CRLF
284
+ (which defeated a `\n`-only conflict-resolution regex during the TRUE-80965 merge to
285
+ `_dev-sandbox`). tsc/eslint/build clean, 385/385 tests, `/landing` still prerendered
286
+ static. (tcox)
226
287
  - 2026-08-25 — VERIFIED the primary-CTA booking link is dead: the mockup's Calendly URL and
227
288
  the whole `calendly.com/toga-technology` org 404. TOGA books via Cal.com team
228
289
  `toga-technology`, which has only the healthcare `discovery-call` event type (both prod
229
290
  campaigns use it); no security event type exists. Recorded the fix path (create Cal.com
230
291
  event → set `primaryCtaHref` + campaign `cta_url`). (tcox)
231
- - 2026-08-25 — The source mockup folder `BDR/AI BDR - Security/` was **deleted from the
232
- repo** (port complete; shipped code is the reference now). Its `eslint.config.mjs`
233
- `globalIgnores` entry was removed with it; the porting-related gotchas are retired.
292
+ - 2026-08-25 — **[SUPERSEDED 2026-08-26 — the folder was restored; see above.]** The
293
+ source mockup folder `BDR/AI BDR - Security/` was **deleted from the repo** (port
294
+ believed complete). Its `eslint.config.mjs` `globalIgnores` entry was removed with it.
234
295
  (tcox)
235
296
  - 2026-08-24 — Closed both of the doc's open gaps. BUILT the **mobile pass** as original
236
297
  work (560px and 420px breakpoints under `.security-shell`; the `.lp-value-motes` inset
@@ -6,7 +6,7 @@ project: AI-BDR
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-08-24
9
+ updated: 2026-08-26
10
10
  owners: [tcox]
11
11
  files:
12
12
  - bdr/PLAN.md
@@ -67,6 +67,7 @@ related:
67
67
  - bdr-web-funnel-plan.md
68
68
  - live-call-status.md
69
69
  - security-landing-page.md
70
+ - landing-call-flow.md
70
71
  - ../architecture.md
71
72
  - ../workflows/safe-call-loop-testing.md
72
73
  - ../../worker2/features/vapi-webhook-handler.md
@@ -458,6 +459,15 @@ everything?" question in this doc:
458
459
  even in info's own code — do not carry them into BDR.
459
460
  - **BDR deploy env vars = 7 total:** the 5 server-side ones + `NEXT_PUBLIC_GA_MEASUREMENT_ID` +
460
461
  (new 2026-07-21) **`TOGA_WORKER_URL`** (the fast-dial gate; unset ⇒ cron-only).
462
+ - **Optional 8th, deliberately UNSET (new 2026-08-26): `TOGA_SECURITY_CAMPAIGN_UUID`** —
463
+ declared in `.env.example`, the on/off gate for `/landing`'s real callback route. Unset ⇒
464
+ `POST /api/landing-call` returns a 503 *"Live callback is not configured"* before touching
465
+ Toga, and the landing call flow runs its simulation. **Leave it unset until a cybersecurity
466
+ Toga campaign + Vapi assistant exist** (none does), and never set it to a Talos chat
467
+ `campaign_id` — different system. See
468
+ [landing-call-flow.md](landing-call-flow.md). It needs no `amplify.yml` change: the
469
+ build-time `env | grep -E '^(HUBSPOT_|TOGA_|NEXT_PUBLIC_|RATE_LIMIT_)'` line already
470
+ matches `TOGA_*`.
461
471
 
462
472
  ## Local dev setup (BDR)
463
473
 
@@ -751,6 +761,12 @@ would be cached by browsers and crawlers and become painful to undo.
751
761
  harness; pull it with `npx toga-ai`.
752
762
 
753
763
  ## Change history
764
+ - 2026-08-26 — Env-var accounting only: recorded an **optional 8th deploy var**,
765
+ `TOGA_SECURITY_CAMPAIGN_UUID`, declared in `.env.example` and deliberately **left unset**.
766
+ It gates `/landing`'s new real callback route (`POST /api/landing-call`), which 503s and
767
+ falls back to simulation without it; no `amplify.yml` change is needed because the
768
+ build-time `env | grep` recipe already matches `TOGA_*`. The funnel itself is unchanged.
769
+ See [landing-call-flow.md](landing-call-flow.md). (tcox)
754
770
  - 2026-08-24 — **DECIDED: `/` 307-redirects to `/landing` and the whole Agent Studio flow is
755
771
  parked.** `next.config.ts` gained `redirects()` with `permanent: false` (deliberately
756
772
  temporary so browsers/crawlers do not cache it while design settles). The new
@@ -28,7 +28,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
28
28
  - **toga2-hub** (TOGa Hub) — 2 doc(s) → [2.0/apps/toga2-hub/INDEX.md](2.0/apps/toga2-hub/INDEX.md)
29
29
  - **talos** (TOGa IQ) — 7 doc(s) → [2.0/apps/talos/INDEX.md](2.0/apps/talos/INDEX.md)
30
30
  - **voice-to-voice** (TOGa Voice) — 4 doc(s) → [2.0/apps/voice-to-voice/INDEX.md](2.0/apps/voice-to-voice/INDEX.md)
31
- - **ai-bdr** (AI-BDR) — 12 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
31
+ - **ai-bdr** (AI-BDR) — 13 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
32
32
  - **toga2-commerce** (TOGa Commerce) — 19 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
33
33
  - **toga25-supply** (TOGa 2.5 Supply) — 12 doc(s) → [2.0/apps/toga25-supply/INDEX.md](2.0/apps/toga25-supply/INDEX.md)
34
34
  - **toga-blox** (TOGa Blox) — 10 doc(s) → [2.0/apps/toga-blox/INDEX.md](2.0/apps/toga-blox/INDEX.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.653",
3
+ "version": "1.0.654",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",