Skip to content

Support :ai off to disconnect and :ai to reconnect the agent (#167) - #180

Merged
hellices merged 4 commits into
mainfrom
feat/ai-off
Aug 3, 2026
Merged

Support :ai off to disconnect and :ai to reconnect the agent (#167)#180
hellices merged 4 commits into
mainfrom
feat/ai-off

Conversation

@hellices

@hellices hellices commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Closes #167

Once configured, the agent runtime stayed AI on for the process lifetime: Ctrl+A only hides the chat panel, and actually turning the agent off required editing ~/.config/korvid/config.yaml and restarting.

What changed

:ai off disconnects the runtime for the current session:

  • The live provider is released (background aclose through the same close-task machinery the :ai rebuild uses), and the wiring's provider box is emptied so teardown and a later rebuild never touch the dead provider (_make_disconnect_agent in the composition root, handed to the app as a new disconnect_agent ctor param).
  • The status bar flips to AI off immediately; prompt submission is disabled; a dim agent off — run :ai to reconnect hint is appended — the conversation transcript is never erased.
  • Session-only: agent.enabled and all persisted settings (provider, model, profile, credentials) are untouched.
  • Refused while a turn runs with a clear notification (stopping mid-turn is Ctrl+X's job, not a state command's); idempotent when already off.

Bare :ai after :ai off opens the setup wizard with the kept settings; applying them rebuilds the runtime — status returns to AI on, the input re-enables and refocuses.

Ctrl+A remains a pure visibility toggle.

Tests

  • tests/ui/test_agent_off.py (7): disconnect + status flip + provider release, input disabled + hint, transcript preserved, idempotent when off, busy-turn refusal, reconnect restores AI on/input, Ctrl+A never touches runtime state.
  • tests/test_main_wiring.py::test_disconnect_agent_releases_the_provider: the closure empties the box and closes the provider in the background; idempotent.

Docs: docs/agent.md gains "Turning the agent off and on" distinguishing Ctrl+A (visibility) from :ai off (runtime state).

Full gate green: ruff, mypy --strict, 3098 passed, tach.

Ctrl+A only toggles panel visibility — there was no in-session way to
actually turn the runtime off short of editing config.yaml and
restarting.

- :ai off releases the live provider (background aclose via the same
  close-task machinery the :ai rebuild uses), empties the wiring's
  provider box so teardown/rebuild never touch the dead provider, sets
  the runtime to None (status bar flips to 'AI off'), disables prompt
  submission, and shows a reconnect hint — the transcript stays.
- Session-only: agent.enabled and persisted settings are untouched, so
  bare :ai reopens the wizard with the current settings and
  _apply_agent_settings reconnects (input re-enabled, 'AI on').
- Refused while a turn runs (never cancels midway); idempotent when
  already off.
- New KorvidApp ctor param disconnect_agent, wired from
  _build_agent_wiring's new _make_disconnect_agent closure.

Closes #167

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 3, 2026 18:40

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE — 이슈 #167 :ai off 세션 단위 연결 해제, 깔끔한 구현입니다.

확인한 사항:

  • _make_disconnect_agent: provider box를 먼저 비운 뒤 백그라운드 aclose — teardown 가드나 이후 rebuild가 죽은 provider를 만질 수 없고, 이미 off일 때 멱등. 기존 _close_provider_in_background/close_tasks 기계를 재사용해 reap 경로도 동일합니다.
  • _handle_agent_off의 busy-turn 거부(_agent_task done 검사)가 interrupt-and-submit 드레인과 안전하게 맞물립니다: _agent_replacement는 task가 살아있는(취소 중 포함) 동안에만 존재하고 그동안 :ai off는 거부되므로, off 이후 드레인 콜백이 죽은 runtime으로 _start_agent_turn을 호출해 패널을 영구 running 상태로 만드는 경로가 구조적으로 없습니다 (head에서 _drain_agent_replacement/action_interrupt_agent 직접 확인).
  • 영속 설정 무변경 · transcript 보존 · Ctrl+A 순수 가시성 토글 유지 모두 실질 테스트로 고정 (assert not runtime.calls, closed == [True] 등 비공허).
  • 재연결 경로: _apply_agent_settingsagent_input.disabled = False로 hint가 걸어둔 비활성화를 항상 해제하는 것 확인. 위저드를 열었다 취소하면 입력이 계속 비활성인데, 여전히 off 상태이므로 올바른 동작입니다.

Suggestions (advisory):

  • _handle_agent_off의 멱등 검사 self._agent_runtime is None은 "한 번도 설정 안 된" 상태와 "off한" 상태를 구분하지 않아, 미설정 상태에서 :ai off를 치면 setup 안내 대신 "Agent is already off"가 나옵니다. _agent_configurator/설정 존재 여부로 분기해 :ai 안내를 주면 약간 더 친절합니다.
  • show_setup_hintshow_reconnect_hint가 같은 disable+hint 패턴을 반복하므로, 셋째 상태가 생기면 공통 helper로 뽑을 만합니다.

APPROVE

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds session-only agent disconnection through :ai off, while preserving configuration for reconnection.

Changes:

  • Adds runtime disconnect handling and provider cleanup.
  • Updates agent panel status, input state, and reconnect messaging.
  • Adds documentation and UI/wiring tests.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/korvid/__main__.py Wires provider disconnection and cleanup.
src/korvid/ui/app.py Handles :ai off and runtime state.
src/korvid/ui/widgets/agent_panel.py Adds the disconnected-state hint.
tests/test_main_wiring.py Tests provider release wiring.
tests/ui/test_agent_off.py Tests disconnect and reconnect behavior.
docs/agent.md Documents runtime and visibility controls.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/korvid/ui/app.py
Comment thread src/korvid/ui/app.py
…erves transcript after :ai off

- AgentSetupScreen takes current_settings: the provider option is
  pre-highlighted, the endpoint/API-key-env inputs and model default
  start from the kept values when the same provider is chosen —
  reconnecting is confirm-through, not re-entry.
- action_toggle_agent distinguishes disconnected-from-configured
  (_agent_disconnected, set by :ai off, cleared on reconnect) from
  never-configured: reopening the panel shows the reconnect hint and
  keeps the conversation instead of the setup wipe. The hint is
  idempotent across repeated toggles.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Suppressed comments (2)

src/korvid/ui/widgets/agent_setup_screen.py:133

  • The retained Azure authentication method is not restored. Even when current_settings.auth_method == "entra", selecting the pre-highlighted Azure provider resets the auth list to index 0 (api_key), so confirming through changes the auth flow and asks for an API-key variable instead of reconnecting with Entra. Pre-highlight the auth choice from the matching current settings when the Azure auth step opens, and cover the Azure/Entra reconnect path.
        if self._current_settings is not None:
            providers = list(_DEFAULTS)
            if self._current_settings.provider in providers:
                provider_list.highlighted = providers.index(self._current_settings.provider)

src/korvid/ui/widgets/agent_setup_screen.py:133

  • This exact membership check loses valid configured providers supported by providers/registry.py:23-32 and documented in docs/agent.md:130-152 (for example github, anthropic, vllm, openai, or differently cased names). Those runtimes can be active before :ai off, but bare :ai then leaves the wizard on the first GitHub Copilot option; the later exact provider checks also fail to prefill endpoint/model/credentials. Normalize registry aliases to the wizard's canonical provider before highlighting and comparing retained settings.

This issue also appears on line 130 of the same file.

        if self._current_settings is not None:
            providers = list(_DEFAULTS)
            if self._current_settings.provider in providers:
                provider_list.highlighted = providers.index(self._current_settings.provider)

@hellices
hellices requested a review from Copilot August 3, 2026 19:27
… alias normalization

- The Azure auth step pre-highlights the retained method: a
  confirm-through reconnect keeps Entra instead of silently switching
  to api_key.
- Registry provider aliases (openai, vllm, github, anthropic, claude)
  normalize onto the wizard's openai-compat entry, so settings
  configured under an alias still pre-highlight the provider and
  prefill endpoint/key-env/model.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/korvid/ui/widgets/agent_setup_screen.py:196

  • Auth retention is limited to Azure/Entra. OpenAI-compatible providers validly support auth_method="none" (for example, a local vLLM endpoint; see providers/registry.py:125-140), but confirming the preselected provider resets it to api_key, prompts for a nonexistent key env, and prevents reconnecting. Preserve the retained auth method whenever the canonical provider matches.
                if (
                    self._current_canonical == "azure"
                    and current is not None
                    and current.auth_method == "entra"
                ):

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재리뷰 (신규 커밋 2건) — 이전 리뷰의 Suggestion 두 건을 정확히 반영했습니다.

  1. _agent_disconnected 플래그로 never-configured와 :ai off 상태를 분리: action_toggle_agent가 off 상태에서 reconnect hint를 보여 setup wipe로 대화가 지워지던 경로가 사라졌고, show_reconnect_hint가 마지막 ChatEntry의 raw 비교로 멱등이라 토글 반복 시 힌트가 쌓이지 않습니다. _apply_agent_settings에서 플래그 해제도 확인. Ctrl+A 토글 왕복 + 힌트 1회 카운트 테스트가 실질적입니다.

  2. 위저드 prefill: current_settings 주입으로 provider 사전 하이라이트, 같은 provider일 때 endpoint/api_key_env/model이 기본값 대신 보존값에서 시작 — confirm-through reconnect가 됩니다. Azure Entra 사전 하이라이트로 조용한 api_key 전환도 차단, registry alias(openai/vllm/github/anthropic/claude)→openai-compat 정규화도 테스트로 고정.

Suggestions (advisory):

  • _OPENAI_COMPAT_ALIASES는 providers/registry.py의 alias 목록과 수동 동기화됩니다. registry에 alias가 추가되면 여기서 조용히 빠져 prefill이 무시됩니다. registry에서 alias 집합을 import하거나 drift 계약 테스트를 권장.
  • Azure auth 사전 하이라이트의 auth_list.highlighted = 1은 옵션 순서(api_key=0, entra=1) 하드코딩입니다. 테스트가 prompt 문자열로 고정하고 있어 당장은 안전하지만, 옵션 목록에서 index를 조회하는 편이 견고합니다.

APPROVE

… matching provider

A no-auth OpenAI-compatible endpoint (local vLLM) was reset to api_key
on confirm-through and prompted for a nonexistent key env. The retained
auth method now survives whenever the canonical provider matches.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재리뷰 (신규 커밋 1건) — confirm-through 재연결 시 유지된 auth_method 보존 수정 확인.

  • 로컬 vLLM 같은 no-auth OpenAI 호환 엔드포인트가 provider 기본값 api_key로 리셋되어 존재하지 않는 key env를 묻던 문제를, 기존 base_url/model prefill과 동일한 가드 패턴(current is not None and self._current_canonical == self._provider and current.auth_method)으로 해결 — canonical 일치 조건 덕분에 다른 provider로 갈아탈 때 이전 auth가 새 provider에 새는 일이 없습니다.
  • azure 분기는 기존 entra pre-highlight 경로가 별도로 유지되어 이번 가드와 충돌 없음.
  • 테스트 test_reconnect_preserves_a_no_auth_method_auth_method == "none"과 key-env 입력 미표시를 모두 단언해 비공허합니다.

지적사항 없음. APPROVE

@hellices
hellices merged commit b45bd07 into main Aug 3, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support :ai off to disconnect and :ai to reconnect the agent

2 participants