voice-vibecoder 2.2.0__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/CHANGELOG.md +11 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/PKG-INFO +1 -2
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/pyproject.toml +1 -2
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/__init__.py +1 -1
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/app_config.py +7 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/cli_serve.py +0 -2
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/code_providers/claude.py +3 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/server.py +19 -94
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/tools/agent_task.py +5 -1
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/tools/definitions.py +9 -3
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/tools/dispatch.py +1 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/.github/workflows/ci.yml +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/.github/workflows/release.yml +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/.gitignore +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/LICENSE +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/README.md +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/app.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/index.html +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/audio.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/code_providers/__init__.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/code_providers/cursor.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/code_providers/registry.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/config.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/instances.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/session.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/tools/__init__.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/tools/file_tree.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/tools/handlers.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/__init__.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/callbacks.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/diff_view.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/help_modal.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/panels.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/screen.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/setup.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/state.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/styles.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/update_modal.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/ui/wizard.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/voice_providers/__init__.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/voice_providers/openai.py +0 -0
- {voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/worktrees.py +0 -0
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v2.4.0 (2026-02-19)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- **server**: Handle unexpected errors during authentication
|
|
10
|
+
([`f0ac102`](https://github.com/snokam/voice-vibecoder/commit/f0ac1025cac939f8fc048c796d63668c1d687f9e))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## v2.3.0 (2026-02-19)
|
|
14
|
+
|
|
15
|
+
|
|
5
16
|
## v2.2.0 (2026-02-19)
|
|
6
17
|
|
|
7
18
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: voice-vibecoder
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Olaf The Vibecoder — a voice-controlled coding assistant using OpenAI Realtime API + Claude Code
|
|
5
5
|
Project-URL: Homepage, https://github.com/snokam/voice-vibecoder
|
|
6
6
|
Project-URL: Repository, https://github.com/snokam/voice-vibecoder
|
|
@@ -23,7 +23,6 @@ Requires-Python: >=3.10
|
|
|
23
23
|
Requires-Dist: claude-agent-sdk>=0.1.30
|
|
24
24
|
Requires-Dist: numpy>=1.24
|
|
25
25
|
Requires-Dist: platformdirs>=4.0
|
|
26
|
-
Requires-Dist: pyjwt[crypto]>=2.8
|
|
27
26
|
Requires-Dist: rich>=13.0
|
|
28
27
|
Requires-Dist: sounddevice>=0.4
|
|
29
28
|
Requires-Dist: textual>=0.50
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "voice-vibecoder"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.4.0"
|
|
8
8
|
description = "Olaf The Vibecoder — a voice-controlled coding assistant using OpenAI Realtime API + Claude Code"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -31,7 +31,6 @@ dependencies = [
|
|
|
31
31
|
"numpy>=1.24",
|
|
32
32
|
"claude-agent-sdk>=0.1.30",
|
|
33
33
|
"platformdirs>=4.0",
|
|
34
|
-
"PyJWT[crypto]>=2.8",
|
|
35
34
|
]
|
|
36
35
|
|
|
37
36
|
[project.urls]
|
|
@@ -9,6 +9,7 @@ Embedders can override any directory to colocate everything (e.g. ~/.snokam/).
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
+
from collections.abc import Awaitable
|
|
12
13
|
from dataclasses import dataclass, field
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from typing import Callable
|
|
@@ -65,3 +66,9 @@ class VoiceConfig:
|
|
|
65
66
|
# Called before an agent task runs. Receives (mcp_servers: dict, user_jwt: str | None)
|
|
66
67
|
# and can mutate mcp_servers in place (e.g. inject OBO tokens).
|
|
67
68
|
pre_agent_hook: Callable[[dict, str | None], None] | None = None
|
|
69
|
+
|
|
70
|
+
# Async callback for WebSocket authentication.
|
|
71
|
+
# Receives the raw token string, returns (user_email, raw_token).
|
|
72
|
+
# Raises ValueError on validation failure.
|
|
73
|
+
# When None, auth is disabled (no auth frame expected from clients).
|
|
74
|
+
authenticate: Callable[[str], Awaitable[tuple[str, str]]] | None = None
|
|
@@ -61,7 +61,6 @@ def main(voice_config: VoiceConfig | None = None) -> None:
|
|
|
61
61
|
parser.add_argument("--host", default="127.0.0.1", help="Bind address (default: 127.0.0.1)")
|
|
62
62
|
parser.add_argument("--port", type=int, default=8765, help="Port (default: 8765)")
|
|
63
63
|
parser.add_argument("--repo", type=str, default=None, help="Repo root (auto-detects git root)")
|
|
64
|
-
parser.add_argument("--no-auth", action="store_true", help="Disable JWT authentication (for local dev)")
|
|
65
64
|
parser.add_argument("-v", "--verbose", action="store_true", help="Debug logging")
|
|
66
65
|
args = parser.parse_args()
|
|
67
66
|
|
|
@@ -94,7 +93,6 @@ def main(voice_config: VoiceConfig | None = None) -> None:
|
|
|
94
93
|
repo_root=repo_root,
|
|
95
94
|
voice_config=config,
|
|
96
95
|
settings=settings,
|
|
97
|
-
no_auth=args.no_auth,
|
|
98
96
|
))
|
|
99
97
|
|
|
100
98
|
|
{voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/code_providers/claude.py
RENAMED
|
@@ -78,6 +78,9 @@ def describe_tool_call(name: str, inp: dict) -> str:
|
|
|
78
78
|
elif name == "WebFetch":
|
|
79
79
|
url = inp.get("url", "")
|
|
80
80
|
return f"\\[WebFetch] {url[:60]}"
|
|
81
|
+
elif name == "ToolSearch":
|
|
82
|
+
query = inp.get("query", "")
|
|
83
|
+
return f"\\[Search tools] {query}" if query else "\\[Search tools]"
|
|
81
84
|
elif name.startswith("mcp__"):
|
|
82
85
|
# MCP tool: mcp__<server>__<tool> → [Server: tool description]
|
|
83
86
|
parts = name.split("__", 2)
|
|
@@ -29,12 +29,9 @@ import asyncio
|
|
|
29
29
|
import json
|
|
30
30
|
import logging
|
|
31
31
|
import os
|
|
32
|
-
import time
|
|
33
32
|
from pathlib import Path
|
|
34
33
|
from typing import Any
|
|
35
|
-
from urllib.parse import urlparse
|
|
36
34
|
|
|
37
|
-
import jwt
|
|
38
35
|
import websockets
|
|
39
36
|
from websockets.asyncio.server import ServerConnection
|
|
40
37
|
from websockets.http11 import Request, Response
|
|
@@ -47,70 +44,6 @@ from voice_vibecoder.voice_providers.openai import OpenAIVoiceProvider
|
|
|
47
44
|
|
|
48
45
|
logger = logging.getLogger(__name__)
|
|
49
46
|
|
|
50
|
-
# ── JWT validation for Azure AD ──────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
_JWKS_CACHE_TTL = 3600 # 1 hour
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class _JWKSEntry:
|
|
56
|
-
__slots__ = ("client", "fetched_at")
|
|
57
|
-
|
|
58
|
-
def __init__(self, client: jwt.PyJWKClient, fetched_at: float) -> None:
|
|
59
|
-
self.client = client
|
|
60
|
-
self.fetched_at = fetched_at
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
_jwks_cache: dict[str, _JWKSEntry] = {}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def _get_jwks_client(tenant_id: str) -> jwt.PyJWKClient:
|
|
67
|
-
"""Return a cached JWKS client for the given Azure AD tenant."""
|
|
68
|
-
now = time.monotonic()
|
|
69
|
-
entry = _jwks_cache.get(tenant_id)
|
|
70
|
-
if entry and (now - entry.fetched_at) < _JWKS_CACHE_TTL:
|
|
71
|
-
return entry.client
|
|
72
|
-
|
|
73
|
-
jwks_url = f"https://login.microsoftonline.com/{tenant_id}/discovery/v2.0/keys"
|
|
74
|
-
client = jwt.PyJWKClient(jwks_url)
|
|
75
|
-
_jwks_cache[tenant_id] = _JWKSEntry(client, now)
|
|
76
|
-
return client
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def validate_token(token: str, tenant_id: str, audience: str) -> dict:
|
|
80
|
-
"""Validate an Azure AD JWT and return decoded claims.
|
|
81
|
-
|
|
82
|
-
Raises ValueError if the token is invalid.
|
|
83
|
-
"""
|
|
84
|
-
try:
|
|
85
|
-
jwks_client = _get_jwks_client(tenant_id)
|
|
86
|
-
signing_key = jwks_client.get_signing_key_from_jwt(token)
|
|
87
|
-
# Accept both v1 (aud=client_id) and v2 (aud=api://client_id) tokens,
|
|
88
|
-
# and both v1/v2 issuer formats.
|
|
89
|
-
audiences = [audience]
|
|
90
|
-
if audience.startswith("api://"):
|
|
91
|
-
audiences.append(audience[len("api://"):])
|
|
92
|
-
issuers = [
|
|
93
|
-
f"https://login.microsoftonline.com/{tenant_id}/v2.0",
|
|
94
|
-
f"https://sts.windows.net/{tenant_id}/",
|
|
95
|
-
]
|
|
96
|
-
claims = jwt.decode(
|
|
97
|
-
token,
|
|
98
|
-
signing_key.key,
|
|
99
|
-
algorithms=["RS256"],
|
|
100
|
-
audience=audiences,
|
|
101
|
-
issuer=issuers,
|
|
102
|
-
)
|
|
103
|
-
return claims
|
|
104
|
-
except jwt.ExpiredSignatureError:
|
|
105
|
-
raise ValueError("Token has expired")
|
|
106
|
-
except jwt.InvalidAudienceError:
|
|
107
|
-
raise ValueError("Invalid audience")
|
|
108
|
-
except jwt.InvalidIssuerError:
|
|
109
|
-
raise ValueError("Invalid issuer")
|
|
110
|
-
except Exception as exc:
|
|
111
|
-
raise ValueError(f"Token validation failed: {exc}")
|
|
112
|
-
|
|
113
|
-
|
|
114
47
|
# ── Auth timeout ─────────────────────────────────────────────────────────────
|
|
115
48
|
|
|
116
49
|
AUTH_TIMEOUT_SECONDS = 10
|
|
@@ -234,6 +167,7 @@ class WebSocketBridge:
|
|
|
234
167
|
"status": inst.status.value,
|
|
235
168
|
"branch": inst.branch,
|
|
236
169
|
"agentType": inst.agent_type,
|
|
170
|
+
"displayName": inst.display_key,
|
|
237
171
|
})
|
|
238
172
|
# Send buffered output from previous session
|
|
239
173
|
if inst.output_buffer:
|
|
@@ -283,12 +217,14 @@ class WebSocketBridge:
|
|
|
283
217
|
inst = self._session.registry.get_by_id(instance_id) if self._session else None
|
|
284
218
|
branch = inst.branch if inst else ""
|
|
285
219
|
agent_type = inst.agent_type if inst else "claude"
|
|
220
|
+
display_name = inst.display_key if inst else branch
|
|
286
221
|
self._send({
|
|
287
222
|
"type": "instance_status",
|
|
288
223
|
"instanceId": instance_id,
|
|
289
224
|
"status": status.value,
|
|
290
225
|
"branch": branch,
|
|
291
226
|
"agentType": agent_type,
|
|
227
|
+
"displayName": display_name,
|
|
292
228
|
})
|
|
293
229
|
|
|
294
230
|
def _on_tool_call(self, name: str, arguments: dict) -> None:
|
|
@@ -310,11 +246,11 @@ def _log_send_error(future: asyncio.Future) -> None:
|
|
|
310
246
|
|
|
311
247
|
async def _authenticate_ws(
|
|
312
248
|
ws: ServerConnection,
|
|
313
|
-
|
|
314
|
-
audience: str,
|
|
249
|
+
authenticate: Any,
|
|
315
250
|
) -> tuple[str, str] | None:
|
|
316
251
|
"""Wait for an auth message as the first frame.
|
|
317
252
|
|
|
253
|
+
*authenticate* is the ``VoiceConfig.authenticate`` async callback.
|
|
318
254
|
Returns ``(user_email, raw_token)`` on success, ``None`` on failure.
|
|
319
255
|
"""
|
|
320
256
|
try:
|
|
@@ -335,15 +271,18 @@ async def _authenticate_ws(
|
|
|
335
271
|
token = token[7:]
|
|
336
272
|
|
|
337
273
|
try:
|
|
338
|
-
|
|
274
|
+
result = await authenticate(token)
|
|
339
275
|
except ValueError as exc:
|
|
340
|
-
logger.warning("
|
|
276
|
+
logger.warning("Authentication failed: %s", exc)
|
|
277
|
+
await ws.send(json.dumps({"type": "error", "message": "Authentication failed"}))
|
|
278
|
+
return None
|
|
279
|
+
except Exception as exc:
|
|
280
|
+
logger.error("Unexpected error during authentication: %s", exc, exc_info=True)
|
|
341
281
|
await ws.send(json.dumps({"type": "error", "message": "Authentication failed"}))
|
|
342
282
|
return None
|
|
343
283
|
|
|
344
284
|
await ws.send(json.dumps({"type": "auth_ok"}))
|
|
345
|
-
|
|
346
|
-
return (user_email, token)
|
|
285
|
+
return result
|
|
347
286
|
|
|
348
287
|
|
|
349
288
|
async def _handle_connection(
|
|
@@ -351,17 +290,15 @@ async def _handle_connection(
|
|
|
351
290
|
settings: RealtimeSettings,
|
|
352
291
|
repo_root: Path,
|
|
353
292
|
voice_config: VoiceConfig | None,
|
|
354
|
-
auth_enabled: bool,
|
|
355
|
-
tenant_id: str,
|
|
356
|
-
audience: str,
|
|
357
293
|
) -> None:
|
|
358
294
|
remote = ws.remote_address
|
|
359
295
|
logger.info("Client connected from %s", remote)
|
|
360
296
|
|
|
361
297
|
user_email = None
|
|
362
298
|
user_jwt = None
|
|
363
|
-
if
|
|
364
|
-
|
|
299
|
+
authenticate = voice_config.authenticate if voice_config else None
|
|
300
|
+
if authenticate:
|
|
301
|
+
auth_result = await _authenticate_ws(ws, authenticate)
|
|
365
302
|
if not auth_result:
|
|
366
303
|
logger.warning("Auth failed for %s — closing", remote)
|
|
367
304
|
await ws.close(4001, "Unauthorized")
|
|
@@ -384,7 +321,6 @@ async def serve(
|
|
|
384
321
|
repo_root: Path | None = None,
|
|
385
322
|
voice_config: VoiceConfig | None = None,
|
|
386
323
|
settings: RealtimeSettings | None = None,
|
|
387
|
-
no_auth: bool = False,
|
|
388
324
|
allowed_origins: list[str] | None = None,
|
|
389
325
|
) -> None:
|
|
390
326
|
"""Start the WebSocket server. Blocks until stopped."""
|
|
@@ -401,22 +337,11 @@ async def serve(
|
|
|
401
337
|
logger.error("Not configured. Run 'vibecoder' TUI first to set API keys.")
|
|
402
338
|
return
|
|
403
339
|
|
|
404
|
-
|
|
405
|
-
tenant_id = os.environ.get("AZURE_AD_TENANT_ID", "")
|
|
406
|
-
client_id = os.environ.get("AZURE_AD_CLIENT_ID", "")
|
|
407
|
-
auth_enabled = bool(tenant_id and client_id) and not no_auth
|
|
340
|
+
auth_enabled = voice_config is not None and voice_config.authenticate is not None
|
|
408
341
|
if auth_enabled:
|
|
409
|
-
|
|
410
|
-
logger.info("JWT auth enabled tenant=%s audience=%s", tenant_id, audience)
|
|
411
|
-
elif no_auth:
|
|
412
|
-
audience = ""
|
|
413
|
-
logger.warning("JWT auth explicitly disabled via --no-auth")
|
|
342
|
+
logger.info("WebSocket auth enabled (authenticate callback provided)")
|
|
414
343
|
else:
|
|
415
|
-
|
|
416
|
-
logger.warning(
|
|
417
|
-
"JWT auth disabled (AZURE_AD_TENANT_ID / AZURE_AD_CLIENT_ID not set). "
|
|
418
|
-
"Set these env vars or pass --no-auth to suppress this warning."
|
|
419
|
-
)
|
|
344
|
+
logger.info("WebSocket auth disabled (no authenticate callback)")
|
|
420
345
|
|
|
421
346
|
# Origin checking — build allowed set from env var + explicit param
|
|
422
347
|
origin_env = os.environ.get("VOICE_ALLOWED_ORIGINS", "")
|
|
@@ -441,7 +366,7 @@ async def serve(
|
|
|
441
366
|
)
|
|
442
367
|
|
|
443
368
|
async def handler(ws: ServerConnection) -> None:
|
|
444
|
-
await _handle_connection(ws, settings, repo_root, voice_config
|
|
369
|
+
await _handle_connection(ws, settings, repo_root, voice_config)
|
|
445
370
|
|
|
446
371
|
async with websockets.serve(handler, host, port, process_request=process_request):
|
|
447
372
|
await asyncio.Future() # Block forever
|
|
@@ -176,7 +176,7 @@ def _handle_send_to_agent(message: str, branch: str | None = None, agent: str |
|
|
|
176
176
|
return f"Sent to {agent_label}."
|
|
177
177
|
|
|
178
178
|
|
|
179
|
-
def _handle_send_to_session(message: str, session_name: str | None = None, agent: str | None = None) -> str:
|
|
179
|
+
def _handle_send_to_session(message: str, session_name: str | None = None, agent: str | None = None, display_name: str | None = None) -> str:
|
|
180
180
|
from voice_vibecoder.tools.dispatch import (
|
|
181
181
|
_default_agent_type, _helper_override,
|
|
182
182
|
_registry, _repo_root,
|
|
@@ -200,6 +200,10 @@ def _handle_send_to_session(message: str, session_name: str | None = None, agent
|
|
|
200
200
|
instance = _registry.get_session_by_name(session_name)
|
|
201
201
|
if not instance:
|
|
202
202
|
instance = _registry.create_session(session_name, agent_type=target_agent)
|
|
203
|
+
# Update display_name if a better one was provided by the LLM
|
|
204
|
+
if display_name and display_name != instance.display_name:
|
|
205
|
+
instance.display_name = display_name
|
|
206
|
+
_registry._save_state()
|
|
203
207
|
|
|
204
208
|
# Update agent type if switching
|
|
205
209
|
resolved_agent = agent or instance.agent_type or target_agent
|
|
@@ -52,9 +52,11 @@ _CORE_TOOLS: list[dict[str, Any]] = [
|
|
|
52
52
|
"Send a NON-CODE task to a session agent. Returns immediately — the agent works in the background. "
|
|
53
53
|
"Use for anything that doesn't change code: research, Jira lookups, CI status, questions, "
|
|
54
54
|
"documentation, planning, analysis, etc. "
|
|
55
|
-
"Use
|
|
55
|
+
"Use short lowercase kebab-case session keys (e.g. 'jira', 'ci-status', 'events'). "
|
|
56
56
|
"Session names are fuzzy-matched — reuse existing sessions for the same topic. "
|
|
57
|
-
"If session_name is omitted, one is auto-generated from the message."
|
|
57
|
+
"If session_name is omitted, one is auto-generated from the message. "
|
|
58
|
+
"Always provide a short, human-friendly display_name in the user's language "
|
|
59
|
+
"(e.g. 'Arrangementer', 'Lønn', 'Jira-oppslag', 'Events', 'Salary')."
|
|
58
60
|
),
|
|
59
61
|
"parameters": {
|
|
60
62
|
"type": "object",
|
|
@@ -65,7 +67,11 @@ _CORE_TOOLS: list[dict[str, Any]] = [
|
|
|
65
67
|
},
|
|
66
68
|
"session_name": {
|
|
67
69
|
"type": "string",
|
|
68
|
-
"description": "
|
|
70
|
+
"description": "Short kebab-case key (e.g. 'events', 'salary'). Auto-generated if omitted.",
|
|
71
|
+
},
|
|
72
|
+
"display_name": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"description": "Human-friendly label shown in the UI (e.g. 'Arrangementer', 'Lønn'). Required.",
|
|
69
75
|
},
|
|
70
76
|
"agent": {
|
|
71
77
|
"type": "string",
|
|
@@ -214,6 +214,7 @@ def handle_tool_call(name: str, arguments: dict[str, Any]) -> str:
|
|
|
214
214
|
arguments.get("message", ""),
|
|
215
215
|
arguments.get("session_name"),
|
|
216
216
|
arguments.get("agent"),
|
|
217
|
+
arguments.get("display_name"),
|
|
217
218
|
)
|
|
218
219
|
elif name == "switch_agent":
|
|
219
220
|
return _handle_switch_agent(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/code_providers/__init__.py
RENAMED
|
File without changes
|
{voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/code_providers/cursor.py
RENAMED
|
File without changes
|
{voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/code_providers/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/voice_providers/__init__.py
RENAMED
|
File without changes
|
{voice_vibecoder-2.2.0 → voice_vibecoder-2.4.0}/src/voice_vibecoder/voice_providers/openai.py
RENAMED
|
File without changes
|
|
File without changes
|