fix(voice-agent): remove committed run logs, timing-safe webhook verify, lazy httpx

- Delete accidentally committed .app.err / .tunnel.log.err (leaked local
  paths, Cloudflare tunnel URLs, connector IDs); ignore *.err
- verify.py: use hmac.compare_digest for constant-time secret comparison
- client.py: lazy-import httpx (optional dep) per AGENTS.md lazy-import rule
- Fix import ordering (ruff I001) in app.py / voice_routes.py

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
MervinPraisonandClaude Opus 4.8 committed 2026-09-07 10:06:59 +00:00
1 parent dc98a79f6e
commit ecf2118bb2
7 files changed
+12 -97

No files matched your search

+1
View File
@@ -44,6 +44,7 @@ Thumbs.db
# Project specific
aiui/
*.log
*.err
.praison/
.gemini/
.internal/
-34
View File
@@ -1,34 +0,0 @@
[19:22:22] server.py:902 WARNING No auth_token provided for Gateway server. Generated temporary token: gw_****bfdf. For production, set GATEWAY_AUTH_TOKEN.
INFO: Started server process [16168]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8001 (Press CTRL+C to quit)
C:\Users\DELL\AppData\Local\Programs\Python\Python313\Lib\site-packages\praisonaiagents\_warning_patch.py:50: RuntimeWarning: Failed to read version from pyproject.toml: Version not found in pyproject.toml. Using 'unknown'.
_original_warn(message, category, stacklevel, source)
[19:22:26] agent.py:5802 WARNING Agent memory enabled without an explicit user_id; using an auto-generated, non-shared id (agent-8e0d97fd8839). Pass user_id=... explicitly to persist/share memory across runs.
[19:22:27] agent.py:5802 WARNING Agent memory enabled without an explicit user_id; using an auto-generated, non-shared id (agent-b8bcf4e24c8f). Pass user_id=... explicitly to persist/share memory across runs.
[19:22:28] agent.py:5802 WARNING Agent memory enabled without an explicit user_id; using an auto-generated, non-shared id (agent-5f0a892fe1a0). Pass user_id=... explicitly to persist/share memory across runs.
[19:22:29] agent.py:5802 WARNING Agent memory enabled without an explicit user_id; using an auto-generated, non-shared id (agent-e985038c68b7). Pass user_id=... explicitly to persist/share memory across runs.
[19:22:29] agent.py:5802 WARNING Agent memory enabled without an explicit user_id; using an auto-generated, non-shared id (agent-5b10026bd4ce). Pass user_id=... explicitly to persist/share memory across runs.
[19:22:29] agent.py:5802 WARNING Agent memory enabled without an explicit user_id; using an auto-generated, non-shared id (agent-c709a6041f47). Pass user_id=... explicitly to persist/share memory across runs.
[19:22:30] agent.py:5802 WARNING Agent memory enabled without an explicit user_id; using an auto-generated, non-shared id (agent-4079e4963da6). Pass user_id=... explicitly to persist/share memory across runs.
[19:22:31] agent.py:5802 WARNING Agent memory enabled without an explicit user_id; using an auto-generated, non-shared id (agent-85acb1a5e90c). Pass user_id=... explicitly to persist/share memory across runs.
[19:22:33] server.py:48 WARNING Jobs API using in-memory store - jobs and idempotency keys are LOST on restart. Set PRAISONAI_JOBS_DB_PATH to persist across restarts.
INFO: 127.0.0.1:51238 - "WebSocket /api/chat/ws" [accepted]
INFO: connection open
INFO: connection closed
INFO: 127.0.0.1:51312 - "WebSocket /api/chat/ws" [accepted]
INFO: connection open
INFO: connection closed
INFO: 127.0.0.1:51493 - "WebSocket /api/chat/ws" [accepted]
INFO: connection open
INFO: connection closed
INFO: 127.0.0.1:51501 - "WebSocket /api/chat/ws" [accepted]
INFO: connection open
INFO: connection closed
INFO: 127.0.0.1:51848 - "WebSocket /api/chat/ws" [accepted]
INFO: connection open
INFO: connection closed
INFO: 127.0.0.1:51883 - "WebSocket /api/chat/ws" [accepted]
INFO: connection open
INFO: connection closed
@@ -1,52 +0,0 @@
2026-09-04T13:49:40Z INF Thank you for trying Cloudflare Tunnel. Doing so, without a Cloudflare account, is a quick way to experiment and try it out. However, be aware that these account-less Tunnels have no uptime guarantee, are subject to the Cloudflare Online Services Terms of Use (https://www.cloudflare.com/website-terms/), and Cloudflare reserves the right to investigate your use of Tunnels for violations of such terms. If you intend to use Tunnels in production you should use a pre-created named tunnel by following: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps
2026-09-04T13:49:40Z INF Requesting new quick Tunnel on trycloudflare.com...
2026-09-04T13:49:47Z INF +--------------------------------------------------------------------------------------------+
2026-09-04T13:49:47Z INF | Your quick Tunnel has been created! Visit it at (it may take some time to be reachable): |
2026-09-04T13:49:47Z INF | https://sleeve-polyphonic-wagon-census.trycloudflare.com |
2026-09-04T13:49:47Z INF +--------------------------------------------------------------------------------------------+
2026-09-04T13:49:47Z INF Cannot determine default configuration path. No file [config.yml config.yaml] in [~/.cloudflared ~/.cloudflare-warp ~/cloudflare-warp]
2026-09-04T13:49:47Z INF Version 2026.8.3 (Checksum 83e726ed18ea78c5ad5213c4c3a3a27051393950d2bc8ed4de69bec12d14eaae)
2026-09-04T13:49:47Z INF GOOS: windows, GOVersion: go1.26.4, GoArch: amd64
2026-09-04T13:49:47Z INF Settings: map[ha-connections:1 protocol:quic url:http://127.0.0.1:8001]
2026-09-04T13:49:47Z INF cloudflared will not automatically update on Windows systems.
2026-09-04T13:49:47Z INF Generated Connector ID: be646d59-ad64-4576-8186-a66f11a2d56b
2026-09-04T13:49:47Z INF Initial protocol quic
2026-09-04T13:49:47Z INF ICMP proxy will use 10.160.21.143 as source for IPv4
2026-09-04T13:49:47Z INF ICMP proxy will use fe80::17a3:312a:8442:a6fa in zone Wi-Fi as source for IPv6
2026-09-04T13:49:47Z INF cloudflared does not support loading the system root certificate pool on Windows. Please use --origin-ca-pool <PATH> to specify the path to the certificate pool
2026-09-04T13:49:47Z INF ICMP proxy will use 10.160.21.143 as source for IPv4
2026-09-04T13:49:47Z INF Tunnel connection curve preferences: [X25519MLKEM768 CurveID(65074) CurveP256] connIndex=0 event=0 ip=198.41.192.7
2026-09-04T13:49:47Z INF ICMP proxy will use fe80::17a3:312a:8442:a6fa in zone Wi-Fi as source for IPv6
2026-09-04T13:49:47Z INF Starting metrics server on 127.0.0.1:20241/metrics
2026-09-04T13:49:47Z INF +-------------------------------------------------------------------------------------+
2026-09-04T13:49:47Z INF | CONNECTIVITY PRE-CHECKS |
2026-09-04T13:49:47Z INF +-------------------------------------------------------------------------------------+
2026-09-04T13:49:47Z INF | COMPONENT TARGET STATUS DETAILS |
2026-09-04T13:49:47Z INF | DNS Resolution region1.v2.argotunnel.com PASS DNS Resolved successfully |
2026-09-04T13:49:47Z INF | DNS Resolution region2.v2.argotunnel.com PASS DNS Resolved successfully |
2026-09-04T13:49:47Z INF | UDP Connectivity region1.v2.argotunnel.com PASS QUIC connection successful |
2026-09-04T13:49:47Z INF | UDP Connectivity region2.v2.argotunnel.com PASS QUIC connection successful |
2026-09-04T13:49:47Z INF | TCP Connectivity region1.v2.argotunnel.com PASS HTTP/2 connection successful |
2026-09-04T13:49:47Z INF | TCP Connectivity region2.v2.argotunnel.com PASS HTTP/2 connection successful |
2026-09-04T13:49:47Z INF | Cloudflare API api.cloudflare.com:443 PASS API is reachable |
2026-09-04T13:49:47Z INF | |
2026-09-04T13:49:47Z INF | SUMMARY: Environment is healthy. cloudflared will use 'quic' as primary protocol. |
2026-09-04T13:49:47Z INF +-------------------------------------------------------------------------------------+
2026-09-04T13:49:47Z INF precheck component="DNS Resolution" details="DNS Resolved successfully" run_id=a43185e9-c9c6-4291-87b1-6e9aa877a683 status=pass target=region1.v2.argotunnel.com
2026-09-04T13:49:47Z INF precheck component="DNS Resolution" details="DNS Resolved successfully" run_id=a43185e9-c9c6-4291-87b1-6e9aa877a683 status=pass target=region2.v2.argotunnel.com
2026-09-04T13:49:47Z INF precheck component="UDP Connectivity" details="QUIC connection successful" run_id=a43185e9-c9c6-4291-87b1-6e9aa877a683 status=pass target=region1.v2.argotunnel.com
2026-09-04T13:49:47Z INF precheck component="UDP Connectivity" details="QUIC connection successful" run_id=a43185e9-c9c6-4291-87b1-6e9aa877a683 status=pass target=region2.v2.argotunnel.com
2026-09-04T13:49:47Z INF precheck component="TCP Connectivity" details="HTTP/2 connection successful" run_id=a43185e9-c9c6-4291-87b1-6e9aa877a683 status=pass target=region1.v2.argotunnel.com
2026-09-04T13:49:47Z INF precheck component="TCP Connectivity" details="HTTP/2 connection successful" run_id=a43185e9-c9c6-4291-87b1-6e9aa877a683 status=pass target=region2.v2.argotunnel.com
2026-09-04T13:49:47Z INF precheck component="Cloudflare API" details="API is reachable" run_id=a43185e9-c9c6-4291-87b1-6e9aa877a683 status=pass target=api.cloudflare.com:443
2026-09-04T13:49:47Z INF precheck complete hard_fail=false run_id=a43185e9-c9c6-4291-87b1-6e9aa877a683 suggested_protocol=quic
2026-09-04T13:49:47Z INF Registered tunnel connection connIndex=0 connection=6ddd478d-e87d-483e-b10d-1c842a230609 event=0 ip=198.41.192.7 location=maa01 protocol=quic
2026-09-04T14:06:25Z INF Initiating graceful shutdown due to signal terminated ...
2026-09-04T14:06:25Z ERR failed to run the datagram handler error="context canceled" connIndex=0 event=0 ip=198.41.192.7
2026-09-04T14:06:25Z ERR failed to serve tunnel connection error="accept stream listener encountered a failure while serving" connIndex=0 event=0 ip=198.41.192.7
2026-09-04T14:06:25Z ERR Serve tunnel error error="accept stream listener encountered a failure while serving" connIndex=0 event=0 ip=198.41.192.7
2026-09-04T14:06:25Z INF Retrying connection in up to 1s connIndex=0 event=0 ip=198.41.192.7
2026-09-04T14:06:25Z ERR Connection terminated connIndex=0
2026-09-04T14:06:25Z ERR no more connections active and exiting
2026-09-04T14:06:25Z INF Tunnel server stopped
2026-09-04T14:06:25Z INF Metrics server stopped
+4 -5
View File
@@ -20,10 +20,7 @@ import sys
from pathlib import Path
from typing import Any
import praisonaiui as aiui
from praisonaiui.server import create_app
from starlette.routing import Route
from voice_routes import (
api_voice_call_detail,
api_voice_config_status,
@@ -32,6 +29,9 @@ from voice_routes import (
webhook_voice,
)
import praisonaiui as aiui
from praisonaiui.server import create_app
_EXAMPLE_DIR = Path(__file__).resolve().parent
if str(_EXAMPLE_DIR) not in sys.path:
sys.path.insert(0, str(_EXAMPLE_DIR))
@@ -63,9 +63,8 @@ def get_agent():
global _agent
if _agent is not None:
return _agent
from praisonaiagents import Agent
from integrations.voice.tools import echo_message, get_current_time, register_tool
from praisonaiagents import Agent
register_tool("get_current_time", get_current_time)
register_tool("echo_message", echo_message)
@@ -5,8 +5,6 @@ from __future__ import annotations
import logging
from typing import Any
import httpx
from integrations.voice.config import VoiceSettings
logger = logging.getLogger(__name__)
@@ -39,6 +37,8 @@ class VoiceClient:
*,
json_body: dict[str, Any] | None = None,
) -> dict[str, Any]:
import httpx # lazy: optional dependency for the example (pip install httpx)
url = f"{self._settings.api_base.rstrip('/')}{path}"
with httpx.Client(timeout=30.0) as client:
response = client.request(method, url, headers=self._headers(), json=json_body)
@@ -2,6 +2,8 @@
from __future__ import annotations
import hmac
_SECRET_HEADERS = ("x-voice-webhook-secret", "x-webhook-secret")
@@ -15,5 +17,5 @@ def verify_webhook_request(*, secret: str | None, headers: dict[str, str]) -> No
return
lowered = {k.lower(): v for k, v in headers.items()}
incoming = next((lowered[name] for name in _SECRET_HEADERS if lowered.get(name)), None)
if not incoming or incoming != secret:
if not incoming or not hmac.compare_digest(incoming, secret):
raise VerificationError("Invalid or missing webhook secret header")
+2 -3
View File
@@ -6,14 +6,13 @@ import asyncio
import json
import logging
from starlette.requests import Request
from starlette.responses import JSONResponse, PlainTextResponse, Response
from integrations.voice.client import VoiceClient
from integrations.voice.config import VoiceConfigError, load_voice_settings
from integrations.voice.processor import process_voice_webhook
from integrations.voice.store import VoiceCallStore
from integrations.voice.verify import VerificationError, verify_webhook_request
from starlette.requests import Request
from starlette.responses import JSONResponse, PlainTextResponse, Response
logger = logging.getLogger(__name__)