agent-cli 0.105.0__tar.gz → 0.105.2__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 (458) hide show
  1. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/pytest.yml +1 -1
  2. {agent_cli-0.105.0 → agent_cli-0.105.2}/PKG-INFO +1 -1
  3. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/audio_format.py +17 -13
  4. agent_cli-0.105.2/agent_cli/server/whisper/backends/audio.py +44 -0
  5. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/base.py +1 -1
  6. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/nemo.py +6 -24
  7. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/transformers.py +49 -3
  8. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/server/whisper.md +1 -0
  9. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/appcast.xml +34 -34
  10. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_audio_format.py +55 -7
  11. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_faster_whisper_backend.py +59 -1
  12. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_nemo_backend.py +39 -27
  13. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_server_whisper.py +57 -1
  14. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_transformers_backend.py +203 -2
  15. agent_cli-0.105.2/tests/test_whisper_backend_audio.py +171 -0
  16. {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude/skills/agent-cli-dev/SKILL.md +0 -0
  17. {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude/skills/agent-cli-dev/examples.md +0 -0
  18. {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude-plugin/README.md +0 -0
  19. {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude-plugin/marketplace.json +0 -0
  20. {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude-plugin/plugin.json +0 -0
  21. {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude-plugin/skills/agent-cli-dev/SKILL.md +0 -0
  22. {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude-plugin/skills/agent-cli-dev/examples.md +0 -0
  23. {agent_cli-0.105.0 → agent_cli-0.105.2}/.cursorrules +0 -0
  24. {agent_cli-0.105.0 → agent_cli-0.105.2}/.dockerignore +0 -0
  25. {agent_cli-0.105.0 → agent_cli-0.105.2}/.env.example +0 -0
  26. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/logo.svg +0 -0
  27. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/release-drafter.yml +0 -0
  28. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/renovate.json +0 -0
  29. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/scripts/check_extras_sync.py +0 -0
  30. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/scripts/check_plugin_skill_sync.py +0 -0
  31. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/scripts/normalize_appcast.py +0 -0
  32. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/scripts/sync_extras.py +0 -0
  33. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/scripts/sync_requirements.py +0 -0
  34. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/automerge.yml +0 -0
  35. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/docker.yml +0 -0
  36. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/docs.yml +0 -0
  37. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/markdown-code-runner.yml +0 -0
  38. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/release-drafter.yml +0 -0
  39. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/release.yml +0 -0
  40. {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/toc.yaml +0 -0
  41. {agent_cli-0.105.0 → agent_cli-0.105.2}/.gitignore +0 -0
  42. {agent_cli-0.105.0 → agent_cli-0.105.2}/.jscpd.json +0 -0
  43. {agent_cli-0.105.0 → agent_cli-0.105.2}/.pre-commit-config.yaml +0 -0
  44. {agent_cli-0.105.0 → agent_cli-0.105.2}/.prompts/docs-review.md +0 -0
  45. {agent_cli-0.105.0 → agent_cli-0.105.2}/.prompts/pr-review.md +0 -0
  46. {agent_cli-0.105.0 → agent_cli-0.105.2}/CLAUDE.md +0 -0
  47. {agent_cli-0.105.0 → agent_cli-0.105.2}/LICENSE +0 -0
  48. {agent_cli-0.105.0 → agent_cli-0.105.2}/LLAMA_SERVER_USAGE.md +0 -0
  49. {agent_cli-0.105.0 → agent_cli-0.105.2}/README.md +0 -0
  50. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/__init__.py +0 -0
  51. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/__main__.py +0 -0
  52. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_extras.json +0 -0
  53. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_overrides/nemo-whisper.txt +0 -0
  54. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/.gitkeep +0 -0
  55. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/audio.txt +0 -0
  56. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/diarization.txt +0 -0
  57. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/faster-whisper.txt +0 -0
  58. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/kokoro.txt +0 -0
  59. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/llm.txt +0 -0
  60. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/memory.txt +0 -0
  61. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/mlx-whisper.txt +0 -0
  62. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/nemo-whisper.txt +0 -0
  63. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/piper.txt +0 -0
  64. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/rag.txt +0 -0
  65. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/server.txt +0 -0
  66. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/speed.txt +0 -0
  67. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/vad.txt +0 -0
  68. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/vectordb.txt +0 -0
  69. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/whisper-transformers.txt +0 -0
  70. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/wyoming.txt +0 -0
  71. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_tools.py +0 -0
  72. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/__init__.py +0 -0
  73. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/_voice_agent_common.py +0 -0
  74. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/assistant.py +0 -0
  75. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/autocorrect.py +0 -0
  76. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/chat.py +0 -0
  77. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/diarize_live_session.py +0 -0
  78. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/memory/__init__.py +0 -0
  79. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/memory/add.py +0 -0
  80. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/memory/proxy.py +0 -0
  81. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/rag_proxy.py +0 -0
  82. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/speak.py +0 -0
  83. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/speakers.py +0 -0
  84. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/transcribe.py +0 -0
  85. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/transcribe_live.py +0 -0
  86. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/voice_edit.py +0 -0
  87. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/api.py +0 -0
  88. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/cli.py +0 -0
  89. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/config.py +0 -0
  90. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/config_cmd.py +0 -0
  91. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/constants.py +0 -0
  92. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/__init__.py +0 -0
  93. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/alignment.py +0 -0
  94. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/audio.py +0 -0
  95. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/chroma.py +0 -0
  96. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/deps.py +0 -0
  97. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/diarization.py +0 -0
  98. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/openai_proxy.py +0 -0
  99. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/process.py +0 -0
  100. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/reranker.py +0 -0
  101. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/speaker_identity.py +0 -0
  102. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/sse.py +0 -0
  103. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/transcription_logger.py +0 -0
  104. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/utils.py +0 -0
  105. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/vad.py +0 -0
  106. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/watch.py +0 -0
  107. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/daemon/__init__.py +0 -0
  108. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/daemon/cli.py +0 -0
  109. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/__init__.py +0 -0
  110. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/_branch_name.py +0 -0
  111. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/_config.py +0 -0
  112. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/_output.py +0 -0
  113. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/cleanup.py +0 -0
  114. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/cli.py +0 -0
  115. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/__init__.py +0 -0
  116. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/aider.py +0 -0
  117. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/base.py +0 -0
  118. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/claude.py +0 -0
  119. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/codex.py +0 -0
  120. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/continue_dev.py +0 -0
  121. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/copilot.py +0 -0
  122. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/cursor_agent.py +0 -0
  123. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/gemini.py +0 -0
  124. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/opencode.py +0 -0
  125. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/registry.py +0 -0
  126. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/__init__.py +0 -0
  127. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/base.py +0 -0
  128. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/cursor.py +0 -0
  129. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/emacs.py +0 -0
  130. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/jetbrains.py +0 -0
  131. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/nano.py +0 -0
  132. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/neovim.py +0 -0
  133. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/registry.py +0 -0
  134. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/sublime.py +0 -0
  135. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/vim.py +0 -0
  136. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/vscode.py +0 -0
  137. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/zed.py +0 -0
  138. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/hooks.py +0 -0
  139. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/launch.py +0 -0
  140. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/project.py +0 -0
  141. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/registry.py +0 -0
  142. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/skill/SKILL.md +0 -0
  143. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/skill/examples.md +0 -0
  144. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/__init__.py +0 -0
  145. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/apple_terminal.py +0 -0
  146. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/base.py +0 -0
  147. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/cmux.py +0 -0
  148. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/gnome.py +0 -0
  149. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/iterm2.py +0 -0
  150. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/kitty.py +0 -0
  151. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/registry.py +0 -0
  152. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/tmux.py +0 -0
  153. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/warp.py +0 -0
  154. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/zellij.py +0 -0
  155. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/worktree.py +0 -0
  156. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/docs_gen.py +0 -0
  157. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/example-config.toml +0 -0
  158. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/__init__.py +0 -0
  159. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/common.py +0 -0
  160. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/extras.py +0 -0
  161. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/hotkeys.py +0 -0
  162. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/launchd.py +0 -0
  163. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/service_config.py +0 -0
  164. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/services.py +0 -0
  165. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/systemd.py +0 -0
  166. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/__init__.py +0 -0
  167. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_files.py +0 -0
  168. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_filters.py +0 -0
  169. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_git.py +0 -0
  170. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_indexer.py +0 -0
  171. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_ingest.py +0 -0
  172. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_persistence.py +0 -0
  173. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_prompt.py +0 -0
  174. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_retrieval.py +0 -0
  175. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_store.py +0 -0
  176. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_streaming.py +0 -0
  177. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_tasks.py +0 -0
  178. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/api.py +0 -0
  179. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/client.py +0 -0
  180. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/engine.py +0 -0
  181. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/entities.py +0 -0
  182. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/models.py +0 -0
  183. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/opts.py +0 -0
  184. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/py.typed +0 -0
  185. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/__init__.py +0 -0
  186. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_indexer.py +0 -0
  187. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_indexing.py +0 -0
  188. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_prompt.py +0 -0
  189. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_retriever.py +0 -0
  190. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_store.py +0 -0
  191. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_utils.py +0 -0
  192. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/api.py +0 -0
  193. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/client.py +0 -0
  194. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/engine.py +0 -0
  195. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/models.py +0 -0
  196. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/.runtime/.gitkeep +0 -0
  197. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/__init__.py +0 -0
  198. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/linux-hotkeys/README.md +0 -0
  199. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/linux-hotkeys/toggle-autocorrect.sh +0 -0
  200. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/linux-hotkeys/toggle-transcription.sh +0 -0
  201. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/linux-hotkeys/toggle-voice-edit.sh +0 -0
  202. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/macos-hotkeys/README.md +0 -0
  203. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/macos-hotkeys/skhd-config-example +0 -0
  204. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/macos-hotkeys/toggle-autocorrect.sh +0 -0
  205. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/macos-hotkeys/toggle-transcription.sh +0 -0
  206. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/macos-hotkeys/toggle-voice-edit.sh +0 -0
  207. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/nvidia-asr-server/README.md +0 -0
  208. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/nvidia-asr-server/pyproject.toml +0 -0
  209. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/nvidia-asr-server/server.py +0 -0
  210. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/nvidia-asr-server/shell.nix +0 -0
  211. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/nvidia-asr-server/uv.lock +0 -0
  212. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/run-openwakeword.sh +0 -0
  213. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/setup-linux-hotkeys.sh +0 -0
  214. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/setup-linux.sh +0 -0
  215. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/setup-macos-hotkeys.sh +0 -0
  216. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/setup-macos.sh +0 -0
  217. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/setup-windows.ps1 +0 -0
  218. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/start-all-services-windows.ps1 +0 -0
  219. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/start-all-services.sh +0 -0
  220. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/__init__.py +0 -0
  221. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/cli.py +0 -0
  222. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/common.py +0 -0
  223. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/model_manager.py +0 -0
  224. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/model_registry.py +0 -0
  225. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/proxy/__init__.py +0 -0
  226. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/proxy/api.py +0 -0
  227. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/proxy/diarization.py +0 -0
  228. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/streaming.py +0 -0
  229. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/__init__.py +0 -0
  230. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/api.py +0 -0
  231. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/backends/__init__.py +0 -0
  232. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/backends/base.py +0 -0
  233. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/backends/kokoro.py +0 -0
  234. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/backends/piper.py +0 -0
  235. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/model_manager.py +0 -0
  236. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/model_registry.py +0 -0
  237. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/wyoming_handler.py +0 -0
  238. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/__init__.py +0 -0
  239. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/api.py +0 -0
  240. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/__init__.py +0 -0
  241. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/faster_whisper.py +0 -0
  242. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/mlx.py +0 -0
  243. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/languages.py +0 -0
  244. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/model_manager.py +0 -0
  245. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/model_registry.py +0 -0
  246. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/wyoming_handler.py +0 -0
  247. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/__init__.py +0 -0
  248. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/_wyoming_utils.py +0 -0
  249. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/asr.py +0 -0
  250. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/llm.py +0 -0
  251. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/tts.py +0 -0
  252. {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/wake_word.py +0 -0
  253. {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/diarization.Dockerfile +0 -0
  254. {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/docker-compose.yml +0 -0
  255. {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/memory-proxy.Dockerfile +0 -0
  256. {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/rag-proxy.Dockerfile +0 -0
  257. {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/transcribe-proxy.Dockerfile +0 -0
  258. {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/tts.Dockerfile +0 -0
  259. {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/whisper.Dockerfile +0 -0
  260. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/CNAME +0 -0
  261. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/architecture/index.md +0 -0
  262. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/architecture/memory.md +0 -0
  263. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/architecture/rag.md +0 -0
  264. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/assistant.md +0 -0
  265. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/autocorrect.md +0 -0
  266. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/chat.md +0 -0
  267. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/config.md +0 -0
  268. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/daemon.md +0 -0
  269. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/dev.md +0 -0
  270. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/diarize-live-session.md +0 -0
  271. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/index.md +0 -0
  272. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/install-extras.md +0 -0
  273. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/install-hotkeys.md +0 -0
  274. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/install-services.md +0 -0
  275. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/memory.md +0 -0
  276. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/rag-proxy.md +0 -0
  277. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/server/index.md +0 -0
  278. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/server/transcribe-proxy.md +0 -0
  279. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/server/tts.md +0 -0
  280. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/speak.md +0 -0
  281. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/speakers.md +0 -0
  282. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/start-services.md +0 -0
  283. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/transcribe-live.md +0 -0
  284. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/transcribe.md +0 -0
  285. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/voice-edit.md +0 -0
  286. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/configuration.md +0 -0
  287. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/getting-started.md +0 -0
  288. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/iOS_Shortcut_Guide.md +0 -0
  289. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/index.md +0 -0
  290. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/docker.md +0 -0
  291. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/index.md +0 -0
  292. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/linux.md +0 -0
  293. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/macos-app.md +0 -0
  294. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/macos.md +0 -0
  295. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/nixos.md +0 -0
  296. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/windows.md +0 -0
  297. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/logo-avatar.svg +0 -0
  298. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/logo-clean.svg +0 -0
  299. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
  300. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/run_markdown_code_runner.py +0 -0
  301. {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/system-integration.md +0 -0
  302. {agent_cli-0.105.0 → agent_cli-0.105.2}/example.agent-cli-config.toml +0 -0
  303. {agent_cli-0.105.0 → agent_cli-0.105.2}/justfile +0 -0
  304. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Package.resolved +0 -0
  305. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Package.swift +0 -0
  306. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/README.md +0 -0
  307. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Resources/AgentCLI.entitlements +0 -0
  308. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Resources/Info.plist +0 -0
  309. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Resources/dmg-background.svg +0 -0
  310. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AgentCLIApp.swift +0 -0
  311. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AgentCommand.swift +0 -0
  312. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AgentCommandRunner.swift +0 -0
  313. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AgentRuntime.swift +0 -0
  314. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AppDelegate.swift +0 -0
  315. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AppMetadata.swift +0 -0
  316. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AppUpdater.swift +0 -0
  317. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/BootstrapState.swift +0 -0
  318. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/CommandResult.swift +0 -0
  319. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/ConfigurableHotkeyController.swift +0 -0
  320. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/FocusedTextTarget.swift +0 -0
  321. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/LiveTranscriptionPreview.swift +0 -0
  322. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/LoginItemController.swift +0 -0
  323. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/MenuActivityStatus.swift +0 -0
  324. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/MenuActivityStatusRow.swift +0 -0
  325. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/MenuActivityTracker.swift +0 -0
  326. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/MenuBarIcon.swift +0 -0
  327. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/RecentTranscriptionReader.swift +0 -0
  328. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/RecordingIndicatorController.swift +0 -0
  329. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/RecordingSoundSettings.swift +0 -0
  330. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/RuntimeSettings.swift +0 -0
  331. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/SettingsWindowController.swift +0 -0
  332. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/Shortcuts.swift +0 -0
  333. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/StatusMenuController.swift +0 -0
  334. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/TranscriptPasteController.swift +0 -0
  335. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/TranscriptionSettings.swift +0 -0
  336. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/VoiceLevelOverlay.swift +0 -0
  337. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/AgentCommandTests.swift +0 -0
  338. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/ConfigurableHotkeyControllerTests.swift +0 -0
  339. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/LivePreviewSwiftTestingTests.swift +0 -0
  340. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/LiveTranscriptionPreviewTests.swift +0 -0
  341. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/LoginItemControllerTests.swift +0 -0
  342. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/RecentTranscriptionReaderTests.swift +0 -0
  343. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/RecordingIndicatorControllerTests.swift +0 -0
  344. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/ShortcutDefaultsTests.swift +0 -0
  345. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/VoiceLevelMeterTests.swift +0 -0
  346. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/build-macos-app.sh +0 -0
  347. {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/test-macos-app-e2e.sh +0 -0
  348. {agent_cli-0.105.0 → agent_cli-0.105.2}/pyproject.toml +0 -0
  349. {agent_cli-0.105.0 → agent_cli-0.105.2}/reddit.md +0 -0
  350. {agent_cli-0.105.0 → agent_cli-0.105.2}/shell.nix +0 -0
  351. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/__init__.py +0 -0
  352. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/__init__.py +0 -0
  353. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_fix_my_text.py +0 -0
  354. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_interactive.py +0 -0
  355. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_interactive_extra.py +0 -0
  356. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_memory_add.py +0 -0
  357. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_speak.py +0 -0
  358. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_speak_e2e.py +0 -0
  359. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_speakers.py +0 -0
  360. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_transcribe.py +0 -0
  361. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_transcribe_agent.py +0 -0
  362. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_transcribe_e2e.py +0 -0
  363. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_transcribe_live.py +0 -0
  364. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_transcribe_recovery.py +0 -0
  365. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_tts_common.py +0 -0
  366. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_tts_common_extra.py +0 -0
  367. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_voice_agent_common.py +0 -0
  368. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_voice_edit.py +0 -0
  369. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_voice_edit_e2e.py +0 -0
  370. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_wake_word_assistant.py +0 -0
  371. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/conftest.py +0 -0
  372. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/__init__.py +0 -0
  373. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_audio.py +0 -0
  374. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_audio_levels.py +0 -0
  375. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_chroma.py +0 -0
  376. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_sse.py +0 -0
  377. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_vad.py +0 -0
  378. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_watch.py +0 -0
  379. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/__init__.py +0 -0
  380. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_cleanup.py +0 -0
  381. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_cli.py +0 -0
  382. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_coding_agents.py +0 -0
  383. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_editors.py +0 -0
  384. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_hooks.py +0 -0
  385. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_launch.py +0 -0
  386. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_project.py +0 -0
  387. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_terminals.py +0 -0
  388. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_verification.py +0 -0
  389. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_worktree.py +0 -0
  390. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/install/__init__.py +0 -0
  391. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/install/test_extras.py +0 -0
  392. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/install/test_launchd.py +0 -0
  393. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/__init__.py +0 -0
  394. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_api_health.py +0 -0
  395. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_api_integration_liveish.py +0 -0
  396. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_client.py +0 -0
  397. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_engine.py +0 -0
  398. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_files.py +0 -0
  399. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_filters.py +0 -0
  400. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_git_integration.py +0 -0
  401. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_indexer.py +0 -0
  402. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_memory_integration.py +0 -0
  403. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_proxy_passthrough.py +0 -0
  404. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_store.py +0 -0
  405. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_utils.py +0 -0
  406. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/mocks/__init__.py +0 -0
  407. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/mocks/audio.py +0 -0
  408. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/mocks/llm.py +0 -0
  409. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/mocks/wyoming.py +0 -0
  410. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/__init__.py +0 -0
  411. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_api.py +0 -0
  412. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_engine.py +0 -0
  413. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_history.py +0 -0
  414. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_indexer.py +0 -0
  415. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_indexing.py +0 -0
  416. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_rag_client.py +0 -0
  417. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_rag_integration_liveish.py +0 -0
  418. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_rag_proxy_passthrough.py +0 -0
  419. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_retriever.py +0 -0
  420. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_store.py +0 -0
  421. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_utils.py +0 -0
  422. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_alignment.py +0 -0
  423. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_api.py +0 -0
  424. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_api_integration.py +0 -0
  425. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_asr.py +0 -0
  426. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_asr_recovery.py +0 -0
  427. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_audio_e2e.py +0 -0
  428. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_cli.py +0 -0
  429. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_config.py +0 -0
  430. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_config_cmd.py +0 -0
  431. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_daemon.py +0 -0
  432. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_diarization.py +0 -0
  433. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_diarize_live_session.py +0 -0
  434. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_docs_gen.py +0 -0
  435. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_env_vars.py +0 -0
  436. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_json_output.py +0 -0
  437. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_llm.py +0 -0
  438. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_llm_gemini.py +0 -0
  439. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_macos_app.py +0 -0
  440. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_memory_tools.py +0 -0
  441. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_mlx_backend.py +0 -0
  442. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_normalize_appcast.py +0 -0
  443. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_process_manager.py +0 -0
  444. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_proxy_diarization.py +0 -0
  445. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_requires_extras.py +0 -0
  446. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_server_streaming.py +0 -0
  447. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_server_tts.py +0 -0
  448. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_server_tts_wyoming.py +0 -0
  449. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_server_whisper_wyoming.py +0 -0
  450. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_services.py +0 -0
  451. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_speaker_identity.py +0 -0
  452. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_tools.py +0 -0
  453. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_tts.py +0 -0
  454. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_utils.py +0 -0
  455. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_wake_word.py +0 -0
  456. {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_wyoming_utils.py +0 -0
  457. {agent_cli-0.105.0 → agent_cli-0.105.2}/uv.lock +0 -0
  458. {agent_cli-0.105.0 → agent_cli-0.105.2}/zensical.toml +0 -0
@@ -43,6 +43,6 @@ jobs:
43
43
  run: uv run --extra audio --extra diarization --extra llm --extra rag --extra memory --extra vad --extra faster-whisper --extra piper --extra kokoro --extra server --extra speed --extra test pytest -vvv
44
44
  - name: Upload coverage reports to Codecov
45
45
  if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13'
46
- uses: codecov/codecov-action@v7.0.0
46
+ uses: codecov/codecov-action@v7.1.0
47
47
  env:
48
48
  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agent-cli
3
- Version: 0.105.0
3
+ Version: 0.105.2
4
4
  Summary: A suite of AI-powered command-line tools for text correction, audio transcription, and voice assistance.
5
5
  Project-URL: Homepage, https://github.com/basnijholt/agent-cli
6
6
  Author-email: Bas Nijholt <bas@nijho.lt>
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
5
6
  import io
6
7
  import logging
7
8
  import shutil
@@ -152,13 +153,13 @@ def convert_audio_to_wyoming_format(
152
153
  output_path.unlink(missing_ok=True)
153
154
 
154
155
 
155
- def convert_audio_to_wav_format(
156
+ async def convert_audio_to_wav_format(
156
157
  audio_data: bytes,
157
158
  source_filename: str,
158
159
  *,
159
- timeout: int | None = 60,
160
+ timeout: int | None = 60, # noqa: ASYNC109 - bounds and cleans up the child process
160
161
  ) -> bytes:
161
- """Convert audio data to a 16kHz mono 16-bit PCM WAV container using FFmpeg."""
162
+ """Convert to 16kHz mono 16-bit WAV, stopping FFmpeg on cancellation or timeout."""
162
163
  if not shutil.which("ffmpeg"):
163
164
  msg = "FFmpeg not found in PATH. Please install FFmpeg to convert audio formats."
164
165
  raise RuntimeError(msg)
@@ -185,20 +186,23 @@ def convert_audio_to_wav_format(
185
186
  ]
186
187
 
187
188
  logger.debug("Running FFmpeg WAV conversion: %s", " ".join(cmd))
189
+ process = await asyncio.create_subprocess_exec(
190
+ *cmd,
191
+ stdout=asyncio.subprocess.DEVNULL,
192
+ stderr=asyncio.subprocess.PIPE,
193
+ )
188
194
  try:
189
- result = subprocess.run(
190
- cmd,
191
- capture_output=True,
192
- text=False,
193
- check=False,
194
- timeout=timeout,
195
- )
196
- except subprocess.TimeoutExpired as e:
195
+ _, stderr = await asyncio.wait_for(process.communicate(), timeout=timeout)
196
+ except TimeoutError as e:
197
197
  msg = f"FFmpeg conversion timed out after {timeout} seconds"
198
198
  raise RuntimeError(msg) from e
199
+ finally:
200
+ if process.returncode is None:
201
+ process.kill()
202
+ await process.communicate()
199
203
 
200
- if result.returncode != 0:
201
- stderr_text = result.stderr.decode("utf-8", errors="replace")
204
+ if process.returncode != 0:
205
+ stderr_text = (stderr or b"").decode("utf-8", errors="replace")
202
206
  logger.error("FFmpeg WAV conversion failed: %s", stderr_text)
203
207
  msg = f"FFmpeg WAV conversion failed: {stderr_text}"
204
208
  raise RuntimeError(msg)
@@ -0,0 +1,44 @@
1
+ """Audio preparation for ASR backends that require PCM WAV input."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import io
6
+ import wave
7
+
8
+ from agent_cli import constants
9
+ from agent_cli.core.audio_format import convert_audio_to_wav_format
10
+ from agent_cli.server.whisper.backends.base import InvalidAudioError
11
+
12
+
13
+ async def prepare_wav_audio(
14
+ audio: bytes,
15
+ source_filename: str | None,
16
+ *,
17
+ backend_label: str,
18
+ ) -> bytes:
19
+ """Return 16kHz mono 16-bit PCM WAV bytes, converting only when needed.
20
+
21
+ Container validity alone is insufficient: Transformers reads samples as
22
+ int16 mono. Normalize other sample formats before model-worker dispatch.
23
+ Backends with their own decoders, such as faster-whisper, do not need this.
24
+ """
25
+ try:
26
+ with wave.open(io.BytesIO(audio), "rb") as wav_file:
27
+ if (
28
+ wav_file.getsampwidth() == constants.AUDIO_FORMAT_WIDTH
29
+ and wav_file.getnchannels() == constants.AUDIO_CHANNELS
30
+ and wav_file.getframerate() == constants.AUDIO_RATE
31
+ ):
32
+ return audio
33
+ except (wave.Error, EOFError, RuntimeError):
34
+ # wave can raise RuntimeError when a malformed RIFF chunk seeks past EOF.
35
+ pass
36
+
37
+ try:
38
+ return await convert_audio_to_wav_format(audio, source_filename or "audio")
39
+ except RuntimeError as exc:
40
+ msg = (
41
+ f"Unsupported audio format for {backend_label}. "
42
+ "Provide a valid WAV file or install ffmpeg to convert uploads."
43
+ )
44
+ raise InvalidAudioError(msg) from exc
@@ -89,7 +89,7 @@ class WhisperBackend(Protocol):
89
89
  """Transcribe audio data.
90
90
 
91
91
  Args:
92
- audio: Audio data as bytes (WAV format, 16kHz, 16-bit, mono)
92
+ audio: Encoded audio bytes (16kHz mono 16-bit PCM WAV preferred).
93
93
  source_filename: Optional filename to help detect audio format.
94
94
  language: Language code or None for auto-detection
95
95
  task: "transcribe" or "translate" (to English)
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import inspect
7
- import io
8
7
  import logging
9
8
  import os
10
9
  import sys
@@ -18,11 +17,10 @@ from multiprocessing import get_context
18
17
  from pathlib import Path
19
18
  from typing import Any, Literal
20
19
 
21
- from agent_cli.core.audio_format import convert_audio_to_wav_format
22
20
  from agent_cli.core.process import set_process_title
21
+ from agent_cli.server.whisper.backends.audio import prepare_wav_audio
23
22
  from agent_cli.server.whisper.backends.base import (
24
23
  BackendConfig,
25
- InvalidAudioError,
26
24
  TranscriptionResult,
27
25
  )
28
26
 
@@ -182,26 +180,6 @@ def _audio_duration_seconds(wav_path: str) -> float:
182
180
  return 0.0
183
181
 
184
182
 
185
- def _prepare_audio_for_nemo(audio_bytes: bytes, source_filename: str | None) -> bytes:
186
- """Return a WAV container suitable for NeMo file-path transcription."""
187
- try:
188
- with wave.open(io.BytesIO(audio_bytes), "rb"):
189
- return audio_bytes
190
- except (wave.Error, EOFError):
191
- pass
192
-
193
- filename = source_filename or "audio"
194
- try:
195
- return convert_audio_to_wav_format(audio_bytes, filename)
196
- except RuntimeError as exc:
197
- logger.warning("FFmpeg conversion failed for NeMo Whisper: %s", exc)
198
- msg = (
199
- "Unsupported audio format for NeMo Whisper. "
200
- "Provide a WAV file or install ffmpeg to convert uploads."
201
- )
202
- raise InvalidAudioError(msg) from exc
203
-
204
-
205
183
  def _build_transcribe_kwargs(
206
184
  transcribe_func: Any,
207
185
  *,
@@ -433,7 +411,11 @@ class NemoWhisperBackend:
433
411
  )
434
412
  self._warned_initial_prompt_ignored = True
435
413
 
436
- audio = await asyncio.to_thread(_prepare_audio_for_nemo, audio, source_filename)
414
+ audio = await prepare_wav_audio(
415
+ audio,
416
+ source_filename,
417
+ backend_label="NeMo Whisper",
418
+ )
437
419
 
438
420
  kwargs: dict[str, Any] = {
439
421
  "language": language,
@@ -6,6 +6,7 @@ import asyncio
6
6
  import logging
7
7
  import tempfile
8
8
  import time
9
+ import traceback
9
10
  import wave
10
11
  from concurrent.futures import ProcessPoolExecutor
11
12
  from dataclasses import dataclass
@@ -14,6 +15,7 @@ from pathlib import Path
14
15
  from typing import Any, Literal
15
16
 
16
17
  from agent_cli.core.process import set_process_title
18
+ from agent_cli.server.whisper.backends.audio import prepare_wav_audio
17
19
  from agent_cli.server.whisper.backends.base import (
18
20
  BackendConfig,
19
21
  TranscriptionResult,
@@ -428,8 +430,8 @@ def _transcribe_with_generate(
428
430
  )
429
431
 
430
432
 
431
- def _transcribe_in_subprocess(kwargs: dict[str, Any]) -> dict[str, Any]:
432
- """Run transcription in subprocess. Reuses model from _state."""
433
+ def _transcribe_with_loaded_model(kwargs: dict[str, Any]) -> dict[str, Any]:
434
+ """Dispatch transcription using the model retained in this worker."""
433
435
  if _state.model is None or _state.processor is None:
434
436
  msg = "Model not loaded in subprocess. Call _load_model_in_subprocess first."
435
437
  raise RuntimeError(msg)
@@ -479,6 +481,44 @@ def _transcribe_in_subprocess(kwargs: dict[str, Any]) -> dict[str, Any]:
479
481
  )
480
482
 
481
483
 
484
+ def _clear_exception_frames(exc: BaseException) -> None:
485
+ """Release helper locals retained by an exception and its chained failures."""
486
+ pending = [exc]
487
+ seen: set[int] = set()
488
+ while pending:
489
+ current = pending.pop()
490
+ if id(current) in seen:
491
+ continue
492
+ seen.add(id(current))
493
+ traceback.clear_frames(current.__traceback__)
494
+ pending.extend(
495
+ error for error in (current.__cause__, current.__context__) if error is not None
496
+ )
497
+ if isinstance(current, BaseExceptionGroup):
498
+ pending.extend(current.exceptions)
499
+
500
+
501
+ def _transcribe_in_subprocess(kwargs: dict[str, Any]) -> dict[str, Any]:
502
+ """Transcribe and return unused CUDA memory while keeping model weights loaded."""
503
+ if not _state.device or not _state.device.startswith("cuda"):
504
+ return _transcribe_with_loaded_model(kwargs)
505
+
506
+ import torch # noqa: PLC0415
507
+
508
+ try:
509
+ return _transcribe_with_loaded_model(kwargs)
510
+ except BaseException as exc:
511
+ # Failed helper frames otherwise keep temporary tensors live until the
512
+ # executor serializes the exception, which happens after our finally.
513
+ # Clear locals without losing the traceback's function names and lines.
514
+ _clear_exception_frames(exc)
515
+ raise
516
+ finally:
517
+ # Helper frames have returned, so their temporary tensors are now free.
518
+ # Only unused allocator blocks are released; model weights stay resident.
519
+ torch.cuda.empty_cache()
520
+
521
+
482
522
  class TransformersWhisperBackend:
483
523
  """ASR backend using HuggingFace transformers.
484
524
 
@@ -554,7 +594,7 @@ class TransformersWhisperBackend:
554
594
  self,
555
595
  audio: bytes,
556
596
  *,
557
- source_filename: str | None = None, # noqa: ARG002
597
+ source_filename: str | None = None,
558
598
  language: str | None = None,
559
599
  task: Literal["transcribe", "translate"] = "transcribe",
560
600
  initial_prompt: str | None = None,
@@ -567,6 +607,12 @@ class TransformersWhisperBackend:
567
607
  msg = "Model not loaded. Call load() first."
568
608
  raise RuntimeError(msg)
569
609
 
610
+ audio = await prepare_wav_audio(
611
+ audio,
612
+ source_filename,
613
+ backend_label="transformers ASR",
614
+ )
615
+
570
616
  # Write audio to temp file for wave parsing in subprocess
571
617
  with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
572
618
  tmp.write(audio)
@@ -55,6 +55,7 @@ Run a local ASR server with automatic backend selection based on your platform:
55
55
  - **OpenAI-compatible API** at `/v1/audio/transcriptions` - drop-in replacement for OpenAI's Whisper API
56
56
  - **Wyoming protocol** for [Home Assistant](https://www.home-assistant.io/) voice integration (Wyoming is the standard protocol for local voice services)
57
57
  - **TTL-based memory management** - models unload after idle period, freeing RAM/VRAM
58
+ - **CUDA cache cleanup** - the transformers backend releases unused GPU cache after each transcription while keeping model weights loaded
58
59
  - **Multiple models** - run different model sizes with independent TTLs
59
60
  - **Background preloading** - downloads start at startup without blocking; use `--preload` to wait
60
61
  - **Multi-platform support** - automatically uses the optimal backend for your hardware (`auto` switches to `nemo` for Parakeet models)
@@ -4,6 +4,40 @@
4
4
  <title>Agent CLI Updates</title>
5
5
  <link>https://github.com/basnijholt/agent-cli</link>
6
6
  <description>Signed Agent CLI macOS app updates.</description>
7
+ <item>
8
+ <title>0.105.1</title>
9
+ <pubDate>Tue, 08 Sep 2026 13:10:30 +0000</pubDate>
10
+ <link>https://github.com/basnijholt/agent-cli</link>
11
+ <sparkle:version>205</sparkle:version>
12
+ <sparkle:shortVersionString>0.105.1</sparkle:shortVersionString>
13
+ <sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion>
14
+ <sparkle:hardwareRequirements>arm64</sparkle:hardwareRequirements>
15
+ <description sparkle:format="markdown"><![CDATA[## Bug fixes
16
+
17
+ - Release unused CUDA cache after each transcription in the transformers ASR backend, keeping model weights loaded for the next request. Long recordings no longer leave their peak temporary GPU allocation reserved throughout the idle timeout. In a Qwen3-ASR test, idle process VRAM dropped from 10.89 GiB to 4.13 GiB. (#642)
18
+ - Clear temporary tensor references held by failed-request tracebacks, including chained and grouped exceptions, before releasing CUDA cache. CPU and MPS behavior is unchanged. (#642)
19
+
20
+ **Full changelog:** https://github.com/basnijholt/agent-cli/compare/v0.105.0...v0.105.1
21
+
22
+ ]]></description>
23
+ <enclosure url="https://github.com/basnijholt/agent-cli/releases/download/v0.105.1/AgentCLI.zip" length="18455354" type="application/octet-stream" sparkle:edSignature="vZqacIut3YOxE/I4mgUeHiI9jviHLgzymE3jK8R7aLEooAPZhNqkzxTFpB95iWShRRWfR8lf9T4ztVFs0EHUDw=="/>
24
+ </item>
25
+ <item>
26
+ <title>0.105.0</title>
27
+ <pubDate>Tue, 08 Sep 2026 10:29:52 +0000</pubDate>
28
+ <link>https://github.com/basnijholt/agent-cli</link>
29
+ <sparkle:version>204</sparkle:version>
30
+ <sparkle:shortVersionString>0.105.0</sparkle:shortVersionString>
31
+ <sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion>
32
+ <sparkle:hardwareRequirements>arm64</sparkle:hardwareRequirements>
33
+ <description sparkle:format="markdown"><![CDATA[## What’s Changed
34
+
35
+ * Prevent Qwen long-audio truncation (#641) @basnijholt
36
+ * Publish diarization Docker image (#640) @basnijholt
37
+
38
+ ]]></description>
39
+ <enclosure url="https://github.com/basnijholt/agent-cli/releases/download/v0.105.0/AgentCLI.zip" length="18454913" type="application/octet-stream" sparkle:edSignature="Z3253GuNAV6GC2V4Qib0Bm7m8bSdlWHPiXeHIZNa06qAJaY+qRXzrSPRyT8iZW5GnkZOJHciIbqvkkoJ9/DoCw=="/>
40
+ </item>
7
41
  <item>
8
42
  <title>0.104.0</title>
9
43
  <pubDate>Mon, 07 Sep 2026 21:38:19 +0000</pubDate>
@@ -174,39 +208,5 @@
174
208
  ]]></description>
175
209
  <enclosure url="https://github.com/basnijholt/agent-cli/releases/download/v0.101.0/AgentCLI.zip" length="22912290" type="application/octet-stream" sparkle:edSignature="9re7kqubAoQgSpsmDvAT/LCWy8/tI1RzR2uGkb+BCwGJbtfvuzDU2ETm/JD+lpQt3Ayik6ULAOv+/5S6JSmdBA=="/>
176
210
  </item>
177
- <item>
178
- <title>0.100.0</title>
179
- <pubDate>Wed, 10 Jun 2026 17:59:16 +0000</pubDate>
180
- <link>https://github.com/basnijholt/agent-cli</link>
181
- <sparkle:version>195</sparkle:version>
182
- <sparkle:shortVersionString>0.100.0</sparkle:shortVersionString>
183
- <sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion>
184
- <sparkle:hardwareRequirements>arm64</sparkle:hardwareRequirements>
185
- <description sparkle:format="markdown"><![CDATA[## What’s Changed
186
-
187
- * Add cmux support to agent-cli dev (#589) @basnijholt
188
- * ⬆️ Update sparkle-project/Sparkle to v2.9.3 (#587) @[renovate[bot]](https://github.com/apps/renovate)
189
- * [pre-commit.ci] pre-commit autoupdate (#588) @[pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci)
190
- * ⬆️ Update codecov/codecov-action action to v7 (#586) @[renovate[bot]](https://github.com/apps/renovate)
191
- * ⬆️ Update codecov/codecov-action action to v6.0.2 (#585) @[renovate[bot]](https://github.com/apps/renovate)
192
-
193
- ]]></description>
194
- <enclosure url="https://github.com/basnijholt/agent-cli/releases/download/v0.100.0/AgentCLI.zip" length="22908645" type="application/octet-stream" sparkle:edSignature="2xn/3yQOqyqwCQ5871EAgFra/D/z6UrDl1B+ZxUh7PnOxJpL4Oe44UzQgjFB+LJXzqrY6Kmqvu9GBmk+3aZ1Ag=="/>
195
- </item>
196
- <item>
197
- <title>0.99.0</title>
198
- <pubDate>Fri, 05 Jun 2026 22:57:28 +0000</pubDate>
199
- <link>https://github.com/basnijholt/agent-cli</link>
200
- <sparkle:version>194</sparkle:version>
201
- <sparkle:shortVersionString>0.99.0</sparkle:shortVersionString>
202
- <sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion>
203
- <sparkle:hardwareRequirements>arm64</sparkle:hardwareRequirements>
204
- <description sparkle:format="markdown"><![CDATA[## What’s Changed
205
-
206
- * Add live transcription preview (#571) @basnijholt
207
-
208
- ]]></description>
209
- <enclosure url="https://github.com/basnijholt/agent-cli/releases/download/v0.99.0/AgentCLI.zip" length="22924494" type="application/octet-stream" sparkle:edSignature="COI8nqDd0cBtOO9jw/QRmsucsxE0SPiSgNF/o+s/nv3FCjSXy0C+gZX/bIFtCYyz/DYkff+IaHaLzhw8Z5aEBQ=="/>
210
- </item>
211
211
  </channel>
212
212
  </rss>
@@ -2,9 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
5
6
  import shutil
6
7
  import struct
7
- from unittest.mock import MagicMock, patch
8
+ import sys
9
+ from pathlib import Path
10
+ from unittest.mock import AsyncMock, MagicMock, patch
8
11
 
9
12
  import pytest
10
13
 
@@ -78,28 +81,73 @@ def test_convert_audio_arguments() -> None:
78
81
  assert "s16le" in cmd
79
82
 
80
83
 
81
- def test_convert_audio_to_wav_arguments() -> None:
84
+ @pytest.mark.asyncio
85
+ async def test_convert_audio_to_wav_arguments() -> None:
82
86
  """Regression test: WAV conversion should produce a PCM WAV container."""
83
87
  with (
84
88
  patch("shutil.which", return_value="/usr/bin/ffmpeg"),
85
- patch("subprocess.run") as mock_run,
89
+ patch("asyncio.create_subprocess_exec") as mock_run,
86
90
  patch("pathlib.Path.read_bytes", return_value=b"wav_data"),
87
91
  patch("shutil.rmtree"),
88
92
  ):
89
- mock_run.return_value = MagicMock(returncode=0, stdout=b"", stderr=b"")
93
+ mock_run.return_value = MagicMock(
94
+ returncode=0, communicate=AsyncMock(return_value=(None, b""))
95
+ )
90
96
 
91
- converted = audio_format.convert_audio_to_wav_format(b"input_data", "test.mp3")
97
+ converted = await audio_format.convert_audio_to_wav_format(b"input_data", "test.mp3")
92
98
 
93
99
  assert converted == b"wav_data"
94
100
  args, kwargs = mock_run.call_args
95
- assert kwargs.get("text") is False
96
- cmd = args[0]
101
+ assert kwargs["stderr"] == asyncio.subprocess.PIPE
102
+ cmd = args
97
103
  assert cmd[0] == "ffmpeg"
98
104
  assert "-acodec" in cmd
99
105
  assert "pcm_s16le" in cmd
100
106
  assert "-f" not in cmd
101
107
 
102
108
 
109
+ @pytest.mark.parametrize("cancel", [False, True], ids=["timeout", "cancellation"])
110
+ @pytest.mark.asyncio
111
+ async def test_wav_conversion_stops_process_and_removes_files(
112
+ monkeypatch: pytest.MonkeyPatch, cancel: bool
113
+ ) -> None:
114
+ """Timed-out or cancelled requests must reap FFmpeg before removing its files."""
115
+ spawn = asyncio.create_subprocess_exec
116
+ started = asyncio.Event()
117
+ processes: list[asyncio.subprocess.Process] = []
118
+ directories: list[Path] = []
119
+
120
+ async def start_process(*args: str, stdout: int, stderr: int) -> asyncio.subprocess.Process:
121
+ directories.append(Path(args[-1]).parent)
122
+ process = await spawn(
123
+ sys.executable, "-c", "import time; time.sleep(60)", stdout=stdout, stderr=stderr
124
+ )
125
+ processes.append(process)
126
+ started.set()
127
+ return process
128
+
129
+ monkeypatch.setattr("shutil.which", lambda _name: "ffmpeg")
130
+ monkeypatch.setattr(asyncio, "create_subprocess_exec", start_process)
131
+ task = asyncio.create_task(
132
+ audio_format.convert_audio_to_wav_format(b"input", "voice.m4a", timeout=1),
133
+ )
134
+ try:
135
+ await asyncio.wait_for(started.wait(), timeout=1)
136
+ assert directories[0].exists()
137
+ if cancel:
138
+ task.cancel()
139
+ error = asyncio.CancelledError if cancel else RuntimeError
140
+ with pytest.raises(error):
141
+ await task
142
+ assert processes[0].returncode is not None
143
+ assert not directories[0].exists()
144
+ finally:
145
+ for process in processes:
146
+ if process.returncode is None:
147
+ process.kill()
148
+ await process.communicate()
149
+
150
+
103
151
  def test_convert_audio_integration(sample_wav_data: bytes) -> None:
104
152
  """Integration test using actual ffmpeg if available."""
105
153
  if not shutil.which("ffmpeg"):
@@ -2,6 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import io
6
+ import wave
5
7
  from concurrent.futures.process import BrokenProcessPool
6
8
  from typing import TYPE_CHECKING, cast
7
9
  from unittest.mock import AsyncMock, patch
@@ -58,9 +60,65 @@ async def test_faster_whisper_transcribe_recovers_from_broken_process_pool() ->
58
60
  patch.object(backend, "load", new=AsyncMock(side_effect=fake_load)) as load_mock,
59
61
  ):
60
62
  mock_loop.return_value.run_in_executor = mock_run_in_executor
61
- result = await backend.transcribe(b"fake audio bytes")
63
+ result = await backend.transcribe(_create_test_wav())
62
64
 
63
65
  assert result.text == "hello world"
64
66
  unload_mock.assert_awaited_once()
65
67
  load_mock.assert_awaited_once()
66
68
  assert executors_seen == [initial_executor, recovered_executor]
69
+
70
+
71
+ def _create_test_wav() -> bytes:
72
+ """Create a tiny valid 16kHz mono 16-bit WAV file."""
73
+ buffer = io.BytesIO()
74
+ with wave.open(buffer, "wb") as wav_file:
75
+ wav_file.setnchannels(1)
76
+ wav_file.setsampwidth(2)
77
+ wav_file.setframerate(16000)
78
+ wav_file.writeframes(b"\x00\x00" * 160)
79
+ return buffer.getvalue()
80
+
81
+
82
+ async def _transcribe_capturing_audio(audio: bytes, source_filename: str | None) -> bytes:
83
+ """Run the backend against a stub executor and return the bytes it dispatched."""
84
+ backend = FasterWhisperBackend(BackendConfig(model_name="tiny"))
85
+ backend._executor = cast("ProcessPoolExecutor", object())
86
+ dispatched: dict[str, bytes] = {}
87
+
88
+ async def mock_run_in_executor(
89
+ _executor: object,
90
+ _func: object,
91
+ audio_bytes: bytes,
92
+ _kwargs: dict[str, object],
93
+ ) -> dict[str, object]:
94
+ dispatched["audio"] = audio_bytes
95
+ return {
96
+ "text": "hello",
97
+ "language": "en",
98
+ "language_probability": 1.0,
99
+ "duration": 0.01,
100
+ "segments": [],
101
+ }
102
+
103
+ with patch("asyncio.get_running_loop") as mock_loop:
104
+ mock_loop.return_value.run_in_executor = mock_run_in_executor
105
+ result = await backend.transcribe(audio, source_filename=source_filename)
106
+
107
+ assert result.text == "hello"
108
+ return dispatched["audio"]
109
+
110
+
111
+ @pytest.mark.parametrize(
112
+ ("audio", "filename"),
113
+ [(b"OggS\x00\x02", "voice.ogg"), (b"\x00\x00\x00 ftypM4A ", "voice.m4a")],
114
+ )
115
+ @pytest.mark.asyncio
116
+ async def test_transcribe_preserves_native_decoder_input_without_ffmpeg(
117
+ monkeypatch: pytest.MonkeyPatch, audio: bytes, filename: str
118
+ ) -> None:
119
+ """PyAV owns decoding; pre-conversion would add an unnecessary system dependency.
120
+
121
+ https://github.com/SYSTRAN/faster-whisper/blob/master/faster_whisper/audio.py
122
+ """
123
+ monkeypatch.setattr("shutil.which", lambda _name: None)
124
+ assert await _transcribe_capturing_audio(audio, filename) == audio
@@ -9,15 +9,18 @@ import wave
9
9
  from concurrent.futures import ThreadPoolExecutor
10
10
  from concurrent.futures.process import BrokenProcessPool
11
11
  from types import ModuleType, SimpleNamespace
12
- from typing import TYPE_CHECKING, Any
12
+ from typing import TYPE_CHECKING, Any, cast
13
+ from unittest.mock import patch
13
14
 
14
15
  import pytest
15
16
 
16
17
  from agent_cli.server.cli import _is_parakeet_model
18
+ from agent_cli.server.whisper.backends import audio as audio_preparation
17
19
  from agent_cli.server.whisper.backends import nemo as backend
18
20
  from agent_cli.server.whisper.backends.base import BackendConfig, InvalidAudioError
19
21
 
20
22
  if TYPE_CHECKING:
23
+ from concurrent.futures import ProcessPoolExecutor
21
24
  from pathlib import Path
22
25
 
23
26
 
@@ -308,51 +311,60 @@ def test_audio_duration_seconds_returns_zero_for_non_wav(tmp_path: Path) -> None
308
311
  assert backend._audio_duration_seconds(str(audio_path)) == 0.0
309
312
 
310
313
 
311
- def test_prepare_audio_for_nemo_keeps_valid_wav(
312
- monkeypatch: pytest.MonkeyPatch,
313
- ) -> None:
314
- """Valid WAV uploads should not require FFmpeg conversion."""
315
- audio = _create_test_wav()
316
- monkeypatch.setattr(
317
- backend,
318
- "convert_audio_to_wav_format",
319
- lambda *_args, **_kwargs: pytest.fail("unexpected conversion"),
320
- )
321
-
322
- assert backend._prepare_audio_for_nemo(audio, "sample.wav") == audio
323
-
324
-
325
- def test_prepare_audio_for_nemo_converts_non_wav(
326
- monkeypatch: pytest.MonkeyPatch,
327
- ) -> None:
314
+ @pytest.mark.asyncio
315
+ async def test_transcribe_converts_non_wav_upload(monkeypatch: pytest.MonkeyPatch) -> None:
328
316
  """Non-WAV uploads should be converted to a WAV container before NeMo sees them."""
329
317
  converted = _create_test_wav()
330
318
  calls: dict[str, object] = {}
331
319
 
332
- def fake_convert(audio: bytes, source_filename: str) -> bytes:
320
+ async def fake_convert(audio: bytes, source_filename: str) -> bytes:
333
321
  calls["audio"] = audio
334
322
  calls["source_filename"] = source_filename
335
323
  return converted
336
324
 
337
- monkeypatch.setattr(backend, "convert_audio_to_wav_format", fake_convert)
325
+ monkeypatch.setattr(audio_preparation, "convert_audio_to_wav_format", fake_convert)
326
+ nemo_backend = backend.NemoWhisperBackend(BackendConfig(model_name="parakeet-tdt-0.6b-v2"))
327
+ nemo_backend._executor = cast("ProcessPoolExecutor", object())
328
+ dispatched: dict[str, bytes] = {}
329
+
330
+ async def mock_run_in_executor(
331
+ _executor: object,
332
+ _func: object,
333
+ audio_bytes: bytes,
334
+ _kwargs: dict[str, object],
335
+ ) -> dict[str, Any]:
336
+ dispatched["audio"] = audio_bytes
337
+ return {
338
+ "text": "hello",
339
+ "language": "en",
340
+ "language_probability": 1.0,
341
+ "duration": 0.01,
342
+ "segments": [],
343
+ }
344
+
345
+ with patch("asyncio.get_running_loop") as mock_loop:
346
+ mock_loop.return_value.run_in_executor = mock_run_in_executor
347
+ result = await nemo_backend.transcribe(b"ID3", source_filename="sample.mp3")
338
348
 
339
- assert backend._prepare_audio_for_nemo(b"ID3", "sample.mp3") == converted
349
+ assert result.text == "hello"
350
+ assert dispatched["audio"] == converted
340
351
  assert calls == {"audio": b"ID3", "source_filename": "sample.mp3"}
341
352
 
342
353
 
343
- def test_prepare_audio_for_nemo_raises_invalid_audio_error(
344
- monkeypatch: pytest.MonkeyPatch,
345
- ) -> None:
354
+ @pytest.mark.asyncio
355
+ async def test_transcribe_raises_invalid_audio_error(monkeypatch: pytest.MonkeyPatch) -> None:
346
356
  """Conversion failures should become API-visible invalid-audio errors."""
347
357
 
348
- def fake_convert(audio: bytes, source_filename: str) -> bytes: # noqa: ARG001
358
+ async def fake_convert(audio: bytes, source_filename: str) -> bytes: # noqa: ARG001
349
359
  msg = "ffmpeg failed"
350
360
  raise RuntimeError(msg)
351
361
 
352
- monkeypatch.setattr(backend, "convert_audio_to_wav_format", fake_convert)
362
+ monkeypatch.setattr(audio_preparation, "convert_audio_to_wav_format", fake_convert)
363
+ nemo_backend = backend.NemoWhisperBackend(BackendConfig(model_name="parakeet-tdt-0.6b-v2"))
364
+ nemo_backend._executor = cast("ProcessPoolExecutor", object())
353
365
 
354
366
  with pytest.raises(InvalidAudioError, match="Unsupported audio format for NeMo"):
355
- backend._prepare_audio_for_nemo(b"not audio", "sample.mp3")
367
+ await nemo_backend.transcribe(b"not audio", source_filename="sample.mp3")
356
368
 
357
369
 
358
370
  def test_build_transcribe_kwargs_omits_language_for_plain_asr_models() -> None: