* docs: add video-embedded getting-started pages for API Trigger, Webhook, Telephony, Tools & Knowledge Base
Four new tutorial pages inserted after Your First Agent in 5 Minutes, each
pairing a walkthrough video with a step-by-step practical guide sourced
from the recorded demo: Trigger Calls Automatically (API Trigger), Send
Call Data Back Automatically (Webhook), Connect Your Phone Number
(Twilio telephony), and Give Your Agent Real Data (HTTP tools + KB).
* docs: address greptile review feedback on PR #535
* docs: link Twilio Verified Caller IDs page directly
* Restructure the documents
* docs: embed agent builder walkthrough video on first-agent page
* docs: match link text to renamed Connect with Telephony title
* docs: warn that default outbound telephony config is required for API Trigger
---------
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
* Add support for foreground debugging
* Add support for Cloudonix call transfers
* Improve the customer/agent conference experience with less annoying sounds.
* Update remote_up.sh
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* Resolve a small redundant code segment from cubic
* Resolve an issue with callbacks not providing the correct experience for failed
originated calls
* Yet a small fix
* Remove stale code
* Remove the beeps on transfer
* Remove unrelated remote_up.sh changes
* Update pipecat submodule to main
---------
Co-authored-by: Nir Simionovich <nirs@cloudonix.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
* fix(quota): fail closed when quota verification errors (#331)
Quota enforcement fell open on unexpected errors: the outer `except` in
`authorize_workflow_run_start` returned `has_quota=True`, so a degraded
database or a config-resolution bug let a billable run start unverified.
Billing and abuse protection are control-plane functions, so this is the
wrong default under exactly the degraded conditions that matter.
- Fail closed by default: the outer handler now returns
`has_quota=False` / `quota_check_failed`, reusing the existing message.
- Add `QUOTA_FAIL_MODE=closed|open` (default `closed`) so OSS self-hosters
can explicitly opt back into availability; the open path logs loudly.
- Narrow the try-scope so `get_user_by_id` / `get_workflow_run` DB read
failures surface as their specific `user_not_found` /
`workflow_run_not_found` codes instead of the generic handler.
- Tests cover the config-resolution and DB-read failure paths (denied,
not `has_quota=True`) and the `QUOTA_FAIL_MODE=open` escape hatch.
Fixes#331
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(quota): route DB read failures through the fail-mode policy gate
Review (greptile) flagged that the narrowed get_user_by_id / get_workflow_run
catches returned user_not_found / workflow_run_not_found before the outer
QUOTA_FAIL_MODE handler ran, so QUOTA_FAIL_MODE=open never applied to a DB
failure -- the exact "degraded database" case the escape hatch documents.
Revert the two narrowed catches so DB read exceptions fall through to the
single outer policy gate: closed -> quota_check_failed, open -> allow. The
None checks still return the specific not_found codes for genuinely missing
rows; an exception is a "cannot verify" condition, not a definitive absence.
Add a regression test asserting QUOTA_FAIL_MODE=open allows a run when a DB
read throws, and update the two DB-error tests to expect quota_check_failed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(quota): scope the fail-mode comment to credit-verification failures (#331)
Review (cubic) flagged the outer-handler comment as overclaiming: it said the
handler is the single gate for "all cannot-verify errors", but the earlier
workflow-load and org-membership catches always deny with workflow_not_found
regardless of QUOTA_FAIL_MODE. That distinction is intentional (those are
authorization/existence gates, not credit verification), so scope the comment
accordingly. Comment-only, no behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(quota): fail open only when MPS is unreachable
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
* feat(helm): add HPA for arq-worker + ui, ship a lean k3s prod example
## Problem
The chart's autoscaling story only covers the `web` tier — one
`web-hpa.yaml` template gated by `autoscaling.web.enabled`. Operators
scaling the `arq-worker` (background jobs) or `ui` (Next.js SSR) tiers
have to write their own HPA manifests out-of-band or fork the chart.
Turning the existing memory-utilization target on for freshly-installed
workloads also silently breaks: idle Python at the chart's default
`128Mi` (workers) / `256Mi` (ui) memory request already sits above
`80%`, so HPA scales every tier to `maxReplicas` on cold start with no
traffic. On a tight node this cascades into "insufficient CPU" and
blocks new-workload scheduling.
## Fix
**New HPA templates** — `templates/arq-worker-hpa.yaml` and
`templates/ui-hpa.yaml`, both mirroring the existing
`templates/web-hpa.yaml` shape (autoscaling/v2, resource metrics,
gated on `.Values.autoscaling.<tier>.enabled`).
**Extended `values.yaml`**:
- `autoscaling.workers` and `autoscaling.ui` blocks with sane defaults
(`enabled: true`, `minReplicas: 1`, `maxReplicas: 5`,
`targetCPUUtilizationPercentage: 70`).
- `targetMemoryUtilizationPercentage: null` on both tiers by default,
with an inline comment explaining why memory-utilization HPA is a
broken signal at the chart's default request sizes.
- Header comment reworked to (a) document the `metrics-server`
requirement, (b) note that HPA takes ownership of Deployment
`replicas` after first sync, (c) call out that CPU is a poor signal
for the web tier (long-lived WebSockets), and (d) note that CPU is
a fine signal for workers and ui.
**Example**: `examples/values-k3s-prod.yaml` — a single-node k3s
production override that exercises the new HPA blocks and demonstrates
the paired safety changes (memory targets nulled, sized resource
requests, migration job CPU sized for a tight node). Ship-ready
starting point for the operator flow: hosted-AI only (no local
models), all state on the node's local-path StorageClass, invite-only
signup, TLS terminated at a shared Cloudflare Origin cert.
## Behavior
Fresh install with defaults:
- Workers scale 1 → 5 on CPU 70% target only. No memory-based
scale-up storm on cold start.
- UI scales 1 → 5 on CPU 70% target only.
- Web autoscaling stays `enabled: false` by default (unchanged) —
operators opt in per the existing README warning.
Operators who want memory-based HPA back can:
1. Bump `workers.resources.requests.memory` (~256Mi) or
`ui.resources.requests.memory` (~384Mi).
2. Set `autoscaling.<tier>.targetMemoryUtilizationPercentage: 80`.
* address review: omit replicas when HPA on, suppress empty-metrics HPA, docs
Fixes raised on #516:
- **Worker/UI Replicas Reset On Upgrade** — arq-worker-deployment.yaml and
ui-deployment.yaml now wrap `replicas:` in `{{- if not .Values.autoscaling.<tier>.enabled }}`,
mirroring the existing web-deployment guard. With HPA on, Helm no longer
reapplies the static replicaCount on upgrade and briefly shrink an
HPA-scaled pool.
- **Empty Metrics Render Invalid HPA** — arq-worker-hpa.yaml and ui-hpa.yaml
now short-circuit the whole HPA object when both CPU and memory targets
are null. Previously the template emitted `spec.metrics:` with no items
(rejected by the k8s API server).
- **`enableSignup: false` removed from examples/values-k3s-prod.yaml** — that
knob depends on #514 which hasn't landed; unwiring it here avoids
suggesting a lockdown that isn't in effect until the sibling PR merges.
- **Header comment mismatch** — `# HPA: 1 → 5 on CPU 70% / memory 80%` claimed
memory was on while every tier had `targetMemoryUtilizationPercentage: null`.
Updated to "CPU 70% only (memory HPA opt-in)".
- **Wrong default in comment** — `values.yaml` said workers default is `128Mi`;
actual is `256Mi`. Fixed.
- **UI comment said "idle Python"** — UI is Next.js/Node.js. Corrected on the
UI HPA memory comment and the per-tier comments in values-k3s-prod.yaml
(web: FastAPI, workers: Python/ARQ, ui: Node.js).
All lints pass; verified with `helm template`:
- Defaults render both HPAs and Deployments without static `replicas:`.
- `--set autoscaling.workers.targetCPUUtilizationPercentage=null --set autoscaling.workers.targetMemoryUtilizationPercentage=null`
renders only the Deployment (HPA suppressed).
- `--set autoscaling.workers.enabled=false` renders the Deployment with
static `replicas:` restored.
* address review: align Deployment replicas gate with HPA render gate
Follow-up on #516: my earlier fix guarded `spec.replicas` on only
`autoscaling.<tier>.enabled`, but the HPA-empty-metrics guard I added
suppresses the HPA object when both metric targets are null while
`enabled: true`. That combination produced a Deployment with neither
a `spec.replicas` value nor an HPA owner — a k8s Deployment defaults
to `replicas: 1` in that case, but the chart no longer expresses intent.
Fix: the Deployment `replicas` gate now mirrors the HPA render gate
exactly. Rendered outcomes verified with `helm template`:
| autoscaling.<tier> | HPA rendered? | Deployment replicas? |
|-------------------------------|---------------|----------------------|
| enabled: true, target set | yes | omitted (HPA owns) |
| enabled: true, both null | no | static (kept) |
| enabled: false | no | static (kept) |
* fix(helm): default worker/ui autoscaling off; ui HPA floor of 2
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(helm): align web replicas/HPA gate with worker/ui pattern
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(helm): document worker/ui HPAs in README; polish k3s example
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: prabhat pankaj <prabhatiitbhu@gmail.com>
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat: add ElevenLabs realtime STT provider support (#512)
Wire ElevenLabs scribe_v2_realtime into the STT registry and pipeline factory so BYOK transcribers can use the same provider already supported for TTS.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: address ElevenLabs STT review feedback for language, commits, and host
Pass custom language codes through instead of defaulting to English, use ElevenLabs VAD commit strategy because Dograh VAD runs downstream of STT, and document hostname-only realtime base_url handling.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: preserve ElevenLabs STT endpoint port in realtime host parsing
Use urlparse netloc instead of hostname so validated BYOK/proxy base URLs keep non-default ports when Pipecat builds the websocket endpoint.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: preserve ElevenLabs STT proxy path prefix and remove duplicate tests
Include URL path segments in realtime host normalization for BYOK proxies and delete shadowed pytest definitions.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: allow custom ElevenLabs model input
* fix: normalize ElevenLabs websocket URLs
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
* feat(auth): gate OSS signup behind ENABLE_SIGNUP flag
## Problem
The `POST /api/v1/auth/signup` endpoint is unconditionally exposed on
every OSS install. Operators running an invite-only deployment (private
customer instances, staging environments, internal-only tenants) have
no way to disable public account creation without patching the codebase.
The UI also shows the "Sign up" link on `/auth/login` regardless of
whether signup is available, so a locked-down deployment leaves broken
navigation on the login page.
## Fix
Introduce a single `ENABLE_SIGNUP` env var (default `true` — no behavior
change for existing installs) that controls signup end-to-end:
- **Backend** — `api/constants.ENABLE_SIGNUP` is read at module load.
The signup handler returns 403 when it's false. Also exposed on
`GET /api/v1/health` as `signup_enabled: bool` so the UI can mirror
the operator's choice at runtime instead of at bundle-build time.
- **UI** — `getSignupEnabled()` in `lib/auth/config.ts` proxies the
health field, `/api/config/auth` surfaces it to the browser, the
login page conditionally renders the "Sign up" link via a one-shot
`fetch("/api/config/auth")` in `useEffect`, and the middleware
redirects `/auth/signup` → `/auth/login` when disabled (fires before
Next.js can serve the statically-prerendered signup page).
- **Helm** — `config.enableSignup` (default `true`) is rendered into
the ConfigMap as `ENABLE_SIGNUP` so operators can flip it via
`--set config.enableSignup=false` at install/upgrade time.
Fallbacks default to `signupEnabled: true` in every layer so a fresh
install "just works" and matches the backend default.
* address review: rollout on ConfigMap change, cache TTL, no signup-link flash
Four review points on #514:
**P1 — ConfigMap Change Skips Rollout** (`configmap.yaml`). `helm upgrade
--set config.enableSignup=false` updated the ConfigMap but did NOT roll
the api pods, so running processes kept the ENABLE_SIGNUP env from
startup and continued serving the old signup behavior — including
divergence between replicas mid-upgrade.
Fix: add the standard `checksum/config` pod-template annotation on the
four backend Deployments that `envFrom` the ConfigMap (`web`,
`arq-worker`, `ari-manager`, `campaign-orchestrator`). Verified with
`helm template`: all four Deployments share the same checksum on any
given render, and flipping `config.enableSignup` changes the checksum
uniformly so kubectl sees a pod-template diff and rolls all four.
**P1 — Signup Flag Stays Cached (server)** (`ui/src/lib/auth/config.ts`).
Module-scoped cache had no TTL. `revalidate: 300` was passed on the
underlying `fetch()` but the in-memory short-circuit above ran first, so
the value never refreshed until the UI pod restarted.
Fix: add `AUTH_CONFIG_TTL_MS = 5 * 60 * 1000` (matching the fetch
revalidate hint) so the module cache and the Next fetch cache stay in
sync. Backend flag flips propagate within 5 minutes without a pod
restart.
**P1 — Middleware Redirect Uses Stale State** (`ui/src/middleware.ts`).
Same shape as above — a separate module cache with no expiry could keep
redirecting `/auth/signup → /auth/login` after signup was re-enabled, or
keep serving the statically-prerendered signup page after lockdown.
Fix: same `SERVER_CONFIG_TTL_MS = 5 * 60 * 1000` TTL on the middleware
cache.
**P2 — Signup link flash on login page** (`ui/src/app/auth/login/page.tsx`).
Initial `signupEnabled` state was `null`, so `{signupEnabled && ...}`
hid the link on first paint and it popped in after the fetch resolved
— a CLS on every login-page load on stock installs where signup is
enabled.
Fix: initialise the state to `true` (matches the backend default). The
fetch still overrides to `false` when the operator has actually
disabled signup, so the lockdown UI behavior is unchanged; only the
happy-path flash is gone.
* simplify signup flag: drop TTL caches and middleware redirect
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* resolve signup flag server-side to avoid signup link flicker
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: prabhat pankaj <prabhatiitbhu@gmail.com>
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Expose MiniMax-M3 in the MiniMax model suggestions now that the provider integration supports MiniMax chat models.
Co-authored-by: octo-patch <266937838+octo-patch@users.noreply.github.com>
* fix(auth): allow invited org members to start workflow runs
Users invited to an org could not start workflows belonging to that org
because the authorization check compared actor.selected_organization_id
directly against workflow.organization_id. An invited user's selected
org correctly reflects the invited org, but if the Stack Auth token
resolves to a different org id than expected the strict equality fails.
Per api/AGENTS.md: "Whenever you read or write an organization-scoped
field, you must filter or validate by organization_id." The correct
policy is org membership, not selected-org identity.
- Add is_user_member_of_organization() to OrganizationClient; queries
the organization_users association table directly (no lazy-load risk).
- Replace the identity check in authorize_workflow_run_start() with a
membership lookup. Deny when actor_user.id is not in the org's member
set; error_code stays workflow_not_found to avoid leaking existence.
- Update test: rename rejects_actor_from_another_org to
rejects_actor_not_a_member (reflects actual policy), add positive test
allows_invited_member that seeds membership and asserts has_quota=True.
Closes#491
* fix(auth): skip membership check for personal workflows (organization_id=None)
When workflow.organization_id is None (personal or legacy workflow with no
org), the membership lookup was still called, producing a SQL IS NULL
comparison that matched nothing and denied the run.
Guard the check so it only runs when the workflow is org-scoped.
Adds a regression test confirming that an actor with a known id can start a
personal workflow without triggering is_user_member_of_organization.
* fix(auth): fail closed on workflow membership lookup errors
---------
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
* feat(tts): add xAI as a Voice (TTS) provider
pipecat already ships an xAI TTS service (XAITTSService, WebSocket
streaming) but dograh never wired it into the service configuration, so
xAI could not be selected as a Voice provider in the cascading pipeline.
Wire it through:
- registry: ServiceProviders.XAI + XAITTSConfiguration (voices
eve/ara/leo/rex/sal, language, computed model) registered in TTSConfig
- service_factory: build XAITTSService in create_tts_service
- check_validity: api-key validation hook
- tests for the factory + docs
The Voice provider dropdown is schema-driven, so xAI appears with no UI
changes.
* fix(tts): validate xAI API key and drop misleading auto-language hint
Addresses review feedback on the xAI Voice provider:
- check_validity: replace the no-op xAI key check with real validation
against xAI's OpenAI-compatible API (models.list on https://api.x.ai/v1),
so a bad BYOK key is caught at configuration time instead of at call time.
- registry: remove the "auto" language hint from the field description.
pipecat's Language enum has no "auto" member, so the factory fell back to
English silently; the description no longer advertises detection we don't do.
- tests: cover xAI key validation (registered, accepts valid, rejects bad).
* fix(tts): validate xAI key against the TTS voices endpoint
xAI supports endpoint-scoped API keys, so a key scoped to Text-to-Speech
may lack the /v1/models ACL and would be wrongly rejected by the previous
models.list() check. Validate against GET /v1/tts/voices instead — the
scope the key actually needs for TTS — treating 401/403 as an invalid key
and connection errors as a clean, actionable message.
* fix: harden xAI TTS integration
---------
Co-authored-by: Sabiha Khan <sabihak89@gmail.com>
Co-authored-by: Sabiha Khan <87858386+chewwbaka@users.noreply.github.com>
Two call sites dropped the MPS billing-v2 protocol, so orgs on model
config v2 got 400 "Service Key uses billing v2" from MPS:
- text_chat_runner built PipecatEngine without embeddings_provider
(extracted but never passed), so knowledge-base retrieval fell through
to the plain OpenAI-compatible client instead of DograhEmbeddingService
and sent no correlation_id/mps_billing_version metadata. Also pass
endpoint/api_version for Azure BYOK parity with run_pipeline.
- node_summary built its QA LLM without the run's correlation id, unlike
its sibling call in qa/analysis.py.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix: gate OSS email/password auth endpoints outside local auth mode
The /auth signup/login/me routes were mounted unconditionally, so the
SaaS deployment accepted unauthenticated signups that created oss_*
provider-id users (and auto-provisioned MPS service keys) bypassing
Stack Auth entirely.
Gate them with a router-level dependency that 404s when AUTH_PROVIDER
is not "local", rather than conditionally mounting the router, so the
OpenAPI spec and the clients generated from it stay identical across
deployment modes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: keep current user route available in stack auth
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>