voice-vibecoder 2.9.5__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.
Files changed (43) hide show
  1. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/CHANGELOG.md +8 -0
  2. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/PKG-INFO +1 -1
  3. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/pyproject.toml +1 -1
  4. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/__init__.py +1 -1
  5. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/openclaw.py +33 -30
  6. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/.github/workflows/ci.yml +0 -0
  7. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/.github/workflows/release.yml +0 -0
  8. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/.gitignore +0 -0
  9. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/LICENSE +0 -0
  10. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/README.md +0 -0
  11. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/app.py +0 -0
  12. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/index.html +0 -0
  13. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/app_config.py +0 -0
  14. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/audio.py +0 -0
  15. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/cli_serve.py +0 -0
  16. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/__init__.py +0 -0
  17. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/claude.py +0 -0
  18. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/cursor.py +0 -0
  19. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/code_providers/registry.py +0 -0
  20. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/config.py +0 -0
  21. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/instances.py +0 -0
  22. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/server.py +0 -0
  23. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/session.py +0 -0
  24. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/__init__.py +0 -0
  25. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/agent_task.py +0 -0
  26. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/definitions.py +0 -0
  27. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/dispatch.py +0 -0
  28. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/file_tree.py +0 -0
  29. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/tools/handlers.py +0 -0
  30. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/__init__.py +0 -0
  31. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/callbacks.py +0 -0
  32. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/diff_view.py +0 -0
  33. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/help_modal.py +0 -0
  34. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/panels.py +0 -0
  35. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/screen.py +0 -0
  36. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/setup.py +0 -0
  37. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/state.py +0 -0
  38. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/styles.py +0 -0
  39. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/update_modal.py +0 -0
  40. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/ui/wizard.py +0 -0
  41. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/voice_providers/__init__.py +0 -0
  42. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/voice_providers/openai.py +0 -0
  43. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.6}/src/voice_vibecoder/worktrees.py +0 -0
@@ -2,6 +2,14 @@
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
+
5
13
  ## v2.9.5 (2026-02-21)
6
14
 
7
15
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voice-vibecoder
3
- Version: 2.9.5
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.5"
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"
@@ -3,7 +3,7 @@
3
3
  A voice-controlled coding assistant using OpenAI Realtime API + Claude Code.
4
4
  """
5
5
 
6
- __version__ = "2.9.5"
6
+ __version__ = "2.9.6"
7
7
 
8
8
  from voice_vibecoder.app_config import VoiceConfig
9
9
  from voice_vibecoder.ui.screen import VoiceCodingScreen
@@ -132,7 +132,6 @@ class OpenClawRunner:
132
132
  timeout=5.0
133
133
  )
134
134
 
135
- # Wait for challenge
136
135
  challenge_msg = await asyncio.wait_for(ws.recv(), timeout=5.0)
137
136
  challenge = json.loads(challenge_msg)
138
137
 
@@ -144,7 +143,6 @@ class OpenClawRunner:
144
143
  nonce = challenge["payload"]["nonce"]
145
144
  signed_at_ms = int(time.time() * 1000)
146
145
 
147
- # Build auth payload and sign
148
146
  client_id = "cli"
149
147
  client_mode = "cli"
150
148
  role = "operator"
@@ -162,14 +160,12 @@ class OpenClawRunner:
162
160
  )
163
161
  signature = _sign_payload(self._private_key, payload)
164
162
 
165
- # Get public key in base64
166
163
  public_key_bytes = self._private_key.public_key().public_bytes(
167
164
  encoding=serialization.Encoding.Raw,
168
165
  format=serialization.PublicFormat.Raw
169
166
  )
170
167
  pub_b64 = base64.urlsafe_b64encode(public_key_bytes).decode().rstrip("=")
171
168
 
172
- # Send connect request
173
169
  connect_req = {
174
170
  "type": "req",
175
171
  "id": str(uuid.uuid4()),
@@ -203,7 +199,6 @@ class OpenClawRunner:
203
199
 
204
200
  await ws.send(json.dumps(connect_req))
205
201
 
206
- # Wait for response
207
202
  response_msg = await asyncio.wait_for(ws.recv(), timeout=5.0)
208
203
  response = json.loads(response_msg)
209
204
 
@@ -233,7 +228,6 @@ class OpenClawRunner:
233
228
  req_id = str(uuid.uuid4())
234
229
  idempotency_key = f"voice-{uuid.uuid4().hex[:16]}"
235
230
 
236
- # Use chat.send method
237
231
  chat_req = {
238
232
  "type": "req",
239
233
  "id": req_id,
@@ -248,41 +242,50 @@ class OpenClawRunner:
248
242
  await ws.send(json.dumps(chat_req))
249
243
 
250
244
  result_text = ""
245
+ last_text = ""
251
246
 
252
247
  while True:
253
248
  msg = await asyncio.wait_for(ws.recv(), timeout=300.0)
254
249
  data = json.loads(msg)
255
250
 
256
- # Handle events (streaming output)
257
- if data.get("type") == "event":
258
- event = data.get("event", "")
259
- payload = data.get("payload", {})
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", {})
257
+
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
260
267
 
261
- if event == "chat.chunk":
262
- text = payload.get("text", "")
263
- if text and on_output:
264
- on_output(AgentOutput(category="text", content=text))
265
- result_text += text
266
- elif event == "chat.tool":
267
- tool = payload.get("name", payload.get("tool", ""))
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}]"))
270
- elif event == "chat.done":
271
- # Final result
272
- final_text = payload.get("text", "")
273
- if final_text:
274
- result_text = final_text
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"]
275
280
  break
276
- elif event == "chat.error":
277
- error = payload.get("message", "Unknown error")
281
+ elif state == "error":
282
+ error = payload.get("error", "Unknown error")
278
283
  raise RuntimeError(f"Chat error: {error}")
279
284
 
280
- # Handle response (initial ack)
281
285
  elif data.get("type") == "res" and data.get("id") == req_id:
282
286
  if not data.get("ok"):
283
287
  error = data.get("error", {}).get("message", "Unknown error")
284
288
  raise RuntimeError(f"Chat send error: {error}")
285
- # Continue listening for events
286
289
 
287
290
  return AgentResult(text=result_text.strip(), session_id=session_id)
288
291
 
@@ -345,13 +348,13 @@ class OpenClawRunner:
345
348
  effective_session = session_id or self._session_id
346
349
 
347
350
  if on_output:
348
- on_output(AgentOutput(category="text", content="🦞 Connecting to OpenClaw..."))
351
+ on_output(AgentOutput(category="text", content="🦞 Connecting to OpenClaw...\n"))
349
352
 
350
353
  ws = await self._connect_websocket()
351
354
  if ws:
352
355
  try:
353
356
  if on_output:
354
- on_output(AgentOutput(category="text", content="🦞 Connected! Streaming..."))
357
+ on_output(AgentOutput(category="text", content="🦞 Connected! Streaming...\n"))
355
358
  result = await self._run_via_websocket(ws, message, cwd, effective_session, on_output)
356
359
  if result:
357
360
  return result
@@ -359,7 +362,7 @@ class OpenClawRunner:
359
362
  await ws.close()
360
363
 
361
364
  if on_output:
362
- on_output(AgentOutput(category="text", content="WebSocket unavailable, using CLI..."))
365
+ on_output(AgentOutput(category="text", content="WebSocket unavailable, using CLI...\n"))
363
366
 
364
367
  return await self._run_via_cli(message, cwd, effective_session, on_output)
365
368
 
File without changes
File without changes