Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
bb0e6fe
feat(voice): core voice pipeline + UI
Co-vengers Apr 14, 2026
46866cd
ci: limit unit workflow to tests/unit
Co-vengers Apr 18, 2026
45e65d2
chore: remove duplicate voice model ignore patterns
Co-vengers Apr 18, 2026
276917c
docs: generalize voice extension provider wording
Co-vengers Apr 18, 2026
29eb9c6
did: harden windows acl username fallback and rollback
Co-vengers Apr 18, 2026
0146faf
voice: fix timeout fallback text and history trimming
Co-vengers Apr 18, 2026
0c1009b
docs(voice): expand callback parameter docs
Co-vengers Apr 18, 2026
2d47170
voice(redis): pipeline active-session lookups
Co-vengers Apr 18, 2026
bf196b5
voice(tts): narrow fallback exceptions and log type
Co-vengers Apr 18, 2026
c8bc8e7
voice(session): remove redundant __aexit__ on __aenter__ failure
Co-vengers Apr 18, 2026
f3751ea
grpc: guard optional client dependencies
Co-vengers Apr 18, 2026
15bf80a
grpc: guard optional server dependencies
Co-vengers Apr 18, 2026
f48a913
grpc: guard optional service dependencies
Co-vengers Apr 18, 2026
7a7a71e
penguin: trim and validate deployment url hostname
Co-vengers Apr 18, 2026
8b56abc
server: type voice session manager and tidy imports
Co-vengers Apr 18, 2026
88e4a5e
voice(endpoints): validate context id and websocket token timeout
Co-vengers Apr 18, 2026
393c0f8
settings(voice): add websocket token read timeout
Co-vengers Apr 18, 2026
ed264ea
notifications: enforce resolved-ip delivery with safer errors
Co-vengers Apr 18, 2026
f5def92
worker: skip malformed file parts before mime checks
Co-vengers Apr 18, 2026
2c18dca
frontend(env): add public agent base url and normalize empty vars
Co-vengers Apr 18, 2026
38b2570
frontend(chat): replace alert fallback with error store
Co-vengers Apr 18, 2026
80b5a4b
frontend(chat): clarify intentional stream consumption comment
Co-vengers Apr 18, 2026
21a2670
frontend(voice): add aria live log attributes
Co-vengers Apr 18, 2026
f16b97c
frontend(voice): avoid float32 copy during playback
Co-vengers Apr 18, 2026
88423b1
frontend(migrations): use satisfies for semaphore insert
Co-vengers Apr 18, 2026
8b3d027
frontend(migrations): avoid pipeline updateMany for reports
Co-vengers Apr 18, 2026
14ef676
frontend(config): expose default PUBLIC_AGENT_BASE_URL
Co-vengers Apr 18, 2026
93ec3e9
frontend(db): reject pipeline updates and dedupe stream emits
Co-vengers Apr 18, 2026
1f889ea
frontend(files): attach upload listeners before write/end
Co-vengers Apr 18, 2026
7f2d29c
frontend(api): expose typed base url accessor
Co-vengers Apr 18, 2026
96fc83b
frontend(voice): guard stop/start races and use base url helper
Co-vengers Apr 18, 2026
7d435f0
frontend(chat): type message parts and combine text parts
Co-vengers Apr 18, 2026
43acdb0
frontend(voice): reset call state and invalidate stale starts
Co-vengers Apr 18, 2026
5525da5
frontend(agent): read base url from public env
Co-vengers Apr 18, 2026
f16c22a
frontend(payment): read base url from public env
Co-vengers Apr 18, 2026
3369a2a
test: exclude integration marker from default run
Co-vengers Apr 18, 2026
99d4892
test(grpc): narrow polling exceptions and improve timeout diagnostics
Co-vengers Apr 18, 2026
6ca04d8
test(voice): assert update_state no-op for missing session
Co-vengers Apr 18, 2026
d953039
test(voice): extract shared pipecat fixture and allowlist token
Co-vengers Apr 18, 2026
fef0f96
test(penguin): keep non-callable handler case in utility class
Co-vengers Apr 18, 2026
6e1b2bb
test(scheduler): assert send path blocks until release
Co-vengers Apr 18, 2026
4bdab87
test(minimax): mark integration suite
Co-vengers Apr 18, 2026
9c47a4d
frontend(migrations): fix semaphore lock insert typing
Co-vengers Apr 18, 2026
87e8f15
build: fix uv lock python-docx source ambiguity
Co-vengers Apr 18, 2026
57703bb
chore: apply pre-commit fixes
Co-vengers Apr 18, 2026
2b5b1e6
voice: fix websocket bridge typing
Co-vengers Apr 18, 2026
5661846
fix voice endpoint safety and pre-commit follow-ups
Co-vengers Apr 18, 2026
0c29f53
refactor: streamline voice agent settings and improve session management
Co-vengers Apr 18, 2026
5b4f3fa
deps(voice): include piper extra and bump requests
Co-vengers Apr 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,21 @@ sdks/typescript/node_modules/
sdks/kotlin/build/
sdks/kotlin/.gradle/
sdks/kotlin/bin/

# Frontend build/cache artifacts
frontend/.svelte-kit/
frontend/build/
frontend/.vite/
frontend/.vercel/

# Voice model artifacts downloaded at runtime (Piper)
en_US-*.onnx
en_US-*.onnx.json

# Runtime logs and provider test outputs
logs/

# Local tooling/worktree manager state
.kilo/
.idea/
.local/
5 changes: 3 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
exclude: ^\.agents/
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
Expand All @@ -21,15 +22,15 @@ repos:
hooks:
- id: ty
name: ty type checker
entry: uv run ty check bindu/ tests/ --exclude bindu/grpc/generated/
entry: uv run ty check bindu/ --exclude bindu/grpc/generated/
language: system
types: [python]
pass_filenames: false
exclude: ^(examples/|bindu/grpc/generated/)

- id: pytest
name: pytest with coverage
entry: bash -c 'uv run pytest -n auto --cov=bindu --cov-report= && coverage report --skip-covered --fail-under=60'
entry: bash -c 'uv run pytest -n auto --cov=bindu --cov-report= && uv run coverage report --skip-covered --fail-under=60'
language: system
pass_filenames: false
always_run: true
Expand Down
11 changes: 9 additions & 2 deletions bindu/common/protocol/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class FileWithUri(FileWithBytes):


@pydantic.with_config(ConfigDict(alias_generator=to_camel))
class FilePart(TextPart):
class FilePart(TypedDict):
"""Represents a file segment within a message or artifact.

The file content can be provided either directly as bytes or as a URI.
Expand All @@ -168,11 +168,15 @@ class FilePart(TextPart):
file: Required[FileWithBytes | FileWithUri]
"""The file of the part."""

metadata: NotRequired[dict[str, Any]]
"""Metadata about the file part."""

embeddings: NotRequired[list[float]]
"""The embeddings of File. <NotPartOfA2A>"""


class DataPart(TextPart):
@pydantic.with_config(ConfigDict(alias_generator=to_camel))
class DataPart(TypedDict):
"""Represents a structured data segment (e.g., JSON) within a message or artifact."""

kind: Required[Literal["data"]]
Expand All @@ -181,6 +185,9 @@ class DataPart(TextPart):
data: Required[dict[str, Any]]
"""The data of the part."""

metadata: NotRequired[dict[str, Any]]
"""Metadata about the data part."""

embeddings: NotRequired[list[float]]
"""The embeddings of Data. <NotPartOfA2A>"""

Expand Down
7 changes: 7 additions & 0 deletions bindu/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@
negotiate prices, request payments, and execute transactions based on cryptographic
mandates, creating a true agent economy.

**Voice (Real-time Voice Conversations)**
Adds Vapi-like real-time voice capability to Bindu agents via WebSocket.
The pipeline uses Pipecat with configurable provider-based STT and TTS to provide
bidirectional voice conversations. Providers can be selected via the ``voice``
config passed to ``bindufy()`` or through ``VOICE__ENABLED``-based environment
settings.


Each extension follows the A2A protocol specification for extensions:
https://a2a-protocol.org/v0.3.0/topics/extensions/
Expand Down
63 changes: 54 additions & 9 deletions bindu/extensions/did/did_agent_extension.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
# |---------------------------------------------------------|
# | |
# | Give Feedback / Get Help |
# | https://github.com/getbindu/Bindu/issues/new/choose |
# | |
# |---------------------------------------------------------|
#
# Thank you users! We ❤️ you! - 🌻

"""DID (Decentralized Identifier) Extension for Bindu Agents.

Why is DID an Extension?
Expand All @@ -29,6 +20,8 @@

import os
import platform
import subprocess
import getpass

from datetime import datetime, timezone
from functools import cached_property
Expand Down Expand Up @@ -222,17 +215,31 @@ def generate_and_save_key_pair(self) -> dict[str, str]:
# Windows does not enforce POSIX permissions — write directly
self.private_key_path.write_bytes(private_pem)
self.public_key_path.write_bytes(public_pem)
try:
self._harden_windows_key_file_acl(self.private_key_path)
self._harden_windows_key_file_acl(self.public_key_path)
except Exception:
for key_path in (self.private_key_path, self.public_key_path):
try:
key_path.unlink(missing_ok=True)
except OSError:
logger.warning(
f"Failed to remove key file during ACL hardening rollback: {key_path}"
)
raise
else:
# POSIX: use os.open to set permissions atomically on creation
fd = os.open(
str(self.private_key_path), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600
)
os.fchmod(fd, 0o600)
with os.fdopen(fd, "wb") as f:
f.write(private_pem)

fd = os.open(
str(self.public_key_path), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644
)
os.fchmod(fd, 0o644)
with os.fdopen(fd, "wb") as f:
f.write(public_pem)

Expand All @@ -241,6 +248,44 @@ def generate_and_save_key_pair(self) -> dict[str, str]:
"public_key_path": str(self.public_key_path),
}

@staticmethod
def _harden_windows_key_file_acl(path: Path) -> None:
"""Restrict Windows ACLs to current user for key file confidentiality."""
username = (os.getenv("USERNAME") or "").strip()
if not username:
try:
username = os.getlogin().strip()
except Exception:
username = ""
if not username:
try:
username = (getpass.getuser() or "").strip()
except Exception:
username = ""
if not username:
raise RuntimeError(
"Unable to determine Windows username for key ACL hardening"
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Remove inherited ACLs and grant full control to current user only.
cmd = [
"icacls",
str(path),
"/inheritance:r",
"/grant:r",
f"{username}:F",
"/remove:g",
"Users",
"Authenticated Users",
"Everyone",
]
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
if result.returncode != 0:
raise RuntimeError(
"Failed to harden ACL for key file "
f"{path}: {result.stderr.strip() or result.stdout.strip()}"
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

def _load_key_from_file(self, key_path: Path, key_type: str) -> bytes:
"""Load key PEM data from file.

Expand Down
19 changes: 19 additions & 0 deletions bindu/extensions/voice/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Voice Agent extension for real-time voice conversations.

Provides Vapi-like voice capabilities (STT → Agent → TTS) integrated
into Bindu's A2A protocol and extension system.

Usage::

from bindu.extensions.voice import VoiceAgentExtension

voice = VoiceAgentExtension(
stt_provider="deepgram",
tts_provider="elevenlabs",
tts_voice_id="21m00Tcm4TlvDq8ikWAM",
)
"""

from .voice_agent_extension import VoiceAgentExtension

__all__ = ["VoiceAgentExtension"]
Loading
Loading