feat: add posthog events (#231)

* feat: add posthog events

* fix: workflow_duplicated event

* chore: add events to enum
This commit is contained in:
Sabiha Khan 2026-04-10 17:52:21 +05:30 committed by GitHub
parent bb5f56bfb7
commit 3f19a16e7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 450 additions and 93 deletions

View file

@ -51,6 +51,10 @@ S3_REGION = os.environ.get("S3_REGION", "us-east-1")
# Sentry configuration
SENTRY_DSN = os.getenv("SENTRY_DSN")
# PostHog configuration
POSTHOG_API_KEY = os.getenv("POSTHOG_API_KEY")
POSTHOG_HOST = os.getenv("POSTHOG_HOST", "https://us.i.posthog.com")
ENABLE_ARI_STASIS = os.getenv("ENABLE_ARI_STASIS", "false").lower() == "true"
SERIALIZE_LOG_OUTPUT = os.getenv("SERIALIZE_LOG_OUTPUT", "false").lower() == "true"

View file

@ -140,3 +140,18 @@ class ToolStatus(Enum):
ACTIVE = "active" # Tool is available for use
ARCHIVED = "archived" # Tool is soft-deleted
DRAFT = "draft" # Tool is being configured (not ready for use)
class PostHogEvent(str, Enum):
"""PostHog event names — backend events only."""
WORKFLOW_CREATED = "workflow_created"
WORKFLOW_PUBLISHED = "workflow_published"
WORKFLOW_DUPLICATED = "workflow_duplicated"
CALL_STARTED = "call_started"
CALL_COMPLETED = "call_completed"
CALL_FAILED = "call_failed"
TELEPHONY_CONFIGURED = "telephony_configured"
KNOWLEDGE_BASE_CREATED = "knowledge_base_created"
TOOL_CREATED = "tool_created"
AGENT_EMBEDDED = "agent_embedded"

View file

@ -17,3 +17,4 @@ docling[rapidocr]==2.68.0
pgvector==0.4.2
bcrypt==5.0.0
email-validator==2.3.0
posthog==3.24.0

View file

@ -60,6 +60,7 @@ async def signup(request: SignupRequest):
email=user.email,
name=request.name,
organization_id=organization.id,
provider_id=user.provider_id,
),
)
@ -84,6 +85,7 @@ async def login(request: LoginRequest):
id=user.id,
email=user.email,
organization_id=user.selected_organization_id,
provider_id=user.provider_id,
),
)
@ -94,4 +96,5 @@ async def get_current_user(user: UserModel = Depends(get_user)):
id=user.id,
email=user.email,
organization_id=user.selected_organization_id,
provider_id=user.provider_id,
)

View file

@ -7,6 +7,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query
from loguru import logger
from api.db import db_client
from api.enums import PostHogEvent
from api.schemas.knowledge_base import (
ChunkSearchRequestSchema,
ChunkSearchResponseSchema,
@ -17,6 +18,7 @@ from api.schemas.knowledge_base import (
ProcessDocumentRequestSchema,
)
from api.services.auth.depends import get_user
from api.services.posthog_client import capture_event
from api.services.storage import storage_fs
from api.tasks.arq import enqueue_job
from api.tasks.function_names import FunctionNames
@ -142,6 +144,18 @@ async def process_document(
f"with OpenAI embeddings, org {user.selected_organization_id}"
)
capture_event(
distinct_id=str(user.provider_id),
event=PostHogEvent.KNOWLEDGE_BASE_CREATED,
properties={
"document_id": document.id,
"document_uuid": str(request.document_uuid),
"filename": filename,
"retrieval_mode": request.retrieval_mode,
"organization_id": user.selected_organization_id,
},
)
return DocumentResponseSchema(
id=document.id,
document_uuid=request.document_uuid,

View file

@ -6,7 +6,7 @@ from pydantic import BaseModel
from api.constants import DEFAULT_CAMPAIGN_RETRY_CONFIG, DEFAULT_ORG_CONCURRENCY_LIMIT
from api.db import db_client
from api.db.models import UserModel
from api.enums import OrganizationConfigurationKey
from api.enums import OrganizationConfigurationKey, PostHogEvent
from api.schemas.telephony_config import (
ARIConfigurationRequest,
ARIConfigurationResponse,
@ -24,6 +24,7 @@ from api.schemas.telephony_config import (
)
from api.services.auth.depends import get_user
from api.services.configuration.masking import is_mask_of, mask_key
from api.services.posthog_client import capture_event
from api.services.worker_sync.manager import get_worker_sync_manager
from api.services.worker_sync.protocol import WorkerSyncEventType
@ -257,6 +258,16 @@ async def save_telephony_configuration(
config_value,
)
capture_event(
distinct_id=str(user.provider_id),
event=PostHogEvent.TELEPHONY_CONFIGURED,
properties={
"provider": request.provider,
"phone_number_count": len(request.from_numbers),
"organization_id": user.selected_organization_id,
},
)
return {"message": "Telephony configuration saved successfully"}

View file

@ -9,8 +9,9 @@ from pydantic import BaseModel, Field, field_validator
from api.db import db_client
from api.db.models import UserModel
from api.enums import ToolCategory, ToolStatus
from api.enums import PostHogEvent, ToolCategory, ToolStatus
from api.services.auth.depends import get_user
from api.services.posthog_client import capture_event
router = APIRouter(prefix="/tools")
@ -327,6 +328,16 @@ async def create_tool(
icon_color=request.icon_color,
)
capture_event(
distinct_id=str(user.provider_id),
event=PostHogEvent.TOOL_CREATED,
properties={
"tool_name": request.name,
"tool_category": request.category,
"organization_id": user.selected_organization_id,
},
)
return build_tool_response(tool)

View file

@ -281,7 +281,12 @@ class SignalingManager:
# Start pipeline in background
asyncio.create_task(
run_pipeline_smallwebrtc(
pc, workflow_id, workflow_run_id, user.id, call_context_vars
pc,
workflow_id,
workflow_run_id,
user.id,
call_context_vars,
user_provider_id=str(user.provider_id),
)
)

View file

@ -13,7 +13,7 @@ from api.constants import DEPLOYMENT_MODE
from api.db import db_client
from api.db.models import UserModel
from api.db.workflow_template_client import WorkflowTemplateClient
from api.enums import CallType, StorageBackend
from api.enums import CallType, PostHogEvent, StorageBackend
from api.schemas.workflow import WorkflowRunResponseSchema
from api.services.auth.depends import get_user
from api.services.configuration.check_validity import UserConfigurationValidator
@ -23,6 +23,7 @@ from api.services.configuration.masking import (
)
from api.services.configuration.resolve import resolve_effective_config
from api.services.mps_service_key_client import mps_service_key_client
from api.services.posthog_client import capture_event
from api.services.storage import storage_fs
from api.services.workflow.dto import ReactFlowDTO
from api.services.workflow.duplicate import duplicate_workflow
@ -287,6 +288,17 @@ async def create_workflow(
user.selected_organization_id,
)
capture_event(
distinct_id=str(user.provider_id),
event=PostHogEvent.WORKFLOW_CREATED,
properties={
"workflow_id": workflow.id,
"workflow_name": workflow.name,
"source": "direct",
"organization_id": user.selected_organization_id,
},
)
# Sync agent triggers if workflow definition contains any
if request.workflow_definition:
trigger_paths = extract_trigger_paths(request.workflow_definition)
@ -365,6 +377,19 @@ async def create_workflow_from_template(
organization_id=user.selected_organization_id,
)
capture_event(
distinct_id=str(user.provider_id),
event=PostHogEvent.WORKFLOW_CREATED,
properties={
"workflow_id": workflow.id,
"workflow_name": workflow.name,
"source": "template",
"call_type": request.call_type,
"use_case": request.use_case,
"organization_id": user.selected_organization_id,
},
)
# Sync agent triggers if workflow definition contains any
if workflow_def:
trigger_paths = extract_trigger_paths(workflow_def)
@ -569,6 +594,16 @@ async def publish_workflow(
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
capture_event(
distinct_id=str(user.provider_id),
event=PostHogEvent.WORKFLOW_PUBLISHED,
properties={
"workflow_id": workflow_id,
"version_number": published.version_number,
"organization_id": user.selected_organization_id,
},
)
return {
"id": published.id,
"version_number": published.version_number,
@ -787,6 +822,18 @@ async def duplicate_workflow_endpoint(
organization_id=user.selected_organization_id,
user_id=user.id,
)
capture_event(
distinct_id=str(user.provider_id),
event=PostHogEvent.WORKFLOW_DUPLICATED,
properties={
"workflow_id": workflow.id,
"workflow_name": workflow.name,
"source_workflow_id": workflow_id,
"organization_id": user.selected_organization_id,
},
)
return {
"id": workflow.id,
"name": workflow.name,

View file

@ -9,7 +9,9 @@ from pydantic import BaseModel
from api.constants import BACKEND_API_ENDPOINT, ENVIRONMENT, UI_APP_URL
from api.db import db_client
from api.db.models import EmbedTokenModel, UserModel
from api.enums import PostHogEvent
from api.services.auth.depends import get_user
from api.services.posthog_client import capture_event
router = APIRouter(prefix="/workflow")
@ -103,6 +105,17 @@ async def create_or_update_embed_token(
expires_at=expires_at,
)
capture_event(
distinct_id=str(user.provider_id),
event=PostHogEvent.AGENT_EMBEDDED,
properties={
"workflow_id": workflow_id,
"is_new_token": len(existing_tokens) == 0,
"has_domain_restriction": bool(embed_request.allowed_domains),
"organization_id": user.selected_organization_id,
},
)
# Generate embed script
embed_script = generate_embed_script(token)

View file

@ -24,6 +24,7 @@ class UserResponse(BaseModel):
email: str | None
name: str | None = None
organization_id: int | None = None
provider_id: str | None = None
class AuthResponse(BaseModel):

View file

@ -3,7 +3,7 @@ import asyncio
from loguru import logger
from api.db import db_client
from api.enums import WorkflowRunState
from api.enums import PostHogEvent, WorkflowRunState
from api.services.campaign.circuit_breaker import circuit_breaker
from api.services.pipecat.audio_config import AudioConfig
from api.services.pipecat.in_memory_buffers import (
@ -12,6 +12,7 @@ from api.services.pipecat.in_memory_buffers import (
)
from api.services.pipecat.pipeline_metrics_aggregator import PipelineMetricsAggregator
from api.services.pipecat.tracing_config import get_trace_url
from api.services.posthog_client import capture_event
from api.services.workflow.pipecat_engine import PipecatEngine
from api.tasks.arq import enqueue_job
from api.tasks.function_names import FunctionNames
@ -22,6 +23,37 @@ from pipecat.processors.audio.audio_buffer_processor import AudioBufferProcessor
from pipecat.utils.enums import EndTaskReason
async def _capture_call_event(
workflow_run_id: int,
user_provider_id: str | None,
event: str,
extra_properties: dict | None = None,
) -> None:
"""Look up workflow_run for call metadata and fire a PostHog event.
Meant to be run via asyncio.create_task() so it never blocks the pipeline."""
try:
workflow_run = await db_client.get_workflow_run_by_id(workflow_run_id)
properties = {
"workflow_run_id": workflow_run_id,
"workflow_id": workflow_run.workflow_id if workflow_run else None,
"call_type": workflow_run.mode if workflow_run else None,
"call_direction": (workflow_run.initial_context or {}).get(
"direction", "outbound"
)
if workflow_run
else None,
}
if extra_properties:
properties.update(extra_properties)
capture_event(
distinct_id=user_provider_id,
event=event,
properties=properties,
)
except Exception:
logger.exception(f"Background PostHog capture failed for '{event}'")
def register_event_handlers(
task: PipelineTask,
transport,
@ -32,6 +64,7 @@ def register_event_handlers(
pipeline_metrics_aggregator: PipelineMetricsAggregator,
audio_config=AudioConfig,
pre_call_fetch_task: asyncio.Task | None = None,
user_provider_id: str | None = None,
):
"""Register all event handlers for transport and task events.
@ -75,6 +108,12 @@ def register_event_handlers(
):
ready_state["initial_response_triggered"] = True
asyncio.create_task(
_capture_call_event(
workflow_run_id, user_provider_id, PostHogEvent.CALL_STARTED
)
)
# Wait for pre-call fetch if in progress, playing ringer meanwhile
if pre_call_fetch_task is not None:
if not pre_call_fetch_task.done():
@ -161,6 +200,14 @@ def register_event_handlers(
await circuit_breaker.record_and_evaluate(
campaign_id=workflow_run.campaign_id, is_failure=True
)
asyncio.create_task(
_capture_call_event(
workflow_run_id,
user_provider_id,
PostHogEvent.CALL_FAILED,
extra_properties={"error_reason": "pipeline_error"},
)
)
except Exception as e:
logger.error(f"Error recording circuit breaker failure: {e}", exc_info=True)
@ -269,6 +316,12 @@ def register_event_handlers(
state=WorkflowRunState.COMPLETED.value,
)
asyncio.create_task(
_capture_call_event(
workflow_run_id, user_provider_id, PostHogEvent.CALL_COMPLETED
)
)
# Save real-time feedback logs to workflow run
if not in_memory_logs_buffer.is_empty:
try:

View file

@ -483,6 +483,7 @@ async def run_pipeline_smallwebrtc(
workflow_run_id: int,
user_id: int,
call_context_vars: dict = {},
user_provider_id: str | None = None,
) -> None:
"""Run pipeline for WebRTC connections"""
logger.debug(
@ -524,6 +525,7 @@ async def run_pipeline_smallwebrtc(
user_id,
call_context_vars=call_context_vars,
audio_config=audio_config,
user_provider_id=user_provider_id,
)
@ -534,6 +536,7 @@ async def _run_pipeline(
user_id: int,
call_context_vars: dict = {},
audio_config: AudioConfig = None,
user_provider_id: str | None = None,
) -> None:
"""
Run the pipeline with the given transport and configuration
@ -962,7 +965,10 @@ async def _run_pipeline(
in_memory_logs_buffer, user_context_aggregator, assistant_context_aggregator
)
# Register event handlers
# Register event handlers — resolve provider_id for PostHog tracking
if not user_provider_id:
user_obj = await db_client.get_user_by_id(user_id)
user_provider_id = str(user_obj.provider_id) if user_obj else None
in_memory_audio_buffer = register_event_handlers(
task,
transport,
@ -973,6 +979,7 @@ async def _run_pipeline(
pipeline_metrics_aggregator=pipeline_metrics_aggregator,
audio_config=audio_config,
pre_call_fetch_task=pre_call_fetch_task,
user_provider_id=user_provider_id,
)
register_audio_data_handler(audio_buffer, workflow_run_id, in_memory_audio_buffer)

View file

@ -0,0 +1,31 @@
from loguru import logger
from posthog import Posthog
from api.constants import POSTHOG_API_KEY, POSTHOG_HOST
_posthog_client: Posthog | None = None
def get_posthog() -> Posthog | None:
"""Return the lazily-initialised PostHog client, or None if not configured."""
global _posthog_client
if _posthog_client is None and POSTHOG_API_KEY:
_posthog_client = Posthog(POSTHOG_API_KEY, host=POSTHOG_HOST)
return _posthog_client
def capture_event(
distinct_id: str,
event: str,
properties: dict | None = None,
) -> None:
"""Fire a PostHog event. Silently no-ops if PostHog is not configured."""
client = get_posthog()
if not client:
return
try:
client.capture(
distinct_id=distinct_id, event=event, properties=properties or {}
)
except Exception:
logger.exception(f"Failed to send PostHog event '{event}'")

175
ui/package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "ui",
"version": "1.20.0",
"version": "1.21.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ui",
"version": "1.20.0",
"version": "1.21.0",
"dependencies": {
"@dagrejs/dagre": "^1.1.4",
"@nangohq/frontend": "^0.69.47",
@ -717,7 +717,6 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@ -919,6 +918,7 @@
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
"license": "MIT",
"peer": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@ -1033,6 +1033,7 @@
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
"integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3",
@ -1051,13 +1052,15 @@
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@emotion/babel-plugin/node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
"license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -1067,6 +1070,7 @@
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
"integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@emotion/memoize": "^0.9.0",
"@emotion/sheet": "^1.4.0",
@ -1079,19 +1083,22 @@
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@emotion/memoize": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@emotion/react": {
"version": "11.14.0",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.13.5",
@ -1116,6 +1123,7 @@
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
"integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@emotion/hash": "^0.9.2",
"@emotion/memoize": "^0.9.0",
@ -1128,19 +1136,22 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
"integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@emotion/unitless": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
"integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"react": ">=16.8.0"
}
@ -1149,13 +1160,15 @@
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
"integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@emotion/weak-memoize": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.7",
@ -1798,7 +1811,6 @@
"integrity": "sha512-lk5C+WKl5yqEmliQihEyhX/jNcWlAykTSEqkDeKa9xSq5YDAzOFvx7oos8YTqiIzdc4TemtlEaB8Rns7+8A0qg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@hey-api/codegen-core": "0.7.4",
"@hey-api/json-schema-ref-parser": "1.3.1",
@ -2444,6 +2456,7 @@
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
@ -2702,7 +2715,6 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=8.0.0"
}
@ -2753,7 +2765,6 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz",
"integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@opentelemetry/api-logs": "0.57.2",
"@types/shimmer": "^1.2.0",
@ -3425,7 +3436,6 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz",
"integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=14"
}
@ -8133,7 +8143,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -10017,7 +10026,6 @@
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.9.0.tgz",
"integrity": "sha512-ggs5k+/0FUJcIgNY08aZTqpBTtbExkJMYMLSMwyucrhtWexVOEY1KJmhBsxf+E/Q15f5rbwBpj+t0t2AW2oCsQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12.16"
}
@ -10429,6 +10437,7 @@
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
@ -10439,6 +10448,7 @@
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
@ -10491,7 +10501,8 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/pg": {
"version": "8.6.1",
@ -10518,7 +10529,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz",
"integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==",
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@ -10529,7 +10539,6 @@
"integrity": "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==",
"devOptional": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^19.0.0"
}
@ -10539,6 +10548,7 @@
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
"integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "*"
}
@ -11061,6 +11071,7 @@
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
"integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/helper-numbers": "1.13.2",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2"
@ -11070,25 +11081,29 @@
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
"integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@webassemblyjs/helper-api-error": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
"integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
"integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
"integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
"@webassemblyjs/helper-api-error": "1.13.2",
@ -11099,13 +11114,15 @@
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
"integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
"integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
@ -11118,6 +11135,7 @@
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
"integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
}
@ -11127,6 +11145,7 @@
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
"integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@xtuc/long": "4.2.2"
}
@ -11135,13 +11154,15 @@
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
"integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
"integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
@ -11158,6 +11179,7 @@
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
"integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
@ -11171,6 +11193,7 @@
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
"integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
@ -11183,6 +11206,7 @@
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
"integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-api-error": "1.13.2",
@ -11197,6 +11221,7 @@
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
"integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@xtuc/long": "4.2.2"
@ -11212,13 +11237,15 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
"license": "BSD-3-Clause"
"license": "BSD-3-Clause",
"peer": true
},
"node_modules/@xtuc/long": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"license": "Apache-2.0"
"license": "Apache-2.0",
"peer": true
},
"node_modules/@xyflow/react": {
"version": "12.9.2",
@ -11285,7 +11312,6 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -11307,6 +11333,7 @@
"resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz",
"integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.13.0"
},
@ -11358,6 +11385,7 @@
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"license": "MIT",
"peer": true,
"dependencies": {
"ajv": "^8.0.0"
},
@ -11375,6 +11403,7 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -11390,7 +11419,8 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/ansi-colors": {
"version": "4.1.3",
@ -11719,6 +11749,7 @@
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.12.5",
"cosmiconfig": "^7.0.0",
@ -11846,7 +11877,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.10.12",
"caniuse-lite": "^1.0.30001782",
@ -12050,6 +12080,7 @@
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
"integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.0"
}
@ -12320,6 +12351,7 @@
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
@ -12491,7 +12523,6 @@
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=12"
}
@ -12843,6 +12874,7 @@
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
@ -12929,6 +12961,7 @@
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"license": "MIT",
"peer": true,
"dependencies": {
"is-arrayish": "^0.2.1"
}
@ -12937,7 +12970,8 @@
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/es-abstract": {
"version": "1.23.9",
@ -13055,7 +13089,8 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
"integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
@ -13205,7 +13240,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@ -13379,7 +13413,6 @@
"integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.8",
@ -13667,6 +13700,7 @@
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.8.x"
}
@ -13772,7 +13806,8 @@
"url": "https://opencollective.com/fastify"
}
],
"license": "BSD-3-Clause"
"license": "BSD-3-Clause",
"peer": true
},
"node_modules/fast-xml-builder": {
"version": "1.1.4",
@ -13854,7 +13889,8 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/find-up": {
"version": "5.0.0",
@ -14140,7 +14176,8 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
"license": "BSD-2-Clause"
"license": "BSD-2-Clause",
"peer": true
},
"node_modules/globals": {
"version": "14.0.0",
@ -14351,7 +14388,6 @@
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
"license": "MIT",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@ -14961,6 +14997,7 @@
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@ -14975,6 +15012,7 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@ -15064,7 +15102,8 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/json-schema": {
"version": "0.4.0",
@ -15402,13 +15441,15 @@
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/loader-runner": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz",
"integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.11.5"
},
@ -15488,13 +15529,15 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/merge2": {
"version": "1.4.1",
@ -15631,14 +15674,14 @@
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/next": {
"version": "15.5.14",
"resolved": "https://registry.npmjs.org/next/-/next-15.5.14.tgz",
"integrity": "sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@next/env": "15.5.14",
"@swc/helpers": "0.5.15",
@ -16066,6 +16109,7 @@
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
@ -16140,6 +16184,7 @@
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=8"
}
@ -16573,7 +16618,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -16604,7 +16648,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.26.0"
},
@ -16631,7 +16674,6 @@
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.72.1.tgz",
"integrity": "sha512-RhwBoy2ygeVZje+C+bwJ8g0NjTdBmDlJvAUHTxRjTmSUKPYsKfMphkS2sgEMotsY03bP358yEYlnUeZy//D9Ig==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18.0.0"
},
@ -16656,15 +16698,13 @@
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/react-redux": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0"
@ -16804,6 +16844,7 @@
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"license": "BSD-3-Clause",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
@ -16869,8 +16910,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/redux-thunk": {
"version": "3.1.0",
@ -16908,7 +16948,8 @@
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/regexp.prototype.flags": {
"version": "1.5.4",
@ -16945,6 +16986,7 @@
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -17029,7 +17071,6 @@
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz",
"integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@ -17201,6 +17242,7 @@
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
"integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/json-schema": "^7.0.9",
"ajv": "^8.9.0",
@ -17237,6 +17279,7 @@
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3"
},
@ -17248,7 +17291,8 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/secure-json-parse": {
"version": "4.0.0",
@ -17804,7 +17848,8 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/supports-color": {
"version": "7.2.0",
@ -17844,8 +17889,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
"integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/tailwindcss-animate": {
"version": "1.0.7",
@ -17874,6 +17918,7 @@
"resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz",
"integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==",
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0",
@ -17892,6 +17937,7 @@
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz",
"integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.25",
"jest-worker": "^27.4.5",
@ -17924,7 +17970,8 @@
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/thread-stream": {
"version": "3.1.0",
@ -18001,7 +18048,6 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -18202,7 +18248,6 @@
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -18389,6 +18434,7 @@
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz",
"integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
@ -18475,6 +18521,7 @@
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
"integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
"license": "MIT",
"peer": true,
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
@ -18500,6 +18547,7 @@
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz",
"integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.8",
@ -18563,6 +18611,7 @@
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
@ -18576,6 +18625,7 @@
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
"license": "BSD-2-Clause",
"peer": true,
"engines": {
"node": ">=4.0"
}
@ -18753,6 +18803,7 @@
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">= 6"
}
@ -18861,7 +18912,6 @@
"resolved": "https://registry.npmjs.org/yup/-/yup-1.7.1.tgz",
"integrity": "sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==",
"license": "MIT",
"peer": true,
"dependencies": {
"property-expr": "^2.0.5",
"tiny-case": "^1.0.3",
@ -18904,7 +18954,6 @@
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz",
"integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12.20.0"
},

View file

@ -1,4 +1,5 @@
import { Loader2, Mic, Pause, Play, Square, Trash2Icon, Upload, X } from "lucide-react";
import posthog from "posthog-js";
import { useCallback, useEffect, useRef, useState } from "react";
import {
@ -28,6 +29,7 @@ import {
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { LANGUAGE_DISPLAY_NAMES } from "@/constants/languages";
import { PostHogEvent } from "@/constants/posthog-events";
import { useUserConfig } from "@/context/UserConfigContext";
import { useAudioPlayback } from "@/hooks/useAudioPlayback";
@ -357,6 +359,10 @@ export const RecordingsDialog = ({
const handlePlay = async (rec: RecordingResponseSchema) => {
try {
await togglePlayback(rec.recording_id, rec.storage_key, rec.storage_backend);
posthog.capture(PostHogEvent.RECORDING_PLAYED, {
recording_id: rec.recording_id,
source: 'recordings_dialog',
});
} catch {
setError("Failed to play recording");
}

View file

@ -3,6 +3,7 @@
import { ReactFlowInstance } from "@xyflow/react";
import { AlertCircle, ArrowLeft, ChevronDown, Copy, Download, Eye, History, LoaderCircle, MoreVertical, Phone, Rocket } from "lucide-react";
import { useRouter } from "next/navigation";
import posthog from "posthog-js";
import { useState } from "react";
import { toast } from "sonner";
@ -24,6 +25,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { PostHogEvent } from "@/constants/posthog-events";
import { WORKFLOW_RUN_MODES } from "@/constants/workflowRunModes";
interface WorkflowEditorHeaderProps {
@ -259,7 +261,13 @@ export const WorkflowEditorHeader = ({
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="bg-[#1a1a1a] border-[#3a3a3a]">
<DropdownMenuItem
onClick={() => onRun(WORKFLOW_RUN_MODES.SMALL_WEBRTC)}
onClick={() => {
posthog.capture(PostHogEvent.WEB_CALL_INITIATED, {
workflow_id: workflowId,
workflow_name: workflowName,
});
onRun(WORKFLOW_RUN_MODES.SMALL_WEBRTC);
}}
className="text-white hover:bg-[#2a2a2a] cursor-pointer"
>
<Phone className="w-4 h-4 mr-2" />

View file

@ -8,6 +8,7 @@ import {
} from "@xyflow/react";
import { EdgeChange, NodeChange } from "@xyflow/system";
import { useRouter } from "next/navigation";
import posthog from "posthog-js";
import { useCallback, useEffect, useRef } from "react";
import { useWorkflowStore } from "@/app/workflow/[workflowId]/stores/workflowStore";
@ -18,6 +19,7 @@ import {
} from "@/client";
import { WorkflowError } from "@/client/types.gen";
import { FlowEdge, FlowNode, NodeType } from "@/components/flow/types";
import { PostHogEvent } from "@/constants/posthog-events";
import logger from '@/lib/logger';
import { getNextNodeId, getRandomId } from "@/lib/utils";
import { DEFAULT_WORKFLOW_CONFIGURATIONS, WorkflowConfigurations } from "@/types/workflow-configurations";
@ -290,8 +292,12 @@ export const useWorkflowState = ({
// Use addNodes from ReactFlow instance
rfInstance.current.addNodes([newNode]);
posthog.capture(PostHogEvent.WORKFLOW_NODE_ADDED, {
node_type: nodeType,
workflow_id: workflowId,
});
setIsAddNodePanelOpen(false);
}, [nodes, setIsAddNodePanelOpen]);
}, [nodes, setIsAddNodePanelOpen, workflowId]);
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setWorkflowName(e.target.value);

View file

@ -1,6 +1,7 @@
'use client';
import { useParams } from 'next/navigation';
import posthog from 'posthog-js';
import { useEffect, useMemo, useState } from 'react';
import RenderWorkflow from '@/app/workflow/[workflowId]/RenderWorkflow';
@ -8,6 +9,7 @@ import { getWorkflowApiV1WorkflowFetchWorkflowIdGet } from '@/client/sdk.gen';
import type { WorkflowResponse } from '@/client/types.gen';
import { FlowEdge, FlowNode } from '@/components/flow/types';
import SpinLoader from '@/components/SpinLoader';
import { PostHogEvent } from '@/constants/posthog-events';
import { useAuth } from '@/lib/auth';
import logger from '@/lib/logger';
import { DEFAULT_WORKFLOW_CONFIGURATIONS, WorkflowConfigurations } from '@/types/workflow-configurations';
@ -39,6 +41,10 @@ export default function WorkflowDetailPage() {
});
const workflow = response.data;
setWorkflow(workflow);
posthog.capture(PostHogEvent.WORKFLOW_EDITOR_OPENED, {
workflow_id: workflow?.id,
workflow_name: workflow?.name,
});
} catch (err) {
setError('Failed to fetch workflow');
logger.error(`Error fetching workflow: ${err}`);

View file

@ -3,6 +3,7 @@
import { Check, Copy, ExternalLink, FileText, LoaderCircle, Phone, Video } from 'lucide-react';
import Link from 'next/link';
import { useParams, useRouter } from 'next/navigation';
import posthog from 'posthog-js';
import { useEffect, useRef, useState } from 'react';
import BrowserCall from '@/app/workflow/[workflowId]/run/[runId]/BrowserCall';
@ -17,6 +18,7 @@ import { OnboardingTooltip } from '@/components/onboarding/OnboardingTooltip';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Skeleton } from '@/components/ui/skeleton';
import { PostHogEvent } from '@/constants/posthog-events';
import { WORKFLOW_RUN_MODES } from '@/constants/workflowRunModes';
import { useOnboarding } from '@/context/OnboardingContext';
import { useAuth } from '@/lib/auth';
@ -114,7 +116,7 @@ export default function WorkflowRunPage() {
},
});
setIsLoading(false);
setWorkflowRun({
const runData = {
is_completed: response.data?.is_completed ?? false,
transcript_url: response.data?.transcript_url ?? null,
recording_url: response.data?.recording_url ?? null,
@ -122,6 +124,14 @@ export default function WorkflowRunPage() {
gathered_context: response.data?.gathered_context as Record<string, string> | null ?? null,
logs: response.data?.logs as WorkflowRunLogs | null ?? null,
annotations: response.data?.annotations as Record<string, unknown> | null ?? null,
};
setWorkflowRun(runData);
posthog.capture(PostHogEvent.WORKFLOW_RUN_DETAILS_VIEWED, {
workflow_id: Number(workflowId),
run_id: Number(runId),
is_completed: runData.is_completed,
has_recording: !!runData.recording_url,
has_transcript: !!runData.transcript_url,
});
};
fetchWorkflowRun();

View file

@ -1,6 +1,7 @@
'use client';
import { Headphones, Loader2 } from 'lucide-react';
import posthog from 'posthog-js';
import { useCallback, useState } from 'react';
import { Button } from '@/components/ui/button';
@ -12,6 +13,7 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { PostHogEvent } from '@/constants/posthog-events';
import { downloadFile, getSignedUrl } from '@/lib/files';
export function MediaPreviewDialog() {
@ -48,6 +50,11 @@ export function MediaPreviewDialog() {
const response = await fetch(transcriptResult);
const text = await response.text();
setTranscriptContent(text);
posthog.capture(PostHogEvent.TRANSCRIPT_VIEWED, {
run_id: runId,
source: 'media_preview_dialog',
transcript_length: text.length,
});
} catch (error) {
console.error('Error fetching transcript:', error);
}
@ -78,7 +85,16 @@ export function MediaPreviewDialog() {
)}
{!mediaLoading && audioSignedUrl && (
<audio src={audioSignedUrl} controls autoPlay className="w-full mt-4" />
<audio
src={audioSignedUrl}
controls
autoPlay
className="w-full mt-4"
onPlay={() => posthog.capture(PostHogEvent.RECORDING_PLAYED, {
run_id: selectedRunId,
source: 'media_preview_dialog',
})}
/>
)}
{!mediaLoading && transcriptContent && (

View file

@ -8,39 +8,67 @@ import { useAuth } from '@/lib/auth';
/**
* PostHogIdentify
* ---------------
* A tiny client-side component that calls `posthog.identify` once the
* authenticated user object is available. It also resets PostHog when the
* user logs out or switches accounts.
* Calls `posthog.identify` once the authenticated user object is available,
* using the Stack Auth user ID as distinct_id with email and name as properties.
*
* This component is intended to be rendered high in the React tree (e.g. in
* `app/layout.tsx`) so that PostHog always knows which user is active for the
* current browser session.
* Rendered in the root layout (`app/layout.tsx`) so it fires on every session
* for every logged-in user.
*/
export default function PostHogIdentify() {
const { user } = useAuth();
useEffect(() => {
// Only run if PostHog is enabled
if (process.env.NEXT_PUBLIC_ENABLE_POSTHOG !== 'true') {
return;
}
if (user) {
try {
// Identify the user in PostHog with their unique id and useful traits
posthog.identify(String(user.id ?? ''));
} catch (err) {
// Silently ignore identification errors so they don't break the app
const identify = () => {
try {
// Stack Auth users expose primaryEmail/displayName,
// local users expose email/name — handle both.
const email =
'primaryEmail' in user ? user.primaryEmail :
'email' in user ? user.email :
undefined;
const name =
'displayName' in user ? user.displayName :
'name' in user ? user.name :
undefined;
console.warn('Failed to identify user in PostHog', err);
// Use provider_id as distinct_id to match backend PostHog events.
// Stack Auth users: user.id is already the provider_id.
// OSS users: provider_id is returned from the auth API.
const distinctId =
'provider_id' in user && user.provider_id
? String(user.provider_id)
: String(user.id);
posthog.identify(distinctId, {
...(email && { email }),
...(name && { name }),
});
} catch (err) {
console.warn('Failed to identify user in PostHog', err);
}
};
if (posthog.__loaded) {
identify();
} else {
// PostHog initializes async — retry until ready
let attempts = 0;
const interval = setInterval(() => {
attempts++;
if (posthog.__loaded) {
identify();
clearInterval(interval);
} else if (attempts >= 20) {
clearInterval(interval);
}
}, 200);
return () => clearInterval(interval);
}
} else {
// If the user logs out, clear the PostHog identity so future anonymous
// interactions aren't associated with the previous account.
posthog.reset();
}
}, [user]);
// This component does not render anything
return null;
}

View file

@ -0,0 +1,11 @@
/**
* PostHog event names frontend events only.
*/
export const PostHogEvent = {
WORKFLOW_EDITOR_OPENED: "workflow_editor_opened",
WORKFLOW_NODE_ADDED: "workflow_node_added",
WORKFLOW_RUN_DETAILS_VIEWED: "workflow_run_details_viewed",
RECORDING_PLAYED: "recording_played",
TRANSCRIPT_VIEWED: "transcript_viewed",
WEB_CALL_INITIATED: "web_call_initiated",
} as const;

View file

@ -13,6 +13,7 @@ export interface LocalUser extends BaseUser {
provider: 'local';
organizationId?: string;
displayName?: string;
provider_id?: string;
}
// Union type for all user types