agent-cli 0.105.1__tar.gz → 0.106.0__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.
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/workflows/docker.yml +4 -4
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/workflows/pytest.yml +1 -1
- {agent_cli-0.105.1 → agent_cli-0.106.0}/PKG-INFO +1 -1
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/transcribe.py +24 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/audio_format.py +17 -13
- agent_cli-0.106.0/agent_cli/server/whisper/backends/audio.py +44 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/backends/base.py +1 -1
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/backends/nemo.py +6 -24
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/backends/transformers.py +8 -1
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/AgentCommandRunner.swift +6 -1
- agent_cli-0.106.0/macos/AgentCLI/Sources/AgentCLI/CommandResult.swift +17 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/VoiceLevelOverlay.swift +85 -28
- agent_cli-0.106.0/macos/AgentCLI/Tests/AgentCLITests/TranscriptionResultTests.swift +23 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Tests/AgentCLITests/VoiceLevelMeterTests.swift +89 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/appcast.xml +37 -34
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_transcribe.py +2 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_transcribe_agent.py +61 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_transcribe_recovery.py +5 -1
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/core/test_audio_format.py +55 -7
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_faster_whisper_backend.py +59 -1
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_nemo_backend.py +39 -27
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_server_whisper.py +57 -1
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_transformers_backend.py +101 -2
- agent_cli-0.106.0/tests/test_whisper_backend_audio.py +171 -0
- agent_cli-0.105.1/macos/AgentCLI/Sources/AgentCLI/CommandResult.swift +0 -6
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.claude/skills/agent-cli-dev/SKILL.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.claude/skills/agent-cli-dev/examples.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.claude-plugin/README.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.claude-plugin/marketplace.json +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.claude-plugin/plugin.json +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.claude-plugin/skills/agent-cli-dev/SKILL.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.claude-plugin/skills/agent-cli-dev/examples.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.cursorrules +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.dockerignore +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.env.example +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/logo.svg +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/release-drafter.yml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/renovate.json +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/scripts/check_extras_sync.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/scripts/check_plugin_skill_sync.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/scripts/normalize_appcast.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/scripts/sync_extras.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/scripts/sync_requirements.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/workflows/automerge.yml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/workflows/docs.yml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/workflows/markdown-code-runner.yml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/workflows/release-drafter.yml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/workflows/release.yml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.github/workflows/toc.yaml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.gitignore +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.jscpd.json +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.pre-commit-config.yaml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.prompts/docs-review.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/.prompts/pr-review.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/CLAUDE.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/LICENSE +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/LLAMA_SERVER_USAGE.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/README.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/__main__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_extras.json +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_overrides/nemo-whisper.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/.gitkeep +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/audio.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/diarization.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/faster-whisper.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/kokoro.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/llm.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/memory.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/mlx-whisper.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/nemo-whisper.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/piper.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/rag.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/server.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/speed.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/vad.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/vectordb.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/whisper-transformers.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_requirements/wyoming.txt +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/_tools.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/_voice_agent_common.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/assistant.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/autocorrect.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/chat.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/diarize_live_session.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/memory/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/memory/add.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/memory/proxy.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/rag_proxy.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/speak.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/speakers.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/transcribe_live.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/agents/voice_edit.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/api.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/cli.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/config.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/config_cmd.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/constants.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/alignment.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/audio.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/chroma.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/deps.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/diarization.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/openai_proxy.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/process.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/reranker.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/speaker_identity.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/sse.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/transcription_logger.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/utils.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/vad.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/core/watch.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/daemon/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/daemon/cli.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/_branch_name.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/_config.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/_output.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/cleanup.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/cli.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/aider.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/base.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/claude.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/codex.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/continue_dev.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/copilot.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/cursor_agent.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/gemini.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/opencode.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/coding_agents/registry.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/base.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/cursor.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/emacs.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/jetbrains.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/nano.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/neovim.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/registry.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/sublime.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/vim.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/vscode.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/editors/zed.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/hooks.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/launch.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/project.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/registry.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/skill/SKILL.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/skill/examples.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/apple_terminal.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/base.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/cmux.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/gnome.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/iterm2.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/kitty.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/registry.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/tmux.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/warp.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/terminals/zellij.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/dev/worktree.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/docs_gen.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/example-config.toml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/install/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/install/common.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/install/extras.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/install/hotkeys.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/install/launchd.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/install/service_config.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/install/services.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/install/systemd.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_files.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_filters.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_git.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_indexer.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_ingest.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_persistence.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_prompt.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_retrieval.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_store.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_streaming.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/_tasks.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/api.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/client.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/engine.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/entities.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/memory/models.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/opts.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/py.typed +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/_indexer.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/_indexing.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/_prompt.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/_retriever.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/_store.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/_utils.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/api.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/client.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/engine.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/rag/models.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/.runtime/.gitkeep +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/linux-hotkeys/README.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/linux-hotkeys/toggle-autocorrect.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/linux-hotkeys/toggle-transcription.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/linux-hotkeys/toggle-voice-edit.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/macos-hotkeys/README.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/macos-hotkeys/skhd-config-example +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/macos-hotkeys/toggle-autocorrect.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/macos-hotkeys/toggle-transcription.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/macos-hotkeys/toggle-voice-edit.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/nvidia-asr-server/README.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/nvidia-asr-server/pyproject.toml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/nvidia-asr-server/server.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/nvidia-asr-server/shell.nix +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/nvidia-asr-server/uv.lock +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/run-openwakeword.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/setup-linux-hotkeys.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/setup-linux.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/setup-macos-hotkeys.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/setup-macos.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/setup-windows.ps1 +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/start-all-services-windows.ps1 +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/scripts/start-all-services.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/cli.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/common.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/model_manager.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/model_registry.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/proxy/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/proxy/api.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/proxy/diarization.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/streaming.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/tts/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/tts/api.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/tts/backends/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/tts/backends/base.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/tts/backends/kokoro.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/tts/backends/piper.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/tts/model_manager.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/tts/model_registry.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/tts/wyoming_handler.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/api.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/backends/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/backends/faster_whisper.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/backends/mlx.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/languages.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/model_manager.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/model_registry.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/server/whisper/wyoming_handler.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/services/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/services/_wyoming_utils.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/services/asr.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/services/llm.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/services/tts.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/agent_cli/services/wake_word.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docker/diarization.Dockerfile +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docker/docker-compose.yml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docker/memory-proxy.Dockerfile +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docker/rag-proxy.Dockerfile +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docker/transcribe-proxy.Dockerfile +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docker/tts.Dockerfile +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docker/whisper.Dockerfile +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/CNAME +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/architecture/index.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/architecture/memory.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/architecture/rag.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/assistant.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/autocorrect.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/chat.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/config.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/daemon.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/dev.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/diarize-live-session.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/index.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/install-extras.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/install-hotkeys.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/install-services.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/memory.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/rag-proxy.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/server/index.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/server/transcribe-proxy.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/server/tts.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/server/whisper.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/speak.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/speakers.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/start-services.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/transcribe-live.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/transcribe.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/commands/voice-edit.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/configuration.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/getting-started.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/iOS_Shortcut_Guide.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/index.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/installation/docker.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/installation/index.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/installation/linux.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/installation/macos-app.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/installation/macos.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/installation/nixos.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/installation/windows.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/logo-avatar.svg +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/logo-clean.svg +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/run_markdown_code_runner.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/docs/system-integration.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/example.agent-cli-config.toml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/justfile +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Package.resolved +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Package.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/README.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Resources/AgentCLI.entitlements +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Resources/Info.plist +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Resources/dmg-background.svg +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/AgentCLIApp.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/AgentCommand.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/AgentRuntime.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/AppDelegate.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/AppMetadata.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/AppUpdater.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/BootstrapState.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/ConfigurableHotkeyController.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/FocusedTextTarget.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/LiveTranscriptionPreview.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/LoginItemController.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/MenuActivityStatus.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/MenuActivityStatusRow.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/MenuActivityTracker.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/MenuBarIcon.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/RecentTranscriptionReader.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/RecordingIndicatorController.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/RecordingSoundSettings.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/RuntimeSettings.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/SettingsWindowController.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/Shortcuts.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/StatusMenuController.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/TranscriptPasteController.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/TranscriptionSettings.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Tests/AgentCLITests/AgentCommandTests.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Tests/AgentCLITests/ConfigurableHotkeyControllerTests.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Tests/AgentCLITests/LivePreviewSwiftTestingTests.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Tests/AgentCLITests/LiveTranscriptionPreviewTests.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Tests/AgentCLITests/LoginItemControllerTests.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Tests/AgentCLITests/RecentTranscriptionReaderTests.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Tests/AgentCLITests/RecordingIndicatorControllerTests.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Tests/AgentCLITests/ShortcutDefaultsTests.swift +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/build-macos-app.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/macos/test-macos-app-e2e.sh +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/pyproject.toml +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/reddit.md +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/shell.nix +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_fix_my_text.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_interactive.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_interactive_extra.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_memory_add.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_speak.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_speak_e2e.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_speakers.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_transcribe_e2e.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_transcribe_live.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_tts_common.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_tts_common_extra.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_voice_agent_common.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_voice_edit.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_voice_edit_e2e.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/agents/test_wake_word_assistant.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/conftest.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/core/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/core/test_audio.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/core/test_audio_levels.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/core/test_chroma.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/core/test_sse.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/core/test_vad.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/core/test_watch.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/test_cleanup.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/test_cli.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/test_coding_agents.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/test_editors.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/test_hooks.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/test_launch.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/test_project.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/test_terminals.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/test_verification.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/dev/test_worktree.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/install/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/install/test_extras.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/install/test_launchd.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_api_health.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_api_integration_liveish.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_client.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_engine.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_files.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_filters.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_git_integration.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_indexer.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_memory_integration.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_proxy_passthrough.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_store.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/memory/test_utils.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/mocks/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/mocks/audio.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/mocks/llm.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/mocks/wyoming.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/__init__.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_api.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_engine.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_history.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_indexer.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_indexing.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_rag_client.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_rag_integration_liveish.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_rag_proxy_passthrough.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_retriever.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_store.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/rag/test_utils.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_alignment.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_api.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_api_integration.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_asr.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_asr_recovery.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_audio_e2e.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_cli.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_config.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_config_cmd.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_daemon.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_diarization.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_diarize_live_session.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_docs_gen.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_env_vars.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_json_output.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_llm.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_llm_gemini.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_macos_app.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_memory_tools.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_mlx_backend.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_normalize_appcast.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_process_manager.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_proxy_diarization.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_requires_extras.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_server_streaming.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_server_tts.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_server_tts_wyoming.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_server_whisper_wyoming.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_services.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_speaker_identity.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_tools.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_tts.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_utils.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_wake_word.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/tests/test_wyoming_utils.py +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/uv.lock +0 -0
- {agent_cli-0.105.1 → agent_cli-0.106.0}/zensical.toml +0 -0
|
@@ -49,7 +49,7 @@ jobs:
|
|
|
49
49
|
fetch-depth: 0 # Full history for version detection
|
|
50
50
|
|
|
51
51
|
- name: Set up Docker Buildx
|
|
52
|
-
uses: docker/setup-buildx-action@v4.
|
|
52
|
+
uses: docker/setup-buildx-action@v4.4.1
|
|
53
53
|
|
|
54
54
|
- name: Log in to Container Registry
|
|
55
55
|
uses: docker/login-action@v4.5.1
|
|
@@ -79,7 +79,7 @@ jobs:
|
|
|
79
79
|
type=sha,suffix=-${{ matrix.suffix }}
|
|
80
80
|
|
|
81
81
|
- name: Build and push
|
|
82
|
-
uses: docker/build-push-action@v7.
|
|
82
|
+
uses: docker/build-push-action@v7.4.0
|
|
83
83
|
with:
|
|
84
84
|
context: .
|
|
85
85
|
file: ${{ matrix.dockerfile }}
|
|
@@ -122,7 +122,7 @@ jobs:
|
|
|
122
122
|
fetch-depth: 0 # Full history for version detection
|
|
123
123
|
|
|
124
124
|
- name: Set up Docker Buildx
|
|
125
|
-
uses: docker/setup-buildx-action@v4.
|
|
125
|
+
uses: docker/setup-buildx-action@v4.4.1
|
|
126
126
|
|
|
127
127
|
- name: Log in to Container Registry
|
|
128
128
|
uses: docker/login-action@v4.5.1
|
|
@@ -152,7 +152,7 @@ jobs:
|
|
|
152
152
|
type=sha
|
|
153
153
|
|
|
154
154
|
- name: Build and push
|
|
155
|
-
uses: docker/build-push-action@v7.
|
|
155
|
+
uses: docker/build-push-action@v7.4.0
|
|
156
156
|
with:
|
|
157
157
|
context: .
|
|
158
158
|
file: ${{ matrix.dockerfile }}
|
|
@@ -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.
|
|
46
|
+
uses: codecov/codecov-action@v7.1.1
|
|
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.
|
|
3
|
+
Version: 0.106.0
|
|
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>
|
|
@@ -477,6 +477,9 @@ async def _async_main( # noqa: PLR0912, PLR0915, C901
|
|
|
477
477
|
live_preview_config=live_preview_config,
|
|
478
478
|
)
|
|
479
479
|
|
|
480
|
+
if transcript and not transcript.strip():
|
|
481
|
+
transcript = None
|
|
482
|
+
|
|
480
483
|
elapsed = time.monotonic() - start_time
|
|
481
484
|
|
|
482
485
|
# Apply diarization if enabled
|
|
@@ -554,6 +557,9 @@ async def _async_main( # noqa: PLR0912, PLR0915, C901
|
|
|
554
557
|
live=live,
|
|
555
558
|
context=combined_context,
|
|
556
559
|
)
|
|
560
|
+
if general_cfg.clipboard and not (processed_transcript or "").strip():
|
|
561
|
+
# Keep the raw speech recoverable if cleanup overwrote it with blank text.
|
|
562
|
+
pyperclip.copy(transcript)
|
|
557
563
|
|
|
558
564
|
# Log transcription if requested
|
|
559
565
|
if transcription_log:
|
|
@@ -615,6 +621,21 @@ async def _async_main( # noqa: PLR0912, PLR0915, C901
|
|
|
615
621
|
)
|
|
616
622
|
|
|
617
623
|
|
|
624
|
+
def _require_transcript(result: TranscriptResult, provider: str) -> None:
|
|
625
|
+
"""Do not report successful dictation when the backend produced no text."""
|
|
626
|
+
if not (result.get("transcript") or "").strip():
|
|
627
|
+
if result.get("raw_transcript"):
|
|
628
|
+
message = "LLM cleanup returned no text. Retry with --no-llm to use the raw transcript."
|
|
629
|
+
else:
|
|
630
|
+
message = f"No transcript returned by {provider}. Check the microphone and ASR server."
|
|
631
|
+
typer.echo(
|
|
632
|
+
f"{message} "
|
|
633
|
+
"If a recording was saved, retry with agent-cli transcribe --last-recording.",
|
|
634
|
+
err=True,
|
|
635
|
+
)
|
|
636
|
+
raise typer.Exit(1)
|
|
637
|
+
|
|
638
|
+
|
|
618
639
|
@app.command("transcribe", rich_help_panel="Voice Commands")
|
|
619
640
|
@requires_extras("audio", "llm", process_name="transcribe")
|
|
620
641
|
def transcribe( # noqa: PLR0912, PLR0911, PLR0915, C901
|
|
@@ -896,6 +917,7 @@ def transcribe( # noqa: PLR0912, PLR0911, PLR0915, C901
|
|
|
896
917
|
LOGGER.exception("Diarization failed")
|
|
897
918
|
_print_diarization_error(exc)
|
|
898
919
|
raise typer.Exit(1) from None
|
|
920
|
+
_require_transcript(result, asr_provider)
|
|
899
921
|
if json_output:
|
|
900
922
|
print(json.dumps(result))
|
|
901
923
|
return
|
|
@@ -937,6 +959,7 @@ def transcribe( # noqa: PLR0912, PLR0911, PLR0915, C901
|
|
|
937
959
|
):
|
|
938
960
|
return
|
|
939
961
|
|
|
962
|
+
result = TranscriptResult(raw_transcript=None, transcript=None, llm_enabled=False)
|
|
940
963
|
# Use context manager before audio setup so --stop can target startup reliably.
|
|
941
964
|
try:
|
|
942
965
|
with process.pid_file_context(process_name), suppress(KeyboardInterrupt):
|
|
@@ -992,5 +1015,6 @@ def transcribe( # noqa: PLR0912, PLR0911, PLR0915, C901
|
|
|
992
1015
|
LOGGER.exception("Diarization failed")
|
|
993
1016
|
_print_diarization_error(exc)
|
|
994
1017
|
raise typer.Exit(1) from None
|
|
1018
|
+
_require_transcript(result, asr_provider)
|
|
995
1019
|
if json_output:
|
|
996
1020
|
print(json.dumps(result))
|
|
@@ -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
|
|
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
|
-
|
|
190
|
-
|
|
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
|
|
201
|
-
stderr_text =
|
|
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:
|
|
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
|
|
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,
|
|
@@ -15,6 +15,7 @@ from pathlib import Path
|
|
|
15
15
|
from typing import Any, Literal
|
|
16
16
|
|
|
17
17
|
from agent_cli.core.process import set_process_title
|
|
18
|
+
from agent_cli.server.whisper.backends.audio import prepare_wav_audio
|
|
18
19
|
from agent_cli.server.whisper.backends.base import (
|
|
19
20
|
BackendConfig,
|
|
20
21
|
TranscriptionResult,
|
|
@@ -593,7 +594,7 @@ class TransformersWhisperBackend:
|
|
|
593
594
|
self,
|
|
594
595
|
audio: bytes,
|
|
595
596
|
*,
|
|
596
|
-
source_filename: str | None = None,
|
|
597
|
+
source_filename: str | None = None,
|
|
597
598
|
language: str | None = None,
|
|
598
599
|
task: Literal["transcribe", "translate"] = "transcribe",
|
|
599
600
|
initial_prompt: str | None = None,
|
|
@@ -606,6 +607,12 @@ class TransformersWhisperBackend:
|
|
|
606
607
|
msg = "Model not loaded. Call load() first."
|
|
607
608
|
raise RuntimeError(msg)
|
|
608
609
|
|
|
610
|
+
audio = await prepare_wav_audio(
|
|
611
|
+
audio,
|
|
612
|
+
source_filename,
|
|
613
|
+
backend_label="transformers ASR",
|
|
614
|
+
)
|
|
615
|
+
|
|
609
616
|
# Write audio to temp file for wave parsing in subprocess
|
|
610
617
|
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
|
|
611
618
|
tmp.write(audio)
|
{agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/AgentCommandRunner.swift
RENAMED
|
@@ -252,7 +252,12 @@ final class AgentCommandRunner: ObservableObject {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
let
|
|
255
|
+
let commandResult = AgentRuntime.shared.runAgentCLI(arguments: commandArguments)
|
|
256
|
+
// A stop acknowledgement may be empty; a completed recording must contain text.
|
|
257
|
+
// Validate here too because user-installed CLIs can predate the CLI-side check.
|
|
258
|
+
let result = shouldStartRecording && command.identifier == AgentCommand.toggleTranscription.identifier
|
|
259
|
+
? commandResult.requiringTranscript()
|
|
260
|
+
: commandResult
|
|
256
261
|
let message = Self.statusMessage(for: command, result: result)
|
|
257
262
|
let notificationTitle = Self.notificationTitle(for: command, result: result)
|
|
258
263
|
let notificationBody = Self.notificationBody(for: command, result: result, statusMessage: message)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
struct CommandResult {
|
|
4
|
+
let exitCode: Int32
|
|
5
|
+
let output: String
|
|
6
|
+
|
|
7
|
+
func requiringTranscript() -> CommandResult {
|
|
8
|
+
guard exitCode == 0, output.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
|
9
|
+
return self
|
|
10
|
+
}
|
|
11
|
+
return CommandResult(
|
|
12
|
+
exitCode: 1,
|
|
13
|
+
output: "No transcript returned. Check the microphone and ASR server, then try again. "
|
|
14
|
+
+ "If a recording was saved, recover it with agent-cli transcribe --last-recording."
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
}
|
{agent_cli-0.105.1 → agent_cli-0.106.0}/macos/AgentCLI/Sources/AgentCLI/VoiceLevelOverlay.swift
RENAMED
|
@@ -3,7 +3,7 @@ import Foundation
|
|
|
3
3
|
import SwiftUI
|
|
4
4
|
|
|
5
5
|
enum VoiceLevelOverlayLayout {
|
|
6
|
-
static let pillSize = CGSize(width:
|
|
6
|
+
static let pillSize = CGSize(width: 190, height: 38)
|
|
7
7
|
static let textWidth = CGFloat(420)
|
|
8
8
|
static let textHeight = CGFloat(86)
|
|
9
9
|
static let shadowRadius = CGFloat(13)
|
|
@@ -72,41 +72,57 @@ struct VoiceLevelOverlayView: View {
|
|
|
72
72
|
height: VoiceLevelOverlayLayout.panelSize(showsPreviewSpace: showsPreviewSpace).height,
|
|
73
73
|
alignment: .bottom
|
|
74
74
|
)
|
|
75
|
-
.
|
|
75
|
+
.accessibilityElement(children: .ignore)
|
|
76
|
+
.accessibilityLabel(Text(meter.captureState == .recording
|
|
77
|
+
? "Recording \(meter.recordingDuration)"
|
|
78
|
+
: meter.captureState.label))
|
|
79
|
+
.accessibilityValue(Text(showsPreviewSpace ? preview.text : ""))
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
private var levelMeter: some View {
|
|
79
|
-
HStack(
|
|
83
|
+
HStack(spacing: 8) {
|
|
84
|
+
if meter.captureState == .recording {
|
|
85
|
+
Text(meter.recordingDuration)
|
|
86
|
+
.font(.system(size: 12, weight: .semibold))
|
|
87
|
+
.monospacedDigit()
|
|
88
|
+
waveform
|
|
89
|
+
} else {
|
|
90
|
+
ProgressView()
|
|
91
|
+
.controlSize(.small)
|
|
92
|
+
Text(meter.captureState.label)
|
|
93
|
+
.font(.system(size: 12, weight: .semibold))
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
.foregroundStyle(statusColor)
|
|
97
|
+
.frame(width: VoiceLevelOverlayLayout.pillSize.width, height: VoiceLevelOverlayLayout.pillSize.height)
|
|
98
|
+
.background(Capsule().fill(statusColor.opacity(isLightMode ? 0.10 : 0.14)))
|
|
99
|
+
.background(Capsule().fill(backgroundColor))
|
|
100
|
+
.overlay(Capsule().stroke(statusColor.opacity(0.55), lineWidth: 1))
|
|
101
|
+
.shadow(
|
|
102
|
+
color: shadowColor,
|
|
103
|
+
radius: VoiceLevelOverlayLayout.shadowRadius,
|
|
104
|
+
y: VoiceLevelOverlayLayout.shadowYOffset
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private var waveform: some View {
|
|
109
|
+
HStack(alignment: .center, spacing: 2) {
|
|
80
110
|
ForEach(Array(meter.amplitudes.enumerated()), id: \.offset) { _, amplitude in
|
|
81
111
|
Capsule()
|
|
82
112
|
.fill(
|
|
83
113
|
LinearGradient(
|
|
84
114
|
colors: [
|
|
85
|
-
|
|
86
|
-
|
|
115
|
+
statusColor.opacity(0.65),
|
|
116
|
+
statusColor
|
|
87
117
|
],
|
|
88
118
|
startPoint: .bottom,
|
|
89
119
|
endPoint: .top
|
|
90
120
|
)
|
|
91
121
|
)
|
|
92
|
-
.frame(width:
|
|
122
|
+
.frame(width: 2, height: max(5, 25 * amplitude))
|
|
93
123
|
.animation(.easeOut(duration: 0.11), value: amplitude)
|
|
94
124
|
}
|
|
95
125
|
}
|
|
96
|
-
.frame(width: VoiceLevelOverlayLayout.pillSize.width, height: VoiceLevelOverlayLayout.pillSize.height)
|
|
97
|
-
.background(
|
|
98
|
-
Capsule()
|
|
99
|
-
.fill(backgroundColor)
|
|
100
|
-
)
|
|
101
|
-
.overlay(
|
|
102
|
-
Capsule()
|
|
103
|
-
.stroke(borderColor, lineWidth: 1)
|
|
104
|
-
)
|
|
105
|
-
.shadow(
|
|
106
|
-
color: shadowColor,
|
|
107
|
-
radius: VoiceLevelOverlayLayout.shadowRadius,
|
|
108
|
-
y: VoiceLevelOverlayLayout.shadowYOffset
|
|
109
|
-
)
|
|
110
126
|
}
|
|
111
127
|
|
|
112
128
|
private var isLightMode: Bool {
|
|
@@ -133,12 +149,15 @@ struct VoiceLevelOverlayView: View {
|
|
|
133
149
|
Color.black.opacity(isLightMode ? 0.16 : 0.24)
|
|
134
150
|
}
|
|
135
151
|
|
|
136
|
-
private var
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
152
|
+
private var statusColor: Color {
|
|
153
|
+
if meter.captureState == .recording {
|
|
154
|
+
return isLightMode
|
|
155
|
+
? Color(red: 0.04, green: 0.40, blue: 0.18)
|
|
156
|
+
: Color(red: 0.42, green: 0.91, blue: 0.58)
|
|
157
|
+
}
|
|
158
|
+
return isLightMode
|
|
159
|
+
? Color(red: 0.62, green: 0.34, blue: 0.0)
|
|
160
|
+
: Color(red: 1.0, green: 0.74, blue: 0.25)
|
|
142
161
|
}
|
|
143
162
|
}
|
|
144
163
|
|
|
@@ -306,10 +325,28 @@ enum VoiceLevelLog {
|
|
|
306
325
|
}()
|
|
307
326
|
}
|
|
308
327
|
|
|
328
|
+
enum VoiceCaptureState {
|
|
329
|
+
case idle
|
|
330
|
+
case connecting
|
|
331
|
+
case recording
|
|
332
|
+
case waitingForAudio
|
|
333
|
+
|
|
334
|
+
var label: String {
|
|
335
|
+
switch self {
|
|
336
|
+
case .idle: return "Ready"
|
|
337
|
+
case .connecting: return "Connecting…"
|
|
338
|
+
case .recording: return "Recording"
|
|
339
|
+
case .waitingForAudio: return "Waiting for audio…"
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
309
344
|
final class VoiceLevelMeter: ObservableObject {
|
|
310
345
|
static let shared = VoiceLevelMeter()
|
|
311
346
|
|
|
312
347
|
@Published private(set) var amplitudes = VoiceLevelMeter.idleAmplitudes
|
|
348
|
+
@Published private(set) var captureState = VoiceCaptureState.idle
|
|
349
|
+
@Published private(set) var recordingDuration = "00:00"
|
|
313
350
|
|
|
314
351
|
private static let barCount = 16
|
|
315
352
|
private static let idleAmplitudes = Array(repeating: CGFloat(0.16), count: barCount)
|
|
@@ -319,6 +356,7 @@ final class VoiceLevelMeter: ObservableObject {
|
|
|
319
356
|
private let levelLogURL: URL
|
|
320
357
|
private let now: () -> Date
|
|
321
358
|
private var timer: Timer?
|
|
359
|
+
private var recordingStartedAt: Date?
|
|
322
360
|
private var phase = 0.0
|
|
323
361
|
private var smoothedLevel = CGFloat(0.16)
|
|
324
362
|
|
|
@@ -330,6 +368,9 @@ final class VoiceLevelMeter: ObservableObject {
|
|
|
330
368
|
func start() {
|
|
331
369
|
guard timer == nil else { return }
|
|
332
370
|
VoiceLevelLog.reset(levelLogURL)
|
|
371
|
+
captureState = .connecting
|
|
372
|
+
recordingStartedAt = nil
|
|
373
|
+
recordingDuration = "00:00"
|
|
333
374
|
phase = 0
|
|
334
375
|
smoothedLevel = Self.idleLevel
|
|
335
376
|
amplitudes = Self.idleAmplitudes
|
|
@@ -345,13 +386,29 @@ final class VoiceLevelMeter: ObservableObject {
|
|
|
345
386
|
func stop() {
|
|
346
387
|
timer?.invalidate()
|
|
347
388
|
timer = nil
|
|
389
|
+
captureState = .idle
|
|
390
|
+
recordingStartedAt = nil
|
|
391
|
+
recordingDuration = "00:00"
|
|
348
392
|
phase = 0
|
|
349
393
|
smoothedLevel = Self.idleLevel
|
|
350
394
|
amplitudes = Self.idleAmplitudes
|
|
351
395
|
}
|
|
352
396
|
|
|
353
|
-
|
|
354
|
-
|
|
397
|
+
func pollLevel() {
|
|
398
|
+
guard timer != nil else { return }
|
|
399
|
+
let now = now()
|
|
400
|
+
guard let level = VoiceLevelLog.latestLevel(from: levelLogURL, now: now) else {
|
|
401
|
+
if captureState == .recording {
|
|
402
|
+
captureState = .waitingForAudio
|
|
403
|
+
}
|
|
404
|
+
amplitudes = Self.idleAmplitudes
|
|
405
|
+
return
|
|
406
|
+
}
|
|
407
|
+
captureState = .recording
|
|
408
|
+
let startedAt = recordingStartedAt ?? now
|
|
409
|
+
recordingStartedAt = startedAt
|
|
410
|
+
let elapsedSeconds = max(0, Int(now.timeIntervalSince(startedAt)))
|
|
411
|
+
recordingDuration = String(format: "%02d:%02d", elapsedSeconds / 60, elapsedSeconds % 60)
|
|
355
412
|
updateDisplay(level: level)
|
|
356
413
|
}
|
|
357
414
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#if canImport(XCTest)
|
|
2
|
+
import XCTest
|
|
3
|
+
@testable import AgentCLI
|
|
4
|
+
|
|
5
|
+
final class TranscriptionResultTests: XCTestCase {
|
|
6
|
+
func testEmptySuccessfulRecordingBecomesFailure() {
|
|
7
|
+
for output in ["", " \n\t"] {
|
|
8
|
+
let result = CommandResult(exitCode: 0, output: output).requiringTranscript()
|
|
9
|
+
XCTAssertNotEqual(result.exitCode, 0)
|
|
10
|
+
XCTAssertTrue(result.output.contains("No transcript"))
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
func testTranscriptAndBackendFailureArePreserved() {
|
|
15
|
+
let success = CommandResult(exitCode: 0, output: "Hello world").requiringTranscript()
|
|
16
|
+
XCTAssertEqual(success.exitCode, 0)
|
|
17
|
+
XCTAssertEqual(success.output, "Hello world")
|
|
18
|
+
let failure = CommandResult(exitCode: 7, output: "Connection lost").requiringTranscript()
|
|
19
|
+
XCTAssertEqual(failure.exitCode, 7)
|
|
20
|
+
XCTAssertEqual(failure.output, "Connection lost")
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
#endif
|
|
@@ -4,6 +4,95 @@ import XCTest
|
|
|
4
4
|
@testable import AgentCLI
|
|
5
5
|
|
|
6
6
|
final class VoiceLevelMeterTests: XCTestCase {
|
|
7
|
+
func testRecordingTimerExcludesStartupAndResetsBetweenRecordings() throws {
|
|
8
|
+
let logURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
|
9
|
+
var now = try XCTUnwrap(Self.iso8601.date(from: "2026-06-04T12:00:00Z"))
|
|
10
|
+
let meter = VoiceLevelMeter(levelLogURL: logURL, now: { now })
|
|
11
|
+
defer {
|
|
12
|
+
meter.stop()
|
|
13
|
+
try? FileManager.default.removeItem(at: logURL)
|
|
14
|
+
}
|
|
15
|
+
meter.start()
|
|
16
|
+
now = now.addingTimeInterval(10)
|
|
17
|
+
meter.pollLevel()
|
|
18
|
+
XCTAssertEqual(meter.recordingDuration, "00:00")
|
|
19
|
+
|
|
20
|
+
func publishAudio() throws {
|
|
21
|
+
try "{\"timestamp\":\"\(Self.iso8601.string(from: now))\",\"level\":0}"
|
|
22
|
+
.write(to: logURL, atomically: true, encoding: .utf8)
|
|
23
|
+
meter.pollLevel()
|
|
24
|
+
}
|
|
25
|
+
try publishAudio()
|
|
26
|
+
XCTAssertEqual(meter.recordingDuration, "00:00")
|
|
27
|
+
now = now.addingTimeInterval(9)
|
|
28
|
+
try publishAudio()
|
|
29
|
+
XCTAssertEqual(meter.recordingDuration, "00:09")
|
|
30
|
+
now = now.addingTimeInterval(56)
|
|
31
|
+
try publishAudio()
|
|
32
|
+
XCTAssertEqual(meter.recordingDuration, "01:05")
|
|
33
|
+
now = now.addingTimeInterval(3600)
|
|
34
|
+
try publishAudio()
|
|
35
|
+
XCTAssertEqual(meter.recordingDuration, "61:05")
|
|
36
|
+
|
|
37
|
+
meter.stop()
|
|
38
|
+
XCTAssertEqual(meter.recordingDuration, "00:00")
|
|
39
|
+
meter.start()
|
|
40
|
+
try publishAudio()
|
|
41
|
+
XCTAssertEqual(meter.recordingDuration, "00:00")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func testRecordingReadinessFollowsFreshAudioIncludingSilence() throws {
|
|
45
|
+
let logURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
|
46
|
+
var now = try XCTUnwrap(Self.iso8601.date(from: "2026-06-04T12:00:00Z"))
|
|
47
|
+
let meter = VoiceLevelMeter(levelLogURL: logURL, now: { now })
|
|
48
|
+
defer {
|
|
49
|
+
meter.stop()
|
|
50
|
+
try? FileManager.default.removeItem(at: logURL)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// A previous recording must not make a new request look ready.
|
|
54
|
+
try #"{"timestamp":"2026-06-04T12:00:00Z","level":0.9}"#
|
|
55
|
+
.write(to: logURL, atomically: true, encoding: .utf8)
|
|
56
|
+
meter.start()
|
|
57
|
+
XCTAssertEqual(meter.captureState, .connecting)
|
|
58
|
+
|
|
59
|
+
try #"{"timestamp":"2026-06-04T12:00:00Z","level":0}"#
|
|
60
|
+
.write(to: logURL, atomically: true, encoding: .utf8)
|
|
61
|
+
meter.pollLevel()
|
|
62
|
+
XCTAssertEqual(meter.captureState, .recording, "Silent audio still proves capture is running.")
|
|
63
|
+
|
|
64
|
+
now = now.addingTimeInterval(2)
|
|
65
|
+
meter.pollLevel()
|
|
66
|
+
XCTAssertEqual(meter.captureState, .waitingForAudio)
|
|
67
|
+
|
|
68
|
+
try #"{"timestamp":"2026-06-04T12:00:02Z","level":0.6}"#
|
|
69
|
+
.write(to: logURL, atomically: true, encoding: .utf8)
|
|
70
|
+
meter.pollLevel()
|
|
71
|
+
XCTAssertEqual(meter.captureState, .recording)
|
|
72
|
+
|
|
73
|
+
meter.stop()
|
|
74
|
+
meter.pollLevel()
|
|
75
|
+
XCTAssertEqual(meter.captureState, .idle, "Late polling must not revive a released overlay.")
|
|
76
|
+
meter.start()
|
|
77
|
+
XCTAssertEqual(meter.captureState, .connecting)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func testConnectingDoesNotAnimateFakeMicrophoneActivity() throws {
|
|
81
|
+
let logURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
|
82
|
+
let meter = VoiceLevelMeter(levelLogURL: logURL)
|
|
83
|
+
defer {
|
|
84
|
+
meter.stop()
|
|
85
|
+
try? FileManager.default.removeItem(at: logURL)
|
|
86
|
+
}
|
|
87
|
+
meter.start()
|
|
88
|
+
let initialAmplitudes = meter.amplitudes
|
|
89
|
+
meter.pollLevel()
|
|
90
|
+
XCTAssertEqual(meter.captureState, .connecting)
|
|
91
|
+
XCTAssertEqual(meter.amplitudes, initialAmplitudes)
|
|
92
|
+
meter.stop()
|
|
93
|
+
XCTAssertEqual(meter.captureState, .idle)
|
|
94
|
+
}
|
|
95
|
+
|
|
7
96
|
func testDisplayAmplitudesUseSineWaveShape() {
|
|
8
97
|
let amplitudes = VoiceLevelMeter.displayAmplitudes(level: 0.6, phase: 0.22)
|
|
9
98
|
|