voice-vibecoder 2.9.5__tar.gz → 2.9.7__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.7}/CHANGELOG.md +16 -0
  2. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/PKG-INFO +1 -1
  3. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/pyproject.toml +1 -1
  4. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/__init__.py +1 -1
  5. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/code_providers/openclaw.py +38 -30
  6. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/.github/workflows/ci.yml +0 -0
  7. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/.github/workflows/release.yml +0 -0
  8. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/.gitignore +0 -0
  9. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/LICENSE +0 -0
  10. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/README.md +0 -0
  11. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/app.py +0 -0
  12. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/index.html +0 -0
  13. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/app_config.py +0 -0
  14. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/audio.py +0 -0
  15. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/cli_serve.py +0 -0
  16. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/code_providers/__init__.py +0 -0
  17. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/code_providers/claude.py +0 -0
  18. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/code_providers/cursor.py +0 -0
  19. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/code_providers/registry.py +0 -0
  20. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/config.py +0 -0
  21. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/instances.py +0 -0
  22. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/server.py +0 -0
  23. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/session.py +0 -0
  24. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/tools/__init__.py +0 -0
  25. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/tools/agent_task.py +0 -0
  26. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/tools/definitions.py +0 -0
  27. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/tools/dispatch.py +0 -0
  28. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/tools/file_tree.py +0 -0
  29. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/tools/handlers.py +0 -0
  30. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/__init__.py +0 -0
  31. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/callbacks.py +0 -0
  32. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/diff_view.py +0 -0
  33. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/help_modal.py +0 -0
  34. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/panels.py +0 -0
  35. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/screen.py +0 -0
  36. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/setup.py +0 -0
  37. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/state.py +0 -0
  38. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/styles.py +0 -0
  39. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/update_modal.py +0 -0
  40. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/ui/wizard.py +0 -0
  41. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/voice_providers/__init__.py +0 -0
  42. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/voice_providers/openai.py +0 -0
  43. {voice_vibecoder-2.9.5 → voice_vibecoder-2.9.7}/src/voice_vibecoder/worktrees.py +0 -0
@@ -2,6 +2,22 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v2.9.7 (2026-02-21)
6
+
7
+ ### Bug Fixes
8
+
9
+ - Handle content as list of blocks in final message
10
+ ([`5ac75ac`](https://github.com/snokam/voice-vibecoder/commit/5ac75ac42cd7bf64cf06e48b102386dedf52ed26))
11
+
12
+
13
+ ## v2.9.6 (2026-02-21)
14
+
15
+ ### Bug Fixes
16
+
17
+ - Handle agent/chat events correctly for streaming
18
+ ([`2abd488`](https://github.com/snokam/voice-vibecoder/commit/2abd488a7e408a4227375bb332f9e2d76ae8df85))
19
+
20
+
5
21
  ## v2.9.5 (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.5
3
+ Version: 2.9.7
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.7"
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.7"
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,55 @@ 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
+ content = msg_content["content"]
280
+ if isinstance(content, list):
281
+ texts = [c.get("text", "") for c in content if isinstance(c, dict) and c.get("type") == "text"]
282
+ result_text = "\n".join(texts)
283
+ elif isinstance(content, str):
284
+ result_text = content
275
285
  break
276
- elif event == "chat.error":
277
- error = payload.get("message", "Unknown error")
286
+ elif state == "error":
287
+ error = payload.get("error", "Unknown error")
278
288
  raise RuntimeError(f"Chat error: {error}")
279
289
 
280
- # Handle response (initial ack)
281
290
  elif data.get("type") == "res" and data.get("id") == req_id:
282
291
  if not data.get("ok"):
283
292
  error = data.get("error", {}).get("message", "Unknown error")
284
293
  raise RuntimeError(f"Chat send error: {error}")
285
- # Continue listening for events
286
294
 
287
295
  return AgentResult(text=result_text.strip(), session_id=session_id)
288
296
 
@@ -345,13 +353,13 @@ class OpenClawRunner:
345
353
  effective_session = session_id or self._session_id
346
354
 
347
355
  if on_output:
348
- on_output(AgentOutput(category="text", content="🦞 Connecting to OpenClaw..."))
356
+ on_output(AgentOutput(category="text", content="🦞 Connecting to OpenClaw...\n"))
349
357
 
350
358
  ws = await self._connect_websocket()
351
359
  if ws:
352
360
  try:
353
361
  if on_output:
354
- on_output(AgentOutput(category="text", content="🦞 Connected! Streaming..."))
362
+ on_output(AgentOutput(category="text", content="🦞 Connected! Streaming...\n"))
355
363
  result = await self._run_via_websocket(ws, message, cwd, effective_session, on_output)
356
364
  if result:
357
365
  return result
@@ -359,7 +367,7 @@ class OpenClawRunner:
359
367
  await ws.close()
360
368
 
361
369
  if on_output:
362
- on_output(AgentOutput(category="text", content="WebSocket unavailable, using CLI..."))
370
+ on_output(AgentOutput(category="text", content="WebSocket unavailable, using CLI...\n"))
363
371
 
364
372
  return await self._run_via_cli(message, cwd, effective_session, on_output)
365
373
 
File without changes
File without changes