voice-vibecoder 2.9.7__tar.gz → 2.10.1__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.7 → voice_vibecoder-2.10.1}/CHANGELOG.md +16 -0
  2. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/PKG-INFO +1 -1
  3. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/pyproject.toml +1 -1
  4. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/__init__.py +1 -1
  5. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/code_providers/openclaw.py +41 -7
  6. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/.github/workflows/ci.yml +0 -0
  7. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/.github/workflows/release.yml +0 -0
  8. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/.gitignore +0 -0
  9. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/LICENSE +0 -0
  10. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/README.md +0 -0
  11. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/app.py +0 -0
  12. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/index.html +0 -0
  13. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/app_config.py +0 -0
  14. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/audio.py +0 -0
  15. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/cli_serve.py +0 -0
  16. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/code_providers/__init__.py +0 -0
  17. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/code_providers/claude.py +0 -0
  18. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/code_providers/cursor.py +0 -0
  19. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/code_providers/registry.py +0 -0
  20. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/config.py +0 -0
  21. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/instances.py +0 -0
  22. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/server.py +0 -0
  23. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/session.py +0 -0
  24. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/tools/__init__.py +0 -0
  25. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/tools/agent_task.py +0 -0
  26. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/tools/definitions.py +0 -0
  27. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/tools/dispatch.py +0 -0
  28. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/tools/file_tree.py +0 -0
  29. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/tools/handlers.py +0 -0
  30. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/__init__.py +0 -0
  31. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/callbacks.py +0 -0
  32. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/diff_view.py +0 -0
  33. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/help_modal.py +0 -0
  34. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/panels.py +0 -0
  35. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/screen.py +0 -0
  36. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/setup.py +0 -0
  37. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/state.py +0 -0
  38. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/styles.py +0 -0
  39. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/update_modal.py +0 -0
  40. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/ui/wizard.py +0 -0
  41. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/voice_providers/__init__.py +0 -0
  42. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/voice_providers/openai.py +0 -0
  43. {voice_vibecoder-2.9.7 → voice_vibecoder-2.10.1}/src/voice_vibecoder/worktrees.py +0 -0
@@ -2,6 +2,22 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v2.10.1 (2026-02-21)
6
+
7
+ ### Bug Fixes
8
+
9
+ - Buffer streaming text to avoid fragmented output
10
+ ([`f46e2c1`](https://github.com/snokam/voice-vibecoder/commit/f46e2c13f9eca5a42df4fa8d46ebb56e479d4bea))
11
+
12
+
13
+ ## v2.10.0 (2026-02-21)
14
+
15
+ ### Features
16
+
17
+ - Add tool and thinking stream support
18
+ ([`21a9589`](https://github.com/snokam/voice-vibecoder/commit/21a9589bc11fbc928a6f7a8c9742948c9c38e113))
19
+
20
+
5
21
  ## v2.9.7 (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.7
3
+ Version: 2.10.1
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"
7
+ version = "2.10.1"
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.7"
6
+ __version__ = "2.10.1"
7
7
 
8
8
  from voice_vibecoder.app_config import VoiceConfig
9
9
  from voice_vibecoder.ui.screen import VoiceCodingScreen
@@ -243,6 +243,14 @@ class OpenClawRunner:
243
243
 
244
244
  result_text = ""
245
245
  last_text = ""
246
+ text_buffer = ""
247
+ last_flush_time = time.time()
248
+
249
+ async def flush_buffer():
250
+ nonlocal text_buffer
251
+ if text_buffer and on_output:
252
+ on_output(AgentOutput(category="text", content=text_buffer))
253
+ text_buffer = ""
246
254
 
247
255
  while True:
248
256
  msg = await asyncio.wait_for(ws.recv(), timeout=300.0)
@@ -257,22 +265,48 @@ class OpenClawRunner:
257
265
 
258
266
  if stream == "assistant" and "text" in agent_data:
259
267
  text = agent_data["text"]
260
- # Send delta (new text since last)
268
+ # Buffer delta and flush on sentence boundaries or timeout
261
269
  if text and len(text) > len(last_text):
262
270
  delta = text[len(last_text):]
263
- if on_output and delta:
264
- on_output(AgentOutput(category="text", content=delta))
271
+ text_buffer += delta
265
272
  last_text = text
266
273
  result_text = text
274
+
275
+ # Flush on sentence boundaries or every ~500ms worth of text
276
+ now = time.time()
277
+ should_flush = (
278
+ delta.endswith((".", "!", "?", "\n", ":", "|"))
279
+ or len(text_buffer) > 80
280
+ or (now - last_flush_time) > 0.5
281
+ )
282
+ if should_flush:
283
+ await flush_buffer()
284
+ last_flush_time = now
285
+
286
+ elif stream == "tool":
287
+ # Tool execution
288
+ name = agent_data.get("name", "")
289
+ status = agent_data.get("status", "")
290
+ if name and on_output:
291
+ if status == "started":
292
+ on_output(AgentOutput(category="tool_call", content=f"\n🔧 [{name}] "))
293
+ elif status == "completed":
294
+ result = agent_data.get("result", "")
295
+ if result:
296
+ preview = str(result)[:200]
297
+ on_output(AgentOutput(category="tool_result", content=f"✓ {preview}...\n"))
267
298
 
268
- elif stream == "tool_use" and "name" in agent_data:
269
- tool = agent_data["name"]
270
- if on_output:
271
- on_output(AgentOutput(category="tool_call", content=f"\n[{tool}] "))
299
+ elif stream == "thinking":
300
+ # Reasoning/thinking
301
+ text = agent_data.get("text", "")
302
+ delta = agent_data.get("delta", "")
303
+ if delta and on_output:
304
+ on_output(AgentOutput(category="thinking", content=delta))
272
305
 
273
306
  elif event == "chat":
274
307
  state = payload.get("state")
275
308
  if state == "final":
309
+ await flush_buffer() # Flush remaining text
276
310
  # Get final message
277
311
  msg_content = payload.get("message", {})
278
312
  if isinstance(msg_content, dict) and "content" in msg_content:
File without changes