voice-vibecoder 2.9.4__tar.gz → 2.9.6__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.9.4 → voice_vibecoder-2.9.6}/CHANGELOG.md +16 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/PKG-INFO +1 -1
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/pyproject.toml +1 -1
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/__init__.py +1 -1
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/openclaw.py +47 -34
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/.github/workflows/ci.yml +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/.github/workflows/release.yml +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/.gitignore +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/LICENSE +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/README.md +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/app.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/index.html +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/app_config.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/audio.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/cli_serve.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/__init__.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/claude.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/cursor.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/registry.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/config.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/instances.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/server.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/session.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/__init__.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/agent_task.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/definitions.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/dispatch.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/file_tree.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/handlers.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/__init__.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/callbacks.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/diff_view.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/help_modal.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/panels.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/screen.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/setup.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/state.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/styles.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/update_modal.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/wizard.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/voice_providers/__init__.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/voice_providers/openai.py +0 -0
- {voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/worktrees.py +0 -0
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v2.9.6 (2026-02-21)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Handle agent/chat events correctly for streaming
|
|
10
|
+
([`2abd488`](https://github.com/snokam/voice-vibecoder/commit/2abd488a7e408a4227375bb332f9e2d76ae8df85))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## v2.9.5 (2026-02-21)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
- Use chat.send method for gateway streaming
|
|
18
|
+
([`a955747`](https://github.com/snokam/voice-vibecoder/commit/a955747a793514463f932376f0866cb140e364c6))
|
|
19
|
+
|
|
20
|
+
|
|
5
21
|
## v2.9.4 (2026-02-21)
|
|
6
22
|
|
|
7
23
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: voice-vibecoder
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.6
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "voice-vibecoder"
|
|
7
|
-
version = "2.9.
|
|
7
|
+
version = "2.9.6"
|
|
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"
|
{voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/openclaw.py
RENAMED
|
@@ -68,11 +68,7 @@ def _build_auth_payload(
|
|
|
68
68
|
token: str | None,
|
|
69
69
|
nonce: str | None,
|
|
70
70
|
) -> str:
|
|
71
|
-
"""Build the payload to sign for device auth.
|
|
72
|
-
|
|
73
|
-
Format (v2 with nonce):
|
|
74
|
-
v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce
|
|
75
|
-
"""
|
|
71
|
+
"""Build the payload to sign for device auth."""
|
|
76
72
|
scopes_str = ",".join(scopes)
|
|
77
73
|
token_str = token or ""
|
|
78
74
|
|
|
@@ -136,7 +132,6 @@ class OpenClawRunner:
|
|
|
136
132
|
timeout=5.0
|
|
137
133
|
)
|
|
138
134
|
|
|
139
|
-
# Wait for challenge
|
|
140
135
|
challenge_msg = await asyncio.wait_for(ws.recv(), timeout=5.0)
|
|
141
136
|
challenge = json.loads(challenge_msg)
|
|
142
137
|
|
|
@@ -148,7 +143,6 @@ class OpenClawRunner:
|
|
|
148
143
|
nonce = challenge["payload"]["nonce"]
|
|
149
144
|
signed_at_ms = int(time.time() * 1000)
|
|
150
145
|
|
|
151
|
-
# Build auth payload and sign
|
|
152
146
|
client_id = "cli"
|
|
153
147
|
client_mode = "cli"
|
|
154
148
|
role = "operator"
|
|
@@ -166,14 +160,12 @@ class OpenClawRunner:
|
|
|
166
160
|
)
|
|
167
161
|
signature = _sign_payload(self._private_key, payload)
|
|
168
162
|
|
|
169
|
-
# Get public key in base64
|
|
170
163
|
public_key_bytes = self._private_key.public_key().public_bytes(
|
|
171
164
|
encoding=serialization.Encoding.Raw,
|
|
172
165
|
format=serialization.PublicFormat.Raw
|
|
173
166
|
)
|
|
174
167
|
pub_b64 = base64.urlsafe_b64encode(public_key_bytes).decode().rstrip("=")
|
|
175
168
|
|
|
176
|
-
# Send connect request
|
|
177
169
|
connect_req = {
|
|
178
170
|
"type": "req",
|
|
179
171
|
"id": str(uuid.uuid4()),
|
|
@@ -207,7 +199,6 @@ class OpenClawRunner:
|
|
|
207
199
|
|
|
208
200
|
await ws.send(json.dumps(connect_req))
|
|
209
201
|
|
|
210
|
-
# Wait for response
|
|
211
202
|
response_msg = await asyncio.wait_for(ws.recv(), timeout=5.0)
|
|
212
203
|
response = json.loads(response_msg)
|
|
213
204
|
|
|
@@ -235,51 +226,71 @@ class OpenClawRunner:
|
|
|
235
226
|
"""Run agent via WebSocket for streaming output."""
|
|
236
227
|
try:
|
|
237
228
|
req_id = str(uuid.uuid4())
|
|
238
|
-
|
|
229
|
+
idempotency_key = f"voice-{uuid.uuid4().hex[:16]}"
|
|
230
|
+
|
|
231
|
+
chat_req = {
|
|
239
232
|
"type": "req",
|
|
240
233
|
"id": req_id,
|
|
241
|
-
"method": "
|
|
234
|
+
"method": "chat.send",
|
|
242
235
|
"params": {
|
|
243
|
-
"
|
|
236
|
+
"sessionKey": session_id,
|
|
244
237
|
"message": message,
|
|
245
|
-
"
|
|
246
|
-
"local": True
|
|
238
|
+
"idempotencyKey": idempotency_key,
|
|
247
239
|
}
|
|
248
240
|
}
|
|
249
241
|
|
|
250
|
-
await ws.send(json.dumps(
|
|
242
|
+
await ws.send(json.dumps(chat_req))
|
|
251
243
|
|
|
252
244
|
result_text = ""
|
|
245
|
+
last_text = ""
|
|
253
246
|
|
|
254
247
|
while True:
|
|
255
248
|
msg = await asyncio.wait_for(ws.recv(), timeout=300.0)
|
|
256
249
|
data = json.loads(msg)
|
|
257
250
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
251
|
+
event = data.get("event")
|
|
252
|
+
payload = data.get("payload", {})
|
|
253
|
+
|
|
254
|
+
if event == "agent":
|
|
255
|
+
stream = payload.get("stream")
|
|
256
|
+
agent_data = payload.get("data", {})
|
|
261
257
|
|
|
262
|
-
if
|
|
263
|
-
text =
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
258
|
+
if stream == "assistant" and "text" in agent_data:
|
|
259
|
+
text = agent_data["text"]
|
|
260
|
+
# Send delta (new text since last)
|
|
261
|
+
if text and len(text) > len(last_text):
|
|
262
|
+
delta = text[len(last_text):]
|
|
263
|
+
if on_output and delta:
|
|
264
|
+
on_output(AgentOutput(category="text", content=delta))
|
|
265
|
+
last_text = text
|
|
266
|
+
result_text = text
|
|
267
|
+
|
|
268
|
+
elif stream == "tool_use" and "name" in agent_data:
|
|
269
|
+
tool = agent_data["name"]
|
|
268
270
|
if on_output:
|
|
269
|
-
on_output(AgentOutput(category="tool_call", content=f"[{tool}]"))
|
|
271
|
+
on_output(AgentOutput(category="tool_call", content=f"\n[{tool}] "))
|
|
272
|
+
|
|
273
|
+
elif event == "chat":
|
|
274
|
+
state = payload.get("state")
|
|
275
|
+
if state == "final":
|
|
276
|
+
# Get final message
|
|
277
|
+
msg_content = payload.get("message", {})
|
|
278
|
+
if isinstance(msg_content, dict) and "content" in msg_content:
|
|
279
|
+
result_text = msg_content["content"]
|
|
280
|
+
break
|
|
281
|
+
elif state == "error":
|
|
282
|
+
error = payload.get("error", "Unknown error")
|
|
283
|
+
raise RuntimeError(f"Chat error: {error}")
|
|
270
284
|
|
|
271
285
|
elif data.get("type") == "res" and data.get("id") == req_id:
|
|
272
|
-
if data.get("ok"):
|
|
273
|
-
result_text = data.get("payload", {}).get("result", "")
|
|
274
|
-
else:
|
|
286
|
+
if not data.get("ok"):
|
|
275
287
|
error = data.get("error", {}).get("message", "Unknown error")
|
|
276
|
-
raise RuntimeError(f"
|
|
277
|
-
break
|
|
288
|
+
raise RuntimeError(f"Chat send error: {error}")
|
|
278
289
|
|
|
279
290
|
return AgentResult(text=result_text.strip(), session_id=session_id)
|
|
280
291
|
|
|
281
292
|
except Exception as e:
|
|
282
|
-
logger.warning("WebSocket
|
|
293
|
+
logger.warning("WebSocket chat failed: %s", e)
|
|
283
294
|
return None
|
|
284
295
|
|
|
285
296
|
async def _run_via_cli(
|
|
@@ -337,11 +348,13 @@ class OpenClawRunner:
|
|
|
337
348
|
effective_session = session_id or self._session_id
|
|
338
349
|
|
|
339
350
|
if on_output:
|
|
340
|
-
on_output(AgentOutput(category="text", content="🦞 Connecting to OpenClaw
|
|
351
|
+
on_output(AgentOutput(category="text", content="🦞 Connecting to OpenClaw...\n"))
|
|
341
352
|
|
|
342
353
|
ws = await self._connect_websocket()
|
|
343
354
|
if ws:
|
|
344
355
|
try:
|
|
356
|
+
if on_output:
|
|
357
|
+
on_output(AgentOutput(category="text", content="🦞 Connected! Streaming...\n"))
|
|
345
358
|
result = await self._run_via_websocket(ws, message, cwd, effective_session, on_output)
|
|
346
359
|
if result:
|
|
347
360
|
return result
|
|
@@ -349,7 +362,7 @@ class OpenClawRunner:
|
|
|
349
362
|
await ws.close()
|
|
350
363
|
|
|
351
364
|
if on_output:
|
|
352
|
-
on_output(AgentOutput(category="text", content="WebSocket unavailable, using CLI
|
|
365
|
+
on_output(AgentOutput(category="text", content="WebSocket unavailable, using CLI...\n"))
|
|
353
366
|
|
|
354
367
|
return await self._run_via_cli(message, cwd, effective_session, on_output)
|
|
355
368
|
|
|
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.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/__init__.py
RENAMED
|
File without changes
|
{voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/claude.py
RENAMED
|
File without changes
|
{voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/cursor.py
RENAMED
|
File without changes
|
{voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/voice_providers/__init__.py
RENAMED
|
File without changes
|
{voice_vibecoder-2.9.4 → voice_vibecoder-2.9.6}/src/voice_vibecoder/voice_providers/openai.py
RENAMED
|
File without changes
|
|
File without changes
|