Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions config-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -2540,7 +2540,8 @@
"mlx",
"apple",
"parakeet",
"transcribe"
"transcribe",
"faster"
],
"defaultValue": "whisper"
},
Expand All @@ -2567,9 +2568,14 @@
"sensitive": false,
"tags": [],
"label": "Model",
"help": "Whisper model size.",
"help": "Whisper model size (whisper and faster providers).",
"hasChildren": false,
"enumValues": [
"tiny",
"base",
"small",
"medium",
"large-v3",
"turbo"
],
"defaultValue": "turbo"
Expand Down
2 changes: 1 addition & 1 deletion docs/system-specs/modules/learn-cron-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,7 @@ React 18 + TypeScript + Vite 5 + Redux Toolkit + React Router v7 + Tailwind CSS
- **Lessons tab**: add lesson form + lesson table with Delete actions
- **Skills tab (CRUD)**: + New button with create form (name + SKILL.md editor), installed skill list with click-to-view, ✏ Edit button with inline textarea editor + Save, ✕ Delete with confirmation, name sanitized to lowercase + hyphens. AIM Skills section shows skills from `~/.aim/` grouped by package with Uninstall button per package. Skills are fully AIM-managed — no bundled skills; `AIPowerUserCapabilities` installed by default via setup/update.
- **MCP Servers tab**: Controls `~/.kiro/settings/mcp.json` (global config that kiro-cli ACP loads at runtime). Server-level enable/disable sets `disabled: true/false` in global config and syncs `@server` to kirocrew.json `tools`/`allowedTools`. Per-tool enable/disable sets `disabledTools` array in global config. Probe All discovers tools per server, preserves enabled/disabledTools state across probes. Enable All / Disable All bulk buttons. Tool chips: green = enabled (clickable to disable), strikethrough = disabled (clickable to enable). Apply & Restart at top bar resets all active sessions. Live server badges (🔌 color-coded by status).
- **Slack tab**: STT (Speech-to-Text) settings card — toggle enabled/disabled, provider selector (`whisper` / `mlx` / `transcribe`), model selector (turbo ~1.6 GB), status badge (ready/not installed), provider-aware install button (`brew install openai-whisper` for `whisper`, `pipx install mlx-whisper` for `mlx`; hidden for `transcribe`, which has no local runtime to install — its availability is "`boto3` + `amazon-transcribe` importable by the gateway process", so the prereq block instead surfaces `<gateway python> -m pip install 'kirocrew[voice]'` (PowerShell `&`-form on Windows; suppressed once the extra is importable) plus a platform ffmpeg command when ffmpeg is missing and a restart-the-gateway hint, and `POST /api/stt/install` refuses with 400 `code: stt_no_local_install` for it rather than installing a Whisper runtime that cannot change Transcribe's status. When no install channel can make the extra importable — frozen build, the desktop app's code-signed bundled interpreter, pip-less interpreter, or a PEP 668 externally-managed python — the response carries `transcribe_unsupported: true` and the Settings page shows an unsupported notice instead of commands; `bundled_interpreter: true` discriminates the desktop-app cause, whose notice names the actual remedy (install Kiro Crew with pip and run the gateway from that environment) because "run the gateway from a different Python environment" is not actionable inside an app bundle. The response also carries `ffmpeg_missing`, served independently of `available`, because Transcribe's availability check treats ffmpeg as optional (it only affects the non-streaming `.webm` remux path) — the Settings page uses it to show the ffmpeg install command even while the status badge reads ready). The `mlx` provider (Apple Silicon Metal GPU) uses the `mlx_model` config key (default `mlx-community/whisper-large-v3-turbo`). Endpoints: `GET/PUT /api/config/stt`, `POST /api/stt/install`. **Homebrew resolution never relies on `PATH`**: a GUI-launched gateway (desktop app / launchd) inherits `/usr/bin:/bin:/usr/sbin:/sbin`, which contains no Homebrew prefix, so `shutil.which("brew")` reported Homebrew MISSING on machines that have it — the prereq list told a Homebrew user to install Homebrew and the `mlx` install script aborted at its first `command -v brew` with `ERROR: Homebrew required`. The prereq probe goes through `transcribe.find_brew()` (PATH, then the fixed prefixes `/opt/homebrew/bin`, `/usr/local/bin`, Linuxbrew system + per-user), and every install script is prefixed with `_stt_install_path_prelude()` — it prepends `transcribe.BREW_PATH_DIRS` (the same prefixes plus `~/.local/bin`, where pipx puts `mlx_whisper`) and then `eval "$(brew shellenv)"`. The prelude is required because the script runs as `bash -c`, which is neither a login nor an interactive shell, so the user's `~/.zprofile` `brew shellenv` line never executes.
- **Slack tab**: STT (Speech-to-Text) settings card — toggle enabled/disabled, provider selector (`whisper` / `mlx` / `apple` / `parakeet` / `transcribe` / `faster`), model selector (turbo ~1.6 GB; for `whisper`/`faster` the picker offers the six Whisper sizes `tiny`/`base`/`small`/`medium`/`large-v3`/`turbo`), status badge (ready/not installed), provider-aware install button (`brew install openai-whisper` for `whisper`, `pipx install mlx-whisper` for `mlx`; hidden for `transcribe`, which has no local runtime to install — its availability is "`boto3` + `amazon-transcribe` importable by the gateway process", so the prereq block instead surfaces `<gateway python> -m pip install 'kirocrew[voice]'` (PowerShell `&`-form on Windows; suppressed once the extra is importable) plus a platform ffmpeg command when ffmpeg is missing and a restart-the-gateway hint, and `POST /api/stt/install` refuses with 400 `code: stt_no_local_install` for it rather than installing a Whisper runtime that cannot change Transcribe's status. When no install channel can make the extra importable — frozen build, the desktop app's code-signed bundled interpreter, pip-less interpreter, or a PEP 668 externally-managed python — the response carries `transcribe_unsupported: true` and the Settings page shows an unsupported notice instead of commands; `bundled_interpreter: true` discriminates the desktop-app cause, whose notice names the actual remedy (install Kiro Crew with pip and run the gateway from that environment) because "run the gateway from a different Python environment" is not actionable inside an app bundle. The response also carries `ffmpeg_missing`, served independently of `available`, because Transcribe's availability check treats ffmpeg as optional (it only affects the non-streaming `.webm` remux path) — the Settings page uses it to show the ffmpeg install command even while the status badge reads ready). The `mlx` provider (Apple Silicon Metal GPU) uses the `mlx_model` config key (default `mlx-community/whisper-large-v3-turbo`). The `faster` provider (faster-whisper / CTranslate2) installs **in-process** via `POST /api/stt/install` — `sys.executable -m pip install faster-whisper`, deliberately the gateway's own interpreter because the import has to succeed in THIS process — and needs no system ffmpeg, since it decodes through PyAV's bundled FFmpeg. Two refusals are specific to it: `GET /api/config/stt` serves `faster_unsupported: true` on native Windows-on-ARM (CTranslate2 publishes no `win_arm64` wheel and no sdist, so pip fails at resolution), and the install endpoint returns 400 `code: stt_no_install_channel` when the gateway python has no usable pip channel. Availability is LOCATED, never imported, on the request path (`importlib.util.find_spec`), because linking CTranslate2's native extension on the event loop would stall every gateway task. Endpoints: `GET/PUT /api/config/stt`, `POST /api/stt/install`. **Homebrew resolution never relies on `PATH`**: a GUI-launched gateway (desktop app / launchd) inherits `/usr/bin:/bin:/usr/sbin:/sbin`, which contains no Homebrew prefix, so `shutil.which("brew")` reported Homebrew MISSING on machines that have it — the prereq list told a Homebrew user to install Homebrew and the `mlx` install script aborted at its first `command -v brew` with `ERROR: Homebrew required`. The prereq probe goes through `transcribe.find_brew()` (PATH, then the fixed prefixes `/opt/homebrew/bin`, `/usr/local/bin`, Linuxbrew system + per-user), and every install script is prefixed with `_stt_install_path_prelude()` — it prepends `transcribe.BREW_PATH_DIRS` (the same prefixes plus `~/.local/bin`, where pipx puts `mlx_whisper`) and then `eval "$(brew shellenv)"`. The prelude is required because the script runs as `bash -c`, which is neither a login nor an interactive shell, so the user's `~/.zprofile` `brew shellenv` line never executes.
- **Agent Config tab**: JSON editor with Save + warning about `kirocrew setup --agent-only`
- **System** (`/system`) — live metrics (1s refresh): CPU %, memory used/total, network RX/TX stat cards; host info with correct Apple Silicon arch detection, load averages; memory, process, network, storage detail cards; uptime ticking every 1s via `useUptime` hook (client-side from `start_time`)
- **Agent Capabilities** (`/capabilities`; `/agents` redirects here) — merged Agents + Capabilities destination, bottom-pinned in the nav. `SidePanelLayout` tabs in order: **Agents** (agent → workspace → memory store bindings, `KiroCrewAgentsPage` embedded), **Agent Templates** (installed agent configurations/packages, `AgentsPage` embedded), **Integrations (MCP)**, **Skills**, **Hooks**, **Prompts**. The templates tab keeps the side-by-side layout: installed agents list (left) with detail panel (right, height from `LAYOUT.AGENT_LIST_HEIGHT`). Installed agents card shows each agent with name, `SourceBadge` source pill (neutral `package`/`kirocrew`/`project`/`local` sources — the former `aim` source was generalized to `package`), model, description, skill count, MCP server count. Click to view full agent config in detail panel: system prompt, tools, auto-approved tools, MCP servers with `--aim` token colors (hover tooltip showing tool list), expandable denied commands list (`<details>` with all patterns). **Agent-package install / uninstall now routes through the capability seam** (`installPlugin`/`uninstallPlugin` in `providers/adapters/acp.ts` dispatch `type === 'agent'` to `/api/capability/agents/{install,uninstall}`). This replaced the earlier "intentionally NOT offered" stance: the pure-AIM `/api/aim/agents/*` + `/api/aim/update` routes were removed, but leaving `GET /api/capability/agents` as list-only meant the seam could SHOW installed agent packages and never manage them — an asymmetry with skills/MCP that forced an edition to shadow the core or mount its own routes. Bulk "Update All" remains unoffered (no `update_*` op on the seam). Skills/MCP install/uninstall still route through the capability seam (see Capability Integration); the manager returns human-friendly errors for invalid packages. The MCP registry browser is hidden entirely when no external capability manager is configured (registry → 503); when present it offers click-to-expand descriptions with all detail lines, clickable URLs (DOMPurify-sanitized), a direct Install button, and tier badges at `text-[11px]` minimum. Striped subagent table with `EmptyState` when empty, kiro credit usage card, context window usage bars per session (agent name in `--aim` color).
Expand Down
47 changes: 45 additions & 2 deletions src/kiro_crew/cli_doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@
from kiro_crew.service import linux as service_linux
from kiro_crew.session_pid_sig import signing_health
from kiro_crew.subprocess_utf8 import UTF8_TEXT
from kiro_crew.transcribe import _find_parakeet_mlx, _find_whisper, ensure_ffmpeg_in_path
from kiro_crew.transcribe import (
_faster_whisper_model,
_find_parakeet_mlx,
_find_whisper,
ensure_ffmpeg_in_path,
)
from kiro_crew.validation import _AGENT_NAME_RE

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -2500,7 +2505,11 @@ def _doctor(platform_boot_error: "Exception | None" = None, bundle: bool = False
print("\nSpeech-to-Text")
stt_active = cfg.stt.enabled
needs_whisper = stt_active and cfg.stt.provider == "whisper"
needs_ffmpeg = stt_active # both providers use ffmpeg
# Every provider but ``faster`` shells out to something that needs the system
# ffmpeg; faster-whisper decodes in-process through PyAV's bundled copy, so
# reporting a missing ffmpeg as an ISSUE there would send the user to install a
# binary their configuration never calls.
needs_ffmpeg = stt_active and cfg.stt.provider != "faster"

if not stt_active:
print(" status: ⏹ disabled (enable from dashboard → Overview → Slack)")
Expand Down Expand Up @@ -2589,6 +2598,40 @@ def _doctor(platform_boot_error: "Exception | None" = None, bundle: bool = False
if stt_fatal:
issues.append("parakeet-mlx")

# faster-whisper (CTranslate2) is installed on demand, not as a declared extra,
# so an unavailable library is the expected first-run state rather than a broken
# install. Windows on ARM is called out separately because no CTranslate2 wheel
# exists there at all — the install button cannot fix it, and telling the user to
# retry would waste their time instead of naming a provider that does work.
if stt_active and cfg.stt.provider == "faster":
if _faster_whisper_model() is not None:
print(" faster: ✅ faster-whisper importable")
elif platform_compat.is_windows_on_arm():
# Deliberately NOT routed through ``stt_mark``/``stt_fatal``. That
# Windows downgrade exists because whisper and ffmpeg are absent from a
# stock Windows box yet trivially installable, so failing a first-run
# doctor over them is noise. This is the opposite case: ``faster`` is
# never the default, so reaching here means the user explicitly selected
# a provider that CANNOT be made to work on this machine. That is a real
# configuration fault, and the whole point of naming the alternatives is
# that the run should not exit 0 as if nothing were wrong.
print(" faster: ❌ not available (Windows on ARM — no CTranslate2 wheel)")
print(
" Alternatives: set stt.provider to 'whisper' "
"(local) or 'transcribe' (AWS)"
)
issues.append("faster-whisper: unavailable on Windows ARM")
else:
# The ordinary not-yet-installed state, which the install button DOES
# fix — so this one follows the platform convention like whisper above.
print(f" faster: {stt_mark} not installed")
print(
" Install from dashboard → Settings → "
"Speech-to-Text, or: pip install faster-whisper"
)
if stt_fatal:
issues.append("faster-whisper")

# ── Slack (optional) ──
print("\nSlack Integration")
if _has_slack:
Expand Down
48 changes: 45 additions & 3 deletions src/kiro_crew/config/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -4364,7 +4364,19 @@ def from_dict(cls, data: dict) -> ChannelConfig:
)


_VALID_STT_PROVIDERS = ("whisper", "mlx", "apple", "parakeet", "transcribe")
_VALID_STT_PROVIDERS = ("whisper", "mlx", "apple", "parakeet", "transcribe", "faster")

#: Whisper model sizes accepted for ``stt.model``.
#:
#: Shared by the ``whisper`` and ``faster`` providers, which both name models this
#: way. (``mlx`` uses ``stt.mlx_model`` and ``parakeet`` uses
#: ``stt.parakeet_model``, HuggingFace repo ids, instead.)
#:
#: ``turbo`` stays the default: it is the only entry the dashboard offered before,
#: and it is the best accuracy-per-second of the set. The smaller sizes exist
#: because they are the difference between usable and unusable on a machine
#: without much RAM, and ``large-v3`` because it is the accuracy ceiling.
_VALID_STT_MODELS = ("tiny", "base", "small", "medium", "large-v3", "turbo")
_VALID_CHANNEL_PREFIXES = ("C", "D", "G")


Expand All @@ -4376,6 +4388,32 @@ def _validated_stt_provider(value: str) -> str:
return "whisper"


def _validated_stt_model(value: object) -> str:
"""Return *value* as the STT model name, warning when it is off the menu.

Unknown STRINGS pass through with a warning rather than being coerced: the
old loader accepted any string, and openai-whisper legitimately takes names
outside the dashboard's size menu (``tiny.en``/``base.en``/``small.en``/
``medium.en``/``large-v2``), so coercing a hand-edited config to ``turbo``
would silently remove a real capability. Providers degrade safely on a bad
name anyway: the whisper CLI errors per-recording, and faster-whisper
resolves an unknown name to a download error, both logged, neither fatal.
Only a NON-STRING (numbers, null, nested json from a mangled edit) falls
back to ``turbo``, since it cannot be passed to any provider at all.
"""
if isinstance(value, str) and value:
if value not in _VALID_STT_MODELS:
logger.warning(
"STT model '%s' is not in the dashboard menu %s; passing it through"
" — the provider will reject it per-recording if it is invalid",
value,
list(_VALID_STT_MODELS),
)
return value
logger.warning("Non-string STT model %r, falling back to turbo", value)
return "turbo"


_VALID_COMPLETION_KEEP = ("head", "tail", "both")


Expand Down Expand Up @@ -4678,7 +4716,11 @@ class SttConfig:
)
model: str = field(
default="turbo",
metadata=_meta("Model", "Whisper model size.", enum=["turbo"]),
metadata=_meta(
"Model",
"Whisper model size (whisper and faster providers).",
enum=list(_VALID_STT_MODELS),
),
)
mlx_model: str = field(
default="mlx-community/whisper-large-v3-turbo",
Expand Down Expand Up @@ -7764,7 +7806,7 @@ def _load_resolved(cls) -> KiroCrewConfig:
whisper_path=stt_data.get("whisper_path", ""),
# Default "turbo" — faster and recommended for most users
# (809M vs 74M, but much better latency).
model=stt_data.get("model", "turbo"),
model=_validated_stt_model(stt_data.get("model", "turbo")),
mlx_model=stt_data.get("mlx_model", "mlx-community/whisper-large-v3-turbo"),
parakeet_model=stt_data.get("parakeet_model", "mlx-community/parakeet-tdt-0.6b-v3"),
device=stt_data.get("device", "cpu"),
Expand Down
Loading
Loading