Compare commits

...
Author SHA1 Message Date
MervinPraison 17cd4d9f86 Release v0.3.145 2026-09-16 13:00:52 +00:00
Mervin Praison 43935e4e41 fix: forward extra kwargs through channels chat bridge (fixes #287)
fix: forward extra kwargs through channels chat bridge (fixes #287)
2026-09-16 13:58:08 +01:00
MervinPraisonandClaude Opus 4.8 135b05435c fix: forward extra kwargs through channels chat bridge (fixes #287)
The chat bridge installed over bot._session.chat used a fixed
(agent, user_id, text) signature. When the bot internals invoked the
session with per-conversation keyword arguments (chat_id, platform,
thread_id, ...) the wrapper raised
`TypeError: _wrapped_chat() got an unexpected keyword argument 'chat_id'`
and the bot silently failed to reply across all platforms (Telegram,
Slack, Discord).

Add `*args, **kwargs` to `_wrapped_chat` and forward them untouched to
the original session chat so memory/session continuity is preserved.
Minimal, backward-compatible change; no new params or dependencies.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-09-15 13:11:51 +00:00
MervinPraisonandCursor be502d823f chore(automation): bump hub to v1.1.8 for dual OAuth failover
Uses pick-oauth-token pre-flight probe and TOKEN_B backup.

Co-authored-by: Cursor <[email protected]>
2026-09-15 07:49:29 +01:00
Mervin Praison 9be1266747 Merge pull request #285 from MervinPraison/fix/voice-doctor-mcp-probe-timeout
fix(voice-agent): warn on slow MCP doctor probe instead of failing
2026-09-14 17:45:00 +01:00
MervinPraisonandCursor 8ce13a9fec fix(voice-agent): warn on slow MCP doctor probe instead of failing
Use a 2s timeout for GET /api/mcp/servers and downgrade timeout-style
errors to warn so stdio MCP connect does not fail the whole doctor run.

Follow-up to #273 (example-only; no core SDK changes).

Co-authored-by: Cursor <[email protected]>
2026-09-14 14:51:08 +01:00
Mervin Praison e6a346aa22 Merge pull request #284 from MervinPraison/chore/automation-hub-v1.1.7
chore(automation): bump github-automation-template hub to v1.1.7
2026-09-14 14:22:51 +01:00
15 changed files with 161 additions and 23 deletions

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@
"stack": "claude",
"profile": "sdk",
"mode": "hub",
"hubRef": "v1.1.7",
"hubRef": "v1.1.8",
"templateVersion": "1.1.7",
"installedAt": "2026-09-07T10:02:11Z"
}
+2 -2
View File
@@ -27,7 +27,7 @@ jobs:
permissions:
issues: write
pull-requests: write
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].7
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].8
secrets: inherit
with:
hub_ref: v1.1.7
hub_ref: v1.1.8
+2 -2
View File
@@ -16,8 +16,8 @@ permissions:
jobs:
recover:
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].7
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].8
secrets: inherit
with:
hub_ref: v1.1.7
hub_ref: v1.1.8
pr_number: ${{ github.event.inputs.pr_number || '' }}
+2 -2
View File
@@ -25,8 +25,8 @@ permissions:
jobs:
detect-and-trigger:
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].7
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].8
secrets: inherit
with:
hub_ref: v1.1.7
hub_ref: v1.1.8
pr_number: ${{ github.event.inputs.pr_number || '' }}
+4 -4
View File
@@ -43,9 +43,9 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: MervinPraison/github-automation-template/.github/actions/[email protected].7
- uses: MervinPraison/github-automation-template/.github/actions/[email protected].8
with:
hub_ref: v1.1.7
hub_ref: v1.1.8
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v1
@@ -87,9 +87,9 @@ jobs:
issues: write
actions: read
id-token: write
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].7
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].8
secrets: inherit
with:
hub_ref: v1.1.7
hub_ref: v1.1.8
pr_number: ${{ github.event.inputs.pr_number || github.event.client_payload.pr_number || '' }}
merge_method: ${{ github.event.inputs.merge_method || 'merge' }}
+2 -2
View File
@@ -18,7 +18,7 @@ jobs:
issues: write
actions: read
id-token: write
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].7
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].8
secrets: inherit
with:
hub_ref: v1.1.7
hub_ref: v1.1.8
+2 -2
View File
@@ -25,9 +25,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: MervinPraison/github-automation-template/.github/actions/[email protected].7
- uses: MervinPraison/github-automation-template/.github/actions/[email protected].8
with:
hub_ref: v1.1.7
hub_ref: v1.1.8
- name: Detect conflicting PRs and trigger Claude
uses: actions/github-script@v7
with:
+2 -2
View File
@@ -13,7 +13,7 @@ permissions:
jobs:
sync:
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].7
uses: MervinPraison/github-automation-template/.github/workflows/[email protected].8
secrets: inherit
with:
hub_ref: v1.1.7
hub_ref: v1.1.8
@@ -17,6 +17,25 @@ def _tcp_open(host: str, port: int, timeout: float = 2.0) -> bool:
return False
_MCP_PROBE_TIMEOUT_SEC = 2.0
def _is_slow_mcp_probe(detail: str) -> bool:
lowered = detail.lower()
return any(token in lowered for token in ("timed out", "timeout", "temporarily unavailable"))
def _mcp_servers_probe(app_port: int) -> tuple[str, str]:
"""Probe MCP list endpoint; slow stdio connect must not fail the whole doctor run."""
status, detail = _http_status(
f"http://127.0.0.1:{app_port}/api/mcp/servers",
timeout=_MCP_PROBE_TIMEOUT_SEC,
)
if status == "fail" and _is_slow_mcp_probe(detail):
return "warn", detail
return status, detail
def _http_status(url: str, timeout: float = 5.0) -> tuple[str, str]:
try:
req = Request(url, headers={"User-Agent": "praisonai-voice-doctor"})
@@ -64,7 +83,7 @@ def run_voice_doctor(*, app_port: int = 8001, sidecar_port: int = 8002) -> dict[
checks.append({"name": "Speech Engine config", "status": status, "detail": detail})
status, detail = _http_status(f"http://127.0.0.1:{app_port}/api/voice/realtime/config")
checks.append({"name": "GPT Realtime config", "status": status, "detail": detail})
status, detail = _http_status(f"http://127.0.0.1:{app_port}/api/mcp/servers")
status, detail = _mcp_servers_probe(app_port)
checks.append({"name": "MCP servers API", "status": status, "detail": detail})
else:
checks.append(
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "aiui"
version = "0.3.144"
version = "0.3.145"
description = "YAML-driven website generator - CLI and compiler"
readme = "README.md"
license = {text = "MIT"}
+1 -1
View File
@@ -1,3 +1,3 @@
"""Version information."""
__version__ = "0.3.144"
__version__ = "0.3.145"
+13 -2
View File
@@ -652,13 +652,24 @@ class ChannelsFeature(BaseFeatureProtocol):
if hasattr(bot, "_session") and hasattr(bot._session, "chat"):
_original_chat = bot._session.chat
async def _wrapped_chat(agent: Any, user_id: str, text: str) -> str:
async def _wrapped_chat(
agent: Any,
user_id: str,
text: str,
*args: Any,
**kwargs: Any,
) -> str:
"""Wrap agent chat to broadcast responses AND intermediate
tool/step events to the Chat UI.
Hooks into the agent's StreamEventEmitter (same pattern as
PraisonAIProvider._run_direct_mode) to capture tool_call,
reasoning, and content events during execution.
Any extra positional/keyword arguments (e.g. ``chat_id``,
``platform``, ``thread_id``) are forwarded untouched to the
wrapped session ``chat`` so per-conversation memory/session
continuity is preserved instead of raising ``TypeError``.
"""
from .chat import _enrich_tool_payload, get_chat_manager
@@ -831,7 +842,7 @@ class ChannelsFeature(BaseFeatureProtocol):
drain_task = asyncio.create_task(_drain_events())
try:
response = await _original_chat(agent, user_id, text)
response = await _original_chat(agent, user_id, text, *args, **kwargs)
finally:
# Signal drain to stop and clean up callback
try:
+88
View File
@@ -0,0 +1,88 @@
"""Regression coverage for issue #287: the channels chat bridge dropped
extra keyword arguments (e.g. ``chat_id``, ``platform``) because the
``_wrapped_chat`` wrapper installed over ``bot._session.chat`` had a fixed
``(agent, user_id, text)`` signature.
When the bot internals invoked the session with per-conversation kwargs the
wrapper raised ``TypeError: _wrapped_chat() got an unexpected keyword
argument 'chat_id'`` and the bot silently failed to reply. The wrapper must
forward any extra positional/keyword arguments untouched to the original
session ``chat`` so memory/session continuity is preserved.
"""
from __future__ import annotations
from typing import Any
import pytest
from praisonaiui.features.channels import ChannelsFeature
class _FakeSession:
"""Minimal session whose ``chat`` records forwarded arguments."""
def __init__(self) -> None:
self.received_args: tuple = ()
self.received_kwargs: dict = {}
async def chat(self, agent: Any, user_id: str, text: str, *args: Any, **kwargs: Any) -> str:
self.received_args = args
self.received_kwargs = dict(kwargs)
return f"reply to {text}"
class _FakeBot:
def __init__(self) -> None:
self._session = _FakeSession()
@pytest.mark.asyncio
async def test_wrapped_chat_forwards_kwargs() -> None:
feature = ChannelsFeature()
bot = _FakeBot()
feature._attach_chat_bridge("chan-1", bot, "telegram")
# The bridge replaced the session chat with the wrapper.
assert bot._session.chat.__name__ == "_wrapped_chat"
reply = await bot._session.chat(
object(),
"user-42",
"Hi",
chat_id="987654321",
platform="telegram",
user_id_meta="987654321",
)
assert reply == "reply to Hi"
assert bot._session.received_kwargs["chat_id"] == "987654321"
assert bot._session.received_kwargs["platform"] == "telegram"
@pytest.mark.asyncio
async def test_wrapped_chat_forwards_extra_positional_args() -> None:
feature = ChannelsFeature()
bot = _FakeBot()
feature._attach_chat_bridge("chan-2", bot, "slack")
reply = await bot._session.chat(object(), "user-7", "Hello", "extra")
assert reply == "reply to Hello"
assert bot._session.received_args == ("extra",)
@pytest.mark.asyncio
async def test_wrapped_chat_still_works_without_kwargs() -> None:
"""No-kwargs path (web UI / existing callers) must keep working."""
feature = ChannelsFeature()
bot = _FakeBot()
feature._attach_chat_bridge("chan-3", bot, "discord")
reply = await bot._session.chat(object(), "user-9", "Ping")
assert reply == "reply to Ping"
assert bot._session.received_kwargs == {}
+20
View File
@@ -478,6 +478,26 @@ def test_voice_doctor_report(monkeypatch):
assert report["summary"]["failed"] >= 1
def test_voice_doctor_mcp_probe_timeout_is_warn(monkeypatch):
monkeypatch.setenv("OPENAI_API_KEY", "sk-test-key-12345678")
def fake_http_status(url: str, timeout: float = 5.0):
if "/api/mcp/servers" in url:
assert timeout == 2.0
return "fail", "timed out"
return "pass", "HTTP 200"
with _voice_modules() as imp:
doctor = imp("integrations.voice.doctor")
monkeypatch.setattr(doctor, "_tcp_open", lambda *args, **kwargs: True)
monkeypatch.setattr(doctor, "_http_status", fake_http_status)
report = doctor.run_voice_doctor(app_port=8001, sidecar_port=59998)
mcp = next(c for c in report["checks"] if c["name"] == "MCP servers API")
assert mcp["status"] == "warn"
assert mcp["detail"] == "timed out"
def test_call_detail_ui_includes_analytics():
with _voice_modules() as imp:
ui = imp("integrations.voice.call_detail_ui")
Generated
+1 -1
View File
@@ -207,7 +207,7 @@ wheels = [
[[package]]
name = "aiui"
version = "0.3.144"
version = "0.3.145"
source = { editable = "." }
dependencies = [
{ name = "pydantic" },